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;
}
相关推荐
消失的旧时光-19438 小时前
Flutter 异步编程:Future 与 Stream 深度解析
android·前端·flutter
星释9 小时前
鸿蒙Flutter三方库适配指南-02.Flutter相关知识基础
flutter·华为·harmonyos
傅里叶17 小时前
Flutter项目使用 buf.build
flutter
恋猫de小郭19 小时前
iOS 26 开始强制 UIScene ,你的 Flutter 插件准备好迁移支持了吗?
android·前端·flutter
yuanlaile19 小时前
Flutter开发HarmonyOS鸿蒙App商业项目实战已出炉
flutter·华为·harmonyos
CodeCaptain20 小时前
可直接落地的「Flutter 桥接鸿蒙 WebSocket」端到端实施方案
websocket·flutter·harmonyos
stringwu20 小时前
Flutter 中的 MVVM 架构实现指南
前端·flutter
消失的旧时光-19431 天前
Flutter 异步体系终章:FutureBuilder 与 StreamBuilder 架构优化指南
flutter·架构
消失的旧时光-19432 天前
Flutter 异步 + 状态管理融合实践:Riverpod 与 Bloc 双方案解析
flutter