built_redux 7.3.5

  • README.md
  • CHANGELOG.md
  • Installing
  • Versions
  • 97

built_redux

Pub codecov.io

built_redux is a state management library written in dart that enforces immutability. built_redux is not only an implementation of redux, but also a framework for building middleware and reducers in a type safe manner.

Inspired by redux

Built using built_value

Framework bindings

flutter

examples

flutter todo_mvc, written by Brian Egan

vanilla dart, angular, and wui_builder todoMVCs

nested state & actions

building reducers for built collections

inheritence in state & actions

libraries

thunk

rx

repatch

Using it in your project

If you are not familiar with Redux or built_value

Since built_redux follows the same architecture as redux we encourage you to gain familiarity with it by reading the redux docs.

Since built_redux requires the use of built_value we encourage you to gain familiarity with it by reading this blog post.

  1. Add the built_redux package as a dependency in your pubspec.yaml.
  dependencies:
    built_redux: "^7.0.0"
  1. Create a script to run generators for generating built_values and built_redux action classes.
  import 'dart:async';

  import 'package:build_runner/build_runner.dart';
  import 'package:built_value_generator/built_value_generator.dart';
  import 'package:source_gen/source_gen.dart';
  import 'package:built_redux/generator.dart';

  Future main(List<String> args) async {
    await build([
      new BuildAction(
          new PartBuilder([
            new BuiltValueGenerator(),
            new BuiltReduxGenerator(),
          ]),
          // your lib name here
          'built_redux',
          // tweak the files that invoke the generator here
          inputs: const ['lib/**/*.dart'])
    ], deleteFilesByDefault: true);
  }
  1. Run the build script from the command line to generate your built_values and built_redux action classes by running dart [build script name].dart

Implementing a built_redux store

import 'package:built_value/built_value.dart';
import 'package:built_redux/built_redux.dart';

// This is a an implementation of ReduxActions. Actions are what middleware and ui
// components invoke a change to the redux store's state. By extending ReduxActions
// the built_redux generator will generate the required boilerplate to create
// each action and an ActionNames class. The ActionNames class is used to register
// reducers
abstract class CounterActions extends ReduxActions {
  ActionDispatcher<int> get increment;
  ActionDispatcher<int> get decrement;

  // factory to create on instance of the generated implementation of CounterActions
  CounterActions._();
  factory CounterActions() => new _$CounterActions();
}

// This is a built value. It is an immutable model that implements the Built interface.
// All of the state in your redux store is contained in a single built value model.
abstract class Counter implements Built<Counter, CounterBuilder> {
  /// [count] value of the counter
  int get count;

  // Built value constructor. The factory is returning the default state
  Counter._();
  factory Counter() => new _$Counter._(count: 1);
 }


// These are reducer functions. They have a (state, action, builder) => void signature.
// They describes how an action transforms the state into the next state by applying changes to the builder supplied.
// You are required to use the builder passed, calling state.rebuild will NOT update the state in your redux store.
increment(Counter state, Action<int> action, CounterBuilder builder) =>
  builder.count = state.count + action.payload;

decrement(Counter state, Action<int> action, CounterBuilder builder) =>
  builder.count = state.count - action.payload;

// This is a reducer builder. Use of ReducerBuilder is not required, however it
// is strongly recommended as it gives you static type checking to make sure
// the payload for action name provided is the same as the expected payload
// for the action provided to your reducer. Calling .build() returns a reducer function
// that can be passed to the store's constructor.
var reducerBuilder = new ReducerBuilder<Counter, CounterBuilder>()
  ..add(CounterActionsNames.increment, increment)
  ..add(CounterActionsNames.decrement, decrement);

// Create a redux store holding the state of your app.
// Its API contains three getters: stream, state, and actions.
var store = new Store<Counter, CounterBuilder, CounterActions>(
  reducerBuilder.build(), // build returns a reducer function
  new Counter(),
  new CounterActions(),
);

// You can use stream.listen() to update the UI in response to state changes.
store.stream.listen((change) => print('prev: ${change.prev.count} next ${change.next.count}'));

// The only way to mutate the internal state is to dispatch an action.
store.actions.increment(1);
print(store.state.count); // 1
store.actions.increment(2);
print(store.state.count); // 3
store.actions.decrement(1);
print(store.state.count); // 2

// when the event dispatching actions finishes and the stream handlers process, the following will be printed:
// prev: 0 next: 1
// prev: 1 next 3
// prev: 3 next: 2

