Riverpod源码分析1:Provider & ProviderFamily

1. intro & motivation

Riverpod是一个响应式的状态管理缓存框架 。如果你用NotifierProviderAsyncNotifierProvider,它用起来就像Android里的ViewModel+ StateFlow的结合体;或者也可以用FutureProviderProvider,去监听某个值的变化以起到缓存的作用。

Provider作为Flutter第一大状态管理框架,它的诸多不便我相信只要用过就能感觉得到。相较于Provider,Riverpod的实用性和易用性都大大的提高了。和Provider对比,Riverpod有以下优势:

  1. 没有那么多奇奇怪怪的Provider,用的最多的就是上面提到的4种。看看Provider里的一长串ChangeNotifierProxyProvider 1 2 3 4 5 6,一个类7个泛型,这是人能想出来的api?
  2. 懒加载。不像Provider一样,一个Provider恨不得给你一万种创建方法。
  3. 自动或手动select,不更新值就不刷新。Provider里写个 Selector6 老费劲了,7个泛型。
  4. 方便的组合多个provider的值,哪怕需要监听一万个值Riverpod实现一样简洁。对应Provider里的ProxyProvider 1 2 3 4 5 6
  5. Riverpod和Provider的作者是同一个人,可以参考作者的动机 Motivation | Riverpod 为什么他要写一个全新的状态管理框架。

我自己一直在用Riverpod,但是网上一直没有它的源码解析,可能国内用Riverpod的人不多。没办法只能自己看着写,不明白工作原理用起来心里总是没底。水平有限,希望来个大神。

本文基于Riverpod 2.6.1

2.Provider源码分析

Riverpod的设计参考了Flutter中的Widget - Element树,Provider只是作为配置,实际上的工作由ProviderElement完成,能写的东西实在不多。

Provider的作用:

  1. ProviderElement提供_createFn方法,创建/刷新自己的状态、注册回调。
  2. 重写createElement()方法,根据Provider的类型返回不同种类的ProviderElement

这篇文章中我会以最简单的Provider为例分析它的源码。实际上所有的Provider都继承自ProviderBase,它们基本的逻辑是相同的。

通常来说这两种写法是等价的。Riverpod的作者推荐使用上面的注解+代码生成的写法。

2.1 _createFndependencies

_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中生效。

如下图所示,在ProviderScopechild中读取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并不是ProviderProviderFamily是一个工厂,根据传入的参数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)时,实际上调用的就是FamilyBasecall方法,根据传入的providerFactory_createFn生成对应的Provider

ProviderFamily继承自FamilyBase_createFn(Ref,Arg)是我们创建ProviderFamily时传入的方法,providerFactoryProvider.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;
}
相关推荐
小墙程序员13 小时前
Flutter 教程(六)路由管理
flutter
zacksleo17 小时前
鸿蒙Flutter开发故事:不,你不需要鸿蒙化
flutter·harmonyos
帅次19 小时前
Flutter DropdownButton 详解
android·flutter·ios·kotlin·gradle·webview
bst@微胖子20 小时前
Flutter项目之构建打包分析
flutter
江上清风山间明月1 天前
Flutter开发There are multiple heroes that share the same tag within a subtree报错
android·javascript·flutter·动画·hero
无知的前端1 天前
Flutter 性能优化:实战指南
flutter·性能优化
飞川0011 天前
Flutter敏感词过滤实战:基于AC自动机的高效解决方案
android·flutter
小墙程序员1 天前
Flutter 教程(五)事件处理
flutter
耳東陈1 天前
Flutter开箱即用一站式解决方案
flutter