StoreKeeper

StoreKeeper is an easy and flexible state management system for Flutter apps. The API is structured in a way that it will not add a lot of boiler plate code regardless of the project size. StoreKeeper is based on the InheritedModel widget from Flutter and it was initially developed as the backend of Kite.

It is an action oriented system. Which means it applies changes to the UI when an action is performed. On the other hand libraries like Redux triggers the render when there is modification in the data.

Store

This is where all the data of your app stored. You can have only a single store in the app. If you want to divide the store, you can create more models and add their instances to this class.

class AppStore extends Store {
  int count = 0;
}

Mutations

This is where the app's logic is written.

// Write it as a class
class Increment extends Mutation<AppStore> {
  exec() => store.count++;
}

// or write it as a function
void increment() {
  (StoreKeeper.store as AppStore).count++;
  StoreKeeper.notify(increment);
}

Initialization

You can attach store to your app like this:

void main() {
  runApp(StoreKeeper(
    store: AppStore(),
    child: MyApp(),
  ));
}

Listening

In your widget if you want to rebuild it whenever a mutation happens call update with list of mutations:

@override
Widget build(BuildContext context) {
  StoreKeeper.update(context, on: [Increment, Multiply]);
  var store = StoreKeeper.store as AppStore;

  return ...
}

APIs

StoreKeeper.store

Returns the current instance of the store.

StoreKeeper.update(BuildContext context, {List<Object> on})

Rebuilds the widget with context everytime a mutation given to on is performed.

StoreKeeper.getStreamOf(Object mutation)

Returns a stream associated with the mutation which sends an update everytime the mutation is performed. Useful if you want to use it with a StreamBuilder. You can use UpdateOn widget which combines these ideas. This is useful if you want to update only a small piece of the screen.

UpdateOn(
  mutations: [increment, multiply],
  builder: (_) => Text("Count: ${store.count}"),
),

StoreKeeper.notify(Object mutation)

Notifies StoreKeeper that the mutation has performed. This will internally notify all the listeners of that mutation. Used when you write mutations in functions.

StoreKeeper.mutate(Object key, Function(Store) mutation)

An inline method to mutate a state. Key defines the name of the mutation. Use this API like this:

StoreKeeper.mutate("increment", (AppStore store) => store.count++);

Simple Example

import 'package:flutter/material.dart';
import 'package:store_keeper/store_keeper.dart';

// Build store and make it part of app
void main() {
  runApp(StoreKeeper(
    store: AppStore(),
    child: MyApp(),
  ));
}

// Store definition
class AppStore extends Store {
  int count = 0;
}

// Mutations
class Increment extends Mutation<AppStore> {
  exec() => store.count++;
}

class Multiply extends Mutation<AppStore> {
  final int by;

  Multiply({this.by});

  exec() => store.count *= by;
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Define when this widget should re render
    StoreKeeper.update(context, on: [Increment, Multiply]);

    // Get access to the store
    var store = StoreKeeper.store as AppStore;

    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Text("Count: ${store.count}"),
              RaisedButton(
                child: Text("Increment"),
                onPressed: () {
                  // Invoke mutation
                  Increment();
                },
              ),
              RaisedButton(
                child: Text("Decrement"),
                onPressed: () {
                  // Invoke mutation with params
                  Multiply(by: 2);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Mutations using functions

import 'package:flutter/material.dart';
import 'package:store_keeper/store_keeper.dart';

// Build store and make it part of app
void main() {
  runApp(StoreKeeper(
    store: AppStore(),
    child: MyApp(),
  ));
}

// Store definition
class AppStore extends Store {
  int count = 0;
}

// Mutations
void increment() {
  (StoreKeeper.store as AppStore).count++;
  StoreKeeper.notify(increment);
}

void multiply({int by}) {
  (StoreKeeper.store as AppStore).count *= by;
  StoreKeeper.notify(multiply);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Define when this widget should re render
    StoreKeeper.update(context, on: [increment, multiply]);

    // Get access to the store
    var store = StoreKeeper.store as AppStore;

    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              Text("Count: ${store.count}"),
              RaisedButton(
                child: Text("Increment"),
                onPressed: () {
                  // Invoke mutation
                  increment();
                },
              ),
              RaisedButton(
                child: Text("Decrement"),
                onPressed: () {
                  // Invoke mutation with params
                  multiply(by: 2);
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Libraries

http
inherited_model
inventory
mutation
store_keeper
update_on