Writing middleware

// Define specific actions to be handled by this middleware.
// A middleware can also listen to and perform side effects on any actions defined
// elsewhere. It is possible that a middleware will not need any of its own actions
// defined.
 abstract class DoubleAction extends ReduxActions {
  ActionDispatcher<int> get increment;

  DoubleAction._();
  factory DoubleAction() => new _$DoubleAction();
}

// This is a middleware builder. Use of MiddlewareBuilder is not required, however
// just like ReducerBuilder it is strongly recommended as it gives you static type checking to make sure
// the payload for action name provided is the same as the expected payload
// for the action provided to your reducer. It will also call next(action) for you
// if an action not handled by this middleware is received. Calling .build() returns the
// middleware function that can be passed to your store at instantiation.
var doubleMiddleware = (new MiddlewareBuilder<Counter, CounterBuilder, CounterActions>()
  ..add(DoubleActionNames.increment, _doubleIt)).build();

void  _doubleIt(MiddlewareApi<Counter, CounterBuilder, CounterActions> api, ActionHandler next, Action<int> action) {
  // doubles the action payload and kicks off a new action to increment the
  // store by that amount.
  api.actions.increment(action.payload * 2);
}

You will notice that MiddlewareBuilder's generics are Counter and CounterActions. These are the same types as the defaultState and actions classes passed when the store was instantiated. In order for DoubleActions to be handled by redux you must add it to definition of CounterActions like so

abstract class CounterActions extends ReduxActions {
  ActionDispatcher<int> increment;
  ActionDispatcher<int> decrement;

  DoubleActions doublerActions;

  // factory to create on instance of the generated implementation of CounterActions
  CounterActions._();
  factory CounterActions() => new _$CounterActions();
}

Check the usage after adding this middleware

// Create a Redux store holding the state of your app.
// Its API is subscribe, state, actions.
var store = new Store<Counter, CounterBuilder, CounterActions>(
  new Counter(),
  new CounterActions(),
  middleware: [doubleMiddleware],
);

store.stream.listen((change) => print(change.next.count));

// The only way to mutate the internal state is to dispatch an action.
store.actions.increment(1);
store.actions.doublerActions.increment(2);
store.actions.decrement(1);
// 1
// 5
// 4

A middleware can also be defined without using a MiddlewareBuilder to execute a function for all actions. For example, the following middleware logs every action dispatched as well the the state after the action was handled:

NextActionHandler loggingMiddleware(MiddlewareApi<Counter, CounterBuilder, CounterActions> api) =>
    (ActionHandler next) => (Action action) {
          next(action);
          print("Action: ${action.name}");
          print("Payload: ${action.payload}");
          print("Next State: ${api.state}");
        };

Observing state

The store's stream fires with a payload of type StoreChange. This is an object that contains the action, the previous state, and the next state. If you would like to set up a stream thats payload is simply the next state, use store.nexState


print(store.state.count); // 1

store.nextState.listen(print);

store.actions.increment(1);
store.actions.increment(3);

// 2
// 5

Observing substate

Streams can easily be accessed to observe any piece of your state tree by passing a mapper the store's substateStream function. For example, say I only care about BaseCounter.count in the previous example and I do not want to be notified when BaseCounter.nestedCounter changes. I can create a stream that will only emit an event when BaseCounter.count changes, as so:

final countStream = store.substateStream<int>((BaseCounter state) => state.count);

countStream.listen((change) => print('prev: ${change.prev}, next: ${change.next}'));

store.actions.increment(1);
store.actions.nestedCounter.increment(2);

// prev: 1, next: 2

In the case of substate streams, the payload is of type SubStateChange. This is an object the previous state, and the next state. If you would like to set up a stream thats payload is simply the next subState, use store.nexSubstate


print(store.state.count); // 1

store.nextSubstate((BaseCounter state) => state.count)
  .listen(print);

store.actions.increment(1);
store.actions.increment(3);

// 2
// 5

7.3.3

  • add changelog
  • update readme to include link to todoMVC repo

7.3.2

  • open dependency range on the build package

7.3.1

  • open dependency range on the built_collection package

7.3.0

  • dispose is now awaitable

7.2.0

  • Added actionStream stream to the store api so one can listen to changes resulting from a specific action name.
  • Fixed issues called out my adding implicit-dynamic and implicit-cast rules.

