The ScopedModel, a simple state management architecture that is unlikely to be used in commercial projects, is the last one, and this one is Redux, an elegant and practical state management framework. This Demo address: github.com/windinwork/…

I. Preparation of Redux

The concept of Redux is derived from React, and it can be a bit complicated to understand for people who don’t work on the front end or have no experience with React. For those of you who are not familiar with Redux, here are two great articles that introduce the concept and knowledge of Redux:

Redux Tutorial part 1: Basic Usage

Redux Tutorial ii: Middleware and Asynchronous Operations

The concept of Redux

After learning Redux, we can learn that Redux mainly involves the following concepts:

  • Store, that’s where you keep your data. There can only be one Store for the entire app. The Store has an important method called dispatch(Action) to send an action.
  • State is a snapshot of data at a certain point in time. One State corresponds to one View. As long as the State is the same, the View is the same.
  • Action is the notification sent by the View, and the State is changed by the Reducer.
  • Reducer, is a pure function that takes Action and the current State as arguments and returns a new State.
  • The Middleware, Middleware, takes place between the actions issued and the Reducer step and is used to add additional capabilities, such as handling asynchronous operations or printing logs.

The workflow of Redux is shown in the figure. The user sends the Action, the Store automatically processes it to the Middleware, and then the Reducer is sent. The Reducer returns a new State, and the View is re-rendered through the Store. The View here exists as a Widget in a Flutter.

This is Redux, so it’s easy to understand.

The use of Redux in Flutter

Flutter includes the Flutter_redux library as well as the Middleware library redux_thunk to support asynchronous operations. There is better support for Thunk actions.

Let’s look at the concepts in Flutter_redux.

  1. StoreProvider, a basic Widget, is typically used as a parent layout at the entrance to an application to pass stores.

  2. StoreConnector, a Widget that retrieves the Store, is used to rebuild the UI in response to state change events emitted by the Store.

  • StoreConnector has two parameters marked @required, one for Converter, which converts state in store to viewModel, and the other for Builder, The Purpose of the Builder is to further transform the viewModel into a UI layout.

  • StoreConnector has a function worth mentioning: onInit, in which initialization can be performed.

  1. For asynchronous operations, we introduced the Redux_Thunk library, a middleware that handles thunkAction. ThunkAction is a function that takes only one Store parameter.

The functions of Redux can be implemented in Flutter using StoreProvider and StoreConnector.

4. Practice of Redux

Here is an example of a common get list select list. One page shows selected items and jumps to the list, and one page shows the list.

1. Introduce Redux’s third-party libraries

Redux: ^3.0.0 flutter_redux: ^0.5.3 redux_thunk: ^0.2.1Copy the code

2. To create a Store

There is only one Store globally, and this encapsulates a method to create a Store. When creating a Store, pass the Reducer and initialize State and Middleware.

Store<AppState> createStore() {
  returnStore(appReducer, initialState: AppState.Initial (), Middleware: [// thunkMiddleware with thunk action]); }Copy the code

3. Create a State

State, you need to create an AppState as the State of the entire application. In addition, you can have different states according to different pages. For example, if you have a ListPage, you can have a ListState. And ListState is managed as a member variable in AppState.

// app state
class AppState {
  ListState listState;

  AppState(this.listState);
}
Copy the code
// list state
class ListState {
  bool _init = false; List<String> _list = []; // List data String _selected ='Not selected'; // The selected list item is ListState(this._init, this._list, this._selected); }Copy the code

4. Create the Action

An Action is a notification that the Widget sends after the user has acted on it. For the ListPage page, create a list_action.dart file with actions that can be issued. In the example, you need two actions. An Action is issued when the list data is loaded to refresh the list, and an Action is issued when the list item is selected.

/ class FetchListAction {List<String> List; FetchListAction(this.list); } /** * select list */ class SelectItemAction {String selected; SelectItemAction(this.selected); }Copy the code

5. Create a Reducer

Reducer is a pure function with State and Action parameters. Here, we only need to worry about returning a new State based on the incoming Action and the old State.

Just like State, Reducer is divided into app_reducer of application and LIST_reducer of list page.

Reducer is a pure function. In the example, a new AppState is returned using app_reducer, and a new ListState is returned using list_Reducer to process FetchListAction and SelectItemAction respectively.

AppState appReducer(AppState state, dynamic action) {
  return AppState(listReducer(state.listState, action));
}
Copy the code
ListState listReducer(ListState pre, dynamic action) {
  if (action is FetchListAction) {
    return ListState(true, action.list, pre.selected);
  }
  if (action is SelectItemAction) {
    return ListState(pre.isInit, pre.list, action.selected);
  }
  return pre;
}
Copy the code

6. Use Thunk Action

ThunkMiddleware was introduced when the Store was created to make projects support Thunk actions.

The project requires an asynchronous operation to load a list. This can be done using the ThunkAction, which takes only one store argument.

ThunkAction<AppState> fetchList = (Store<AppState> Store) async {// Wait (Duration(milliseconds) : 3000)); var list = ["1. Redux State Management"."2. Redux State Management"."3. Redux State Management"."4. Redux State Management"."5. Redux State Management"."6. Redux State Management"."7. Redux State Management"."8. Redux State Management"."9. Redux State Management"."10. Redux State Management"
  ];

  store.dispatch(FetchListAction(list));
};
Copy the code

7. The UI layout

With the code above, we are ready for Redux. Now you can lay out the page.

In Main. dart, all you need to do is create a Store and use StoreProider as the root layout.

Var Store = createStore(); // Use StoreProvider as the root layoutreturn StoreProvider<AppState>(
        store: store,
        child: MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: ShowPage(),
        ));
  }
