Redux for Dart

Overview

Build Status codecov Flutter Community: redux

Redux for Dart using generics for typed State. It includes a rich ecosystem of Docs, Middleware, Dev Tools and can be combined with Flutter using the flutter_redux package.

Docs

  • Motivation and Principles - Learn why Redux might make sense for your app and the principles behind it.
  • Basics - Introduction to the core concepts in Redux
  • Combining Reducers - combineReducers works a bit differently in Dart than it does in JS. Learn why, and how to combine reducers in a type-safe way!
  • Async with Middleware - Learn how to make async calls, such as to a web service or local database, with Redux.
  • Use Selectors to Query Data in the Store - Learn how to write functions to provide consistent access to data in your App State.
  • API Documentation - Rich documentation included in the source code and generated by DartDoc.

Flutter Examples

To integrate Redux.dart with Flutter, please use the flutter_redux package.

Beginner

Intermediate

Advanced

Middleware

  • redux_logging - Connects a Logger to a Store, and can print out actions as they're dispatched to your console.
  • redux_thunk - Allows you to dispatch functions that perform async work as actions.
  • redux_future - For handling Dart Futures that are dispatched as Actions.
  • redux_epics - Middleware that allows you to work with Dart Streams of Actions to perform async work.

Dev Tools

The redux_dev_tools library allows you to create a DevToolsStore during dev mode in place of a normal Redux Store.

This DevToolsStore will act exactly like a normal Store at first. However, it will also allow you to travel back and forth throughout the States of your app or recompute the State of your app by replaying all actions through your reducers. This works perfectly with Hot Reloading!

You can combine the DevToolsStore with your own UI to travel in time, or use one of the existing options for the platform you're working with:

Additional Utilities

  • reselect - Efficiently derive data from your Redux Store with memoized functions.
  • redux_persist - Persist Redux State, works for Web and Flutter

Usage

import 'package:redux/redux.dart';

// Create typed actions. You will dispatch these in order to
// update the state of your application.
enum Actions {
  increment,
  decrement,
}

// Create a Reducer. A reducer is a pure function that takes the 
// current State (int) and the Action that was dispatched. It should
// combine the two into a new state without mutating the state passed
// in! After the state is updated, the store will emit the update to 
// the `onChange` stream.
// 
// Because reducers are pure functions, they should not perform any 
// side-effects, such as making an HTTP request or logging messages
// to a console. For that, use Middleware.
int counterReducer(int state, action) {
  if (action == Actions.increment) {
    return state + 1;
  } else if (action == Actions.decrement) {
    return state - 1;
  }
  
  return state;
}

// A piece of middleware that will log all actions with a timestamp
// to your console!
// 
// Note, this is just an example of how to write your own Middleware.
// See the redux_logging package on pub for a pre-built logging 
// middleware.
loggingMiddleware(Store<int> store, action, NextDispatcher next) {
  print('${new DateTime.now()}: $action');

  next(action);
}

main() {
  // Create the store with our Reducer and Middleware
  final store = new Store<int>(
    counterReducer, 
    initialState: 0, 
    middleware: [loggingMiddleware],
  );

  // Render our State right away
  render(store.state);
  
  // Listen to store changes, and re-render when the state is updated
  store.onChange.listen(render);

  // Attach a click handler to a button. When clicked, the `INCREMENT` action
  // will be dispatched. It will then run through the reducer, updating the 
  // state.
  //
  // After the state changes, the html will be re-rendered by our `onChange`
  // listener above. 
  querySelector('#increment').onClick.listen((_) {
    store.dispatch(Actions.increment);
  });
}

