Flutter state management of the road (five)

Connect a Flutter state management of the road (four)
this post describes flutter_mobx

Fish Redux

Version: 0.2.7

Library address: https: //github.com/alibaba/fish-redux/

Evolution

Busy fish fish_redux evolution 1.png

Busy fish fish_redux evolution 2.png

concept

Objects Explanation Your library
Action Expressed an intention contains two fields
type, payload
Connector Expression of how to read data from a large small data,
while small changes to how to synchronize data to large data, such data connection relationship
Reducer A pure function independent of context
State State value
Middleware Intermediate to form injection logic AOP Aspect Oriented
Component Package for presentation and view logic functions
Effect Action processing side effects
Dependent Expressed widget | how small adapter is connected to a large Component
Page Component inheritance, for page-level of abstraction, a built-Store (child Component share)

use

Examples of sources of official Todos

  1. Inlet Routing Configuration

    /// 创建应用的根 Widget
    /// 1. 创建一个简单的路由,并注册页面
    /// 2. 对所需的页面进行和 AppStore 的连接
    /// 3. 对所需的页面进行 AOP 的增强
    Widget createApp() {
      final AbstractRoutes routes = PageRoutes(
        pages: <String, Page<Object, dynamic>>{
          /// 注册TodoList主页面
          'todo_list': ToDoListPage(),
    
        },
        visitor: (String path, Page<Object, dynamic> page) {
          /// 只有特定的范围的 Page 才需要建立和 AppStore 的连接关系
          /// 满足 Page<T> ,T 是 GlobalBaseState 的子类
          if (page.isTypeof<GlobalBaseState>()) {
            /// 建立 AppStore 驱动 PageStore 的单向数据连接
            /// 1. 参数1 AppStore
            /// 2. 参数2 当 AppStore.state 变化时, PageStore.state 该如何变化
            page.connectExtraStore<GlobalState>(GlobalStore.store,
                (Object pagestate, GlobalState appState) {
              /// 根据appState变化pagestate
              return pagestate;
            });
          }
    
          /// AOP
          /// 页面可以有一些私有的 AOP 的增强, 但往往会有一些 AOP 是整个应用下,所有页面都会有的。
          /// 这些公共的通用 AOP ,通过遍历路由页面的形式统一加入。
          page.enhancer.append(
            ...
    
            /// Store AOP
            middleware: <Middleware<dynamic>>[
              logMiddleware<dynamic>(tag: page.runtimeType.toString()),
            ],
          );
        },
      );
    
      return MaterialApp(
        title: 'Fluro',
        home: routes.buildPage('todo_list', null),
        onGenerateRoute: (RouteSettings settings) {
          return MaterialPageRoute<Object>(builder: (BuildContext context) {
            return routes.buildPage(settings.name, settings.arguments);
          });
        },
      );
    }
    
  2. New Page

class ToDoListPage extends Page<PageState, Map<String, dynamic>> {
  ToDoListPage()
      : super(
          initState: initState,
          effect: buildEffect(),
          reducer: buildReducer(),
          view: buildView,
        );
}
  1. Defined state
class PageState extends MutableSource
    implements GlobalBaseState, Cloneable<PageState> {
  List<ToDoState> toDos;

  @override
  Color themeColor;

  @override
  PageState clone() {
    return PageState()
      ..toDos = toDos
      ..themeColor = themeColor;
  }

  @override
  Object getItemData(int index) => toDos[index];

  @override
  String getItemType(int index) => 'toDo';

  @override
  int get itemCount => toDos?.length ?? 0;

  @override
  void setItemData(int index, Object data) => toDos[index] = data;
}

PageState initState(Map<String, dynamic> args) {
  //just demo, do nothing here...
  return PageState();
}
  1. The definition of Action
enum PageAction { initToDos, onAdd }

class PageActionCreator {
  static Action initToDosAction(List<ToDoState> toDos) {
    return Action(PageAction.initToDos, payload: toDos);
  }

  static Action onAddAction() {
    return const Action(PageAction.onAdd);
  }
}
  1. Defined Reducer
Reducer<PageState> buildReducer() {
  return asReducer(
    <Object, Reducer<PageState>>{PageAction.initToDos: _initToDosReducer},
  );
}

