跟着 Flutter 的开展,这些年 Flutter 上的状况办理结构如“漫山遍野”般层出不穷,而近一年以来最受官方推荐的状况办理结构无疑便是 Riverpod ,甚至已经超过了 Provider ,事实上 Riverpod 官方也称自己为 “Provider,但异乎寻常”。

Provider 自身用它自己的话来说是 “InheritedWidget 的封装,但更简略且复用才能更强。” ,而 Riverpod 便是在 Provider 的根底上重构了新的或许。

关于过去一年状况办理结构的比照能够看 《2021 年的 Flutter 状况办理:怎样选择?》 , 本文首要是带你解剖 RiverPod 的内部是怎样完成,了解它的作业原理,以及怎样做到比 Provider 更少的模板和不依赖 BuildContext

前语

假如说 Riverpod 最显着的特点是什么,那便是外部不依赖 BuildContext (其实便是换了别的一种依赖形状),由于不依赖 BuildContext ,所以它能够比较简略做到相似如下的效果:

Flutter Riverpod 全面深入解析,为什么官方推荐它?

也便是 Riverpod 中的 Provider 能够随意写成全局,而且不依赖 BuildContext 来编写咱们需求的业务逻辑

⚠️ 提前声明下,这儿和后续的 Provider ,和第三方库 provider 没有联系

Riverpod 详细内部是怎样完成的呢?接下来让咱们开端探索 Riverpod 的完成原理。

Riverpod 的完成相对仍是比较杂乱,所以还耐性往下看,由于本篇是逐步解析,所以假如看的进程有些利诱能够先不用在意,通篇看完再回过来翻阅或许就会愈加明亮

从 ProviderScope 开端

在 Flutter 里只需运用了状况办理,就一定避不开 InheritedWidget , Riverpod 里也一样,在 Riverpod 都会有一个 ProviderScope, 一般只需求注册一个顶级的 ProviderScope

假如关于 InheritedWidget 还有疑问,能够看我:《全面了解State与Provider》

先从一个比方开端,如下图所示,是官方的一个简略的比方,能够看到这儿:

  • 嵌套一个顶级 ProviderScope
  • 创立了一个全局的 StateProvider
  • 运用 ConsumerWidgetref 对创立的 counterProvider 进行 read 然后读取 State ,获取到 int 值进行增加 ;
  • 运用另一个 Consumerref 对创立的 counterProvider 进行 watch ,然后读取到每次改动后的 int 值;

Flutter Riverpod 全面深入解析,为什么官方推荐它?

很简略的比方,能够看到没有任何 of(context) , 而全局的 counterProvider 里的数据,就能够经过 ref 进行 read/watch,而且正确地读取和更新。

那这是怎样完成的呢?counterProvider 又是怎样被注入到 ProviderScope 里边?为什么没有看到 context? 带着这些疑问咱们持续往下探索。

首要咱们看 ProviderScope ,它是唯一的顶级 InheritedWidget ,所以 counterProvider 必定是被寄存在这儿:

在 RiverPod 里, ProviderScope 最大的效果便是供给一个 ProviderContainer

更详细地说,便是经过内部嵌套的 UncontrolledProviderScope 供给,所以到这儿咱们能够知道:ProviderScope 能够往下供给状况同享,由于它内部有一个 InheritedWidget ,而首要往下同享的是 ProviderContainer 这个类

Flutter Riverpod 全面深入解析,为什么官方推荐它?

所以首要能够猜测:咱们界说的各种 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 值?

Flutter Riverpod 全面深入解析,为什么官方推荐它?

首要 StateProvider 是一个特别的 Provider ,在它的内部还有一个叫 _NotifierProvider 的帮它完成了一层转化,所以咱们先用最根底的 Provider 类作为剖析目标

根本是各种相似的 Provider 都是 ProviderBase 的子类,所以咱们先解析 ProviderBase

在 RiverPod 内部,每个 ProviderBase 的子类都会有其对应的 ProviderElementBase 子类完成 ,例如前面代码运用的 StateProviderProviderBase 的之类,相同它也有对应的 StateProviderElementProviderElementBase 的子类;