7.1.0

  • Added reducer builders for all collection types in built_collection. This means you can now write reducers that rebuild just a collection. Examples: https://github.com/davidmarne/built_redux/blob/master/test/unit/collection_models.dart
  • The action generator now supports inheritance, meaning one can now have action classes extend one another. AbstractReducerBuilder was added as means of merging a reducer builder defined to rebuild the abstract pieces of state. Examples: https://github.com/davidmarne/built_redux/blob/7.1.0/test/unit/inheritance_test_models.dart for examples

7.0.0

  • Breaking changes:
    • A breaking change in behavior was made to action dispatchers. Before action dispatchers executed the middleware/reducers asynchronously, but they now execute synchronously.

For example you may have:

int getCount(store) {
  print(store.state.count); // prints 0
  store.actions.increment(1);
  print(store.state.count); // would print 1 if actions were sync, 0 if async
  return store.state.count; // would return 1 if actions were sync, 0 if async
}

Before this function would return 0 because the state update that occurs from actions.increment would be processed in a future task. Now this function will return 1 because the state update that occurs from actions.increment is processed in the current task

6.0.0

Removes the BuiltReducer interface and the generated BuiltReducer implementation in favor of building a reducer function and passing it to the store at instantiation.

  • Breaking changes:
    • Store now takes a reducer
    • Nested reducers need to be built with the NestedReducerBuilder and merged wth the main ReducerBuilder using ReducerBuilder.addNestedReducer
    • Models no longer implement BuiltReducer
      • Remove references to the reducer on your models
    • Renamed ActionDispatcher.syncWithStore to setDispatcher
    • Renamed SubStateChange to SubstateChange

Reasoning:

  • Decouples your models and reducers.
  • Requires less generated code.
  • Reducing requires less map look ups. Previously N map look ups were required where N was the number of BuiltReducers in your state tree. Now only 1 map look up is required per action dispatched.

Examples:

  • Example model change. Remove the generated BuiltReducer mixin and the reducer getter

Before:

  abstract class Counter extends Object
     with CounterReducer
     implements Built<Counter, CounterBuilder> {
   int get count;

   get reducer => _reducer;

   Counter._();
   factory BaseCounter() => new _$BaseCounter._(count: 1);
 }

After:

  abstract class Counter implements Built<Counter, CounterBuilder> {
   int get count;

   Counter._();
   factory BaseCounter() => new _$BaseCounter._(count: 1);
 }
  • Example nested reducer change. Change NestedCounter's reducer builder to a NestedReducerBuilder. Pass the NestedReducerBuilder mapper functions from the main state/builder to the nested state/builder.

Before


// Built Reducer
abstract class BaseCounter extends Object
    with BaseCounterReducer
    implements Built<BaseCounter, BaseCounterBuilder> {
  int get count;

  BuiltList<int> get indexOutOfRangeList;

  NestedCounter get nestedCounter;

  @override
  get reducer => _baseReducer;

  // Built value constructor
  BaseCounter._();
  factory BaseCounter() => new _$BaseCounter._(
        count: 1,
        nestedCounter: new NestedCounter(),
      );
}

final _baseReducer = (new ReducerBuilder<BaseCounter, BaseCounterBuilder>()
      ..add(BaseCounterActionsNames.increment, _baseIncrement)
      ..add(BaseCounterActionsNames.decrement, _baseDecrement))
    .build();

abstract class NestedCounter extends Object
    with NestedCounterReducer
    implements Built<NestedCounter, NestedCounterBuilder> {
  int get count;

  get reducer => _nestedReducer;

  // Built value constructor
  NestedCounter._();
  factory NestedCounter() => new _$NestedCounter._(count: 1);
}

final _nestedReducer =  (new ReducerBuilder<NestedCounter, NestedCounterBuilder>()
          ..add(NestedCounterActionsNames.increment, _nestedIncrement)
          ..add(NestedCounterActionsNames.decrement, _nestedDecrement))
        .build();

After

abstract class BaseCounter implements Built<BaseCounter, BaseCounterBuilder> {
  int get count;

  NestedCounter get nestedCounter;

  BaseCounter._();
  factory BaseCounter() => new _$BaseCounter._(
        count: 1,
        nestedCounter: new NestedCounter(),
      );
}

