Redux Saga Middleware Dart and Flutter

Dart CI

Issues related to redux_saga Pub Package Version Latest Dartdocs Join the chat on Gitter

Redux Saga Middleware for Dart and Flutter is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.

The mental model is that a saga is like a separate thread in your application that's solely responsible for side effects. Redux Saga is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well.

It uses synchronous generator functions to make those asynchronous flows easy to read, write and test. By doing so, these asynchronous flows look like your standard synchronous code. (kind of like async/await, but generators have a few more awesome features we need)

You might've used redux_thunk before to handle your data fetching. Contrary to redux thunk, you don't end up in callback hell, you can test your asynchronous flows easily and your actions stay pure.

Redux Saga is ported and compatible with javascript redux-saga implementation and its documentation. If you use Javascript redux-saga before than you can check Migration from Javascript documentation to get help about migration.

Package and installation details can be found at pub.dev.

Usage Example

Suppose we have a UI to fetch some user data from a remote server when a button is clicked. (For brevity, we'll just show the action triggering code.)

class MyComponent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ...
    RaisedButton(
      onPressed: () => StoreProvider.of(context).dispatch(UserFetchRequested()),
      child: Text('Fetch Users'),
    )
    ...
    );
  }
}

The Component dispatches a plain Object action to the Store. We'll create a Saga that watches for all UserFetchRequested actions and triggers an API call to fetch the user data.

sagas.dart

import 'package:redux_saga/redux_saga.dart';
import 'Api.dart';

// worker Saga: will be fired on UserFetchRequested actions
fetchUser({dynamic action}) sync* {
  yield Try(() sync* {
    var user = Result();
    yield Call(Api.fetchUser, args: [action.payload.userId], result: user);
    yield Put(UserFetchSuceeded(user: user.value));
  }, Catch: (e, s) sync* {
    yield Put(UserFetchFailed(message: e.message));
  });
}


//  Starts fetchUser on each dispatched `UserFetchRequested` action.
//  Allows concurrent fetches of user.
mySaga() sync* {
  yield TakeEvery(fetchUser, pattern: UserFetchRequested);
}


//  Alternatively you may use TakeLatest.
//
//  Does not allow concurrent fetches of user. If "UserFetchRequested" gets
//  dispatched while a fetch is already pending, that pending fetch is cancelled
//  and only the latest one will be run.
mySaga() sync* {
  yield TakeLatest(fetchUser, pattern: UserFetchRequested);
}

To run our Saga, we'll have to connect it to the Redux Store using the Redux Saga middleware.

main.dart

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

// create the saga middleware
var sagaMiddleware = createSagaMiddleware();

// Create store and apply middleware
final store = Store(
    counterReducer,
    initialState: 0,
    middleware: [applyMiddleware(sagaMiddleware)],
);

//connect to store
sagaMiddleware.setStore(store);

// then run the saga
sagaMiddleware.run(mySaga);

// render the application

Documentation

Examples

Vanilla Counter

Web based counter demo.

vanilla_counter

Counter

Demo using flutter and high-level API TakeEvery.

counter

Shopping Cart

A basic shopping cart example using flutter.

shopping_cart

Async App

A demo using async functions to fetch reddit posts.

async_app

License

Copyright (c) 2020 Bilal Uslu.

Licensed under The MIT License (MIT).

Libraries

redux_saga
redux_saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.