Flutter Riverpod 全面深入解析,为什么官方推荐它?

所以 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 的类型

Flutter Riverpod 全面深入解析,为什么官方推荐它?

假如在上面代码的根底上增加了 <int> 就更显着,事实上前面咱们一直在说的 State 便是一个泛型,而咱们界说 “Provider” 就需求界说这个泛型 State 的类型,比方这儿的 int

Flutter Riverpod 全面深入解析,为什么官方推荐它?

回归到一般 Provider 的调用,咱们传入的 Create 函数,其实便是在 ProviderElementBase 里被调用履行

Flutter Riverpod 全面深入解析,为什么官方推荐它?

如上图所示,简略来说当 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 的当地。

Flutter Riverpod 全面深入解析,为什么官方推荐它?

Result 的效果首要是经过 Result.dataResult.errormaprequireState 等去供给履行成果,一般情况下状况都是经过 requireState 获取,详细在 RiverPod 体现为:

咱们调用 read() 时,其实最终都调用到 element.readSelf(); ,也便是回来 requireState (其实一般也便是咱们的泛型 State) 。

Flutter Riverpod 全面深入解析,为什么官方推荐它?

是不是有点乱?

简略点了解便是:构建出 “Provider” 之后, “Element” 里会履行setState(_provider.create(this)); 调用咱们传入的 Create 函数,并把 “Element” 自己作为 ref 传入进入,所以咱们运用的 ref 其实便是 ProviderElementBase

所以 RiverPod 里的起名是有原因的,这儿的 “Provider” 和 “Element” 的联系就很有 Flutter 里 WidgetElement 的即视感。

分步骤来说便是:

  • 构建 Provider 时咱们传入了一个 Create 函数;
  • Create 函数会被 ProviderElementBase 内部的 setState 所调用,得到一个 Reuslt
  • Reuslt 内的 requireState 就能够让咱们在运用 read() 的时分,获取到咱们界说的 泛型 State 的值。

WidgetRef

前面介绍了那么多,但仍是没有说 StateProvider 怎样和 ProviderScope 相关到一同,也便是 “Provider” 怎样和 ProviderContainer 相关到一同,凭什么 ref.read 就能够读到 State

那么前面代码里,咱们用到的 ConsumerWidgetConsumer 都是同个东西,而这个 ref 便是前面咱们一直说的 “Element” ,或许说是 ProviderElementBase

Flutter Riverpod 全面深入解析,为什么官方推荐它?

在源码里能够看到, ConsumerWidget 的逻辑首要在 ConsumerStatefulElement, 而ConsumerStatefulElement 承继了 StatefulElement ,并完成了 WidgetRef 接口。

Flutter Riverpod 全面深入解析,为什么官方推荐它?

如上代码就能够看到前面许多了解的身影了: ProviderScopeProviderContainerWidgetRef

首要咱们看 ProviderScope.containerOf(this) ,终于看到咱们了解的 BuildContext 有没有,这个办法其实便是曾经咱们常用的 of(context) ,可是它被放到了 ConsumerStatefulElement 运用,用于获取 ProviderScope 往下同享的 ProviderContainer

Flutter Riverpod 全面深入解析,为什么官方推荐它?

所以咱们看到了,ConsumerWidget 里的 ConsumerStatefulElement 获取到了 ProviderContainer ,所以 ConsumerStatefulElement 能够调用到 ProviderContainer 的 read/watch

Flutter Riverpod 全面深入解析,为什么官方推荐它?

然后回过头来看,ConsumerStatefulElement 完成了 WidgetRef 接口,所以 咱们运用的 WidgetRef 便是 ConsumerStatefulElement 自身

Flutter Riverpod 全面深入解析,为什么官方推荐它?

Flutter Riverpod 全面深入解析,为什么官方推荐它?

也便是 ref.read 便是履行 ConsumerStatefulElementread , 然后履行到 ProviderContainerread

Flutter Riverpod 全面深入解析,为什么官方推荐它?

所以咱们能够总结: BuildContextElement , 然后 Element 又完成了 WidgetRef ,所以此时的 WidgetRef 便是 BuildContext 的代替

