随着 Flutter 的发展,这些年 Flutter 上的状态管理框架如“雨后春笋”般层出不穷,而近一年以来最受官方推荐的状态管理框架无疑就是 Riverpod
,甚至已经超过了 Provider
,事实上 Riverpod
官方也称自己为 “Provider
,但与众不同”。
Provider
本身用它自己的话来说是 “InheritedWidget
的封装,但更简单且复用能力更强。” ,而Riverpod
就是在Provider
的基础上重构了新的可能。
关于过去一年状态管理框架的对比可以看 《2021 年的 Flutter 状态管理:如何选择?》 , 本文主要是带你解剖 RiverPod
的内部是如何实现,理解它的工作原理,以及如何做到比 Provider
更少的模板和不依赖 BuildContext
。
前言
如果说 Riverpod
最明显的特点是什么,那就是外部不依赖 BuildContext
(其实就是换了另外一种依赖形态),因为不依赖 BuildContext
,所以它可以比较简单做到类似如下的效果:
也就是 Riverpod
中的 Provider
可以随意写成全局,并且不依赖 BuildContext
来编写我们需要的业务逻辑。
⚠️ 提前声明下,这里和后续的
Provider
,和第三方库provider
没有关系。
那 Riverpod
具体内部是怎么实现的呢?接下来让我们开始探索 Riverpod
的实现原理。
Riverpod
的实现相对还是比较复杂,所以还耐心往下看,因为本篇是逐步解析,所以如果看的过程有些迷惑可以先不必在意,通篇看完再回过来翻阅可能就会更加明朗。
从 ProviderScope 开始
在 Flutter 里只要使用了状态管理,就一定避不开 InheritedWidget
, Riverpod 里也一样,在 Riverpod 都会有一个 ProviderScope
, 一般只需要注册一个顶级的 ProviderScope
。
如果对于 InheritedWidget 还有疑问,可以看我掘金:《全面理解State与Provider》
先从一个例子开始,如下图所示,是官方的一个简单的例子,可以看到这里:
- 嵌套一个顶级
ProviderScope
; - 创建了一个全局的
StateProvider
; - 使用
ConsumerWidget
的ref
对创建的counterProvider
进行read
从而读取 State ,获取到int
值进行增加 ; - 使用另一个
Consumer
的ref
对创建的counterProvider
进行watch
,从而读取到每次改变后的int
值;
很简单的例子,可以看到没有任何 of(context)
, 而全局的 counterProvider
里的数据,就可以通过 ref
进行 read/watch,并且正确地读取和更新。
那这是怎么实现的呢?
counterProvider
又是如何被注入到ProviderScope
里面?为什么没有看到context
? 带着这些疑问我们继续往下探索。
首先我们看 ProviderScope
,它是唯一的顶级 InheritedWidget
,所以 counterProvider
必定是被存放在这里:
在 RiverPod 里,
ProviderScope
最大的作用就是提供一个ProviderContainer
。
更具体地说,就是通过内部嵌套的 UncontrolledProviderScope
提供,所以到这里我们可以知道:ProviderScope
可以往下提供状态共享,因为它内部有一个 InheritedWidget
,而主要往下共享的是 ProviderContainer
这个类。
所以首先可以猜测:我们定义的各种 Providers, 比如上面的 counterProvider
, 都是被存到 ProviderContainer
中,然后往下共享。
事实上官方对于
ProviderContainer
的定义就是:用于保存各种 Providers 的 State ,并且支持 override 一些特殊 Providers 的行为。
ProviderContainer
这里出现了一个新的类,叫 ProviderContainer
,其实一般情况下使用 RiverPod 你都不需要知道它,因为你不会直接操作和使用它,但是你使用 RiverPod 的每个行为都会涉及到它的实现,例如 :
ref.read
会需要它的Result read<Result>
;ref.watch
会需要它的ProviderSubscription<State> listen<State>
;ref.refresh
会需要它的Created refresh<Created>
就算是各种 Provider
的保存和读取基本也和它有关系,所以它作为一个对各种 Provider
的内部管理的类,实现了 RiverPod 里很关键的一些逻辑。
“Provider” 和 “Element”
那前面我们知道