【问题标题】:Do I need a StatefulWidget if my app is using bloc?如果我的应用使用 bloc,我是否需要 StatefulWidget?
【发布时间】:2018-12-27 17:56:52
【问题描述】:

我错过了一些东西。

我最近观看了 here 的演讲,其中 Flutter 开发人员正在使用 Dart 中的 reactx 的 bloc 开发方法。如果我使用这些流和 streamBuilders 来管理流经我的应用程序的数据并进行适当的重建,那么我是否应该使用 StatefulWidget,无论如何我都在使用 bloc 方法?我想更具体地说,为什么我要使用流和状态使我的应用程序复杂化,而我可以只使用流,将我需要的东西包装在提供程序中,将一些小部件包装在 streamBuilder 中,然后就这样了?

【问题讨论】:

    标签: dart flutter reactivex


    【解决方案1】:

    当使用StreamBuilder 时,您实际上是在使用StatefulWidget 来监听Stream。唯一的区别是你不会自己写setState

    另一个常见的用例是动画。如果您想要诸如淡入淡出/翻译/其他的过渡;你必须使用AnimationController。您将存储在自定义 StatefulWidget

    【讨论】:

    • 也适用于文本输入字段 (TextInputController)
    【解决方案2】:

    我是否应该使用 StatefulWidget,无论如何我都在使用 bloc 方法?我想更具体地说,我为什么要使用流和状态使我的应用程序复杂化,而我可以只使用流,将我需要的东西包装在提供程序中,将一些小部件包装在 streamBuilder 中,然后就这样结束了?

    问题的答案取决于您的目标。

    StatefulWidget 无法扩展到更大的应用程序。 BLOC 模式可以。

    为什么 StatefulWidget 不适合大型应用程序?

    将信息从一个屏幕传递到同级屏幕往往具有挑战性,这意味着您必须编写大量代码才能将数据从一个屏幕传输到另一个屏幕。这是可能的,但它往往是一种痛苦,这就是 BLOC 模式解决的问题。

    它使在我们的应用程序内的多个小部件之间共享信息变得容易。

    BLOC 代表 Business LOgic Component,它的想法是容纳所有数据或在一个区域内的应用程序内部状态。它位于应用程序的其余部分之外,便于访问。

    这与 StatefulWidget 不同,因为使用 BLOC,所有数据都可以存在于组件层次结构之外的一个类中。因此,状态正在集中到某个外部对象。

    因此,对于 BLOC 模式,您确实需要对流有深入的了解,并且您提到了 StreamBuilder 小部件,它是颤振和流真正融合在一起的。

    StreamBuilder 接受一个流和一个构建器函数,只要 StreamBuilder 看到一条新数据,它就会调用构建器函数并在我们的移动设备上重新呈现自己,所以它看起来像这个:

    Widget emailField() {
        return StreamBuilder(
            stream: bloc.email,
            builder: (context, snapshot) {
              return TextField(
                keyboardType: TextInputType.emailAddress,
                decoration: InputDecoration(
                  hintText: 'you@example.com',
                  labelText: 'Email Address',
                ),
              );
            });
      }
    

    上述方法是单个全局实例,适用于小型应用程序。

    您提到的提供程序是一个扩展继承的小部件基类的类。

    import 'package:flutter/material.dart';
    import 'bloc.dart';
    
    class Provider extends InheritedWidget {
      final bloc = Bloc();
    
      bool updateShouldNotify(_) => true;
    
      static Bloc of(BuildContext, context) {
        return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
      }
    }
    

    【讨论】:

      【解决方案3】:

      您可以使用StatelessWidgetStatefulWidget,一切都会正常工作,但是在我需要在我的块中初始化状态的情况下,我使用了StatefulWidget 并将我的初始化逻辑放入initState() 覆盖。

      (如果这不是最佳实践,请纠正我,我是 bloc、flutter 和流的新手!)

      【讨论】:

      • 既然你已经有了一些经验(我想),你还会这样做吗?
      【解决方案4】:

      让我分享我在 Flutter、Bloc 和 StatefulWidgets 工作 2 年后的见解。

      当您发现自己处于想要让两个StatefulWidget 交流的情况时,请三思而后行,如果 Bloc 是答案!正如他们的文档中所建议的那样,我在 bloc-everything 方法上走得太远了 并期望bloc 能帮助我在代码中实现更好的模块化。我使用BlocListeners 来利用listener: 参数并在其中调用setState()...然后我尝试使用BlocBuilders。在将StatefulWidgetBloc 紧密集成时,我总是走到了死胡同,遇到了很多错误。你为什么问?只是为了实现这种干净、闪亮的模块化,将小部件放在单独的文件中,并使它们可重用以及所有干净的代码爵士乐。

      但是,StatefulWidget 在练习时并非如此。让我在接近设计小部件时给你我的心智模型。至于示例,我将处理以下 UI 组件 - Column 和两个 ListItem 和两个 Switches(StatefulWidgets),其中顶部开关切换底部开关。

      好的,有一个列表,有些项目附加了偏好切换。 第一个想法是:

      • 制作TitlePreferenceWidget.dart
      • 制作PreferenceWidget.dart
      • 制作TitleWithPreferenceWidget.dart
      • 制作TextWidget.dart

      然后,制作列表:

      Column(
        children: [
          TitleWithPreferenceWidget(...),
          TextWidget(...),
          TitleWithPreferenceWidget(...),
          TextWidget(...),
        ]
      )
      

      好的,有小部件。如何与 bloc 沟通?

      好吧,问题出现了,因为TitlePreferenceWidgetPreferenceWidget 将有一个Switch 小部件。 Switch 需要value: 参数并且能够运行动画。在每个教程中,都需要在 Widget 中调用 setState 聚合Switch。所以小部件需要扩展StatefulWidget

      StatefulWidgets 的注释 - 它们关联的 State 对象的生命周期比 StatelessWidgets 长,它们是为 性能原因。 Resource here。所以State 对象不是 在小部件树重建阶段销毁,而 StatelessWidgets 被销毁并重新创建。这样可以流畅地运行动画。

      那么如何使用 bloc 呢? 这可能会出现在您的脑海中(伪代码):

      Column(
        children: [
          BlocProvider<...>(
            create: ...
            child: Column(
              children: [
                 TitlePreferenceWidget(...), // will fire bloc events
                 BlocBuilder<...>(
                   builder: (context, state) => PreferenceWidget(state, ...) // this will rebuild upon parent
                 )  
              ]
            )
          ),
          TextWidget(...),
          BlocProvider<...>(
            create: ...
            child: Column(
              children: [
                 TitlePreferenceWidget(...), // will fire bloc events
                 BlocBuilder<...>(
                   builder: (context, state) => PreferenceWidget(state, ...) // this will rebuild upon parent
                 )  
              ]
            )
          ),
          TextWidget(...),
        ]
      )
      

      小心!每当触发事件时,都会创建一个新的 PreferenceWidget 实例。我们之前说过这是一个StatefulWidget,因此没有性能提升。 并且还会有bloc 事件乘法,添加新状态并且代码复杂性将会增加。如果您想将集团推高,该怎么办?就像它会成为主人一样,所以两个TitleWithPreferenceWidget可以交流。然后你需要向像bool wasFiredFromBlocA 这样的块事件添加更多数据。这变成了一个集团地狱

      不幸的是,我想出的唯一合理的解决方案是回归基础并将整个 TitleWithPreferenceWidget 编写为单个 Stateful 小部件。这是由使用单个 Bloc 并使其与机智交流的问题所激发的。

      所以对于TitleWithPreferenceWidgetState 对象包含两个字段,例如:

      bool _switchTitleState;
      bool _switchPreferenceState;
      

      瞧——这样状态对象按预期生活并正确处理状态。

      至于 bloc - 如果您真的想从“外部”(父树层次结构)收听某些 bloc,则可以在 BlocListener 的帮助下管理此类内部状态。 这将在TitleWithPreferenceWidget 的某个地方:

      
      return BlocListener(
        listener: (context, state) {
          setState(() {
              // use state to set state
          });
        },
        child: ... // build the widget tree normally.
      )
      
      

      请记住使用ValueKey 或其他Key 来代替TitleWithPreferenceWidget,因为它们需要通过 Flutter 框架来区分,因为它们 在我的示例中是它们的两个实例。

      这就是我的看法。集团是一个很好的解决方案。但在框架原则方面,最好还是坚持基础。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-01-27
        • 2021-08-12
        • 2017-10-03
        • 1970-01-01
        • 2011-12-29
        • 2016-08-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多