1.案例实现
阿里巴巴闲鱼出品的fish-redux,针对于大型互联网项目推荐使用的一种状态管理框架,今天来撸一撸,还是老样子实现如下一种双向绑定吧:
1.1.安装插件FishReduxTemplate
使用fish-redux时候先安装下插件FishReduxTemplate,不然一步步写代码估计你要疯;
https://plugins.jetbrains.com/plugin/12139-fishreduxtemplate
1.2.添加依赖
dependencies:
flutter:
sdk: flutter
fish_redux: ^0.3.3
1.3. 根据插件一键生成action/effect/page/reducer/state/view等代码;
生存方式直接参考https://plugins.jetbrains.com/plugin/12139-fishreduxtemplate这个插件的截图;
1.4 Demo代码实现
按照官方案例代码,需要配置一个PageRoutes,其内部pages是key/value,就是路由的意思,其他可以先不用配置;
Widget createApp() {
final AbstractRoutes routes = PageRoutes(
pages: <String, Page<Object, dynamic>>{
'counter': CounterPage(),
},
);
return MaterialApp(
title: 'FishDemo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: routes.buildPage('counter', null),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<Object>(builder: (BuildContext context) {
return routes.buildPage(settings.name, settings.arguments);
});
},
);
}
page.dart代码如下,有一个initState,effect,reducer,view,dependencies需要制定,其中effect是副作用的意思,而reducer是状态管理,view是视图管理,initState就是初始化state;
import 'package:fish_redux/fish_redux.dart';
import 'effect.dart';
import 'reducer.dart';
import 'state.dart';
import 'view.dart';
class CounterPage extends Page<CounterState, Map<String, dynamic>> {
CounterPage()
: super(
initState: initState,
effect: buildEffect(),
reducer: buildReducer(),
view: buildView,
dependencies: Dependencies<CounterState>(
adapter: null,
slots: <String, Dependent<CounterState>>{
}),
middleware: <Middleware<CounterState>>[
],);
}
先定义一个CounterState的state类,其中必须实现clone,由于redux关键核心是状态state是不可变类型,状态更新是需要拷贝的,因此需要实现Cloneable接口,重写clone方法,达到状态更新,initState就是初始化时候数据,代码如下:
import 'package:fish_redux/fish_redux.dart';
class CounterState implements Cloneable<CounterState> {
int count;
@override
CounterState clone() {
return CounterState()
..count = count;
}
}
CounterState initState(Map<String, dynamic> args) {
CounterState state = CounterState();
state.count = 10;
return state;
}
然后定义CounterAction去管理Action的生成,这里有4个action,一开始想定义两个,但是发现同一个action只能被 effect或者reducer处理,如果同时定义了则effect先处理:
import 'package:fish_redux/fish_redux.dart';
//TODO replace with your own action
enum CounterAction {
effect_increment,
effect_decrement,
reducer_increment,
reducer_decrement
}
class CounterActionCreator {
static Action onEffectIncrement() {
return const Action(CounterAction.effect_increment);
}
static Action onReducerIncrement() {
return const Action(CounterAction.reducer_increment);
}
static Action onEffectDecrement(){
return const Action(CounterAction.effect_increment);
}
static Action onReducerDecrement(){
return const Action(CounterAction.reducer_decrement);
}
}
来看官方数据给的一张示意图,其中View触发事件是会先经过Effect,在调用reducer,之后生成新state,更新视图View;
effect/redux主要定义了2个action和对应的action处理方法,注意这里他们的action是不相同的,现在来看effect/redux代码:
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Effect<CounterState> buildEffect() {
return combineEffects(<Object, Effect<CounterState>>{
CounterAction.effect_increment: _onIncrement,
CounterAction.effect_decrement: _onDecrement,
});
}
void _onIncrement(Action action, Context<CounterState> ctx) {
print('Effect_onIncrement');
}
void _onDecrement(Action action, Context<CounterState> ctx) {
print('Effect_onDecrement');
}
import 'package:fish_redux/fish_redux.dart';
import 'action.dart';
import 'state.dart';
Reducer<CounterState> buildReducer() {
return asReducer(
<Object, Reducer<CounterState>>{
CounterAction.reducer_increment: _onIncrement,
CounterAction.reducer_decrement: _onDecrement,
},
);
}
CounterState _onIncrement(CounterState state, Action action) {
print('Reducer_onIncrement');
state.count++;
return state.clone();
}
CounterState _onDecrement(CounterState state, Action action) {
print('Reducer_onDecrement');
state.count--;
return state.clone();
}
最后来看视图层代码,FloatingActionButton和ActioChip点击时候分别dispatch一个InCrement/Decrement的Action:
import 'package:fish_redux/fish_redux.dart';
import 'package:flutter/material.dart';
import 'action.dart';
import 'state.dart';
Widget buildView(
CounterState state, Dispatch dispatch, ViewService viewService) {
return Scaffold(
appBar: AppBar(
title: Text('Fish-Redux'),
),
body: CounterHomeWapper(
state: state, dispatch: dispatch, viewService: viewService),
floatingActionButton: FloatingActionButtonWrapper(
state: state, dispatch: dispatch, viewService: viewService),
);
}
class FloatingActionButtonWrapper extends StatelessWidget {
final CounterState state;
final Dispatch dispatch;
final ViewService viewService;
const FloatingActionButtonWrapper(
{Key key, this.state, this.dispatch, this.viewService})
: super(key: key);
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: (){
dispatch(CounterActionCreator.onEffectIncrement());
dispatch(CounterActionCreator.onReducerIncrement());
},
child: Text('${state.count}'),
);
}
}
class CounterHomeWapper extends StatelessWidget {
final CounterState state;
final Dispatch dispatch;
final ViewService viewService;
const CounterHomeWapper(
{Key key, this.state, this.dispatch, this.viewService})
: super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: ActionChip(
label: Text('${state.count}'),
onPressed: () {
dispatch(CounterActionCreator.onEffectDecrement());
dispatch(CounterActionCreator.onReducerDecrement());
}
),
);
}
}
总体看起来确实做到数据分离,和flutter-redux比起来,没有需要自己state-->model方式,使用起来成本是代码量增加,换来的效益是逻辑解耦,界面清晰;
2.源码分析
2.1.界面加载流程
现在来分析下其代码流程吧,从加载界面起,在main方法有如下:
void main() => runApp(createApp());
而createApp返回的是一个
return MaterialApp(
title: 'FishDemo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: routes.buildPage('counter', null),
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute<Object>(builder: (BuildContext context) {
return routes.buildPage(settings.name, settings.arguments);
});
},
);
home界面加载的就是布局界面routes.buildPage:
/// Define a basic behavior of routes.
abstract class AbstractRoutes {
Widget buildPage(String path, dynamic arguments);
}
@(知识体系总结)immutable
class PageRoutes implements AbstractRoutes {
@override
Widget buildPage(String path, dynamic arguments) =>
pages[path]?.buildPage(arguments);
}
Page#buildPage方法会创建_PageWidget,然后被protectedWrapper包裹
Widget buildPage(P param) => protectedWrapper(_PageWidget<T, P>(
page: this,
param: param,
));
来看protectedWrapper
abstract class Component<T> extends Logic<T> implements AbstractComponent<T> {
final WidgetWrapper _wrapper;
WidgetWrapper get protectedWrapper => _wrapper;
}
Component({
...
WidgetWrapper wrapper,
}) : assert(view != null),
_wrapper = wrapper ?? _wrapperByDefault,
...
Page({
...
WidgetWrapper wrapper,
...
}) : super(
...
wrapper: wrapper,
...
);
之前案例代码是没有在CounterPage构造方法中并没有定义这个wrapper,那么protectedWrapper使用的是_wrapperByDefault,如下代码发现,其实_wrapperByDefault放回就是里面的Widget;
static Widget _wrapperByDefault(Widget child) => child;
至此,可以看出Page#buildPage就是生存一个_PageWidget,_PageWidget是继承自StatefulWidget;
Widget buildPage(P param) => protectedWrapper(_PageWidget<T, P>(
page: this,
param: param,
));
class _PageWidget<T, P> extends StatefulWidget {
final Page<T, P> page;
final P param;
const _PageWidget({
Key key,
@required this.page,
@required this.param,
}) : super(key: key);
@override
State<StatefulWidget> createState() => _PageState<T, P>();
}
class _PageState<T, P> extends State<_PageWidget<T, P>> {
@override
Widget build(BuildContext context) {
...
//构建界面的地方
return widget.page.buildComponent(
_store,
_store.getState,
bus: _pageBus,
enhancer: widget.page.enhancer,
);
}
}
Component#buildComponent代码:
abstract class Component<T> extends Logic<T> implements AbstractComponent<T> {
@override
Widget buildComponent(
...
}) {
...
return protectedWrapper(
isPureView()
? _PureViewWidget<T>(
enhancer.viewEnhance(protectedView, this, store),
getter,
bus,
)
: ComponentWidget<T>(
component: this,
getter: _asGetter<T>(getter),
store: store,
key: key(getter()),
bus: bus,
enhancer: enhancer,
),
);
}
}
这里会判断isPureView,isPureView代码如下,
bool isPureView() {
return protectedReducer == null &&
protectedEffect == null &&
protectedDependencies == null;
}
Reducer<T> get protectedReducer => _reducer;
ReducerFilter<T> get protectedFilter => _filter;
Effect<T> get protectedEffect => _effect;
Dependencies<T> get protectedDependencies => _dependencies;
一般情况下是指定了reducer,effect的,因此使用构建的ComponentWidget;
class ComponentWidget<T> extends StatefulWidget {
@override
ComponentState<T> createState() => component.createState();
}
class ComponentState<T> extends State<ComponentWidget<T>> {
Widget build(BuildContext context) => _ctx.buildWidget();
}
class ComponentContext<T> extends LogicContext<T> implements ViewUpdater<T> {
@override
Widget buildWidget() {
//_widgetCache是缓存,一开始是null
Widget result = _widgetCache;
if (result == null) {
//第一次进会执行,view(state, dispatch, this)方法
result = _widgetCache = view(state, dispatch, this);
dispatch(LifecycleCreator.build(name));
}
return result;
}
}
view(state, dispatch, this)追踪到如下就是protectedView
final ViewBuilder<T> view;
typedef ViewBuilder<T> = Widget Function(
T state,
Dispatch dispatch,
ViewService viewService,
);
@override
ComponentContext<T> createContext(
...
}) {
assert(bus != null && enhancer != null);
return ComponentContext<T>(
...
view: enhancer.viewEnhance(protectedView, this, store),
...
);
}
ViewBuilder<T> get protectedView => _view;
其view就是page.dart中CounterPage构造方法中指定的buildView,至此界面从加载到显示过程告一段落;
2.2.Action分发流程
在ComponentContext#buildWidget时候,知道了view(state, dispatch, this)就是调用view.dart中的buildView方法,这里的dispatch是什么呢?
@override
Widget buildWidget() {
Widget result = _widgetCache;
if (result == null) {
result = _widgetCache = view(state, dispatch, this);
dispatch(LifecycleCreator.build(name));
}
return result;
}
ComponentContext extends LogicContext,其dispatch是在LogicContext定义的;
abstract class LogicContext<T> extends ContextSys<T> with _ExtraMixin {
/// create Dispatch
_dispatch = logic.createDispatch(
_effectDispatch,
logic.createNextDispatch(
this,
enhancer,
),
this,
);
@override
dynamic dispatch(Action action) => _dispatch(action);
}
LogicContext#createDispatch调用的是父类 AbstractLogic#createDispatch,而实现类是Logic#createDispatch,代码如下
@override
Dispatch createDispatch(
Dispatch effectDispatch,
Dispatch nextDispatch,
Context<T> ctx,
) =>
helper.createDispatch<T>(effectDispatch, nextDispatch, ctx);
createDispatch代码如下:
/// return [Dispatch]
Dispatch createDispatch<T>(Dispatch onEffect, Dispatch next, Context<T> ctx) =>
(Action action) {
final Object result = onEffect?.call(action);
if (result == null || result == false) {
next(action);
}
return result == _SUB_EFFECT_RETURN_NULL ? null : result;
};
这里判断了onEffect是否为空,如果不为空,需要先执行effect中处理Action的方法,之后根据result的值去看是否需要执行next(action);从这里可以得出一个结论:
1.effect,reducer两个都存在时候effect先执行;
2.effect,reducer两个都定义了相同的action时候,只有effect会执行,而reducer不会执行对应代码
接下来看看这个Dispatch onEffect, Dispatch next分别对应什么,这两个Dispatch都是从logic.createDispatch传过来的;
/// create Dispatch
_dispatch = logic.createDispatch(
_effectDispatch,
logic.createNextDispatch(
this,
enhancer,
),
this,
);
_effectDispatch = logic.createEffectDispatch(this, enhancer);
@override
Dispatch createEffectDispatch(ContextSys<T> ctx, Enhancer<Object> enhancer) {
return helper.createEffectDispatch<T>(
/// enhance userEffect
enhancer.effectEnhance(
protectedEffect,
this,
ctx.store,
),
ctx);
}
/// return [EffectDispatch]
Dispatch createEffectDispatch<T>(Effect<T> userEffect, Context<T> ctx) {
return (Action action) {
final Object result = userEffect?.call(action, ctx);
//skip-lifecycle-actions
if (action.type is Lifecycle && (result == null || result == false)) {
return _SUB_EFFECT_RETURN_NULL;
}
return result;
};
}
@override
Dispatch createNextDispatch(ContextSys<T> ctx, Enhancer<Object> enhancer) =>
helper.createNextDispatch<T>(ctx);
/// return [NextDispatch]
Dispatch createNextDispatch<T>(ContextSys<T> ctx) => (Action action) {
ctx.broadcastEffect(action);
ctx.store.dispatch(action);
};
如上代码一步步跟踪:
onEffect对应createEffectDispatch
next对应createNextDispatch
接下来分析createEffectDispatch代码
/// return [EffectDispatch]
Dispatch createEffectDispatch<T>(Effect<T> userEffect, Context<T> ctx) {
return (Action action) {
//执行userEffect方法
final Object result = userEffect?.call(action, ctx);
//skip-lifecycle-actions
if (action.type is Lifecycle && (result == null || result == false)) {
return _SUB_EFFECT_RETURN_NULL;
}
return result;
};
}
userEffect?.call(action, ctx)执行combineEffects响应的代码:
/// for action.type which override it's == operator
/// return [UserEffecr]
Effect<T> combineEffects<T>(Map<Object, SubEffect<T>> map) =>
(map == null || map.isEmpty)
? null
: (Action action, Context<T> ctx) {
//1.
final SubEffect<T> subEffect = map.entries
.firstWhere(
(MapEntry<Object, SubEffect<T>> entry) =>
action.type == entry.key,
orElse: () => null,
)
?.value;
//2.
if (subEffect != null) {
return subEffect.call(action, ctx) ?? _SUB_EFFECT_RETURN_NULL;
}
//skip-lifecycle-actions
if (action.type is Lifecycle) {
return _SUB_EFFECT_RETURN_NULL;
}
/// no subEffect
return null;
};
标记1处中,这里面的map就是在effect.dart中定义的map,如之前定义了{CounterAction.effect_increment: _onIncrement,
CounterAction.effect_decrement: _onDecrement,},然后找到对应map的value,这value就是_onIncrement/_onDecrement方法
标记2处,就直接回调对应的逻辑方法,如果可以正常回调,返回的值就不是一个null或者_SUB_EFFECT_RETURN_NULL;
在回头看如下代码:
Dispatch createDispatch<T>(Dispatch onEffect, Dispatch next, Context<T> ctx) =>
(Action action) {
//1.
final Object result = onEffect?.call(action);
if (result == null || result == false) {
//2
next(action);
}
return result == _SUB_EFFECT_RETURN_NULL ? null : result;
};
标记1处如果effect定义了action,则effect处理,result返回的不会是一个null,那么next就不会被执行;
标记2处如果effect没定义,则reducer处理,result是会返回一个null的,因此没有对应方法处理;
接下来分析next执行action对应的是createNextDispatch:
/// return [NextDispatch]
Dispatch createNextDispatch<T>(ContextSys<T> ctx) => (Action action) {
ctx.broadcastEffect(action);
//1
ctx.store.dispatch(action);
};
重点关系标记1处,其store创建代码如下:
Store<T> _createStore<T>(final T preloadedState, final Reducer<T> reducer) {
return Store<T>()
..getState = (() => _state)
..dispatch = (Action action) {
...
try {
_isDispatching = true;
//1
_state = _reducer(_state, action);
} finally {
_isDispatching = false;
}
...
}
重点标记1处,在创建dispatch时候,定义了方法(){},其中会回调_reducer方法,而这_reducer,就是这个asReducer,因此最后调用到如下代码:
Reducer<T> asReducer<T>(Map<Object, Reducer<T>> map) => (map == null ||
map.isEmpty)
? null
: (T state, Action action) =>
map.entries
.firstWhere(
(MapEntry<Object, Reducer<T>> entry) =>
action.type == entry.key,
orElse: () => null)
//1
?.value(state, action) ??
state;
重点标记1处,这里.value(state, action) 就触发了reducer.dart中调用的方法;
至此事件分发到effect和reducer逻辑告一段落;
2.3.状态共享实现方式
在之前版本上,状态管理更新state数据源是通过InheritedWidget,在之后版本是手动调用setState方法的;
@deprecated
class PageProvider extends InheritedWidget {}
在ComponentState#initState方法中有两个关键地方:
class ComponentState<T> extends State<ComponentWidget<T>> {
@mustCallSuper
@override
void initState() {
super.initState();
/// init context
_ctx = widget.component.createContext(
widget.store,
context,
() => widget.getter(),
//1
markNeedsBuild: () {
if (mounted) {
setState(() {});
}
},
bus: widget.bus,
enhancer: widget.enhancer,
);
/// register store.subscribe
//2
_ctx.registerOnDisposed(widget.store.subscribe(() => _ctx.onNotify()));
_ctx.onLifecycle(LifecycleCreator.initState());
}
}
标记处1:设置markNeedsBuild回调方法设置setState;
标记处2:通过onNotify回调触发markNeedsBuild;
Store<T>()
..subscribe = (_VoidCallback listener) {
...
_listeners.add(listener);
...
}
可以看到ComponentState#initState标记2处会将_ctx.onNotify()存在_listeners中,接下来看dispatch方法回调:
Store<T>()
..dispatch = (Action action) {
...
try {
_isDispatching = true;
_state = _reducer(_state, action);
} finally {
_isDispatching = false;
}
//1
final List<_VoidCallback> _notifyListeners = _listeners.toList(
growable: false,
);
for (_VoidCallback listener in _notifyListeners) {
//2
listener();
}
_notifyController.add(_state);
}
标记处1:_listeners会转化成回调的_notifyListeners;
标记处2:遍历_notifyListeners方法去依次执行listener方法;
这个listener方法中必然有一个就是 _ctx.onNotify();因此在dispatch(Action)完成后,会触发onNotify方法,在看onNotify源码:
//ComponentContext#onNotify
@override
void onNotify() {
final T now = state;
if (shouldUpdate(_latestState, now)) {
_widgetCache = null;
markNeedsBuild();
_latestState = now;
}
}
其就是经过比较新旧的state,然后调用markNeedsBuild方法,而就是一开始在ComponentState#initState中传入的:
markNeedsBuild: () {
if (mounted) {
setState(() {});
}
},
于是乎,在DisPatch(Action)后就自动更新数据到视图View上了,至此,state状态更新到视图View上逻辑也分析完毕;更多fish-redux功能还未尝试,后续会一步步解析其代码;
demo github:https://github.com/1036711153/fish_redux_demo