颤动|探索政府 - BLoC
Flutter 的大部分灵感来自于 React。其设计是将数据与视图分离,并使用数据映射来渲染视图。因此,在 Flutter 中,他的 Widget 属于 immutable,并且他的所有动态部分都设置为该国家/地区。
在上一篇文章中,我们介绍了flutter中两种状态管理方案的使用,范围模型和redux。他们看起来都不错,但他们仍然有缺陷。今天我将介绍谷歌-BLoC提出的一个全新的解决方案。
在正式开始介绍之前,希望您已经阅读并理解了stream的相关知识,并且以下内容以此为基础。如果你还没有了解过dart:stream,我建议你先阅读这篇文章:Dart:什么是Stream。
BLoC
为什么需要平衡管理
我们寻找强大的平衡管理方法。你可能没有想过,Flutter本身就为我们提供了状态管理,而且你也经常使用它。
是的,这是有状态小部件。当我们接触Flutter时,首先需要了解的是,有些Widget是有状态的,有些是无状态的。 无状态小部件和有状态小部件。
在状态小部件中,我们的小部件的描述信息被插入到状态中,并且状态小部件只是保存一些immutable数据以及创建它的状态。它的所有成员变量都必须是最终的。当状态改变时,我们需要告诉视图重绘。这个过程就是setState。
这看起来不错,只要我们改变状态时设置状态即可。当我们第一次构建应用程序时,它可能非常简单,我们现在可能不需要状态管理。
但是,随着功能的增加,您的应用程序将具有数十甚至数百种状态。此时,您的登录应该如下所示。
当应用交互变得复杂时,setState出现的次数会明显增加,每次使用setState都会再次调用build方法,这必然会影响代码的性能和可读性。不使用setState是否可以刷新页面?如何在多个页面共享状态?我们需要一种更强大的方式来管理我们的状态。
什么是 BLoC
BLoC 是一种使用反应式编程构建应用程序的方法,这是一个由流组成的完全异步的世界。
- 用StreamBuilder包装一个有状态的小部件,stream构建器将监听流
- 这个流来自BLoC
- 有状态小部件中的数据来自受控流。
- 检测到用户交互手势并生成事件。例如,按下了一个按钮。
- 调用块函数来处理此事件
- 当块中的处理完成时,最新的数据将添加到流接收器中
- StreamBuilder 监听新数据,创建新快照并重新启动。调用build方法
- Widget被重建
BLoC让我们可以完全分离业务逻辑!不再考虑何时刷新屏幕,一切都交给 StreamBuilder 和 BLoC!告别 StatefulWidget! !
BLoC 代表业务逻辑组件,由两位 Google 工程师 Paolo Soares 和 Cong Hui 设计,在 DartConf 2018(2018 年 1 月 23-24 日)期间首次推出。点击观看 YouTube 视频。 。
我们行动吧!
这里我们以最简单的应用CountApp为例。简单介绍一下BLoC这个词的用法。整个项目代码已上传至Github。
这是一个使用 BLoC 在各个网站上共享状态信息的应用程序。双方都基于增加按钮按下次数的数字。
第一步:创建一个BLoC
我们这里的要求很简单,只要输出一个数字,然后有一个数字加一的方法。所以我们需要创建一个可以传递int类型数据的流。
import 'dart:async';
class CountBLoC {
int _count;
StreamController<int> _countController;
CountBLoC() {
_count = 0;
_countController = StreamController<int>();
}
Stream<int> get value => _countController.stream;
increment() {
_countController.sink.add(++_count);
}
dispose() {
_countController.close();
}
}
复制代码
为什么要使用私有变量“_”
应用程序需要大量开发人员的配合。您编写的代码可能会在几个月后被其他开发人员看到。如果你的变量当前没有受到保护,也许如果还使用了 count++,它会使用 countController.sink.add(++_count) 而不是调用increment方法。
虽然两种方式的效果是完全一样的,但是第二种方式会让我们的业务逻辑分散并混入到其他代码中,这样会增加代码耦合率,对于维护和阅读代码非常不利,所以如果你想完全分离我们的业务逻辑,不要忘记使用私有变量。 ?所以我们只能选择全局单一模式或者作用域模式。
全局单例
全局单例 我们只需要在方块类文件中创建一个方块实例即可。但是,我不推荐这种方法,因为当不再需要该块时,我们需要释放它。
但是为了让我的解释尽可能简单,我稍后会根据一个的全局模式来呈现。
区域模式
创建块提供程序类。这里我们需要使用InheritWidget来实现of方法,并让updateShouldNotify返回true。
class BlocProvider extends InheritedWidget {
CountBLoC bLoC = CountBLoC();
BlocProvider({Key key, Widget child}) : super(key: key, child: child);
@override
bool updateShouldNotify(_) => true;
static CountBLoC of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).bLoC;
}
复制代码
提示:这里,updateShouldNotify必须传递InheritedWidget oldWidget,但我们强制它返回true,所以它传递了括号“_”。
3。步骤:第
页面使用StreamBuilder,以第一页为例,只显示文字+数字。
StreamBuilder<int>(
stream: bloc.value,
initialData: 0,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
return Text(
'You hit me: ${snapshot.data} times',
style: Theme.of(context).textTheme.display1,
);
})
复制代码
- StreamBuilder中的stream参数表示由该stream构建器控制的流。这里我们监控countBloc的值(就是stream)。
- initData 表示初始值,因为这个控件第一次渲染的时候,还没有和用户交互,所以不会有事件从流中流出。所以需要给第一次渲染一个初始值。
- 构建器函数接收位置参数BuildContext和快照。快照是该流输出的数据的快照。快照中的数据可以通过snapshot.data访问。您还可以使用 snapshot.hasError 来确定是否存在异常,并通过 snapshot.error 检索异常。
- StreamBuilder中的构建器是AsyncWidgetBuilder,它可以异步构建一个widget。当它检测到数据流出流时,就会重建它。
另一边增量调用
floatingActionButton: FloatingActionButton(
onPressed: ()=> bloc.increment(),
child: Icon(Icons.add),
)
复制代码
由于不涉及widget重建,所以我们只需要调用block函数即可。
处理广播流
当我们构建UI时,当我们运行程序时,我们会发现这个奇怪的事情。
对方的数字无法显示,控制台抛出此异常。
flutter: Bad state: Stream has already been listened to.
复制代码
这是反复电流监测的结果。 这个数字必须出现在两边,所以使用了两个StreamBuilder。所有StreamBuilder都监视同一个流,导致流被监视多次。
还记得我们在 Dart 中讨论过的两个流|什么是流?没错,当我们创建一个StreamController时,默认是创建一个订阅流。所以我们需要改变电流来传输电流。
_countController = StreamController.broadcast<int>();
复制代码
您应该只在创建 StreamController 时调用广播方法。
让我们看看效果

