redux_persist 0.6.0

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 95

redux_persist pub package

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

Features:

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

Storage Engines:

Usage

See Flutter example for a full overview.

The library creates a middleware that saves on every action. It also loads on initial load and sets a LoadedAction 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
var persistor = new Persistor<AppState>(
  storage: new FlutterStorage("my-app"), // Or use other engines
  decoder: AppState.fromJson,
);

// Create Store with Persistor middleware
var store = new Store<AppState>(
  reducer,
  initialState: new AppState(),
  middleware: [persistor.createMiddleware()],
);

// Load state to store
persistor.start(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 LoadedAction action (with the generic type), like so:

class IncrementCounterAction {}

AppState reducer(state, action) {
  // !!!
  if (action is LoadedAction<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;
  }
}

Optional Actions

You can also use the LoadAction or PersistorErrorAction to follow the lifecycle of the persistor.

  • LoadAction is dispatched when the store is being loaded
  • PersistorErrorAction is dispatched when an error occurs on loading/saving

Storage Engines

You can use different storage engines for different application types:

  • Flutter

  • Web

  • FileStorage

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

    To create a custom engine, 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
}

Migrations

As your state grows, you will need new state versions. You can version you state and apply migrations between state versions.

Versions are integers, starting at 0. You may use any integer as long as it is higher than the last version number.

Migrations are pure functions taking in a dynamic state, and returning a transformed state (do not modify the original state passed).

final persistor = new Persistor<State>(
  // ...
  version: 1,
  migrations: {
    // Renamed fields from "oldCounter" to "counter"
    0: (dynamic state) => {"counter": state["oldCounter"]},
    // "counter" is now a string
    1: (dynamic state) => {"counter": state["counter"].toString()                                                                       }
  },
);

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

Debug

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

Use it like so:

persistor = new Persistor<AppState>(
  // ...
  debug: true
);

Features and bugs

Please file feature requests and bugs at the issue tracker.

[0.6.0] - 2018-03-18

Breaking changes:

  • 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 changes:

  • Change persistor.load(store) to persistor.start(store) for initial loading.
  • 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

  • Decouple Flutter and Web into different packages.

[0.3.0] - 2018-03-10

  • Add state and raw transformers.

[0.2.0] - 2018-03-10

  • Move Flutter-specific code to separate, unexported file. It is likely this will become it's own pacakge.
  • Add SaveLocation for Flutter storage engine.
  • Add SharedPreference sub-engine for Flutter storage engine (make default).
  • Fix PersistorGate passing 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 Persistor Gate.

[0.0.1] - 2018-03-09

  • Initial release.

example/redux_persist_example.dart

import 'package:redux/redux.dart';
import 'package:redux_persist/redux_persist.dart';

void main() async {
  final persistor = new Persistor<State>(
    storage: new FileStorage("state.json"),
    decoder: State.fromJson,
  );

  final store = new Store<State>(
    reducer,
    initialState: new State(),
    middleware: [persistor.createMiddleware()],
  );

  // Load initial state
  await persistor.start(store);

  // ...
}

class State {
  final int counter;

  State({this.counter = 0});

  State copyWith({int counter}) {
    return new State(counter: counter ?? this.counter);
  }

  static State fromJson(dynamic json) {
    return new State(counter: json["counter"] as int);
  }

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

class IncrementCounterAction {}

State reducer(State state, Object action) {
  if (action is LoadedAction<State>) {
    // Load to state
    return action.state ?? state;
  } else if (action is IncrementCounterAction) {
    // Increment
    return state.copyWith(counter: state.counter + 1);
  }

  return state;
}

Use this package as a library

1. Depend on it

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


dependencies:
  redux_persist: "^0.6.0"

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter packages get

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

3. Import it

Now in your Dart code, you can use:


      import 'package:redux_persist/redux_persist.dart';
  
Version Uploaded Documentation Archive
0.6.0 Mar 19, 2018 Go to the documentation of redux_persist 0.6.0 Download redux_persist 0.6.0 archive
0.5.2 Mar 14, 2018 Go to the documentation of redux_persist 0.5.2 Download redux_persist 0.5.2 archive
0.5.1 Mar 13, 2018 Go to the documentation of redux_persist 0.5.1 Download redux_persist 0.5.1 archive
0.5.0 Mar 12, 2018 Go to the documentation of redux_persist 0.5.0 Download redux_persist 0.5.0 archive
0.4.0 Mar 10, 2018 Go to the documentation of redux_persist 0.4.0 Download redux_persist 0.4.0 archive
0.3.0 Mar 10, 2018 Go to the documentation of redux_persist 0.3.0 Download redux_persist 0.3.0 archive
0.2.0 Mar 10, 2018 Go to the documentation of redux_persist 0.2.0 Download redux_persist 0.2.0 archive
0.1.0 Mar 9, 2018 Go to the documentation of redux_persist 0.1.0 Download redux_persist 0.1.0 archive
0.0.3 Mar 9, 2018 Go to the documentation of redux_persist 0.0.3 Download redux_persist 0.0.3 archive
0.0.2 Mar 9, 2018 Go to the documentation of redux_persist 0.0.2 Download redux_persist 0.0.2 archive

All 13 versions...

Analysis

We analyzed this package on May 22, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0-dev.54.0
  • pana: 0.11.1

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
89 / 100
Health:
Code health derived from static analysis. [more]
100 / 100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100 / 100
Overall score:
Weighted score of the above. [more]
95
Learn more about scoring.

Platforms

Detected platforms: Flutter, other

Primary library: package:redux_persist/redux_persist.dart with components: io.

Suggestions

  • The description is too short.

    Add more detail about the package, what it does and what is its target use case. Try to write at least 60 characters.

  • Package is pre-v1 release.

    While there is nothing inherently wrong with versions of 0.*.*, it usually means that the author is still experimenting with the general direction API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.24.0 <2.0.0
redux ^2.1.1 2.1.1 3.0.0
Dev dependencies
test ^0.12.32+1