flutter_redux和scoped_model都能很好的对我们的应用进行状态管理上一篇文章简单的介绍了scoped_model这篇文章介绍一下flutter_redux的使用和我自己的一些理解
先不说flutter_redux,这个概念前端开发的朋友应该都不会陌生,而对移动端的伙伴就比较陌生了,所以我查看了Redux的相关介绍其中有一个三大原则
单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
State 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers。
说人话我的理解就是所有状态都在store中想要修改state就是要触发action,如何触发action呢,就是写方法就是reducers.
这样看来使用redux就很好管理我们的应用了我们把所有不同的状态定义成枚举也就是action然后,对应action定义很多改变状态的方法也就是reducers,当触发action的时候我们来执行方法从而改变状态
好的回到flutter_redux
既然加了redux用法应该是遵守三大原则的我们就用flutter最基础的Demo两个不同界面通过Redux共享一个信息
三大原则中第一个就是有一个唯一的store所以不用说,这个store一定在App的入口处,进行全局的管理
import 'package:redux/redux.dart';
void main() {
final store =
Store<CountState>(reducer, initialState: CountState.initState());
runApp(MyApp(store: store))
};
看到这个Store有一些摸不清头脑那我们点进去一看究竟
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,
})
大家发现了一个很眼熟的东西this.reducer,那这个reducer是个什么东西再点进去看看
typedef State Reducer<State>(State state, dynamic action);
现在明白了我们需要自己定义一个state同时我们接受一个state和一个action然后返回一个新的state这个state还进行泛型的保护
那我们按部就班先定义一个状态类
class CountState{
int _count;
get count => _count;
CountState(this._count);
CountState.initState() : _count = 0;
}
然后我们定义一个action这个action前面已经提到了应该是一个enum,枚举里现在只有一个状态就是增加当然我们可以加入更多状态
enum Action{
increment
}
然后我们实现reducers这个方法按照要求传入一个状态和一个action返回一个状态
CountState reducer(CountState state,action){
//匹配Action
if(action == Action.increment){
return CountState(state.count+1);
}
return state;
}
好了准备工作完成那我们怎么在各个子控件控制器内拿到我们改变后的状态,同时使用改变后的数据或者再次改变呢flutter_redux给我们提供了一个叫StoreConnector的类
class StoreConnector<S, ViewModel> extends StatelessWidget {
/// Build a Widget using the [BuildContext] and [ViewModel]. The [ViewModel]
/// is created by the [converter] function.
final ViewModelBuilder<ViewModel> builder;
/// Convert the [Store] into a [ViewModel]. The resulting [ViewModel] will be
/// passed to the [builder] function.
final StoreConverter<S, ViewModel> converter;
/// As a performance optimization, the Widget can be rebuilt only when the
/// [ViewModel] changes. In order for this to work correctly, you must
/// implement [==] and [hashCode] for the [ViewModel], and set the [distinct]
/// option to true when creating your StoreConnector.
final bool distinct;
/// A function that will be run when the StoreConnector is initially created.
/// It is run in the [State.initState] method.
///
/// This can be useful for dispatching actions that fetch data for your Widget
/// when it is first displayed.
final OnInitCallback<S> onInit;
/// A function that will be run when the StoreConnector is removed from the
/// Widget Tree.
///
/// It is run in the [State.dispose] method.
///
/// This can be useful for dispatching actions that remove stale data from
/// your State tree.
final OnDisposeCallback<S> onDispose;
/// Determines whether the Widget should be rebuilt when the Store emits an
/// onChange event.
final bool rebuildOnChange;
/// A test of whether or not your [converter] function should run in response
/// to a State change. For advanced use only.
///
/// Some changes to the State of your application will mean your [converter]
/// function can't produce a useful ViewModel. In these cases, such as when
/// performing exit animations on data that has been removed from your Store,
/// it can be best to ignore the State change while your animation completes.
///
/// To ignore a change, provide a function that returns true or false. If the
/// returned value is true, the change will be ignored.
///
/// If you ignore a change, and the framework needs to rebuild the Widget, the
/// [builder] function will be called with the latest [ViewModel] produced by
/// your [converter] function.
final IgnoreChangeTest<S> ignoreChange;
/// A function that will be run on State change, before the Widget is built.
///
/// This function is passed the `ViewModel`, and if `distinct` is `true`,
/// it will only be called if the `ViewModel` changes.
///
/// This can be useful for imperative calls to things like Navigator,
/// TabController, etc. This can also be useful for triggering actions
/// based on the previous state.
final OnWillChangeCallback<ViewModel> onWillChange;
/// A function that will be run on State change, after the Widget is built.
///
/// This function is passed the `ViewModel`, and if `distinct` is `true`,
/// it will only be called if the `ViewModel` changes.
///
/// This can be useful for running certain animations after the build is
/// complete.
///
/// Note: Using a [BuildContext] inside this callback can cause problems if
/// the callback performs navigation. For navigation purposes, please use
/// [onWillChange].
final OnDidChangeCallback<ViewModel> onDidChange;
/// A function that will be run after the Widget is built the first time.
///
/// This function is passed the initial `ViewModel` created by the [converter]
/// function.
///
/// This can be useful for starting certain animations, such as showing
/// Snackbars, after the Widget is built the first time.
final OnInitialBuildCallback<ViewModel> onInitialBuild;
/// Create a [StoreConnector] by passing in the required [converter] and
/// [builder] functions.
///
/// You can also specify a number of additional parameters that allow you to
/// modify the behavior of the StoreConnector. Please see the documentation
/// for each option for more info.
const 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,
}) : assert(builder != null),
assert(converter != null),
super(key: key);
@override
Widget build(BuildContext context) {
return _StoreStreamListener<S, ViewModel>(
store: StoreProvider.of<S>(context),
builder: builder,
converter: converter,
distinct: distinct,
onInit: onInit,
onDispose: onDispose,
rebuildOnChange: rebuildOnChange,
ignoreChange: ignoreChange,
onWillChange: onWillChange,
onDidChange: onDidChange,
onInitialBuild: onInitialBuild,
);
}
}
总之就是通过这个叫StoreConnector的东西我们上能画UI下能拿状态
StoreConnector<CountState, int>(
converter: (store) => store.state.count,
builder: (context, count) {
return Text(
count.toString(),
style: Theme.of(context).textTheme.display1,
);
},
),
通过converter拿到状态类使用状态类的属性
floatingActionButton:StoreConnector<CountState,VoidCallback>(
converter: (store) {
return () => store.dispatch(Action.increment);
},
builder: (context, callback) {
return FloatingActionButton(
onPressed: callback,
child: Icon(Icons.add),
);
},
),
通过converter触发我们定义好的action改变数字
最后
其实flutter_redux还有很多复杂的用法这里只是抛砖引玉给大家提供一个思路更多深入的东西我也会慢慢研究,然后进行记录.因为我是移动端出身个人感觉
flutter_redux过于繁琐,定义action包括实现reducer等很难受我还是更偏向使用scoped_model我觉得scoped_model相对简洁但是可读性没有flutter_redux好个人的一点见解,大家也可以说说自己对flutter_redux的理解,也欢迎拍砖.