redux_persist 0.3.0 redux_persist: ^0.3.0 copied to clipboard
Persist Redux State
redux_persist #
Persist Redux state across app restarts in Flutter or custom storage engines.
Features:
- Save and load from multiple engine (Flutter, custom, etc)
- Fully type safe
- Transform state and raw on load/save
- Flutter integration (
PersistorGate
) - Easy to use, integrate into your codebase in a few minutes!
Usage #
See example for a full overview.
The library creates a middleware that saves on every action.
It also loads on initial load and sets a LoadAction
to your store.
State Setup #
You will need to use a state class, with a required toJson
method, as such:
class AppState {
final int counter;
AppState({this.counter = 0});
AppState copyWith({int counter}) {
return new AppState(counter: counter ?? this.counter);
}
// !!!
static AppState fromJson(dynamic json) {
return new AppState(counter: json["counter"]);
}
// !!!
Map toJson() => {
'counter': counter
};
}
(the copyWith
method is optional, but a great helper.
The fromJson
is required as decoder, but can be renamed)
Persistor #
Next, create your persistor, storage engine,
and store, then load the last state in.
This will usually be in your main
or in your root widget:
// Create Persistor
persistor = new Persistor<AppState>(
storage: new FlutterStorage("my-app"), // Or use other engines
decoder: AppState.fromJson,
);
// Create Store with Persistor middleware
store = new Store<AppState>(
reducer,
initialState: new AppState(),
middleware: [persistor.createMiddleware()],
);
// Load state to store
persistor.load(store);
(the key
param is used as a key of the save file name.
The decoder
param takes in a dynamic
type and outputs
an instance of your state class, see the above example)
Load #
In your reducer, you must add a check for the
LoadAction
action (with the generic type), like so:
class IncrementCounterAction {}
AppState reducer(state, action) {
// !!!
if (action is LoadAction<AppState>) {
return action.state ?? state; // Use existing state if null
}
// !!!
switch (action.runtimeType) {
case IncrementCounterAction:
return state.copyWith(counter: state.counter + 1);
default:
// No change
return state;
}
}
PersistorGate
(Flutter only) #
If you want to wait until rendering you app until the state is loaded,
use the PersistorGate
:
@override
Widget build(BuildContext context) {
return new PersistorGate(
persistor: persistor,
child: MyApp(),
);
}
If you want to display a loading/slash screen while loading,
pass a widget to render to the loading
param of PersistorGate
:
new PersistorGate(
persistor: persistor,
loading: SlashScreen(), // !!!
child: MyApp(),
);
Storage Engines #
FlutterStorage
#
Storage engine to use with Flutter. Defaults to saving to
shared_preferences
(recommended).
To save to a file in your
application document directory,
simply change the location
:
new FlutterStorage("my-app", location: FlutterSaveLocation.documentFile)
You can also directly use
SharedPreferenceEngine
andDocumentFileEngine
, but using theFlutterStorage
is recommended for simplicity.
Custom Storage Engines #
If you are not using redux_persist
with Flutter,
you can pass a custom StorageEngine
to the storage
param of the Persistor.
You will need to implement the following interface to save/load a JSON string to disk:
abstract class StorageEngine {
external Future<void> save(String json);
external Future<String> load();
}
Whitelist/Blacklist #
To only save parts of your state,
simply omit the fields that you wish to not save
from your toJson
and decoder (usually fromJson
) methods.
For instance, if we have a state with counter
and name
,
but we don't want counter
to be saved, you would do:
class AppState {
final int counter;
final String name;
AppState({this.counter = 0, this.name});
// ...
static AppState fromJson(dynamic json) {
return new AppState(name: json["name"]); // Don't load counter, will use default of 0
}
Map toJson() => {'name': name}; // Don't save counter
}
Transforms #
All transformers are ran in order, from first to last.
Make sure all transformation are pure. Do not modify the original state passed.
State #
State transformations transform your state before it's written to disk (on save) or loaded from disk (on load).
persistor = new Persistor<AppState>(
// ...
transforms: new Transforms(
onSave: [
// Set counter to 3 when writing to disk
(state) => state.copyWith(counter: 3),
],
onLoad: [
// Set counter to 0 when loading from disk
(state) => state.copyWith(counter: 0),
],
),
);
Raw #
Raw transformation are applied to the raw text (JSON) before it's written to disk (on save) or loaded from disk (on load).
persistor = new Persistor<AppState>(
// ...
rawTransforms: new RawTransforms(
onSave: [
// Encrypt raw json
(json) => encrypt(json),
],
onLoad: [
// Decrypt raw json
(json) => decrypt(json),
],
)
);
Features and bugs #
Please file feature requests and bugs at the issue tracker.