Code前端首页关于Code前端联系我们

颤动|探索政府 - BLoC

terry 2年前 (2023-09-23) 阅读数 73 #移动小程序

Flutter 的大部分灵感来自于 React。其设计是将数据与视图分离,并使用数据映射来渲染视图。因此,在 Flutter 中,他的 Widget 属于 immutable,并且他的所有动态部分都设置为该国家/地区。

在上一篇文章中,我们介绍了flutter中两种状态管理方案的使用,范围模型和redux。他们看起来都不错,但他们仍然有缺陷。今天我将介绍谷歌-BLoC提出的一个全新的解决方案。

在正式开始介绍之前,希望您已经阅读并理解了stream的相关知识,并且以下内容以此为基础。如果你还没有了解过dart:stream,我建议你先阅读这篇文章:Dart:什么是Stream。

BLoC

为什么需要平衡管理

我们寻找强大的平衡管理方法。你可能没有想过,Flutter本身就为我们提供了状态管理,而且你也经常使用它。

是的,这是有状态小部件。当我们接触Flutter时,首先需要了解的是,有些Widget是有状态的,有些是无状态的。 无状态小部件有状态小部件

在状态小部件中,我们的小部件的描述信息被插入到状态中,并且状态小部件只是保存一些immutable数据以及创建它的状态。它的所有成员变量都必须是最终的。当状态改变时,我们需要告诉视图重绘。这个过程就是setState。

这看起来不错,只要我们改变状态时设置状态即可。当我们第一次构建应用程序时,它可能非常简单,我们现在可能不需要状态管理。 Flutter | 状态管理探索篇——BLoC

但是,随着功能的增加,您的应用程序将具有数十甚至数百种状态。此时,您的登录应该如下所示。 Flutter | 状态管理探索篇——BLoC

当应用交互变得复杂时,setState出现的次数会明显增加,每次使用setState都会再次调用build方法,这必然会影响代码的性能和可读性。不使用setState是否可以刷新页面?如何在多个页面共享状态?我们需要一种更强大的方式来管理我们的状态。

什么是 BLoC

BLoC 是一种使用反应式编程构建应用程序的方法,这是一个由流组成的完全异步的世界。 Flutter | 状态管理探索篇——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 在各个网站上共享状态信息的应用程序。双方都基于增加按钮按下次数的数字。 Flutter | 状态管理探索篇——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 | 状态管理探索篇——BLoC

对方的数字无法显示,控制台抛出此异常。

flutter: Bad state: Stream has already been listened to.
复制代码

这是反复电流监测的结果。 这个数字必须出现在两边,所以使用了两个StreamBuilder。所有StreamBuilder都监视同一个流,导致流被监视多次。

还记得我们在 Dart 中讨论过的两个流|什么是流?没错,当我们创建一个StreamController时,默认是创建一个订阅流。所以我们需要改变电流来传输电流。

    _countController = StreamController.broadcast<int>();
复制代码

您应该只在创建 StreamController 时调用广播方法。

让我们看看效果

Flutter | 状态管理探索篇——BLoC

但是我们还有一个小问题,你发现了它。 ?当我们再次推送时,StreamBuilder 无法监听最后一个事件(它已经在运行)并且只能显示 initiaData。当您再次单击时,正确的数字将添加到流中,并且 StreamController 会侦听它,以便它可以再次显示正确的数据。

这个问题能解决吗?

答案是肯定的,使用rxdart! rxdart极大地改进了流功能,解决方案将在下一篇rxdart文章中介绍。

如何在大型应用程序中组织王牌

大型应用程序需要更多王牌。一种好的模式是为每个屏幕使用一个顶级组件,并为每个足够复杂的小部件使用一个顶级组件。但BLoC过多可能会变得棘手。此外,如果您的应用程序中有数百个可观察对象(流),这将对性能产生负面影响。换句话说:不要过度设计你的应用程序。

- Filip Hracek

更复杂的应用

Flutter | 状态管理探索篇——BLoC

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前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门