1. intro & motivation
Riverpod
是一个响应式的状态管理 及缓存框架 。如果你用NotifierProvider
和AsyncNotifierProvider
,它用起来就像Android里的ViewModel
+ StateFlow
的结合体;或者也可以用FutureProvider
或Provider
,去监听某个值的变化以起到缓存的作用。
Provider作为Flutter第一大状态管理框架,它的诸多不便我相信只要用过就能感觉得到。相较于Provider,Riverpod的实用性和易用性都大大的提高了。和Provider
对比,Riverpod
有以下优势:
- 没有那么多奇奇怪怪的Provider,用的最多的就是上面提到的4种。看看Provider里的一长串ChangeNotifierProxyProvider 1 2 3 4 5 6,一个类7个泛型,这是人能想出来的api?
- 懒加载。不像
Provider
一样,一个Provider恨不得给你一万种创建方法。 - 自动或手动
select
,不更新值就不刷新。Provider里写个 Selector6 老费劲了,7个泛型。 - 方便的组合多个
provider
的值,哪怕需要监听一万个值Riverpod实现一样简洁。对应Provider
里的ProxyProvider 1 2 3 4 5 6。 - Riverpod和Provider的作者是同一个人,可以参考作者的动机 Motivation | Riverpod 为什么他要写一个全新的状态管理框架。
我自己一直在用Riverpod
,但是网上一直没有它的源码解析,可能国内用Riverpod的人不多。没办法只能自己看着写,不明白工作原理用起来心里总是没底。水平有限,希望来个大神。
本文基于Riverpod 2.6.1
2.Provider
源码分析
Riverpod
的设计参考了Flutter
中的Widget
- Element
树,Provider
只是作为配置,实际上的工作由ProviderElement
完成,能写的东西实在不多。
Provider
的作用:
- 给
ProviderElement
提供_createFn
方法,创建/刷新自己的状态、注册回调。 - 重写
createElement()
方法,根据Provider的类型返回不同种类的ProviderElement
。
这篇文章中我会以最简单的Provider
为例分析它的源码。实际上所有的Provider
都继承自ProviderBase
,它们基本的逻辑是相同的。
通常来说这两种写法是等价的。Riverpod的作者推荐使用上面的注解+代码生成的写法。
2.1 _createFn
与dependencies
_createFn(Ref ref)
决定读取这个Provider
时返回的值。在上文的例子中返回值恒定为1。
可以通过Ref
观测(watch
)其他Provider
的值,当观测的provider
的值发生变化时,本provider
的_createFn
会重新执行,得到修改后的值。
当该provider
监听(watch
)/读取(read
)了一个被覆写(override
)的provider
时,需要在dependencies
中声明,这样Riverpod会尝试从最近(而不是最顶层)的ProviderScope
中找到对应的状态。
dependencies
涉及到作用域的知识,可以参考下一篇文章。
2.2 Provider#overrideWith(Ref ref)
一部分Provider
可以 在ProviderScope
中 被覆写(override
)以改变其逻辑,返回一个新的provider
。覆写后的Provider
仅在其所属的作用域ProviderScope
中生效。
如下图所示,在ProviderScope
的child
中读取collectionIdProvider
时,返回的是被覆写的id。
并不是所有的
provider
都支持override
。
2.3 ProviderBase
所有的Provider
都继承自ProviderBase
。因为实在没什么好说的,我把参数的作用写成注释打在源码里了
dart
@immutable
abstract class ProviderBase<StateT> extends ProviderOrFamily
with ProviderListenable<StateT>
implements ProviderOverride, Refreshable<StateT> {
const ProviderBase({
// 这个Provider的名字
required super.name,
// 来自哪个ProviderFamily,或为空
required this.from,
// 通过ProviderFamily接收的参数,或为空
required this.argument,
required this.debugGetCreateSourceHash,
// 直接依赖
required super.dependencies,
// 直接和间接依赖
required super.allTransitiveDependencies,
});
// 用作key的Provider,ProviderScope通过它找到对应的ProviderElement
@override
ProviderBase<Object?> get _origin => this;
// 提供_createFn的Provider,可能由于覆写的原因和_origin不是同一个
@override
ProviderBase<Object?> get _override => this;
// 省略read和listen的逻辑,实际上交由`ProviderElement`处理
// Provider子类实现,创建对应的ProviderElement
ProviderElementBase<StateT> createElement();
}
3 ProviderFamily
:可以接收参数的Provider
...?
事实上ProviderFamily
并不是Provider
。ProviderFamily
是一个工厂,根据传入的参数args
生成对应的provider
。判断通过ProviderFamily
产生的provider
实例是否相同的方法是比较ProviderFamily
和参数是否等同。
通常来说这两种写法是等价的。泛型1是该Provider返回值,泛型2是该ProviderFamily接收参数的类型。可以把Provider.family理解成ProviderFamily。
不同类型的Provider对应不同类型的Family。
通过ProviderFamily
生成的Provider
,它的from
参数是对应的family
,而argument
参数是_createFn(ref,arg)
中额外的参数arg
。
如上图所示,idProvider
实际上是一个接收一个int
参数的ProviderFamily
。当我们调用idProvider(3)
时,实际上调用的就是FamilyBase
的call
方法,根据传入的providerFactory
和_createFn
生成对应的Provider
。
ProviderFamily
继承自FamilyBase
。_createFn(Ref,Arg)
是我们创建ProviderFamily
时传入的方法,providerFactory
是Provider.internal
,Provider
的构造方法之一。
dart
@internal
class FamilyBase<RefT extends Ref<R>, R, Arg, Created,
ProviderT extends ProviderBase<R>> extends Family<R>
with _FamilyMixin<R, Arg, ProviderT> {
const FamilyBase(
this._createFn, {
required ProviderCreate<ProviderT, Created, RefT> providerFactory,
required this.name,
required this.dependencies,
required this.allTransitiveDependencies,
required this.debugGetCreateSourceHash,
}) : _providerFactory = providerFactory;
// 这里是Provider.internal,Provider的构造方法之一
final ProviderCreate<ProviderT, Created, RefT> _providerFactory;
// 我们自己定义的 (ref,int) => '${i + 1}';
final Created Function(RefT ref, Arg arg) _createFn;
// 执行call方法,返回新的Provider
@override
ProviderT call(Arg argument) => _providerFactory(
(ref) => _createFn(ref, argument),
name: name,
// 普通的Provider,from和argument均为null
from: this,
argument: argument,
dependencies: dependencies,
allTransitiveDependencies: allTransitiveDependencies,
debugGetCreateSourceHash: debugGetCreateSourceHash,
);
@override
final String? name;
@override
final Iterable<ProviderOrFamily>? dependencies;
@override
final Set<ProviderOrFamily>? allTransitiveDependencies;
}