// the reducer passed to the store
final reducer = (new ReducerBuilder<BaseCounter, BaseCounterBuilder>()
      ..add(BaseCounterActionsNames.increment, _baseIncrement)
      ..add(BaseCounterActionsNames.decrement, _baseDecrement)
      ..combineNested(_nestedReducer))
    .build();

abstract class NestedCounter implements Built<NestedCounter, NestedCounterBuilder> {
  int get count;

  // Built value constructor
  NestedCounter._();
  factory NestedCounter() => new _$NestedCounter._(count: 1);
}

final _nestedReducer = new NestedReducerBuilder<BaseCounter, BaseCounterBuilder,
    NestedCounter, NestedCounterBuilder>(
  (state) => state.nestedCounter,
  (builder) => builder.nestedCounter,
)
  ..add(NestedCounterActionsNames.increment, _nestedIncrement)
  ..add(NestedCounterActionsNames.decrement, _nestedDecrement);

1. Depend on it

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


dependencies:
  built_redux: "^7.3.5"

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 packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:built_redux/built_redux.dart';
        
Version Uploaded Documentation Archive
7.3.5 Jan 31, 2018 Go to the documentation of built_redux 7.3.5 Download built_redux 7.3.5 archive
7.3.4 Jan 26, 2018 Go to the documentation of built_redux 7.3.4 Download built_redux 7.3.4 archive
7.3.3 Dec 31, 2017 Go to the documentation of built_redux 7.3.3 Download built_redux 7.3.3 archive
7.3.2 Dec 5, 2017 Go to the documentation of built_redux 7.3.2 Download built_redux 7.3.2 archive
7.3.1 Nov 30, 2017 Go to the documentation of built_redux 7.3.1 Download built_redux 7.3.1 archive
7.3.0 Nov 18, 2017 Go to the documentation of built_redux 7.3.0 Download built_redux 7.3.0 archive
7.2.0 Nov 5, 2017 Go to the documentation of built_redux 7.2.0 Download built_redux 7.2.0 archive
7.1.0 Nov 1, 2017 Go to the documentation of built_redux 7.1.0 Download built_redux 7.1.0 archive
7.0.0 Oct 22, 2017 Go to the documentation of built_redux 7.0.0 Download built_redux 7.0.0 archive
6.1.1 Oct 13, 2017 Go to the documentation of built_redux 6.1.1 Download built_redux 6.1.1 archive

All 33 versions...

Analysis

This feature is new.
We welcome feedback.
More details: scoring.

We analyzed this package, and provided a score, details, and suggestions below.

  • completed on Feb 3, 2018
  • Dart: 2.0.0-dev.20.0
  • pana: 0.10.1

Scores

Popularity:
Describes how popular the package is relative to other packages. [more]
95 / 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]
97

Platforms

Detected platforms: Flutter, web, other

No platform restriction found in primary library package:built_redux/built_redux.dart.

Suggestions

  • Maintain an example.

    Create a short demo in the example/ directory to show how to use this package. Common file name patterns include: main.dart, example.dart or you could also use built_redux.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.17.1 <2.0.0
analyzer >=0.29.0 <0.31.0 0.30.0+4 0.31.0+1
build >=0.10.0 <0.12.0 0.11.2 0.12.0+1
built_collection >=1.0.0 <4.0.0 3.0.4
built_value >=4.1.0 <6.0.0 5.1.1
source_gen ^0.7.0 0.7.4+3
Transitive dependencies
args 1.3.0
async 2.0.3
charcode 1.1.1
cli_util 0.1.2+1
collection 1.14.5
convert 2.0.1
crypto 2.0.2+1
csslib 0.14.1
fixnum 0.10.6
front_end 0.1.0-alpha.4.1 0.1.0-alpha.8
glob 1.1.5
html 0.13.2+2
isolate 1.1.0
kernel 0.3.0-alpha.1.1 0.3.0-alpha.5
logging 0.11.3+1
matcher 0.12.1+4
meta 1.1.2
package_config 1.0.3
path 1.5.1
plugin 0.2.0+2
quiver 0.28.0
source_span 1.4.0
stack_trace 1.9.1
string_scanner 1.0.2
typed_data 1.1.5
utf 0.9.0+4
watcher 0.9.7+7
yaml 2.1.13
Dev dependencies
build_runner ^0.6.0
built_value_generator >=4.1.0 <6.0.0
coverage ^0.9.0
dart_dev ^1.0.0
dart_style >=0.2.4 <2.0.0 1.0.9+1
test ^0.12.0