This article is from written by Jorge Coca, Thank you Jorge for allowing me translate your awesome article into Chinese
本文来源于,由Jorge Coca撰写,并准许我翻译成中文
状态管理在Flutter中是一个很热的话题。可选的方案有很多,这可能很好,但却很容易陷入其中,在项目中选择最适合方案时感到迷失。我也是,不过我已经找到了适合我的方案,让我来分享给你。
为了找到适合需求的方案,头一件事就是确认需求,然后设置目标和期望。对我而言,我定义了如下:
- 允许稳定的开发速度,而不牺牲代码质量
- 分离展示逻辑和业务逻辑
- 容易理解;难以破坏
- 可预期并且可以广泛部署
在给定了这些限制后,我们来看看我们可选的方案:
- 使用StatefulWidgets的setState()
- ScopedModel
- BLoC(Business Logic Component)
- Redux
理解局部状态和全局状态的不同
在深入分析不同方案前,有一件事可能会帮助我们更好的理解怎样选择——什么是局部状态,什么是全局状态
为了这件事,我们来考虑一件事:想象一个简单的登陆表单,用户可以输入用户名和密码,如果登陆成功,就可以从后台获得到id。在这个例子中,登录表单的任何验证类型,都可以考虑为局部状态,因为这些规则仅适用于这个组件,而App的其他部分不需要知道这个类型。但是从后台获取的id,就需要考虑成全局状态,因为它影响整个app的作用域(未登录和已登陆),而且可能别的组件会依赖它。
简而言之:告诉我结果
如果你不想等太久,或者对深入探索不感兴趣,下面这个表可以快速告诉你我的发现:
我的建议是:使用BLoC进行局部状态管理,使用Redux进行全局状态管理。特别是对持续迭代的复杂应用而言。
为什么不用setState()?
快速制作原型的时候,在widget中使用setState()非常爽,而且你会立即得到反馈。但是这并不能帮助我们实现目标:展示逻辑和业务逻辑在同一个类里,打破了干净和高质量代码的原则。以后应用的规模扩张的时候,代码的维护将会是一个挑战。因此除了快速原型设计,我不建议使用setState()。
ScopedModel,正确方向上的一步
ScopedModel是Brian Egan维护的第三方包。它让我们创建Model对象,然后在我们需要的时候调用notifyListeners();例如,在我们的模型(model)属性改变的时候:
class CounterModel extends Model { int _counter = 0; int get counter = _counter; void increment() { _counter++; notifyListeners(); }}复制代码
在我们的widget中,我们可以使用ScopedModelDescendant来响应模型的变化:
class CounterApp extends StatelessWidget { @override Widget build(BuildContext context) { return new ScopedModel( model: new CounterModel(), child: new Column(children: [ new ScopedModelDescendant ( builder: (context, child, model) => new Text('${model.counter}'), ), new Text("Another widget that doesn't depend on the CounterModel") ]) ); }}复制代码
和setState()相反,如果我们使用ScopedModel,就可以分离展示逻辑和业务逻辑,这样就很好了。但是也会有一些局限:
- 如果你的模型变得复杂,挑选调用notifyListeners()的时机以防止没必要的更新,将会是一个挑战。
- Model的API并没有准确描述UI应用程序的异步性。
综上,除非状态很容易控制,否则我不建议你使用ScopedModel;如果你的应用足够复杂,我不相信这是支持复杂性和迭代的正确答案。
BLoC,一个强大的方案
是一个被Google创造并使用的设计模式;他会帮助我们完成这几件事:
- 分离展示逻辑和业务逻辑
- 拥抱UI应用的异步性
- 可以在不同的dart应用中复用,不论是Flutter应用还是Angular dart应用
BLoC背后的想法很简单:
- BLoC开放
Sink<I>
API来描述我们组件的异步输入 - BLoC开放
Stream<T>
API来描述我们组件的异步输出 - 最终,我们使用
StreamBuilder
widget来管理数据流,我们不需要再维护对数据流的订阅和widget的重绘
因为它被Google重度使用和推荐,他们有个非常好的例子:
我非常推荐你在应用中使用BLoC,特别是管理局部状态。即使是全局状态管理,我相信它也会有很好的解决;但是你将会在这个领域面临一些挑战,例如怎么在不同UI组件中插入BLoC更合适,我想这是Redux的闪光点。
Redux和BLoC,对我来说完美的搭配
在文章的开头,其中一个目标就是找到一个可以广泛应用并且可预期的方案。好吧,就是Redux。
Redux是一个设计模式,融合了一些工具,来帮助我们管理全局状态。它建立在3个基本原则上:
- 单一事实来源:你的整个应用的状态,都存在在单一的store中,并且以一个树状对象存在
- 状态是只读的:改变状态的唯一方法就是发射action,一个描述发生了什么的对象
- 改变被纯函数处理:为了说明action怎么改变状态,你想要写一个纯函数的reducer(译者注:纯函数就是这个函数内部不存储状态,在相同的参数的前提下,无论在何时调用,返回值严格一致)
Redux是网页开发着广泛使用的设计模式(译者注:比如用在React.js中),所以在移动端也有广泛的基础,会让我们互相收益。
Brian Egan维护redux和flutter_redux,而且创造了一个非常棒的, 集成了很多不同的架构模式,包括Redux。
鉴于Redux的所有特性,我完全建议使用它来管理全局状态,但是如果你想扩张你的应用,请确保在Redux架构里面不包含局部状态。
最后的思考
这里并没有正确与错误答案。为了选一个工具,或者应用一个设计模式,理解你自己的需求非常重要。对我和我的需求来说,Redux和BLoC搭配能帮助我快速安全地扩张我的应用。同时因为工具的可用性被社区很好地理解,更多的开发者开始使用它。话说回来,每个人都有不同的需求和看问题的角度,重要的是始终有好奇心,去学习和思考,什么才是最好的方案
你可以访问原作者的
也欢迎关注我的更多文章,以及我的