Claude-skill-registry 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.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/asyncredux-persistence" ~/.claude/skills/majiayu000-claude-skill-registry-asyncredux-persistence && rm -rf "$T"
skills/data/asyncredux-persistence/SKILL.mdOverview
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:
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:
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:
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.
// 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:
store.dispatch(PersistAction());
Pausing and Resuming Persistence
Control persistence with these store methods:
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:
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:
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:
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.
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-waiting