preface

A lot of Inspiration for Flutter comes from React. The idea behind Flutter is to separate data from views and render them using data maps. Therefore, in Flutter, its widgets are immutable, and its dynamic parts are all placed in states. So state management naturally becomes the object of our close attention.

Earlier we discussed the use of scoped_model for state management in FLUTTER. Since the article was published, many students have asked me whether Redux or Scoped is better.

This series will delve into several state management scenarios:

  • Scoped_model
  • redux
  • BLoC
  • Contrast summary

So today I’m going to introduce you to the use of Redux for state management in Flutter. I want you to think about these questions before reading this article.

  • What is the story
  • What does Redux give us, and why should we use it
  • What is its basic idea
  • Whether Redux is really for us

Ok, let’s begin the formal introduction of Redux.

Redux

Why do YOU need state management

When we first started building the application, it might have been simple. We have some states, and we just map them to views. This simple application may not require state management.

But as you add functionality, your application will have dozens or even hundreds of states. This is what your application should look like.

At this point, we urgently needed a framework to help us clarify these relationships, and a state management framework came into being.

What is the story

Redux is a one-way data flow architecture that makes it easy to develop, maintain, and test applications.

  • We’re in Redux, where all the states are stored in Store. This Store is going to be on top of the App.
  • The View takes the State stored in the Store and maps it to the View. The View also interacts with the user, the user clicks a button to slide the screen and so on, and the data changes because of the interaction.
  • Redux let us not have the View manipulate the data directly, but instead send an action to tell the Reducer that the state has changed.
  • When the Reducer receives this action, it goes back to the Action table, finds the matched action, generates a new state based on the action and puts the new state in the Store.
  • Store discards the old state object and stores the new state object, notifying all views that use the state of updates (similar to setState). So we can synchronize the states in different views.

Lets do it!

Let’s use CountApp as a simple example. This section describes the usage of flutter_redux/redux. The complete code for the project has been uploaded to Github.

This is an app that uses Redux to share status information between different pages. Both pages rely on a number that increases with the number of times we press the button.

Step 1: Add dependencies

  • For details, see redux,flutter_redux
  • For details, see juejin.cn/post/684490…

Step 2: Create State

We have just introduced the process of Redux. The state is generated by reducer and stored in the Store. When the Store updates the state, it does not change the original state object, but directly replaces the old state object generated by the reducer with the new state object. Therefore, our state should be immutable.

import 'package:meta/meta.dart';
/** * all attributes in State should be read-only */
@immutable
class CountState{
  int _count;
  get count => _count;

  CountState(this._count);
}
Copy the code

Step 3: Create the Action

You may be confused about Action at first. What exactly is an action? How a View issues an action. In fact, action is just a code name for the way we operate on the state. In our application, the only function is to make count +1, so we only have one action.

/** * define all actions that operate on the State */
enum Action{
  increment
}
Copy the code

Step 4: Create a Reducer

Reducer is our state generator, which receives our original state, then receives an action, and matches that action to create a new state.

/** * Reducer generates a new CountState */ based on incoming actions
CountState reducer(CountState state,action){
  / / to match the Action
    if(action == Action.increment){
      return CountState(state.count+1);
    }
    return state;
}
Copy the code

Step 5: Create a Store

The Store receives a Reducer and initializable State. If we want to use Redux to manage global State, we need to Store the Store in the entry of the application. The state of the application must be initialized once before the application is opened. So add an initialization function to State.

// This code is written in State
CountState.initState(){ _count = 0; }Copy the code
// Apply the top layer
void main() {
  final store =
      Store<CountState>(reducer, initialState: CountState.initState());
  runApp(new MyApp(store));
}
Copy the code

Step 6: Place Store at the top level

Flutter_redux provides a great widget called StoreProvider, which is also very simple to use, receiving a store and child widget.

class MyApp extends StatelessWidget {
  final Store<CountState> store;

  MyApp(this.store);

