asyncredux-persistence
Implement local state persistence using Persistor. Covers creating a custom Persistor class, implementing `readState()`, `persistDifference()`, `deleteState()`, using LocalPersist helper, throttling saves, and pausing/resuming persistence with app lifecycle.
Best use case
asyncredux-persistence is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Implement local state persistence using Persistor. Covers creating a custom Persistor class, implementing `readState()`, `persistDifference()`, `deleteState()`, using LocalPersist helper, throttling saves, and pausing/resuming persistence with app lifecycle.
Teams using asyncredux-persistence should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/asyncredux-persistence/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How asyncredux-persistence Compares
| Feature / Agent | asyncredux-persistence | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Implement local state persistence using Persistor. Covers creating a custom Persistor class, implementing `readState()`, `persistDifference()`, `deleteState()`, using LocalPersist helper, throttling saves, and pausing/resuming persistence with app lifecycle.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
## Overview
AsyncRedux provides state persistence by passing a `persistor` object to the Store. This maintains app state on disk, enabling restoration between sessions.
## Store Initialization with Persistor
At startup, read any existing state from disk, create default state if none exists, then initialize the store:
```dart
var persistor = MyPersistor();
var initialState = await persistor.readState();
if (initialState == null) {
initialState = AppState.initialState();
await persistor.saveInitialState(initialState);
}
var store = Store<AppState>(
initialState: initialState,
persistor: persistor,
);
```
## The Persistor Abstract Class
The `Persistor<St>` base class defines these methods:
```dart
abstract class Persistor<St> {
/// Read persisted state, or return null if none exists
Future<St?> readState();
/// Delete state from disk
Future<void> deleteState();
/// Save state changes. Provides both newState and lastPersistedState
/// so you can compare them and save only the difference.
Future<void> persistDifference({
required St? lastPersistedState,
required St newState
});
/// Convenience method for initial saves
Future<void> saveInitialState(St state) =>
persistDifference(lastPersistedState: null, newState: state);
/// Controls save frequency. Return null to disable throttling.
Duration get throttle => const Duration(seconds: 2);
}
```
## Creating a Custom Persistor
Extend the abstract class and implement the required methods:
```dart
class MyPersistor extends Persistor<AppState> {
@override
Future<AppState?> readState() async {
// Read state from disk (e.g., from SharedPreferences, file, etc.)
return null;
}
@override
Future<void> deleteState() async {
// Delete state from disk
}
@override
Future<void> persistDifference({
required AppState? lastPersistedState,
required AppState newState,
}) async {
// Save state to disk.
// You can compare lastPersistedState with newState to save only changes.
}
@override
Duration get throttle => const Duration(seconds: 2);
}
```
## Throttling
The `throttle` getter controls how often state is saved. All changes within the throttle window are collected and saved in a single call. The default is 2 seconds.
```dart
// Save at most every 5 seconds
@override
Duration get throttle => const Duration(seconds: 5);
// Disable throttling (save immediately on every change)
@override
Duration? get throttle => null;
```
## Forcing Immediate Save
Dispatch `PersistAction()` to save immediately, bypassing the throttle:
```dart
store.dispatch(PersistAction());
```
## Pausing and Resuming Persistence
Control persistence with these store methods:
```dart
store.pausePersistor(); // Pause saving
store.persistAndPausePersistor(); // Save current state, then pause
store.resumePersistor(); // Resume saving
```
## App Lifecycle Integration
Pause persistence when the app goes to background and resume when it becomes active. Create an `AppLifecycleManager` widget:
```dart
class AppLifecycleManager extends StatefulWidget {
final Widget child;
const AppLifecycleManager({
Key? key,
required this.child,
}) : super(key: key);
@override
_AppLifecycleManagerState createState() => _AppLifecycleManagerState();
}
class _AppLifecycleManagerState extends State<AppLifecycleManager>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState lifecycle) {
store.dispatch(ProcessLifecycleChange_Action(lifecycle));
}
@override
Widget build(BuildContext context) => widget.child;
}
```
Create an action to handle lifecycle changes:
```dart
class ProcessLifecycleChange_Action extends ReduxAction<AppState> {
final AppLifecycleState lifecycle;
ProcessLifecycleChange_Action(this.lifecycle);
@override
Future<AppState?> reduce() async {
if (lifecycle == AppLifecycleState.resumed ||
lifecycle == AppLifecycleState.inactive) {
store.resumePersistor();
} else if (lifecycle == AppLifecycleState.paused ||
lifecycle == AppLifecycleState.detached) {
store.persistAndPausePersistor();
} else {
throw AssertionError(lifecycle);
}
return null;
}
}
```
Wrap your app with the lifecycle manager:
```dart
StoreProvider<AppState>(
store: store,
child: AppLifecycleManager(
child: MaterialApp( ... ),
),
)
```
## LocalPersist Helper
The `LocalPersist` class simplifies disk operations for Android/iOS. It works with simple object structures containing only primitives, lists, and maps.
```dart
import 'package:async_redux/local_persist.dart';
// Create instance with a file name
var persist = LocalPersist("myFile");
// Save data
List<Object> simpleObjs = [
'Hello',
42,
true,
[100, 200, {"name": "John"}],
];
await persist.save(simpleObjs);
// Load data
List<Object> loaded = await persist.load();
// Append data
List<Object> moreObjs = ['more', 'data'];
await persist.save(moreObjs, append: true);
// File operations
int length = await persist.length();
bool exists = await persist.exists();
await persist.delete();
// JSON operations for single objects
await persist.saveJson(simpleObj);
Object? simpleObj = await persist.loadJson();
```
**Note:** `LocalPersist` only supports simple objects. For complex nested structures or custom classes, you need to implement serialization yourself (e.g., using JSON encoding with `toJson`/`fromJson` methods).
## References
URLs from the documentation:
- https://asyncredux.com/sitemap.xml
- https://asyncredux.com/flutter/miscellaneous/persistence
- https://asyncredux.com/flutter/basics/store
- https://asyncredux.com/flutter/miscellaneous/database-and-cloud
- https://asyncredux.com/flutter/intro
- https://asyncredux.com/flutter/testing/mocking
- https://asyncredux.com/flutter/advanced-actions/redux-action
- https://asyncredux.com/flutter/about
- https://asyncredux.com/flutter/testing/store-tester
- https://asyncredux.com/flutter/miscellaneous/advanced-waitingRelated Skills
asyncredux-wait-fail-succeed
Show loading states and handle action failures in widgets. Covers `isWaiting(ActionType)` for spinners, `isFailed(ActionType)` for error states, `exceptionFor(ActionType)` for error messages, and `clearExceptionFor()` to reset failure states.
asyncredux-wait-condition
Use `waitCondition()` inside actions to pause execution until state meets criteria. Covers waiting for price thresholds, coordinating between actions, and implementing conditional workflows.
asyncredux-user-exceptions
Handle user-facing errors with UserException. Covers throwing UserException from actions, setting up UserExceptionDialog, customizing error dialogs with `onShowUserExceptionDialog`, and using UserExceptionAction for non-interrupting error display.
asyncredux-sync-actions
Creates AsyncRedux (Flutter) synchronous actions that update state immediately by implementing reduce() to return a new state.
asyncredux-streams-timers
Manage Streams and Timers with AsyncRedux. Covers creating actions to start/stop streams, storing stream subscriptions in store props, dispatching actions from stream callbacks, and proper cleanup with disposeProps().
asyncredux-state-access
Access store state in widgets using `context.state`, `context.select()`, and `context.read()`. Covers when to use each method, setting up BuildContext extensions, and optimizing widget rebuilds with selective state access.
asyncredux-setup
Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
asyncredux-selectors
Create and cache selectors for efficient state access. Covers writing selector functions, caching with `cache1` and `cache2`, the reselect pattern, and avoiding repeated computations in widgets.
asyncredux-optimistic-update-mixin
Add the OptimisticUpdate mixin for instant UI feedback before server confirmation. Covers immediate state changes, automatic rollback on failure, and optionally notifying users of rollback.
asyncredux-navigation
Handle navigation through actions using NavigateAction. Covers setting up the navigator key, dispatching NavigateAction for push/pop/replace, and testing navigation in isolation.
asyncredux-events
Use the Event class to interact with Flutter's stateful widgets (TextField, ListView, etc.). Covers creating Event objects in state, consuming events with `context.event()`, scrolling lists, changing text fields, and the event lifecycle.
asyncredux-dependency-injection
Inject dependencies into actions using the environment pattern. Covers creating an Environment class, passing it to the Store, accessing `env` from actions, and using dependency injection for testability.