GSYFlutterBook/Flutter-StateM.md

100 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

> 原文链接: https://levelup.gitconnected.com/flutter-state-management-in-2021-when-to-use-what-98722093b8bc
有时候选择比较少也是一件好事,例如在 `React` 中通常只盛行一到两个状态管理解决方案,而`Flutter` 自从 2020 年末开始,每个月似乎都有新的状态管理方案出现,因此这里主要罗列出它们的一些优劣,从而帮助你选择最适合的状态管理方案。
## 基础
一般在无需修改 `pubspec.yaml`文件的情况下,你默认有两种状态管理解决方案可以选择,大多数时候这就足够了。
### setState
![](http://img.cdn.guoshuyu.cn/20220328_Flutter-StateM/image1)
`setState` 仅在本地范围内有效,如果一个 `Widget` 需要改变它自己的状态,那么 `setState` 就是你最好的选择。
例如:修改开关是打开还是关闭,或者存储改变正在输入的文本内容,**这种场景你真的不需要考虑任何其它状态管理包**。
我的经验法则是:**如果只在此 `Widget` 中需要有状态变量,或者在该控件树下恰好只有 1 个上下的 `Widget`,则它属于本地范围,这时候直接使用 `StatefulWidget` 是最合适不过。**
如果你需要将状态在控件树内向下传递树,那么只需将变量放在子 `Widget` 的构造函数中;如果相同的变量需要传递到 2+ `Widget` 的构造函数,那么这时候才需要研究更高范围的状态管理。
### InheretedWidget
前面我们在 `setState` 中讨论的那个开关,如果它是控制应用是处于暗模式还是亮模式,如果在这种情况下,你就需要将状态提升到可以更好地沿控件树传播的某个位置。
而在你使用第三方框架去完成这个需求时,让我们看一下 `InheretedWidget`
`InheretedWidget` 允许它下面的任何 `Widget` 访问它的属性,这意味着可以有一个变量,例如:
```dart
enum Theme {
dark,
light
}
```
`InheretedWidget` 内部,任何与主题有关的 `Widget` 都可以通过 `MyInheretedWidget.of(context).theme` 访问主题,并且该`Widget` 还会在主题更新时自动重建。
直接使用 `InheretedWidget` 不好的地方在于会有很多样板,`Widget` 系统有大量重复的代码。
## 你需要安装的那些
### BLoC (Cubit?)
`Bloc` 可能是 Flutter 中状态管理最古老的解决方案之一(不考虑 `scoped_model` 的话),并且现在看来仍然还不错。
最近 `BLoC` 已将 `Cubit` 添加到组合中,这使得 `BLoC` 或多或少不会显得过气,因为 `Cubit` 降低了所需的样板,这意味着以后迁移更容易,而在我看来 `BLoC` 在这两个不同的领域中表现出色:
#### 1、与团队合作
`BLoC`*不灵活* 方面做得非常好,可能对于很多人来说这是一件坏事:他们希望能更快速地更改他们的应用,而无需编写或更改太多代码。
但是对于团队来说情况并非如此:通过让事情变得不灵活,你可以保证一切都按照最初开发人员的预期工作——例如 `BLoC` 中的状态仅仅有 1、2 和 3 这样的值,你在使用 `BLoC` 更改为这些值时,其他程序员不会意外地将其值移动到 4这就是它不灵活的好处。
#### 1、事件驱动状态
`BLoC` 是基于事件驱动的,你必须定义你的事件,执行 API 调用可能会触发一个事件,该事件会推出一个 `CallingAPIState` 的 state然后当 API 调用完成时,它会推出一个`HaveAPIResultsState`.
**如果你想严格定义你的事件和状态,那么 `BLoC` 很适合你,如果你需要灵活性和开发速度,那么 `BLoC` 可能不是正确的选择。**
## Provider
![](http://img.cdn.guoshuyu.cn/20220328_Flutter-StateM/image2)
出于遗留原因这里将 `Provider` 列入介绍,它很简单,很干净,很棒……但有一些缺陷和改进的余地。
`Provider` 视为 `InheretedWidget` 使用可以减少样板文件,事实上 `Provider` 是建立在 `InheretedWidget` 之上,它只是减少了你需要编写的代码量。
如果你的应用已经在使用 `Provider`,那么你可以继续使用它,这是一个非常好的状态管理包,没有理由需要迁移到另一个解决方案。
但是它还有一些改进的余地,我认为 **`RiverPod``Provider` 有改进余地的地方做得更好**。
## RiverPod
![](http://img.cdn.guoshuyu.cn/20220328_Flutter-StateM/image3)
`RiverPod` 的网站上可以看到,他们称自己为“`Provider`,但与众不同”。
这样的形容很贴切,`Provider` 即使削减了很多的模版,但仍然有一些是可以进一步减少的。此外`Provider` 依赖于 `BuildContext`——我认为在很多情况下这确实很棒(它会迫使你使用 `Widget` 树),但有时就像应用的生命周期一样,在任何的地方获取 `BuildContext` 是不切实际的。
`RiverPod``Provider` 的优点上改进如下:
- **比 `Provider` 更少的样板**`RiverPod` 在减少 `Provider` 模版方面做得很好,允许开发者只注册一个顶级存储而不必单独提供每个提供。(可能有人一想到把所有东西都集中在一个地方而畏缩——别担心,你可以确定你的 pod。
- 不依赖 `BuildContext`:这也是一个很好的选择,原因前面已经提到。有时你只是无法在需要的地方获得 `BuildContext`
- **编译安全**:到目前为止这是状态管理的最佳创新,只要代码能编译就是安全的,我们不再需要知道为什么不能在树中找到我们的 `Provider` 这是一项巨大的创新,可以为你节省很多的时间。
`RiverPod` 只是 `Provider `的不同皮肤——但是它是更光滑、更好的皮肤,如果您正在启动一个新应用并想使用 `Provider` ,我强烈建议你考虑 `RiverPod`
## 其他
以下是在考虑状态管理的时候调研过的方案,但最终没有使用:
- `GetX`:我不是 `GetX` 的粉丝,`GetX` 试图完成很多工作,但这限制了你的灵活性,如果你希望有“完整应用场景” 的第三方包,那么 `GetX` 是你的最佳选择没,我对此尝试过接入,但不喜欢它。
- `get_it``get_it` 不是一种状态管理方案——但大家一直在使用它,如果用作状态管理方案来看,它会非常混乱。
- `redux` / `fish_redux` / `mobx`:这些都来自 `React`,并且具有非常相似的风格——但我认为 `React``Flutter` 是两个看起来相似却不同的框架,如果你习惯了它们,那么你可以使用它们,但在我看来,为 `Flutter` 设计的状态管理框架更为干净。