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