Copy the code

Show_page. Dart displays the selected list item and provides a button entry to the ListPage.

In show_page.dart, the data for the selected list item comes from the Store, so StoreConnector is used to build the UI from that data.

StoreConnector<AppState, String>(
  converter: (store) => store.state.listState.selected,
  builder: (context, selected) {
    returnText(selected); },),Copy the code

As you can see from above, StoreConnector requires two generics, one for the AppState we created and the other for the ViewModel, where we directly use String as the ViewModel.

StoreConnector to define two functions, one is the converter, and took out the selected list item from Store data Store. State. ListState. Selected, the other is a builder, Convert the selected returned by Converter further to the screen: Text(selected).

This is how StoreConnector is used.

(3) list_page. Dart

Dart provides the loading and clicking functions for list_page.dart.

List_page.dart displays the loading interface at first and waits for the data to load successfully to display the list interface. StoreConnector is also used to build the UI. StoreConnector converts AppState to ListState to determine whether the loading interface or the list interface is displayed.

Also, load the list in StoreConnector’s onInit function.

Since loading a list is an asynchronous operation, you use the thunk action of the fetchList defined earlier, which is performed via store.dispatch(fetchList).

StoreConnector<AppState, ListState>(
    // 初始化时加载列表
    onInit: (store) {
  if(! store.state.listState.isInit) { store.dispatch(fetchList); } // Convert store state to viewModel, Converter: (store) {returnstore.state.listState; // Update interface by viewModel}, Builder: (contxet, state) {// Display interface by stateif(! State. IsInit) {// Display the loading interfacereturn buildLoad();
  } elseVar list = state.list;returnbuildList(list); }})Copy the code

Also, when you click on the list, issue an Action for the selected list item, which is the SelectItemAction defined earlier.

var store = StoreProvider.of<AppState>(context); store.dispatch(SelectItemAction(list[index])); // Return to navigator.pop (context);Copy the code

Actions sent by Store.dispatch (Action) are processed by Middleware and Reducer to State, and StoreConnector automatically reconstructs the UI based on State.

At this point, an application using the Redux architecture is basically complete. The full code can be found at github.com/windinwork/…

Five, the summary

In Redux, data always flows “in one direction”, ensuring clarity in the process. Compared to the ScopedModel architecture, Redux is definitely more normative and easier to develop and maintain. However, Redux adds relative complexity, so small and simple projects do not need to consider introducing Redux architecture, but for large Flutter projects, Redux is a useful architecture.

Six, think

Since Redux has only one app and only one Store, the application needs to maintain the entire AppState. During large client code iterations, this conflicts with the need to compile and run componentizations and separate modules. Will this prevent Redux from becoming a major architectural factor for large projects?

Refer to the directory

Flutter Redux Thunk, an example finally.

Introduction to Redux in Flutter

Sample: inKino