PageState _initToDosReducer(PageState state, Action action) {
  final List<ToDoState> toDos = action.payload ?? <ToDoState>[];
  final PageState newState = state.clone();
  newState.toDos = toDos;
  return newState;
}

  1. Define Effect
Effect<PageState> buildEffect() {
  return combineEffects(<Object, Effect<PageState>>{
    Lifecycle.initState: _init,
    PageAction.onAdd: _onAdd,
  });
}

void _init(Action action, Context<PageState> ctx) {
  final List<ToDoState> initToDos = <ToDoState>[];
  /// 可作网络/IO等耗时操作
  ctx.dispatch(PageActionCreator.initToDosAction(initToDos));
}

void _onAdd(Action action, Context<PageState> ctx) {
  Navigator.of(ctx.context)
      .pushNamed('todo_edit', arguments: null)
      .then((dynamic toDo) {
    if (toDo != null &&
        (toDo.title?.isNotEmpty == true || toDo.desc?.isNotEmpty == true)) {
      ctx.dispatch(list_action.ToDoListActionCreator.add(toDo));
    }
  });
}
  1. View definitions View
Widget buildView(PageState state, Dispatch dispatch, ViewService viewService) {
  return Scaffold(
    appBar: AppBar(
      backgroundColor: state.themeColor,  /// 获取state状态
      title: const Text('ToDoList'),
    ),
    body: Container(
      child: Column(
        children: <Widget>[
        
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () => dispatch(PageActionCreator.onAddAction()),  /// 发出意图改变状态
      tooltip: 'Add',
      child: const Icon(Icons.add),
    ),
  );
}

Component sub-assembly

  1. Defined state
class ReportState implements Cloneable<ReportState> {
  int total;
  int done;

  ReportState({this.total = 0, this.done = 0});

  @override
  ReportState clone() {
    return ReportState()
      ..total = total
      ..done = done;
  }

}

  1. Component definitions
class ReportComponent extends Component<ReportState> {
  ReportComponent()
      : super(
          view: buildView,
        );
}
  1. Define Views
Widget buildView(
  ReportState state,
  Dispatch dispatch,
  ViewService viewService,
) {
  return Container(
      margin: const EdgeInsets.all(8.0),
      padding: const EdgeInsets.all(8.0),
      color: Colors.blue,
      child: Row(
        children: <Widget>[
          Container(
            child: const Icon(Icons.report),
            margin: const EdgeInsets.only(right: 8.0),
          ),
          Text(
            'Total ${state.total} tasks, ${state.done} done.',
            style: const TextStyle(fontSize: 18.0, color: Colors.white),
          )
        ],
      ));
}
  1. Connector connected Sons defined Component
class ReportConnector extends ConnOp<PageState, ReportState>
    with ReselectMixin<PageState, ReportState> {
  @override
  ReportState computed(PageState state) {
    return ReportState()
      ..done = state.toDos.where((ToDoState tds) => tds.isDone).length
      ..total = state.toDos.length;
  }

  @override
  void set(PageState state, ReportState subState) {
    throw Exception('Unexcepted to set PageState from ReportState');
  }
}
  1. page used, as follows transformation of page
class ToDoListPage extends Page<PageState, Map<String, dynamic>> {
  ToDoListPage()
      : super(
          initState: initState,
          effect: buildEffect(),
          reducer: buildReducer(),
          view: buildView,
          dependencies: Dependencies<PageState>(
              slots: <String, Dependent<PageState>>{
                'report': ReportConnector() + ReportComponent()
              }),
        );
}
  1. buildView follows the transformation of page
Widget buildView(PageState state, Dispatch dispatch, ViewService viewService) {
  final ListAdapter adapter = viewService.buildAdapter();
  return Scaffold(
    appBar: AppBar(
      backgroundColor: state.themeColor,
      title: const Text('ToDoList'),
    ),
    body: Container(
      child: Column(
        children: <Widget>[
          viewService.buildComponent('report'),   /// 加载子Component
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () => dispatch(PageActionCreator.onAddAction()),
      tooltip: 'Add',
      child: const Icon(Icons.add),
    ),
  );
}

Key Objects

Middleware

StoreMiddleware, is actually the dispatch function to strengthen the Store

store_middleware.png

/// fish-redux-master/lib/src/redux/apply_middleware.dart
StoreEnhancer<T> applyMiddleware<T>(List<Middleware<T>> middleware) {
  return middleware == null || middleware.isEmpty
      ? null
      : (StoreCreator<T> creator) => (T initState, Reducer<T> reducer) {
            final Store<T> store = creator(initState, reducer);
            final Dispatch initialValue = store.dispatch;   /// 原始的dispatch
            store.dispatch = middleware
                .map((Middleware<T> middleware) => middleware(   /// 执行middleware最外层函数返回Composable<T>,此函数在这里用于对dispatch包装
                      dispatch: (Action action) => store.dispatch(action),
                      getState: store.getState,
                    ))
                .fold(
                  initialValue,
                  (Dispatch previousValue,
                          Dispatch Function(Dispatch) element) =>
                      element(previousValue),		/// 每次将上一个dispatch传入,返回一个新的dispatch,利用闭包,新的dispatch持有了上一个dispatch的引用
                );

            return store;
          };
}

Middleware wherein one example is as follows:

Middleware<T> logMiddleware<T>({
  String tag = 'redux',
  String Function(T) monitor,
}) {
  return ({Dispatch dispatch, Get<T> getState}) {
    return (Dispatch next) {   /// 此方法在上一个示意代码段里,是fold方法里的element
      return isDebug()
          ? (Action action) {   /// 返回包装的Dispatch
              print('---------- [$tag] ----------');
              print('[$tag] ${action.type} ${action.payload}');

              final T prevState = getState();
              if (monitor != null) {
                print('[$tag] prev-state: ${monitor(prevState)}');
              }

              next(action);

              final T nextState = getState();
              if (monitor != null) {
                print('[$tag] next-state: ${monitor(nextState)}');
              }
            }
          : next;
    };
  };
}

Global Store

fish_redux_ store global timing chart .png

 page.connectExtraStore<GlobalState>(GlobalStore.store,
            (Object pagestate, GlobalState appState) {
          final GlobalBaseState p = pagestate;
          if (p.themeColor != appState.themeColor) {
            if (pagestate is Cloneable) {
              final Object copy = pagestate.clone();
              final GlobalBaseState newState = copy;
              newState.themeColor = appState.themeColor;
              return newState;
            }
          }
          return pagestate;
        });

Connector

fish_redux_connector_reducer.png

abstract class MutableConn<T, P> implements AbstractConnector<T, P> {
  const MutableConn();

  void set(T state, P subState);

  @override
  SubReducer<T> subReducer(Reducer<P> reducer) {
  	/// 将本Component的reducer包装成新的reducer给父的store注入
    return (T state, Action action, bool isStateCopied) {
      final P props = get(state);
      if (props == null) {
        return state;
      }
      final P newProps = reducer(props, action);  /// 调用本Component的reducer,返回子的state
      final bool hasChanged = newProps != props;
      final T copy = (hasChanged && !isStateCopied) ? _clone<T>(state) : state;
      if (hasChanged) {
        set(copy, newProps);  /// 通知父Component同步状态
      }
      return copy;
    };
  }
}

See the rest of the official document: https: //github.com/alibaba/fish-redux/blob/master/doc/README-cn.md

to sum up

advantage:

  1. Each Page a Store, share their child Component Store, a single Component still has redux features to achieve partition
  2. Promoter reducer automatically merged page data automatically synchronized with the store
  3. Establish a link between the use of eventbus page, by broadcast effect to distribute Action page itself does not care to other page
  4. A global shared state may be defined in a global Store associated with page.connectExtraStore

Disadvantages:

  1. The concept is more, a higher learning curve
  2. All kinds of objects need to be defined more and more documents
  3. To grasp the scale of the project is not in place is easy to introduce unnecessary complexity
  4. Code structure more invasive

To be continued

Fish_redux conceptual framework defined by many, also need to continue in-depth ...

reference

  1. Getting hands-Fish-Redux development flutter
  2. The principle of Connector
Published 15 original articles · won praise 11 · views 10000 +

Guess you like

Origin blog.csdn.net/Weiye__Lee/article/details/104042562