Let’s get rid of the Platform Of Flutter for a moment and see what basic solutions you can think of if you want to implement data sharing.

  • Global static variable
  • Singleton (XXXMnager, such as UserManger)
  • Persistence (SharePref)
Ok, the above scheme is really simple and rough, and easy to use. However, it is a little difficult to timely notify each concerned party after the data change is designed.

So, because of this demand, some of the industry’s biggest people are working tirelessly to come up with some amazing global data-sharing notification solutions.

So, MY focus on Flutter is on Redux and event_bus. This article is to summarize my understanding of Redux and event_bus.

Redux

The cost of understanding is high and the coupling is high, so to understand how Redux works, you need to understand a few concepts

Store

So here’s his constructor, and let’s just take a look at it and forget about the arguments.

Store(
    this.reducer, {
    State initialState,
    List<Middleware<State>> middleware = const [],
    bool syncStream: false,

    /// If 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.
    bool distinct: false,})Copy the code

A Store can simply be thought of as a repository for various types of data and actions that handle that data. As you can see, it can be configured with a generic type that represents the following State. Ok, let’s move on to State.

State

State is not actually the base type for Dart, it’s actually the S in the Store definition above. Yes, it’s a generic type. It can be the base type for Dart, String, int, double, or whatever class you define. In short, it is the data that stores guard and maintain.

StoreProvider

Here is its constructor, and the arguments here are relatively simple, so you can look at them directly

const StoreProvider({
    Key key,
    @required Store<S> store,
    @required Widget child,
  })Copy the code

A store and a child are widgets that bind the store to the child. If data changes in the store, the child update tree can be notified. Then who decides which children in a specific child need to be updated? Of course, it’s StoreConnector. The matchmaker puts people together for you, and it doesn’t matter whether you hold hands or not.

StoreConnector

Let’s look at the constructors

StoreConnector({
    Key key,
    @required this.builder,
    @required this.converter,
    this.distinct = false,
    this.onInit,
    this.onDispose,
    this.rebuildOnChange = true,
    this.ignoreChange,
    this.onWillChange,
    this.onDidChange,
    this.onInitialBuild,
  })Copy the code

The constructor has a lot of arguments, so if you’re interested in learning all of them, just the required tag if you don’t have the time. The first one is Builder. This is WidgetBuilder. Obviously, the converter parameter is used to build a view.

/// Convert the entire Store into a ViewModel. The ViewModel will be used

/// to build a Widget using the ViewModelBuilder.

typedef StoreConverter<S, ViewModel> = ViewModel Function(

  Store<S> store,

);Copy the code

It’s a little bit less of a mystery when you convert a Store to a ViewModel, and when you convert it, it’s actually a better way to give the data to the Builder to build the View, right? Isn’t it? We mentioned earlier that the store can accept an action that changes the data in it. Who handles these actions? The first parameter of the Store constructor is reducer (reducer), which is a state reducer. The data has one state and becomes another state after action, right? Well, let’s look at the Reducer.

Reducer

Reducer is defined as follows:

typedef State Reducer<State>(State state, dynamic action);Copy the code

It is clear that the state mentioned above should be processed by action and changed to another state. Then, the state processing is only Reducer processing, adding some log records, performance monitoring and other processing, what should we do? Okhttp, you can see it all over the place, processing an HTTP request is a string of request parameters json and you can go through the action backend server and change it to another string of json, right? So some of the processing of the request header, the validation of the request parameters, Is it all given to the interceptor? This design idea is understood, so the Middleware here, although it sounds mysterious, is actually the interceptor, which is executed before the Reducer, as FAR as I know, and those who have different opinions can leave suggestions in the comments.

Middleware

Are defined as follows

/// ### Example
///
///     loggingMiddleware(Store<int> store, action, NextDispatcher next) {
///       print('${new DateTime.now()}: $action');
///
///       next(action);
///     }
///
///     // Create your store with the loggingMiddleware
///     final store = new Store<int>(
///       counterReducer,
///       middleware: [loggingMiddleware],
///     );
typedef void Middleware<State>(
  Store<State> store,
  dynamic action,
  NextDispatcher next,
);Copy the code

Yeah, and once we’re done with that, we’re going to hand it over to the next action, and it’s not going to cost us much to understand. Ok, so to summarize these concepts that we’ve got, we’re going to string them together, and we’re going to represent them in a diagram.



It can be described in one sentence:

The store exposes itself through storeProvider and gives it to StoreConnector to better link to the control. The control (not necessarily where the control is, but we can understand it well) sends actions to the Reducer in the store. If there is middleware, The middleware intercepts it and then passes it to Recuder for processing. After processing, the store data changes and the storeConnector notifies the component of the update. The OK process runs out.

event_bus

The cost of understanding is slightly lower and the coupling is also lower

Initialize the
import 'package:event_bus/event_bus.dart';

EventBus eventBus = new EventBus();Copy the code

Send the event
eventBus.fire(event);Copy the code

Listen for an event
eventBus.on().listen((event) { 
    print(event.runtimeType);
});Copy the code

There is not much concept, shameless theft of a picture is



To sum it up in one sentence: if you want to inform the bus about changes made somewhere, ask the bus who wants to care.

conclusion

In general, both Redux and BUS can implement global data sharing and change notification, but bus is much easier to understand and has many concepts, and unlike Redux, which needs to bind to controls through storeConnector, causing unnecessary coupling, I prefer to use BUS to address the need for global data sharing change notification.


Flutter Event Bus Implementation principle portal