Flutter vs Redux State Management Tutorial

Flutter State management is one cool thing used to manage large amount of data chunks and organize them properly. This tutorial is for beginners who find it difficult to understand flutter state management.


Unlike Redux in React Apps which is easy to understand flutter is very confusing, but we're going to break it down for every one to understand. Before we proceed go ahead to create a flutter app that contains the default boilerplate with the flutter create command

flutter         create flutter_state_management

Next is to install all the neccessary dependencies which will be useful for this tutorial. So here are the below dependencies we will install.

Lets go on to create the store folder inside the lib folder

store -> inside the store folder, 

    create 2 files


actions.dart

// Action Class
class UpdateQuoteAction {
  String _quote;
  // used to update quote
  String get quote => this._quote;
  UpdateQuoteAction(this._quote);
}
// ThunkAction is neccessary for making promised based request
ThunkAction<AppState> getRandomQuote = (Store<AppState> store) async {
  // Dispatch new value to update old state props
  store.dispatch(new UpdateQuoteAction("updated quote"));
};

reducers.dart

// Default state props
class AppState {
  final int count;
  final int clickCount;
  final String quote;
  // Constructor
  AppState({this.count, this.clickCount, this.quote});
  // Used to update state props
  AppState copyWith({count, clickCount, quote}) {
    return AppState(
        count: count ?? this.count,
        clickCount: clickCount ?? this.clickCount,
        quote: quote ?? this.quote);
  }
}
// reducers: Counter Increment Reducer using Increment
AppState counterReducer(AppState statedynamic action) {
  switch (action) {
    case Actions.Increment:
      return state.copyWith(count: state.count + 1);
  }
  return state;
}
// reducers: Counter Update Reducer used to update values
AppState updateQuote(AppState statedynamic action) {
  if (action is UpdateQuoteAction) {
    return state.copyWith(quote: action.quote);
  }
  return state;
}
// reducers: Your Test Reducer
AppState valueReducer(AppState statedynamic action) {
  if (action == Actions.Click) {
    return state.copyWith(clickCount: state.clickCount + 1);
  }
  return state;
}
// combine Reducers
final reducers =
    combineReducers<AppState>([counterReducer, valueReducer, updateQuote]);

One thing I forgot to mention is that here unlike react types of actions, we're using enums. You can make a little research on that to have a deep understanding of using enums and inside your main.dart file add an enum line of code.


enumActions{ Click, Increment, Decrement, AddQuote }        

I have added comments on both files at the top to have an understanding of it. So right now let's move on to connect the store to our main.dart file using the State and AppState.

// Store used to         connect all         our reducers         and state.        
final store = new Store<AppState>(reducers,
    initialState: AppState(
      count: 0,
      clickCount: 0,
    ),
    middleware: [thunkMiddleware]);
​In other for our app to make use of the store, we pass the store to our stateless widget in the main.dart file. Notice we did use the StoreProvider and StoreConnector to have acess State and State Props.


class MyApp extends StatelessWidget {
  final Store<AppState> store;
  final String title;
  MyApp({Key key, this.store, this.title}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    // The store provider, which accepts the store and wraps our UI
    return new StoreProvider<AppState>(
      store: store,
      child: MaterialApp(

 StoreConnector<AppStateint>(
                  converter: (store) => store.state.count,
                  builder: (_, count) {
                    return new Text(
                      '$count',
                    );
                  },
                ),
​Main.dart file code

// actions
enum Actions { ClickIncrementDecrementAddQuote }
// store that hold our current appstate
final store = new Store<AppState>(reducers,
    initialState: AppState(
      count: 0,
      clickCount: 0,
    ),
    middleware: [thunkMiddleware]);
// Pass the store to the main.dart file
void main() => runApp(MyApp(
      store: store,
      title: "Flutter State Management",
    ));
class MyApp extends StatelessWidget {
  final Store<AppState> store;
  final String title;
  MyApp({Key key, this.store, this.title}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    // The store provider, which accepts the store and wraps our UI
    return new StoreProvider<AppState>(
      store: store,
      child: MaterialApp(
        theme: ThemeData.dark(),
        title: title,
        home: Scaffold(
          appBar: AppBar(
            title: new Text(title),
          ),
          body: new Center(
            child: new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                StoreConnector<AppStateint>(
                  converter: (store) => store.state.count,
                  builder: (_, count) {
                    return new Text(
                      '$count',
                    );
                  },
                ),
                // The storeConnector is used to have access to our state 
                // Props and have a return type for the builder.
                
                // display random quote and its author
                StoreConnector<AppStateString>(
                  converter: (store) => store.state.quote,
                  builder: (_, quote) {
                    return new Text(
                      'this is a $quote',
                      textAlign: TextAlign.center,
                      style: const TextStyle(fontSize: 20.0),
                    );
                  },
                ),
                
                // generate quote button
                StoreConnector<AppStateGenerateQuote>(
                  converter: (store) => () => store.dispatch(getRandomQuote),
                  builder: (_, generateQuoteCallback) {
                    // ignore: deprecated_member_use
                    return new FlatButton(
                        color: Colors.lightBlue,
                        onPressed: generateQuoteCallback,
                        child: new Text("Decrease"));
                  },
                )
              ],
            ),
          ),
          floatingActionButton: StoreConnector<AppStateIncrementCounter>(
            converter: (store) => () => store.dispatch(Actions.Increment),
            builder: (_, incrementCallback) {
              return new FloatingActionButton(
                onPressed: incrementCallback,
                tooltip: 'Increment',
                child: new Icon(Icons.add),
              );
            },
          ),
        ),
      ),
    );
  }
}
typedef void IncrementCounter(); // This is sync.
typedef void GenerateQuote();