但是我们还有一个小问题,你发现了它。 ?当我们再次推送时,StreamBuilder 无法监听最后一个事件(它已经在运行)并且只能显示 initiaData。当您再次单击时,正确的数字将添加到流中,并且 StreamController 会侦听它,以便它可以再次显示正确的数据。
这个问题能解决吗?
答案是肯定的,使用rxdart! rxdart极大地改进了流功能,解决方案将在下一篇rxdart文章中介绍。
如何在大型应用程序中组织王牌
大型应用程序需要更多王牌。一种好的模式是为每个屏幕使用一个顶级组件,并为每个足够复杂的小部件使用一个顶级组件。但BLoC过多可能会变得棘手。此外,如果您的应用程序中有数百个可观察对象(流),这将对性能产生负面影响。换句话说:不要过度设计你的应用程序。
- Filip Hracek
更复杂的应用

filip 提供更复杂的BLoC模式。他将购物应用程序重新创建为一个更现实的示例,其中产品目录是从网站逐页获取的,并且我们有无穷无尽的这些产品列表。此外,对于目录中的每个产品,一旦产品已在目录中,我们希望稍微修改 ProductSquare 显示。
更多相关信息
这里有一些很棒的文章可以为您提供更多信息
- [翻译] Flutter 响应式编程:流和BLoC
- 在 Flutter 中构建响应式移动应用程序 - 配套文章
- 技术债务和Streams /BLoC(The Boring Flutter Development Show,Ep. 4)
- 在 Dart 的 Flutter 框架中使用 BloC 模式构建响应式流应用
写在最后
这次使用的代码上传到了 Github:github。 com/OpenFlutter...
bloc 是一种很棒的状态管理方法,可以帮助我们更好地构建复杂的大规模应用程序。但它并不完美(至少现在还不是)。它在处理大量异步事件和分离业务逻辑方面做得很好,但在共享状态方面仍然存在一些缺点。
有人尝试将redux与盖帽结合起来,试图寻找突破口。有一个专门为此编写的库:rebloc。有兴趣的朋友可以自己去了解一下。
作者:Vadaski
链接:https://juejin.im/post/5bb6f344f265da0aa664d68a
来源:掘金
版权归作者所有。商业转载请联系作者获得许可。非商业转载请注明来源。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。