这儿不要把 Flutter 的 Element 和 RiverPod 里的 “ProviderElementBase” 搞混了。

所以 WidgetRef 这个接口成为了 Element 的笼统,代替了 BuildContext ,所以这便是 Riverpod 的“魔法”之一 。

read

所曾经面咱们已经理清了 ProviderScopeProviderProviderElementBaseProviderContainerConsumerWidgetConsumerStatefulElement) 和 WidgetRef 等的联系和功用,那最终咱们就能够开端理清楚 read 的整个作业链条。

咱们理清和知道了 的概念与效果之后,结合 ref.read 来做一个流程剖析,那全体便是:

  • ConsumerWidget 会经过内部的 ConsumerStatefulElement 获取到顶层 ProviderScope 内同享的 ProviderContainer
  • 当咱们经过 ref 调用 read/watch 时,其实便是经过 ConsumerStatefulElement 去调用 ProviderContainer 内的 read 函数;

Flutter Riverpod 全面深入解析,为什么官方推荐它?

那最终便是 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 。

Flutter Riverpod 全面深入解析,为什么官方推荐它?

Flutter Riverpod 全面深入解析,为什么官方推荐它?

所以在 ProviderContainer 内部:

  • 1、首要会依据 read 时传入的 provider 构建得到一个 _StateReader
  • 2、以 provider 为 key , _StateReader 为 value 存入 _stateReaders 这个 Map,并回来 _StateReader
  • 3、经过 _StateReadergetElement() 获取或许创立到 ProviderElementBase

这儿的以 ProviderBase 为 Key , _StateReader 为 value 存入 _stateReaders其实便是把 “provider” 存入到了 ProviderContainer,也便是和 ProviderScope 相关起来,也便是自此 “provider” 和 ProviderScope 就绑定到一同

Flutter Riverpod 全面深入解析,为什么官方推荐它?

没用运用到明面上的 BuildContext 和多余的嵌套,就让 ProviderProviderScope 相关起来。 别的这儿能够看到,ref.read 时,怎样经过 provider 构建或许获取到 ProviderElementBase

Flutter Riverpod 全面深入解析,为什么官方推荐它?

得到 ProviderElementBase 还记得前面咱们介绍 “Provider” 和 “Element” 的部分吗?ProviderElementBase 会调用 setState 来履行咱们传入的 Create 函数,得到 Result 回来 State

Flutter Riverpod 全面深入解析,为什么官方推荐它?

能够看到,这儿获取的 ProviderElementBase 之后 return element.readSelf() ,其实便是回来了 requireState

Flutter Riverpod 全面深入解析,为什么官方推荐它?

自从整个 RiverPod 里最简略的 ref.read 流程就全线贯通了

  • ProviderScope 往下同享 ProviderContainer

  • ConsumerWidget 内部的 ConsumerStatefulElement 经过 BuildContext 读取到 ProviderContainer, 而且完成 WidgetRef 接口;

  • 经过 WidgetRef 接口的 read(provider) 调用到 ProviderContainer 里的 read

  • ProviderContainer 经过 read 办法的 provider 创立或许获取得到 ProviderElementBase

  • ProviderElementBase 会履行 provider 里的 Create 函数,来得到 Result 回来 State

其他的watchrefresh 流程迥然不同,便是一些详细内部完成逻辑更杂乱而已,比方改写时:

经过 ref.refresh 办法, 其实触发的便是 ProviderContainerrefresh ,然后最终仍是会经过 _buildState 去触发 setState(_provider.create(this)) 的履行。

而从这个流程剖析,也看到了 RiverPod 怎样不露出运用 BuildContext 完成全线相关的逻辑

额定剖析

前面根本介绍完整个调用流程,这儿在额定介绍一些常见的调用时怎样完成,比方在 Riverpod 里边会看到许多 “Element” ,比方 ProviderElementStreamProviderElementFutureProviderElement 等这些 ProviderElementBase 的子类。

咱们成果过它们并不是 Flutter 里的 Element ,而是 Riverpod 里的的 State 单位,用于处理 Provider 的状况,比方 FutureProviderElement 便是在 ProviderElementBase 的根底上供给一个 AsyncValue<State>,首要在 FutureProvider 里运用