  @override
  Widget build(BuildContext context) {
    return StoreProvider<CountState>(
      store: store,
      child: new MaterialApp(
        title: 'Flutter Demo',
        theme: newThemeData( primarySwatch: Colors.blue, ), home: TopScreen(), ), ); }}Copy the code

Step 6: Get the state in Store in the child page

It is recommended that you look at the actual code against the explanation below.

StoreConnector<CountState,int>(
              converter: (store) => store.state.count,
              builder: (context, count) {
                returnText( count.toString(), style: Theme.of(context).textTheme.display1, ); },),Copy the code

To get the store we need to use StoreConnector<S,ViewModel>. StoreConnector can find the top-level store through StoreProvider. And able to implement the peoplewidget when state changes.

  • The first thing we need to do is enforce a type declaration. S stands for what type of state we need to get from the store, and ViewModel refers to the actual type when we use this state.
  • And then we need to declare a Converter

    , which just converts the Store to the information that the actual ViewModel is going to use, like we’re actually going to use count here, so we’re going to extract count.
    ,viewmodel>
  • Builder is where we actually create the Widget based on state, it takes a context, and the ViewModel we just converted, so we just need to render the count in the Text Widget.

Step 7: Issue an Action

In the second page, our application sent an action by clicking on floatingActionButton and notified the Reducer that the new state had been generated.

floatingActionButton: StoreConnector<CountState,VoidCallback>(
        converter: (store) {
          return () => store.dispatch(Action.increment);
        },
        builder: (context, callback) {
          returnFloatingActionButton( onPressed: callback, child: Icon(Icons.add), ); },),Copy the code
  • Again, we use StoreConnector

    . In this case, VoidCallback is issued as an action.
    ,viewmodel>
  • Store.dispatch initiates an action, which is intercepted by any middleware, and after the middleware runs, the action is sent to the given Reducer to generate a new state and update the state tree.

That’s all there is to sharing state information with Redux in Flutter.

Q&A

ViewModel performance optimization

Our StoreConnector is able to extract information from stores and translate it into ViewModels, where there is a point of performance optimization. Our example here is very simple. Its ViewModel is just an int value. When our ViewModel is complex, we can use the Distinct attribute of StoreConnector for performance optimization. The way to use it is simple: we need to override the [==] and [hashCode] method in ViewModel and set the distinct attribute to True.

How do I handle asynchronous data

Redux provides a simple way to update the state of an application in response to synchronous operations. However, it lacks tools to handle asynchronous code. How do we deal with asynchronous correspondence.

This requires an interrupt to handle the asynchronous request, and then a new action to tell the Reducer to generate the new State. There is another library, redux_thunk, written by brianegan to help use redux to handle asynchronous requests in flutter. I’ll detail how to handle asynchronous operations in Redux in a later article.

Do you think Redux is really suitable for Flutter

We found that Redux did work well with Flutter. In React, data does not have uplink capability. Therefore, data flows in one direction to form a loop for state management. It seems that the advantages of Flutter are not fully exploited. As you can see from this simple example, using Redux can be a bit of a hassle, and can lead to Redux hell if you don’t use it properly. The high cost of learning is also a big pain point.

Of course, reDUx is a mature state management architecture. If you are used to ReDUx, you can quickly and easily build your own state management application using Flutter_REdux.

So what do you think of Redux now?

Write in the last

The code has been uploaded to Github: github.com/Vadaski/Flu…

The following sources were used for this article

  • redux.js.org/
  • Flutterbyexample.com/what-is-red…

To learn more

You can learn more about Flutter -redux in these places

  • Understand flutter-redux:flutterbyexample.com/what-is-red…
  • Learn about redux’s problems: medium.com/fluttery/th…

If you have any further comments on Flutter_redux or suggestions for this article, or if there are any errors in this article, please leave them in the comments section below and at [email protected], and I will contact you within 24 hours!

In the next chapter, we are supposed to explore the practice of BLoC in Flutter, and BLoC is very Reactive Programming, so I decide to introduce Dart :Stream before introducing it. So in the next article, WE will introduce Stream and Stream Programming. Please pay attention.