内容简介:Flutter的很多灵感来自于React,它的设计思想是数据与视图分离,由数据映射渲染视图。所以在Flutter中,它的Widget是immutable的,而它的动态部分全部放到了状态(State)中。于是状态管理自然便成了我们密切关注的对象。在之前我们已经讨论了关于在flutter中这个系列将会从这几个状态管理方案进行深入研究:
Flutter的很多灵感来自于React,它的设计思想是数据与视图分离,由数据映射渲染视图。所以在Flutter中,它的Widget是immutable的,而它的动态部分全部放到了状态(State)中。于是状态管理自然便成了我们密切关注的对象。
在之前我们已经讨论了关于在flutter中 使用 scoped_model 进行状态管理 的应用。文章发出后,有许多同学都在问我,到底redux和scoped到底谁更好。
这个系列将会从这几个状态管理方案进行深入研究:
- Scoped_model
- redux
- BLoC
- 对比总结篇
所以今天要和大家介绍的是在flutter中使用Redux进行状态管理。 我希望各位在阅读这篇文章之前,先仔细思考以下这几个问题。
- 什么是redux
- redux给我们了什么好处,我们为什么要使用它
- 它的基本思想是什么
- redux是否真的适合我们
ok,我们开始正式的介绍redux。
Redux
为什么需要状态管理
在我们一开始构建应用的时候,也许很简单。我们有一些状态,直接把他们映射成视图就可以了。这种简单应用可能并不需要状态管理。
但是随着功能的增加,你的应用程序将会有几十个甚至上百个状态。这个时候你的应用应该会是这样。
Wow,这是什么鬼。我们很难再清楚的测试维护我们的状态,因为它看上去实在是太复杂了!而且还会有多个页面共享同一个状态,例如当你进入一个文章点赞,退出到外部缩略展示的时候,外部也需要显示点赞数,这时候就需要同步这两个状态。
这时候,我们便迫切的需要一个架构来帮助我们理清这些关系,状态管理框架应运而生。
redux是什么
Redux是一种 单向数据流 架构,可以轻松开发,维护和测试应用程序。
- 我们在Redux中,所有的状态都储存在 Store 里。这个 Store 会放在App顶层。
- View 拿到Store储存的状态(State)并把它映射成视图。View还会与用户进行交互,用户点击按钮滑动屏幕等等,这时会因为交互需要数据发生改变。
- Redux让我们不能让View直接操作数据,而是通过发起一个 action 来告诉 Reducer ,状态得改变啦。
- 这时候 Reducer 接收到了这个 action ,他就回去遍历action表,然后找到那个匹配的action,根据action 生成新的状态 并把新的状态放到Store中。
- Store丢弃了老的状态对象,储存了新的状态对象后,就通知所有使用到了这个状态的View更新(类似setState)。这样我们就能够同步不同view中的状态了。
Lets do it!
这里我们以一个最简单的CountApp举例。简单介绍flutter_redux/redux的用法。该项目完整代码已上传 Github 。
这是一个在不同页面使用Redux共享状态信息的app。这两个页面都依赖于一个数字,这个数字会随着我们按下按钮的次数而增加。
第一步:添加依赖
我们这里使用了redux/flutter_redux库,它们都是由Brian Egan大神编写的。其中flutter_redux是用来简化redux的使用的。
- 实际添加请参考:redux, flutter_redux
- 由于版本冲突添加失败请参考: juejin.im/post/5b8958…
第二步:创建State
我们刚才介绍了Redux的流程,状态是由 reducer生成并储存在Store 里面的。Store更新状态的时候,并 不是更改原来的状态对象 ,而是直接将reducer生成的新的状态对象 替换 掉老的状态对象。所以,我们的状态应该是immutable的。
import 'package:meta/meta.dart'; /** * State中所有属性都应该是只读的 */ @immutable class CountState{ int _count; get count => _count; CountState(this._count); } 复制代码
第三步:创建action
可能各位最开始接触的时候对Action还会摸不着头脑。action到底是什么?View如何发出action。其实,action只是我们对状态进行操作方法的一个代号而已。在我们这个应用中,唯一的一个功能就是让count的值+1,所以我们这里只有一个action。
/** * 定义操作该State的全部Action * 这里只有增加count一个动作 */ enum Action{ increment } 复制代码
第四步:创建reducer
reducer是我们的状态生成器,它接收一个我们原来的状态,然后接收一个action,再匹配这个action生成一个新的状态。
/** * reducer会根据传进来的action生成新的CountState */ CountState reducer(CountState state,action){ //匹配Action if(action == Action.increment){ return CountState(state.count+1); } return state; } 复制代码
第五步:创建store
Store接收一个reducer,以及初始化State,我们想用Redux管理全局的状态的话,需要将store储存在应用的入口才行。而在应用打开时要先初始化一次应用的状态。所以在State中添加一个初始化的函数。
//这段代码写在State中 CountState.initState(){ _count = 0;} 复制代码
//应用顶层 void main() { final store = Store<CountState>(reducer, initialState: CountState.initState()); runApp(new MyApp(store)); } 复制代码
第六步:将Store放入顶层
flutter_redux提供了一个很棒的widget叫做StoreProvider,它的用法也很简单,接收一个store,和child Widget。
class MyApp extends StatelessWidget { final Store<CountState> store; MyApp(this.store); @override Widget build(BuildContext context) { return StoreProvider<CountState>( store: store, child: new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: TopScreen(), ), ); } } 复制代码
第六步:在子页面中获取Store中的state
这里建议大家把实际代码对照下面的解释一起看。
StoreConnector<CountState,int>( converter: (store) => store.state.count, builder: (context, count) { return Text( count.toString(), style: Theme.of(context).textTheme.display1, ); }, ), 复制代码
要想获取store我们需要使用StoreConnector<S,ViewModel>。StoreConnector能够通过StoreProvider找到顶层的store。而且能够在state发生变化时rebuilt Widget。
- 首先这里需要 强制声明类型 ,S代表我们需要从store中获取什么类型的state,ViewModel指的是我们使用这个State时的实际类型。
- 然后我们需要声明一个 converter<S,ViewModel> ,它的作用是将Store转化成实际ViewModel将要使用的信息,比如我们这里实际上要使用的是count,所以这里将count提取出来。
- builder 是我们实际根据state创建Widget的地方,它接收一个上下文context,以及刚才我们转化出来的ViewModel,所以我们就只需要把拿到的count放进Text Widget中进行渲染就好了。
第七步:发出action
我们这个应用在第二个页面中,通过点击floatingActionButton发出了action,并通知reducer生成了新的状态。
floatingActionButton: StoreConnector<CountState,VoidCallback>( converter: (store) { return () => store.dispatch(Action.increment); }, builder: (context, callback) { return FloatingActionButton( onPressed: callback, child: Icon(Icons.add), ); }, ), 复制代码
- 同样,我们还是使用StoreConnector<S,ViewModel>。这里由于是发出了一个动作,所以是VoidCallback。
- store.dispatch发起一个action,任何中间件都会拦截该操作,在运行中间件后,操作将被发送到给定的 reducer 生成新的状态,并更新状态树。
以上便是在flutter中使用redux共享状态信息的全部内容。
Q&A
ViewModel性能优化
我们的StoreConnector能够将store提取出信息并转化成ViewModel,这里其实是有一个性能优化的点的。我们这里的例子非常简单,它的ViewModel就只是一个int的值,当我们ViewModel很复杂的时候,我们可以使用StoreConnector的distinct属性进行性能优化。使用方法很简单:需要我们在ViewModel中重写[==] and [hashCode] 方法,然后把distinct属性设为true。
如何处理异步数据
Redux提供了一种简单的方法来更新应用程序的状态以响应同步操作。但是,它缺少处理异步代码的工具。我们如何应对异步相应呢。
这里就需要一个interrupt来处理异步请求,然后再发出新的action通知reducer生成新的State了。 这里有brianegan大神写的另外一个帮助在flutter中使用redux处理异步请求的库redux_thunk。我会在之后的文章中详细介绍如何在redux中处理异步操作。
你认为redux真的适合flutter吗
我们发现,redux的确能够在flutter中很好的工作。在react中数据是没有上行能力的,所以通过数据单向流动形成一个环来进行状态管理。看上去似乎并没有把flutter中的优势完全发挥出来。在这个简单的例子中我们也可以看出,使用redux还是 稍微有些麻烦的 ,用的不好,可能会陷入redux地狱。学习成本偏高也是它的一大痛点。
当然,redux这套状态管理架构已经比较成熟,假如您已经习惯redux,也能够快速通过flutter_redux轻松构建属于您的状态管理应用。
那么你现在如何看待redux呢?
写在最后
本次所用到的代码已经上传Github: github.com/Vadaski/Flu…
这篇文章参考了以下资料
了解更多
你能在这些地方了解更多关于flutter-redux
- 了解flutter-redux: flutterbyexample.com/what-is-red…
- 了解redux的问题: medium.com/fluttery/th…
如果您对flutter_redux还有任何看法或者文章的建议或者文章中有任何不对之处,欢迎在下方评论区以及我的邮箱1652219550a@gmail.com留言,我会在24小时内与您联系!
按理说下一章我们将探索BLoC在Flutter中的实践,而BLoC非常Reactive Programming,所以我决定先让大家了解一些dart:Stream的知识再介绍它,所以下一篇文章我们会介绍Stream以及流式编程,敬请关注。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Flutter实践:深入探索 flutter 中的状态管理方式(1)
- javascript – 有可能使用angularJs将状态从状态传递到状态吗?
- 前端状态管理与有限状态机
- 如何可视化需求状态和团队状态?
- 给 DSL 开个脑洞:无状态的状态机
- 基于有限状态机的广告状态管理方案及实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。