跟着 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”
那前面咱们知道 ProviderScope
往下同享了 ProviderContainer
之后,Provider
又是怎样作业的呢?为什么 ref.watch
/ ref.read
会能够读取到它 Provider
里的值?
持续前面的代码,这儿只是界说了 StateProvider
,而且运用了 ref.watch
,为什么就能够读取到里边的 state
值?
首要 StateProvider
是一个特别的 Provider
,在它的内部还有一个叫 _NotifierProvider
的帮它完成了一层转化,所以咱们先用最根底的 Provider
类作为剖析目标。
根本是各种相似的 Provider
都是 ProviderBase
的子类,所以咱们先解析 ProviderBase
。
在 RiverPod 内部,每个 ProviderBase
的子类都会有其对应的 ProviderElementBase
子类完成 ,例如前面代码运用的 StateProvider
是 ProviderBase
的之类,相同它也有对应的 StateProviderElement
是 ProviderElementBase
的子类;
所以 RiverPod 里根本是每一个 “Provider” 都会有一个自己的 “Element” 。
⚠️这儿的 “Element” 不是 Flutter 概念里三棵树的
Element
,它是 RiverPod 里Ref
目标的子类。Ref
首要供给 RiverPod 内的 “Provider” 之间交互的接口,而且供给一些笼统的生命周期办法,所以它是 RiverPod 里的独有的 “Element” 单位。
那 “Provider” 和 “Element” 的效果是什么?
首要,在上面比方里咱们构建 StateProvider
时传入的 (ref) => 0
,其实便是 Create<State, StateProviderRef<State>>
函数,咱们就从这个 Create
函数作为入口来探索。
Create<T, R extends Ref> = T Function(R ref)
RiverPod 里构建 “Provider” 时都会传入一个 Create
函数,而这个函数里一遍咱们会写一些需求的业务逻辑,比方 counterProvider
里的 ()=> 0
便是初始化时回来一个 int
为 0 的值,更重要的是决定了 State
的类型。
假如在上面代码的根底上增加了 <int>
就更显着,事实上前面咱们一直在说的 State
便是一个泛型,而咱们界说 “Provider” 就需求界说这个泛型 State
的类型,比方这儿的 int
。
回归到一般 Provider
的调用,咱们传入的 Create
函数,其实便是在 ProviderElementBase
里被调用履行。
如上图所示,简略来说当 ProviderElementBase
履行 “setState” 时,就会调用 Create
函数,然后履行获取到咱们界说的泛型 State
,得到 Result
然后告诉并更新 UI。
⚠️ 这儿的 “setState” 也不是 Flutter Framework 里的
setState
,而是 RiverPod 内自己首要的一个 “setState” 函数,和 Flutter 结构里的State
无关。
所以每个 “Provider” 都会有自己的 “Element” ,而构建 “Provider” 时是传入的 Create
函数会在 “Element” 内经过 setState
调用履行。
“Element” 里的 setState
首要是经过新的 newState 去得到一个 RiverPod 里的 Result
目标,然后经过 _notifyListeners
去把得到 Result
更新到 watch
的当地。
Result
的效果首要是经过 Result.data
、Result.error
、 map
和 requireState
等去供给履行成果,一般情况下状况都是经过 requireState
获取,详细在 RiverPod 体现为:
咱们调用
read()
时,其实最终都调用到element.readSelf();
,也便是回来requireState
(其实一般也便是咱们的泛型State
) 。
是不是有点乱?
简略点了解便是:构建出 “Provider” 之后, “Element” 里会履行setState(_provider.create(this));
调用咱们传入的 Create
函数,并把 “Element” 自己作为 ref
传入进入,所以咱们运用的 ref
其实便是 ProviderElementBase
。
所以 RiverPod 里的起名是有原因的,这儿的 “Provider” 和 “Element” 的联系就很有 Flutter 里
Widget
和Element
的即视感。
分步骤来说便是:
- 构建 Provider 时咱们传入了一个
Create
函数; -
Create
函数会被ProviderElementBase
内部的setState
所调用,得到一个Reuslt
; -
Reuslt
内的requireState
就能够让咱们在运用read()
的时分,获取到咱们界说的 泛型State
的值。
WidgetRef
前面介绍了那么多,但仍是没有说 StateProvider
怎样和 ProviderScope
相关到一同,也便是 “Provider” 怎样和 ProviderContainer
相关到一同,凭什么 ref.read
就能够读到 State
?
那么前面代码里,咱们用到的 ConsumerWidget
和 Consumer
都是同个东西,而这个 ref
便是前面咱们一直说的 “Element” ,或许说是 ProviderElementBase
。
在源码里能够看到, ConsumerWidget
的逻辑首要在 ConsumerStatefulElement
, 而ConsumerStatefulElement
承继了 StatefulElement
,并完成了 WidgetRef
接口。
如上代码就能够看到前面许多了解的身影了: ProviderScope
、ProviderContainer
、 WidgetRef
。
首要咱们看 ProviderScope.containerOf(this)
,终于看到咱们了解的 BuildContext
有没有,这个办法其实便是曾经咱们常用的 of(context)
,可是它被放到了 ConsumerStatefulElement
运用,用于获取 ProviderScope
往下同享的 ProviderContainer
。
所以咱们看到了,ConsumerWidget
里的 ConsumerStatefulElement
获取到了 ProviderContainer
,所以 ConsumerStatefulElement
能够调用到 ProviderContainer
的 read/watch 。
然后回过头来看,ConsumerStatefulElement
完成了 WidgetRef
接口,所以 咱们运用的 WidgetRef
便是 ConsumerStatefulElement
自身
也便是 ref.read
便是履行 ConsumerStatefulElement
的 read
, 然后履行到 ProviderContainer
的 read
。
所以咱们能够总结: BuildContext
是 Element
, 然后 Element
又完成了 WidgetRef
,所以此时的 WidgetRef
便是 BuildContext
的代替。
这儿不要把 Flutter 的
Element
和 RiverPod 里的 “ProviderElementBase
” 搞混了。
所以 WidgetRef
这个接口成为了 Element
的笼统,代替了 BuildContext
,所以这便是 Riverpod 的“魔法”之一 。
read
所曾经面咱们已经理清了 ProviderScope
、 Provider
、 ProviderElementBase
、 ProviderContainer
、 ConsumerWidget
(ConsumerStatefulElement
) 和 WidgetRef
等的联系和功用,那最终咱们就能够开端理清楚 read
的整个作业链条。
咱们理清和知道了 的概念与效果之后,结合 ref.read
来做一个流程剖析,那全体便是:
-
ConsumerWidget
会经过内部的ConsumerStatefulElement
获取到顶层ProviderScope
内同享的ProviderContainer
; - 当咱们经过
ref
调用read
/watch
时,其实便是经过ConsumerStatefulElement
去调用ProviderContainer
内的read
函数;
那最终便是 ProviderContainer
内的 read
函数怎样读取到 State
?
这就要结合前面咱们相同介绍过的 ProviderElementBase
, 事实上 ProviderContainer
在履行 read
函数时会调用 readProviderElement
。
readProviderElement
顾名思义便是经过 Provider
去获取到对应的 Element
,例如 :
ref.read(counterProvider),
一般情况下 read/watch 简略来说便是从 ProviderContainer
里用 proivder
做 key 获取得到 ProviderElementBase
这个 “Element”,这个进程又有一个新的目标需求简略介绍下,便是:_StateReader
。
readProviderElement
其间一个要害便是获取 _StateReader
,在 ProviderContainer
里有一个 _stateReaders
的内部变量,它便是用于缓存 _StateReader
的 Map 。
所以在 ProviderContainer
内部:
- 1、首要会依据
read
时传入的provider
构建得到一个_StateReader
; - 2、以
provider
为 key ,_StateReader
为 value 存入_stateReaders
这个 Map,并回来_StateReader
; - 3、经过
_StateReader
的getElement()
获取或许创立到ProviderElementBase
;
这儿的以
ProviderBase
为 Key ,_StateReader
为 value 存入_stateReaders
,其实便是把 “provider” 存入到了ProviderContainer
,也便是和ProviderScope
相关起来,也便是自此 “provider” 和ProviderScope
就绑定到一同。
没用运用到明面上的 BuildContext
和多余的嵌套,就让 Provider
和 ProviderScope
相关起来。
别的这儿能够看到,在 ref.read
时,怎样经过 provider
构建或许获取到 ProviderElementBase
。
得到 ProviderElementBase
还记得前面咱们介绍 “Provider” 和 “Element” 的部分吗?ProviderElementBase
会调用 setState
来履行咱们传入的 Create
函数,得到 Result
回来 State
。
能够看到,这儿获取的 ProviderElementBase
之后 return element.readSelf()
,其实便是回来了 requireState
。
自从整个 RiverPod 里最简略的 ref.read
流程就全线贯通了:
-
ProviderScope
往下同享ProviderContainer
; -
ConsumerWidget
内部的ConsumerStatefulElement
经过BuildContext
读取到ProviderContainer
, 而且完成WidgetRef
接口; -
经过
WidgetRef
接口的read(provider)
调用到ProviderContainer
里的read
; -
ProviderContainer
经过read
办法的provider
创立或许获取得到ProviderElementBase
-
ProviderElementBase
会履行provider
里的Create
函数,来得到Result
回来State
;
其他的watch
,refresh
流程迥然不同,便是一些详细内部完成逻辑更杂乱而已,比方改写时:
经过
ref.refresh
办法, 其实触发的便是ProviderContainer
的refresh
,然后最终仍是会经过_buildState
去触发setState(_provider.create(this))
的履行。
而从这个流程剖析,也看到了 RiverPod 怎样不露出运用 BuildContext
完成全线相关的逻辑。
额定剖析
前面根本介绍完整个调用流程,这儿在额定介绍一些常见的调用时怎样完成,比方在 Riverpod 里边会看到许多 “Element” ,比方 ProviderElement
、StreamProviderElement
、 FutureProviderElement
等这些 ProviderElementBase
的子类。
咱们成果过它们并不是 Flutter 里的 Element
,而是 Riverpod 里的的 State 单位,用于处理 Provider
的状况,比方 FutureProviderElement
便是在 ProviderElementBase
的根底上供给一个 AsyncValue<State>
,首要在 FutureProvider
里运用。
AsyncValue
在 RiverPod 里正常情况下的 create 办法界说是如下所示:
而在 FutureProvider
下是多了一个 _listenFuture
,这个 Function 履行后的 value
就会是 AsyncValue<State>
的 State 类型。
从 _listenFuture
的履行上看, 内部会对这个 future()
履行,会先进入 AsyncValue<State>.loading()
之后,依据 Future
的成果回来决定回来AsyncValue<State>.data
或许 AsyncValue<State>.error
。
所以比方在 read
/ watch
时,回来的泛型 requireState
其实变成了 AsyncValue<State>
。
而针对 AsyncValue
官方做了一些 extension
,在 AsyncValueX
上,其间出了获取 AsyncData
的data
\ asData
和 T value 之外,最首要供给了起那么所说的不同状况的构建办法,比方 when
办法:
autoDispose & family
在 Riverpod 里应该还很常见一个叫 autoDispose
和 family
的静态变量,几乎每个 Provider
都有,又是用来干什么的呢?
举个比方,前面代码里咱们有个 FutureProvider
, 咱们用到了里的 autoDispose
:
其实 FutureProvider.autoDispose
首要便是 AutoDisposeFutureProvider
,以此类推根本每个 Provider
都有自己的 autoDispose
完成,family
也是同理。
假如说正常的 Provider
是承继了 AlwaysAliveProviderBase
,那 AutoDisposeProvider
便是承继于 AutoDisposeProviderBase
:
从名字能够看出来:
-
AlwaysAliveProviderBase
是一只活泼的; -
AutoDisposeProviderBase
自然便是不listened
的时分就毁掉;
也便是内部 _listeners
、_subscribers
、_dependents
都是空的时分,当然它还有别的一个 maintainState
的操控状况,默认它便是 false
的时分,就能够履行毁掉。
简略了解便是用“完即焚烧” 。
比方前面咱们介绍调用 read
的时分,都会调用 mayNeedDispose
去尝试毁掉:
毁掉也便是调用
element.dispose()
和从_stateReaders
这个map
里移除等等。
相同的 family
对应是 ProviderFamily
,它的效果是:运用额定的参数构建 provider ,也便是增加一个参数。
例如默认是把 :
final tagThemeProvider = Provider<TagTheme>
能够变成
final tagThemeProvider2 = Provider.family<TagTheme, Color>
然后你就能够运用额定的参数,在 read
/watch
的时分 :
final questionsCountProvider = Provider.autoDispose((ref) {
return ref
.watch(tagThemeProvider2(Colors.red));
});
之所以能够完成这个功用,就要看它的完成 ProviderFamily
,比照一般 Provider
默认的 create
,ProviderFamily
的是:
能够看到 create
的是新的一个 Provider
,也便是 family
下其实是 Provider
嵌套 Provider
。
所以从上面的比方出发,曾经咱们是经过 ref.watch(tagThemeProvider);
就能够了,由于咱们的 tagThemeProvider
的直接便是 ProviderBase
。
可是假如运用 ref.watch(tagThemeProvider2);
就会看到过错提示
The argument type 'ProviderFamily<TagTheme, Color>' can't be assigned to the parameter type 'ProviderListenable<dynamic>'.
是的,由于这儿是 Provider
嵌套 Provider
,咱们先得到是的 ProviderFamily<TagTheme, Color>
,所以咱们需求改为 ref.watch(tagThemeProvider2(Colors.red));
。
经过 tagThemeProvider2(Colors.red)
履行一次变为咱们需求的 ProviderBase
。
那 tagThemeProvider2
这个 ProviderFamily
为什么是这样履行? ProviderFamily
明明没有这样的构造函数。
这就涉及到 Dart 语言的特性,假如有爱好能够看 : /post/696836…
首要这儿拿到的是一个 ProviderFamily<TagTheme, Color>
,在 Dart 中一切函数类型都是 Function
的子类型,所以函数都固有地具有 call
办法。
咱们履行 tagThemeProvider2(Colors.red)
其实便是履行了 ProviderFamily
得 call
办法,然后履行了 create
办法,得到 FamilyProvider<State>
,FamilyProvider
也便是 ProviderBase
的子类 。
⚠️注意这儿有点容易看错的当地,一个是
ProviderFamily
, 一个是FamilyProvider
, 咱们从ProviderFamily
里边得到了FamilyProvider
, 作为ProviderBase
给ref.watch
。
最终
很久没有写这么长的源码剖析了,不知不觉就写到了半夜凌晨,其实相对来说,整个 Riverpod 愈加杂乱,所以阅读起来也愈加费事,可是运用起来反而会相对更快捷,特别是没有了 BuildContext
的约束,可是同时也是带来了 ConsumerWidget
的依赖,一切利害只能看你自己的需求,可是全体 Riverpod 肯定是一个优秀的结构,值得一试。