让我分享我在 Flutter、Bloc 和 StatefulWidgets 工作 2 年后的见解。
当您发现自己处于想要让两个StatefulWidget 交流的情况时,请三思而后行,如果 Bloc 是答案!正如他们的文档中所建议的那样,我在 bloc-everything 方法上走得太远了
并期望bloc 能帮助我在代码中实现更好的模块化。我使用BlocListeners 来利用listener: 参数并在其中调用setState()...然后我尝试使用BlocBuilders。在将StatefulWidget 与Bloc 紧密集成时,我总是走到了死胡同,遇到了很多错误。你为什么问?只是为了实现这种干净、闪亮的模块化,将小部件放在单独的文件中,并使它们可重用以及所有干净的代码爵士乐。
但是,StatefulWidget 在练习时并非如此。让我在接近设计小部件时给你我的心智模型。至于示例,我将处理以下 UI 组件 - Column 和两个 ListItem 和两个 Switches(StatefulWidgets),其中顶部开关切换底部开关。
好的,有一个列表,有些项目附加了偏好切换。
第一个想法是:
- 制作
TitlePreferenceWidget.dart
- 制作
PreferenceWidget.dart
- 制作
TitleWithPreferenceWidget.dart
- 制作
TextWidget.dart
然后,制作列表:
Column(
children: [
TitleWithPreferenceWidget(...),
TextWidget(...),
TitleWithPreferenceWidget(...),
TextWidget(...),
]
)
好的,有小部件。如何与 bloc 沟通?
好吧,问题出现了,因为TitlePreferenceWidget 和PreferenceWidget 将有一个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 并使其与机智交流的问题所激发的。
所以对于TitleWithPreferenceWidget,State 对象包含两个字段,例如:
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 框架来区分,因为它们
在我的示例中是它们的两个实例。
这就是我的看法。集团是一个很好的解决方案。但在框架原则方面,最好还是坚持基础。