AsyncValue

在 RiverPod 里正常情况下的 create 办法界说是如下所示:

Flutter Riverpod 全面深入解析,为什么官方推荐它?

而在 FutureProvider 下是多了一个 _listenFuture,这个 Function 履行后的 value 就会是 AsyncValue<State> 的 State 类型。

Flutter Riverpod 全面深入解析,为什么官方推荐它?
Flutter Riverpod 全面深入解析,为什么官方推荐它?

_listenFuture 的履行上看, 内部会对这个 future() 履行,会先进入 AsyncValue<State>.loading() 之后,依据 Future 的成果回来决定回来AsyncValue<State>.data 或许 AsyncValue<State>.error

Flutter Riverpod 全面深入解析,为什么官方推荐它?

所以比方在 read / watch 时,回来的泛型 requireState 其实变成了 AsyncValue<State>

Flutter Riverpod 全面深入解析,为什么官方推荐它?

而针对 AsyncValue 官方做了一些 extension ,在 AsyncValueX 上,其间出了获取 AsyncDatadata \ asData 和 T value 之外,最首要供给了起那么所说的不同状况的构建办法,比方 when 办法:

Flutter Riverpod 全面深入解析,为什么官方推荐它?

autoDispose & family

在 Riverpod 里应该还很常见一个叫 autoDisposefamily静态变量,几乎每个 Provider 都有,又是用来干什么的呢?

举个比方,前面代码里咱们有个 FutureProvider, 咱们用到了里的 autoDispose

Flutter Riverpod 全面深入解析,为什么官方推荐它?

其实 FutureProvider.autoDispose 首要便是 AutoDisposeFutureProvider ,以此类推根本每个 Provider 都有自己的 autoDispose 完成,family 也是同理

假如说正常的 Provider 是承继了 AlwaysAliveProviderBase,那 AutoDisposeProvider 便是承继于 AutoDisposeProviderBase :

从名字能够看出来:

  • AlwaysAliveProviderBase 是一只活泼的;
  • AutoDisposeProviderBase 自然便是不 listened 的时分就毁掉;

也便是内部 _listeners_subscribers_dependents 都是空的时分,当然它还有别的一个 maintainState 的操控状况,默认它便是 false 的时分,就能够履行毁掉。

简略了解便是用“完即焚烧” 。

比方前面咱们介绍调用 read 的时分,都会调用 mayNeedDispose 去尝试毁掉:

Flutter Riverpod 全面深入解析,为什么官方推荐它?

毁掉也便是调用 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 默认的 createProviderFamily 的是:

Flutter Riverpod 全面深入解析,为什么官方推荐它?

能够看到 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 明明没有这样的构造函数。

Flutter Riverpod 全面深入解析,为什么官方推荐它?

这就涉及到 Dart 语言的特性,假如有爱好能够看 : /post/696836…

首要这儿拿到的是一个 ProviderFamily<TagTheme, Color> ,在 Dart 中一切函数类型都是 Function 的子类型,所以函数都固有地具有 call 办法。

咱们履行 tagThemeProvider2(Colors.red) 其实便是履行了 ProviderFamilycall 办法,然后履行了 create 办法,得到 FamilyProvider<State>FamilyProvider 也便是 ProviderBase 的子类 。

Flutter Riverpod 全面深入解析,为什么官方推荐它?

⚠️注意这儿有点容易看错的当地,一个是 ProviderFamily , 一个是 FamilyProvider, 咱们从 ProviderFamily 里边得到了 FamilyProvider, 作为 ProviderBaseref.watch

最终

很久没有写这么长的源码剖析了,不知不觉就写到了半夜凌晨,其实相对来说,整个 Riverpod 愈加杂乱,所以阅读起来也愈加费事,可是运用起来反而会相对更快捷,特别是没有了 BuildContext 的约束,可是同时也是带来了 ConsumerWidget 的依赖,一切利害只能看你自己的需求,可是全体 Riverpod 肯定是一个优秀的结构,值得一试。