render(int state) {
  querySelector('#value').innerHtml = '${state}';
}
Comments
  • Dart 2 issue

    Dart 2 issue

    Hello,

    after updating to dart 2. I get the following error when using Reducer

    error is happend type '(OnBoardingState, LicensesLoadedAction) => OnBoardingState' is not a subtype of type '(OnBoardingState, dynamic) => OnBoardingState'

    My code looks like the following:

    final onBoardingReducer = combineTypedReducers<OnBoardingState>([
      new ReducerBinding <OnBoardingState, LicensesLoadedAction>(_setLoadedLicenses),
    ]);
    
    OnBoardingState _setLoadedLicenses(OnBoardingState onBoardingData, LicensesLoadedAction action) {
      return onBoardingData.copyWith(licenses: action.licenses);
    }
    
    

    If I set the type to dynamic it works but I would prefer to not have to case the action.

    OnBoardingState _setLoadedSchools(OnBoardingState onBoardingData, dynamic action) {
      if(action is SchoolsLoadedAction) {
        return onBoardingData.copyWith(schools: action.schools);
      }
      return onBoardingData;
    }
    

    Any idea?

    opened by lasseschmitt 10
  • Store always trigger even state does not change

    Store always trigger even state does not change

    https://github.com/johnpryan/redux.dart/blob/master/lib/src/store.dart#L197-L206

      // The base [NextDispatcher].
      //
      // This will be called after all other middleware provided by the user have
      // been run. Its job is simple: Run the current state through the reducer,
      // save the result, and notify any subscribers.
      _reduceAndNotify(dynamic action) {
        final state = reducer(_state, action);
        _state = state;
        _changeController.add(state);
      }
    

    Issues

    1. Changes are always triggered even if the state didn't change
    2. The internal state is updated before the stream update is received so validating state == newState is equivalent to using if (1 == 1)

    Possible Solutions

    I'd like to get your thoughts on these two.

    Solution 1 Keep this as-is, but see swap the order so in our onChange handler we can properly compare our states.

    Solution 2 Remove some of the blackbox so we can extend Store with our own implementation to adjust it accordingly to our desired flavor.

    Reasoning

    We use built_value for our state. This means any state change will always return a guaranteed new object or the exact same object due to the reducer mutating the immutable.

    Through immutability we're able to better control React render cycles which greatly improves performance. With a store always triggering, we have to do internal state checks – where we validate each individual value – negating the inherent equality checks you get from using built_value and, technically, "duplicating" generated code.

    Thoughts?

    opened by johnbland-wf 10
  • Use of RxDart BehaviorSubject instead of StremController

    Use of RxDart BehaviorSubject instead of StremController

    Hi @johnpryan is possible for you a redux implementation using rxdart in the Store class?

    In this way it would be possible too to choose using a PublishSubject or a BehaviorSubject for the _changeController.

    Thank you,

    Fabrizio

    opened by ftognetto 8
  • TypedMiddleware not accept specified middleware type.

    TypedMiddleware not accept specified middleware type.

    Problem

    When specify the exact action type, TypedMiddleware complains [dart] The return type '(Store<AppState>, LoginAction, (dynamic) → void) → Null' isn't a '(Store<AppState>, dynamic, (dynamic) → void) → void', as defined by the method 'loginMiddleware'..

    Code

    List<Middleware<AppState>> createMiddlewares({
      AccountService accountService,
    }) {
      accountService = accountService ?? AccountService();
    
      return [
        TypedMiddleware<AppState, LoginAction>(loginMiddleware(accountService)),
      ];
    }
    
    Middleware<AppState> loginMiddleware(AccountService accountService) =>
        // Specify the exact type of action, not dynamic
        (Store<AppState> store, LoginAction action, NextDispatcher next) {
          next(action);
        };
    

    Env

    name: tangbole
    description: A tumblr client app.
    
    dependencies:
      cupertino_icons: ^0.1.2
      english_words: ^3.1.0
      flutter:
        sdk: flutter
      flutter_redux: ^0.5.1
      http: ^0.11.3+16
      meta: ^1.1.5
      uri: ^0.11.2
      redux: ^3.0.0
      redux_logging: ^0.3.0
      redux_persist: ^0.7.0-rc.2
      redux_persist_flutter: ^0.6.0-rc.1
    
    dev_dependencies:
      flutter_driver:
        sdk: flutter
      flutter_test:
        sdk: flutter
      mockito: ^2.2.3
    
    flutter:
      uses-material-design: true
      assets:
        - images/lake.jpg
    
    
    opened by jaggerwang 8
  • Updating only selected fields of immutable

    Updating only selected fields of immutable

    Coming from the JavaScript world, seeing something like this in a reducer is common:

    return {
        ...state,
        foo: "bar"
    };
    

    This is basically creating a new object, but replacing (not setting) the foo field, making it "immutable" (doesn't change the original, just creates a new with the updated field). This is usually compiled down to use Object.assign when using Babel.

    In Dart and redux.dart, when making a new state, I found no way to "copy" the old object and replace a value. The only way I found was to fully recreate the object, reassigning all the fields from the old. This quickly becomes a problem when scaling, because if you creating a new field, you must check and add every single reducer branch. By failing to do so, you'll have an empty property when running that branch/action.

    Is there any solution for this problem?

    opened by Cretezy 8
  • combineReducers is different than the original

    combineReducers is different than the original

    The idea behind combineReducers is for each reducer to control a specific piece of the state. Your version of combineReducers simply chains them over the same state instead of divvying up the state to be controlled by the individual reducers.

    See documentation: https://redux.js.org/docs/basics/Reducers.html

    P.S., your redux port is far more readable than the others. 👍

    opened by jpgilchrist 8
  • Add `distinct` option

    Add `distinct` option

    If the distinct param set to true, the Store will not emit onChange events if the new State that is returned from your reducer in response to an Action is equal to the previous state.

    Under the hood, it will use the == method from your State class to determine whether or not the two States are equal.

    For fun, I've also added stricter analysis rules to improve the "health" score displayed by Pana on the Pub website, fixed a silly mistake in the README example, and updated the examples to reflect the README.

    Example usage: new Store(myReducer, distinct: true);

    /cc @johnbland-wf

    Fixes #14.

    opened by brianegan 7
  • should compare by hashCode.

    should compare by hashCode.

    We need to receive changed state when update collection variables(List, Map, .v.v...) same reference. All are ok if I clone collection variables are like List.from() but the complex time is O(n). It isn't good for the performance of large collection variables

    So I get hashCode before state pass to reducers then compares with new hashCode after update collection variables.

    Please check my repo to see different between redux: 3.0.0 and my contribution.

    Check redux:3.0.0 please comment dependency_overrides then run flutter packages get Check my contribution please uncomment dependency_overrides then run flutter packages get

    https://github.com/phamnhuvu-dev/flutter_card_list_redux

    opened by phamnhuvu-dev 6
  • Added combineTypedReducers()

    Added combineTypedReducers()

    This is needed for the times when Dart 2 can't infer types and is unable to properly convert from Iterable<TypedReducer<S,A>> to Iterable<Reducer<S>>, which can occur in situations like this:

    typedef T F<T>(T t);
    
    F<T> foo<T>(Iterable<F<T>> iter) => iter.first;
    
    class C<T> {
      T call(T t) => t;
    }
    
    List<C<String>> getCs() => [new C<String>()];
    
    void main() {
      foo(getCs());
    }
    

    That code works fine in Dart 1 but fails to compiled in Dart 2

    opened by tvolkert 6
  • Exception AppState' is not a subtype of type 'Reducer<AppState>'  when creating a store.

    Exception AppState' is not a subtype of type 'Reducer' when creating a store.

    Hi, after creating a simple application I get following exception:

    type '(AppState, dynamic) => AppState' is not a subtype of type 'Reducer' of 'reducer'.

    Here's my setup:

    dependencies:
      flutter_redux: "^0.5.0"
      redux: "^3.0.0"
    
    @immutable
    class AppState {
      final List<double> history;
      final bool screen;
    
      AppState(this.history, this.screen) {}
    
      factory AppState.initial() => new AppState(new List.unmodifiable([]), false);
    }
    
    AppState appReducer(AppState state, action) {
      return new AppState.initial();
    }
    
    final store = new Store<AppState>(
    		appReducer,
    		initialState: new AppState.initial(),
    	);
    

    If I change 'AppState' to 'int' in the reducer, the app runs OK. Is there something wrong with my code?

    Cheers, Artur

    opened by arturstaszczyk 6
  • Proposal - have dispatch return original action

    Proposal - have dispatch return original action

    I am trying to port some promise based code from node and with redux thunks it was easy to return a promise from the action. What would be helpful would be if the dispatch method on the store returned the action then we could do something like:

    With a LoginAction object add a Completer property and also a loginCompleted method that returns a future from the Completer. We intercept the LoginAction in the middleware and do some async stuff then call the completer to complete the action. This would keep the async flow in for example a button handler located in the button handler and allow integration with navigators and the ui in a predictable and easy manner.

    class Action { final completer = new Completer();

    loginCompleted() { return completer.future; } }

    store.dispatch(action).loginCompleted().then((result) => print('logged in state $result');

    The middleware will take the action and do some async stuff. when the middleware async operation completed the completer.complete will be called and the future completes.

    opened by Paul-Todd 6
  • add TypedMiddlewareBase abstraction

    add TypedMiddlewareBase abstraction

    Added typed middleware base abstraction, that allows to create a typed middleware as a simple class, like ex.

    class MyMiddleware extends TypedMiddlewareBase<State, MyAction> {
      @override
      dynamic dispatch(Store<String> store, MyAction action, NextDispatcher next) {
        ...
      }
    }
    
    final store = new Store<State>(
      middleware: [MyMiddleware()],
    );
    

    Or TypedMiddlewareClass would be better, as there is already MiddlewareClass abstraction. Please leave a comment, do you see it useful. It's good if you prefer to keep project fully in OOP convention.

    Otherwise, if you want to extend TypedMiddleware, you need to create something ugly like

    class MyMiddleware<State, Action> extends TypedMiddleware<State, Action> {
      MyMiddleware()
          : super(
              (Store<State> store, Action action, NextDispatcher next) {
                ...
              },
            );
    }
    

    And within constructor it is no able to use other class properties.

    opened by dzziwny 0
  • Creating an simple example

    Creating an simple example

    A simple example to help many guys approach redux.

    • [ ] Reducers tests
    • [ ] Thunk middleware tests
    • [ ] Injecting repository
    • [ ] Using built_value and built_collection for:
      • are immutable, if the elements/keys/values used are immutable;
      • are comparable;
      • are hashable;
      • use copy-on-write to avoid copying unnecessarily.

    I will take care of this task.

    opened by phamnhuvu-dev 0
  • Best way to open a dialog

    Best way to open a dialog

    I'm wondering what is the best way to open a dialog?

    My scenario:

    In the middleware, I'm doing some async backend tasks. Depending on the result, I want to open a dialog to display an error message or to indicate that the user needs to log in again.

    When the async task completes, I send another action to change the status, such as showErrorDialog = true. In the UI code, I listen for this status (using https://github.com/brianegan/reselect_dart) and when it becomes true, I open the dialog and set this status to false (so it will not be reopened).

    It works, but it also looks wrong to me.

    Is there a better way to open a dialog only when an action is triggered?

    opened by RalphBergmannKMB 2
  • Use optional type instead of dynamic for actions

    Use optional type instead of dynamic for actions

    Proposal

    It'd be great if Store had another generic type to be used as the action argument type in methods like dispatch, preventing non-action objects from being used as actions.

    This should be non-breaking for most users, as type inference should allow Store constructors to omit the existing state type, and the addition of another type will only break invocations that specify it explicitly.

    Reasoning

    I've separated my project into layers, like so:

    Data: project_data: Platform-agnostic repository implementations

    Domain: project_domain: Core platform-agnostic functionality, including state management

    Platform: project_flutter: A GUI using Flutter project_cli: A CLI

    Platform implementations can build a Redux store with a buildStore function in the domain package, passing in repository implementations to use. The domain package exports different action classes that the platform implementation can dispatch to the store. Each action class extends from an internal (non-exported) AppAction class.

    If the dispatch function only accepts AppAction subtypes, this will prevent non-action objects being used by the platform implementations, requiring all actions to be used from the domain package.

    Workarounds

    At the moment, an assert can be used, but this throws an error at runtime. A compilation error is preferred.

    AppState appReducer(AppState state, dynamic action) {
      assert(action is AppAction,
          'The action object ($action) has an invalid type (${action.runtimeType}! It must extend AppAction.');
    
      // ...
    }
    
    opened by hacker1024 5
  • const reducers

    const reducers

    Allow to build entire reducers tree as const object.

    • add CombinedReducer class to easily combine reducers in const context
    • mark TypedReducer constructor as const
    • add UntypedReducer as shorthand for TypedReducer<State, dynamic>.
    • add related tests
    • add related docs in code and doc/combine_reducers.dart
    opened by 2ZeroSix 4
Releases(4.0.0)
  • 4.0.0(Dec 4, 2019)

    • dispatch returns the provided action
    • Removed Dart 1.x support
    • Enforce Pedantic package lint rules
    • Enforce public_member_api_docs lint rule and add docs to missing parts
    • Update travis.yml with support for dartfmt, analysis, and code coverage
    • Add coverage badge to root README
    • Pub.dev updates
      • Longer description
      • https for package url
      • update example folder to include README
    Source code(tar.gz)
    Source code(zip)
Owner
Flutter Community
A central place for all community made Flutter packages. To get started, see the README of the 'community' repository.
Flutter Community
Flutter app backed by Redux, shows animations, internationalization (i18n), ClipPath, fonts and others...

A Flutter tourism app that is backed-by Redux, shows animations, internationalization (i18n, English <=> Arabic), ClipPath, and fonts. YouTube demo I

Abdulmomen Kadum عبدالمؤمن كاظم 277 Dec 28, 2022
Podcast app with Flutter & Redux

Podcast App - Flutter & Redux Preview Introduction I have built a basic podcast app with an interactive UI, as well as some of its functionalities : L

Bilal El Mahdaoui 31 Oct 28, 2022
FlutterDux - Redux.JS driven Flutter

FlutterDux Flutter + Redux.js Android only at the moment, iOS coming soon. Getting Started Check out this article for a description of how it works. L

Paul DeMarco 6 Jul 28, 2019
A Dart library for creating a Dart object to represent directory trees.

Directory Tree A Dart library for creating a Dart object to represent directory trees. Getting Started Import and initialize package import 'package:d

Chiziaruhoma Ogbonda 5 Dec 1, 2021
A collection of Dart code samples by Dart DevRel

Dart samples A collection of Dart programs that illustrate features and best practices. For a list of community-maintained projects, see Awesome Dart.

Dart 458 Dec 30, 2022
A discord bot, made with Dart, which lets you run your own pure Dart code snippets directly via a discord ping, and get the output in an instant.

A discord bot, made with Dart, which lets you run your own pure Dart code snippets directly via a discord ping, and get the output in an instant.

Anikate De 3 Oct 21, 2022
A multiplatform Dart movie app with 40% of code sharing between Flutter and the Web.

A multiplatform Dart movie app with 40% of code sharing between Flutter and the Web.

Iiro Krankka 3.4k Dec 30, 2022
server side dart micro-framework to handle incoming http requests

Queen Palace ???? Introduction server side dart micro-framework to handle incoming http requests

Ahmed Masoud 32 Aug 30, 2022
Nhost Dart & Flutter packages

Nhost Packages for Dart & Flutter Package nhost_sdk Dart authentication and file storage API clients nhost_flutter_auth Flutter widgets for exposing N

Nhost 58 Dec 29, 2022
A simple and easy to use Redis client for Dart

redis_dart A simple and minimalist Redis client for Dart See it in pub: https://pub.dev/packages/redis_dart and GitHub: https://github.com/gabrielpach

Gabriel Pacheco 7 Dec 25, 2022
Upper is a open source back-end framework based on the Dart language.

What is Upper? Upper is a open source back-end framework based on the Dart language. With it, it is possible to automatically generate a gRPC API for

Andriws Luna 40 Sep 5, 2022
GChat is a chatting application developed using Flutter(Dart) and firebase for 2 users. Trying to Develop an application that does not sell your data with whatsapp rolling out its privacy policy updates.

Gchat - The Chatting Application A Flutter project for chatting. I used Android Studio and you can you any editor of your choice for ex: VS Code, Inte

Sanchaksh Kaul 6 Nov 6, 2022
a python-like bytes_io implementation for dart

bytes_io A python-like bytes_io implementation for dart A powerful helper for processing raw binary data Usage A simple usage example: import 'package

Kelly 5 Aug 31, 2022
A simple app to keep track the time you Learn, Playing, Reading, ... in every week. This is also my attempt to teach myself Flutter & Dart

Progressor 0.0.1 Sometime you want to set a target for you weekly, for example: "Reading Book for 8 hours every week". This app helps you to do that.

Son Nguyen Hoang 4 Oct 12, 2022
A type-safe command-line parsing library for Dart

plade Plade is a type-safe CLI parsing library for Dart. Highlights Fully type-safe and null-safe. Support for a variety of different parsing styles (

Ryan Gonzalez 6 Jan 1, 2022
Utilities for loading and running WASM modules from Dart code

Provides utilities for loading and running WASM modules Built on top of the Wasmer runtime. Setup Run dart run wasm:setup to build the Wasmer runtime.

Dart 284 Dec 23, 2022
A full, sound, modern and documented JS interop for Dart.

This is (or should be) a full JavaScript interop package using package:js bindings. The bindings are generated by machine-reading WebIDL files for typ

Jonathan Rezende 36 Dec 15, 2022
Agent library for Internet Computer, in Dart

agent_dart An agent library built for Internet Computer, a plugin package for dart and flutter apps. Developers can build ones to interact with Dfinit

null 87 Dec 31, 2022
ThingsBoard PE API client library for Dart developers.

ThingsBoard PE API client library for Dart developers. It's compatible with TB PE 3.3.0. Usage A simple usage example: import 'package:thingsboard_pe_

ThingsBoard - Open-source IoT Platform 45 Sep 28, 2022