如果要使用redux,那么首先得创建一个Store,顾名思义用来储存数据的类,它的泛型需要自己定义真正保存数据的类放在这里
如下:
final store = Store(
searchReducer,
initialState: SearchInitial(),
middleware: [
// The following middleware both achieve the same goal: Load search
// results from github in response to SearchActions.
//
// One is implemented as a normal middleware, the other is implemented as
// an epic for demonstration purposes.
// SearchMiddleware(GithubClient()),
EpicMiddleware(SearchEpic(GithubClient())),
],
);
其中SearchState是用来真正储存信息的类,比如登录的状态,比如主题切换,又比如国家化的方案,方便view树的重新构建,实现逻辑和UI的解耦,其中searchReducer是一个方法,会在你调用store.dispatch方法的时候触发,middleware是一系列方法的组合,会在searchReducer方法之前执行。来看一下Store类的构造方法
Store(
this.reducer, {
State initialState,
List> 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,
})
: _changeController = new StreamController.broadcast(sync: syncStream) {
_state = initialState;
_dispatchers = _createDispatchers(
middleware,
_createReduceAndNotify(distinct),
);
}
构造方法会创建一个StreamController类(用来实现订阅者和发布者的管理类,其实就是用的观察者模式,用来回调seState实现树的部分重新渲染的),其中Middleware就是一个无返回值的传三个参数的方法(typedef在dart中用来声明方法)
typedef void Middleware(
Store store,
dynamic action,
NextDispatcher next,
);
typedef State Reducer(State state, dynamic action);
Reducer也是方法声明, action是dynamic类型的,在操作时由程序员is判断自行使用,Store中的构造函数_createDispatchers方法用来将所有要处理的方法添加到List
List _createDispatchers(
List> middleware,
NextDispatcher reduceAndNotify,
) {
final dispatchers = []..add(reduceAndNotify);
// Convert each [Middleware] into a [NextDispatcher]
for (var nextMiddleware in middleware.reversed) {
final next = dispatchers.last;
dispatchers.add(
(dynamic action) => nextMiddleware(this, action, next),
);
}
return dispatchers.reversed.toList();
}
NextDispatcher _createReduceAndNotify(bool distinct) {
return (dynamic action) {
final state = reducer(_state, action);
if (distinct && state == _state) return;
_state = state;
_changeController.add(state);
};
}
_createReduceAndNotify方法就是创建一个NextDispatcher将reducer的调用加入其中。
Redux例子中的searchReducer返回的是这么一个类
/// Reducer
final searchReducer = combineReducers([
TypedReducer(_onLoad),
TypedReducer(_onError),
TypedReducer(_onResult),
]);
typedef State Reducer(State state, dynamic action);
Reducer combineReducers(Iterable> reducers) {
return (State state, dynamic action) {
for (final reducer in reducers) {
state = reducer(state, action);
}
return state;
};
}
combineReducers方法相当于一个方法的集合包装方法将集合中的所有方法执行一遍,例如这里是3个方法都会被执行到
Reducer是个方法,赋值的时候却是赋值的TypedReducer对象,这就涉及到Dart类中的特殊的用法
class TypedReducer implements ReducerClass {
final State Function(State state, Action action) reducer;
TypedReducer(this.reducer);
@override
State call(State state, dynamic action) {
if (action is Action) {
return reducer(state, action);
}
return state;
}
}
一个类里面有call方法的声明,那么可以把这个类转化为Function方法使用,调用方法时执行的是call方法。
Store声明完了,接下来得用StoreProvider将你的小部件全部包裹起来,以便视图的小部件用来获得Store仓库的属性来刷新子控件的数值
StoreProvider(
store: store,
child: MaterialApp(
title: 'RxDart Github Search',
theme: ThemeData(
brightness: Brightness.dark,
primarySwatch: Colors.grey,
),
home: SearchScreen(),
),
);
}
class StoreProvider extends InheritedWidget
StoreProvider继承自InheritedWidget,而InheritedWidget的用法是用来所有子控件共享数据的,这里共享的就是Store。
最后使用的时候给子部件套上StoreConnector,或者套上StoreBuilder,StoreBuilder内部封装了StoreConnector
StoreConnector(
converter: (store) {
return _SearchScreenViewModel(
state: store.state,
onTextChanged: (term) => store.dispatch(SearchAction(term)),
);
},
builder: (BuildContext context, _SearchScreenViewModel vm) {
return Scaffold(
body: Flex(direction: Axis.vertical, children: [
Container(
padding: EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 4.0),
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
hintText: 'Search Github...',
),
style: TextStyle(
fontSize: 36.0,
fontFamily: "Hind",
decoration: TextDecoration.none,
),
onChanged: vm.onTextChanged,
),
),
Expanded(
child: AnimatedSwitcher(
duration: Duration(milliseconds: 500),
child: _buildVisible(vm.state),
),
)
]),
StoreConnector的真正实现是_StoreStreamListener的类,声明StoreConnector的时一共使用两个泛型,一个是State(保存view改变的信息),一个是ViewModel(可以看做是业务逻辑)(这里就实现了解耦),converter方法用来将store转化为接下来我们要操作的数据类,类似于Stream的map方法。
class _StoreStreamListener extends StatefulWidget
_StoreStreamListener的rebuildOnChange = true属性默认为true,所以最终构建它的小部件为StreamBuilder
Widget build(BuildContext context) {
return widget.rebuildOnChange
? new StreamBuilder(
stream: stream,
builder: (context, snapshot) => widget.builder(
context,
snapshot.hasData ? snapshot.data : latestValue,
),
)
: widget.builder(context, latestValue);
}
}
StreamBuilder继承与StatefulWidget,所以它可以实现setState状态的改变,那么StreamController的观察者必然在这订阅的,StreamBuilder的State类为_StreamBuilderBaseState,_StreamBuilderBaseState 的initState方法实现了事件的订阅
void _subscribe() {
if (widget.stream != null) {
_subscription = widget.stream.listen((T data) {
setState(() {
_summary = widget.afterData(_summary, data);
});
}, onError: (Object error) {
setState(() {
_summary = widget.afterError(_summary, error);
});
}, onDone: () {
setState(() {
_summary = widget.afterDone(_summary);
});
});
_summary = widget.afterConnected(_summary);
}
}
看到这咱们可以明确StoreProvider作用是负责当前上下文,也就是view树共享Store,而StoreConnector的作用是用来订阅数据的变化,只要数据变化了也就是store.dispatch方法被调用的时候会监听到数据的变化从而触发setState实现view树的重新渲染。
接下来来看一看store.dispatch调用过程
void dispatch(dynamic action) {
_dispatchers[0](action);
}
这个方法很简单,从集合中的第一个方法开始调用,也就是先调用middleware的方法call
call(Store store, dynamic action, NextDispatcher next) {
if (!_isSubscribed) {
_epics.stream
.transform(
new SwitchMapStreamTransformer, dynamic>(
(epic) => epic(_actions.stream, new EpicStore(store))))
//订阅store.dispatch
.listen(store.dispatch);
//添加_epic并执行call
_epics.add(_epic);
_isSubscribed = true;
}
//执行下一个NextDispatcher
next(action);
if (supportAsyncGenerators) {
// Future.delayed is an ugly hack to support async* functions.
//
// See: https://github.com/dart-lang/sdk/issues/33818
new Future.delayed(Duration.zero, () {
_actions.add(action);
});
} else {
_actions.add(action);
}
}
再执行searchReducer方法
NextDispatcher _createReduceAndNotify(bool distinct) {
return (dynamic action) {
final state = reducer(_state, action);
if (distinct && state == _state) return;
_state = state;
//发布一个事件
_changeController.add(state);
};
}
执行完searchReducer方法之后,判断state有没有变化,如果变化了就会通过StreamController发布事件,通知StoreConnector调用setState从而重新渲染。