100 lines
6.6 KiB
Markdown
100 lines
6.6 KiB
Markdown
|
|
|||
|
> 原文链接: 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` 设计的状态管理框架更为干净。
|