原文链接: https://levelup.gitconnected.com/flutter-state-management-in-2021-when-to-use-what-98722093b8bc
有时候选择比较少也是一件好事,例如在 React
中通常只盛行一到两个状态管理解决方案,而Flutter
自从 2020 年末开始,每个月似乎都有新的状态管理方案出现,因此这里主要罗列出它们的一些优劣,从而帮助你选择最适合的状态管理方案。
一般在无需修改 pubspec.yaml
文件的情况下,你默认有两种状态管理解决方案可以选择,大多数时候这就足够了。
setState
仅在本地范围内有效,如果一个 Widget
需要改变它自己的状态,那么 setState
就是你最好的选择。
例如:修改开关是打开还是关闭,或者存储改变正在输入的文本内容,这种场景你真的不需要考虑任何其它状态管理包。
我的经验法则是:如果只在此 Widget
中需要有状态变量,或者在该控件树下恰好只有 1 个上下的 Widget
,则它属于本地范围,这时候直接使用 StatefulWidget
是最合适不过。
如果你需要将状态在控件树内向下传递树,那么只需将变量放在子 Widget
的构造函数中;如果相同的变量需要传递到 2+ Widget
的构造函数,那么这时候才需要研究更高范围的状态管理。
前面我们在 setState
中讨论的那个开关,如果它是控制应用是处于暗模式还是亮模式,如果在这种情况下,你就需要将状态提升到可以更好地沿控件树传播的某个位置。
而在你使用第三方框架去完成这个需求时,让我们看一下 InheretedWidget
。
InheretedWidget
允许它下面的任何 Widget
访问它的属性,这意味着可以有一个变量,例如:
enum Theme {
dark,
light
}
在 InheretedWidget
内部,任何与主题有关的 Widget
都可以通过 MyInheretedWidget.of(context).theme
访问主题,并且该Widget
还会在主题更新时自动重建。
直接使用 InheretedWidget
不好的地方在于会有很多样板,Widget
系统有大量重复的代码。
Bloc
可能是 Flutter 中状态管理最古老的解决方案之一(不考虑 scoped_model
的话),并且现在看来仍然还不错。
最近 BLoC
已将 Cubit
添加到组合中,这使得 BLoC
或多或少不会显得过气,因为 Cubit
降低了所需的样板,这意味着以后迁移更容易,而在我看来 BLoC
在这两个不同的领域中表现出色:
BLoC
在 不灵活 方面做得非常好,可能对于很多人来说这是一件坏事:他们希望能更快速地更改他们的应用,而无需编写或更改太多代码。
但是对于团队来说情况并非如此:通过让事情变得不灵活,你可以保证一切都按照最初开发人员的预期工作——例如 BLoC
中的状态仅仅有 1、2 和 3 这样的值,你在使用 BLoC
更改为这些值时,其他程序员不会意外地将其值移动到 4,这就是它不灵活的好处。
BLoC
是基于事件驱动的,你必须定义你的事件,执行 API 调用可能会触发一个事件,该事件会推出一个 CallingAPIState
的 state,然后当 API 调用完成时,它会推出一个HaveAPIResultsState
.
如果你想严格定义你的事件和状态,那么 BLoC
很适合你,如果你需要灵活性和开发速度,那么 BLoC
可能不是正确的选择。
出于遗留原因这里将 Provider
列入介绍,它很简单,很干净,很棒……但有一些缺陷和改进的余地。
将 Provider
视为 InheretedWidget
使用可以减少样板文件,事实上 Provider
是建立在 InheretedWidget
之上,它只是减少了你需要编写的代码量。
如果你的应用已经在使用 Provider
,那么你可以继续使用它,这是一个非常好的状态管理包,没有理由需要迁移到另一个解决方案。
但是它还有一些改进的余地,我认为 RiverPod
在 Provider
有改进余地的地方做得更好。
在 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
设计的状态管理框架更为干净。