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;
}