redurx_persist 0.9.0

redurx_persist pub package

Persist Redux state across app restarts in Flutter, Web, or custom storage engines.


  • Save and load from multiple engine (Flutter, Web, File, custom)
  • Fully type safe
  • Transform state and raw on load/save
  • Custom serializers
  • Easy to use, integrate into your codebase in a few minutes!

Storage Engines:

Usage #

See Flutter example for a full overview.

The library has a Middleware class that saves on every action.

State Setup #

We will be using the JsonSerializer, 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}) =>
      AppState(counter: counter ?? this.counter);

  // !!!
  static AppState fromJson(dynamic json) => AppState(counter: json["counter"]);

  // !!!
  dynamic 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:

void main() async {
  // Create Persistor
  final persistor = Persistor<AppState>(
    storage: FlutterStorage(),
    serializer: JsonSerializer<AppState>(AppState.fromJson),

  // Load initial state
  final initialState = await persistor.load();

  // Create Store with Persistor middleware
  final store = Store<AppState>(initialState ?? AppState(counter: 0))

  // ...

(JsonSerializer takes a single param which turns the JSON into your AppState. Your state will automatically be saved using the middleware)

Storage Engines #

You can use different storage engines for different application types:

  • Flutter

  • Web

  • FileStorage:

    final persistor = Persistor<AppState>(
      // ...
      storage: FileStorage(File("path/to/state")),
  • Build your own custom storage engine:

    To create a custom engine, you will need to implement the following interface to save/load a string to disk:

    abstract class StorageEngine {
      external Future<void> save(Uint8List data);
      external Future<Uint8List> load();

Serializers #

You can use one of the built in serializers such as:

  • JsonSerializer

  • StringSerializer

  • RawSerializer

  • Build your own serializer:

    To create a custom engine, you will need to implement the following interface to encode/decode state to/from Uint8List:

    abstract class StateSerializer<T> {
      external Uint8List encode(T state);
      external T decode(Uint8List data);


    class IntSerializer implements StateSerializer<int> {
      /// Takes [data] and converts it to a [int]
      int decode(Uint8List data) {
        return ByteData.view(data.buffer).getInt64(0);
      /// Takes [state] and converts it to a [Uint8List]
      Uint8List encode(int state) {
        final data = Uint8List(8);
        ByteData.view(data.buffer).setInt64(0, state);
        return data;

Whitelist/Blacklist #

To only save parts of your state, simply omit the fields that you wish to not save from your serializer.

If using the JsonSerializer, you can omit them from you toJson and decoder.

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,});

  // ...

  static AppState fromJson(dynamic json) =>
      AppState(name: json["name"]); // Don't load counter, will use default of 0

  Map toJson() => {'name': name}; // Don't save counter

Transforms #

Transformations are a way to transform ("edit") your state when loading/saving. They are 2 types:

All transformers are ran in order, from first to last.

Make sure all transformation are pure. Do not mutate the original state passed.

State #

State transformations transform your state before it's written to disk (on save) or loaded from disk (on load).

final persistor = Persistor<AppState>(
  // ...
  transforms: Transforms(
    onSave: [
      // Example: set counter to 3 when writing to disk
      (state) => state.copyWith(counter: 3),
    onLoad: [
      // Example: set counter to 0 when loading from disk
      (state) => state.copyWith(counter: 0),

Raw #

Raw transformation are applied to the raw byte (Uint8List) before it's saved/loaded.

final persistor = Persistor<AppState>(
  // ...
  rawTransforms: RawTransforms(
    onSave: [
      // Example: encrypt raw data
      (data) => encrypt(data),
    onLoad: [
      // Example: decrypt raw data
      (data) => decrypt(data),

Debug #

Persistor has a debug option, which will eventually log more debug information.

Use it like so:

final persistor = Persistor<AppState>(
  // ...
  debug: true

Throttle duration #

Persistor has a throttleDuration option, which will throttle saving to disk to prevent excessive writing. You should keep this at a low value to prevent data loss (few seconds is recommended).

Use it like so:

final persistor = Persistor<AppState>(
  // ...
  throttleDuration: Duration(seconds: 2),

Features and bugs #

Please file feature requests and bugs at the issue tracker.

0.9.0 - 2019-02-05

  • Initial release redurx perisist.

0.8.0 - 2018-11-27

  • Release v0.8.0. Last v0.x release line.

0.8.0-rc.1 - 2018-11-06

  • Fix serializers failing on null state.

0.8.0-rc.0 - 2018-11-06

0.7.0 - 2018-08-10

0.7.0-rc.2 - 2018-04-09

  • Fix middleware typing.

0.7.0-rc.1 - 2018-03-31

  • Breaking: Dart 2.
  • Breaking: Upgraded redux to v3.0.0.
  • Breaking: Changed loading flow:
    • persistor.start has been deprecated (will be removed in next major version). Use persistor.load
    • Loading dispatched PersistLoadAction, then PersistLoadedAction once completed.
  • Breaking: Changed action names:
    • LoadAction: PersistLoadingAction
    • LoadedAction: PersistLoadedAction
    • PersistorErrorAction: PersistErrorAction
  • Added saving actions (PersistSavingAction and PersistSavedAction)
  • Added MemoryStorage.
  • Added loadFromStorage and saveToStorage for more manual control.
  • Refactor, new tests.

0.6.0 - 2018-03-18

  • Breaking: Change saved state format. This will break your saved state, will only happen once.
  • Added migrations and version key.
  • Fix JSON deprecation warning.
  • Added tests.
  • Added exceptions.
  • Added FileStorage.

0.5.2 - 2018-03-14

  • Fix library export.

0.5.1 - 2018-03-13

  • Made persistor.start return a Future.
  • Add type to persistor.loadStream.

0.5.0 - 2018-03-12

  • Breaking: Change persistor.load(store) to persistor.start(store) for initial loading.
  • Breaking: Change LoadAction<T> to LoadedAction<T>.
  • Add LoadAction (action dispatch to start loading).
  • Add PersistorErrorAction (action dispatched on save/load error).
  • Add debug persistor option, doesn't do anything yet.

0.4.0 - 2018-03-10

  • Breaking: Decouple Flutter and Web into different packages.

0.3.0 - 2018-03-10

  • Add state and raw transformers.
  • Added better error handling (persistor.errorStream)

0.2.0 - 2018-03-10

  • Move Flutter-specific code to separate, unexported file. It is likely this will become it's own package.
  • Add SaveLocation for Flutter storage engine.
  • Add SharedPreference sub-engine for Flutter storage engine (make default).
  • Fix PersistorGatepassing variables to state and initialization.
  • Add more docs.

0.1.0 - 2018-03-09

  • Create generic StorageEngine.
  • Create FlutterStorage.

0.0.3 - 2018-03-09

  • Added documentation.

0.0.2 - 2018-03-09

  • Added PersistorGate.

0.0.1 - 2018-03-09

  • Initial release.


import 'dart:io';

import 'package:redurx/redurx.dart';
import 'package:redurx_persist/redurx_persist.dart';

void main() async {
  final persistor = Persistor<State>(
    storage: FileStorage(File("state.json")),
    serializer: JsonSerializer<State>(State.fromJson),

  // Load initial state
  final initialState = await persistor.load();

  final store = Store<State>(initialState ?? State(counter: 0));

  // ...

class State {
  final int counter;

  State({this.counter = 0});

  State copyWith({int counter}) => State(counter: counter ?? this.counter);

  static State fromJson(dynamic json) => State(counter: json["counter"] as int);

  dynamic toJson() => {'counter': counter};

class IncrementCounter extends Action<State> {
  State reduce(State state) => state.copyWith(counter: state.counter + 1);

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:

  redurx_persist: ^0.9.0

2. Install it

You can install packages from the command line:

with pub:

$ pub get

with Flutter:

$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:

import 'package:redurx_persist/redurx_persist.dart';
Describes how popular the package is relative to other packages. [more]
Code health derived from static analysis. [more]
Reflects how tidy and up-to-date the package is. [more]
Weighted score of the above. [more]
Learn more about scoring.

We analyzed this package on Feb 13, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.5

Health issues and suggestions

Fix lib/src/persistor.dart. (-90.09 points)

Analysis of lib/src/persistor.dart failed with 8 errors, 2 hints, including:

line 176 col 5: 'PersistorMiddleware.beforeAction' ('T Function(ActionType, T)') isn't a valid override of 'Middleware.beforeAction' ('T Function(Store

line 177 col 30: 3 positional argument(s) expected, but 2 found.

line 177 col 31: The argument type 'ActionType' can't be assigned to the parameter type 'Store

line 177 col 39: The argument type 'T' can't be assigned to the parameter type 'ActionType'.

line 181 col 5: 'PersistorMiddleware.afterAction' ('T Function(ActionType, T)') isn't a valid override of 'Middleware.afterAction' ('T Function(Store

Format lib/redurx_persist.dart.

Run dartfmt to format lib/redurx_persist.dart.

Maintenance issues and suggestions

Support latest dependencies. (-10 points)

The version constraint in pubspec.yaml does not support the latest published versions for 1 dependency (synchronized).

Package is getting outdated. (-1.64 points)

The package was last published 53 weeks ago.


Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0-dev <3.0.0
meta ^1.1.0 1.1.8
redurx ^1.0.0 1.1.1
synchronized ^1.5.0 1.5.3+2 2.2.0
Transitive dependencies
rxdart 0.20.0 0.23.1
Dev dependencies
test ^1.0.0