美文网首页
Riverpod源码分析(二)

Riverpod源码分析(二)

作者: 我爱田Hebe | 来源:发表于2023-01-02 15:54 被阅读0次

    前沿

    上一篇《Riverpod源码分析(一)》我们已经对 Riverpod 做了一个比较全面的介绍,今天我们就开始分析 Riverpod 的源码实现和状态更新执行过程。

    Riverpod源码分析

    在分析源码之前,先确认下Gihub 上 clone 的版本。我这里使用的是 v2.1.3 最新版本:

    riverpod: ^2.1.3
    flutter_riverpod: ^2.1.3
    hooks_riverpod: ^2.1.3
    

    一、项目结构

    可以看到 Riverpod 的主体代码在 packages 中,其中 packages 目录包含:flutter_riverpod、hooks_riverpod、riverpod、riverpod_annotation、riverpod_cli、riverpod_generator、riverpod_graph、riverpod_lint 和 riverpod_lint_flutter_test。下面我们依次来进行介绍:

    1. flutter_riverpod

    可以看到该目录建构还是比较简单的,主要定义了 ProviderScope 和 Consumer 这样提供状态管理的 Widget,还定义了 ChangeNotifierProvider 的相关 builders调用

    1. hooks_riverpod

    可以看到 hook_riverpod 只有一个 consumer.dart 文件有代码,主要定义了 HookConsumerWidgetHookConsumer 两个既可以使用钩子又可以侦听 providersWidget。其中,HookConsumer 继承 HookConsumerWidget 提供 ConsumerBuilder 回调支持。

    1. riverpod

    riverpod 目录是 Riverpod 插件的最核心的目录,其定义的类特别多,这里先不做过多介绍,只需要记住其是实现状态管理的核心 package。

    1. riverpod_annotation

    riverpod_annotation 目录,看其名字我们就可以知道是 Riverpod 提供注解的 package。其主要类 Riverpod 代码非常简单,即一个注解的定义:

    @Target({TargetKind.classType, TargetKind.function})
    class Riverpod {
      /// {@macro riverpod_annotation.provider}
      const Riverpod({
        this.keepAlive = false,
      });
    
      /// Whether the state of the provider should be maintained if it is no-longer used.
      ///
      /// Defaults to false.
      final bool keepAlive;
    }
    
    /// {@macro riverpod_annotation.provider}
    @Target({TargetKind.classType, TargetKind.function})
    const riverpod = Riverpod();
    
    1. Riverpod_cli

    Riverpod_cli 顾名思义是 Riverpod 的脚手架定义,主要是一些 Riverpod 的命令行,帮助升级到 Riverpod 的新版本。

    1. riverpod_generator

    riverpod_generator 该项目是 Riverpod 的一个附带包,旨在通过依赖代码生成来提供不同的定义 “providers” 的语法。

    1. riverpod_graph

    riverpod_graph 项目非常简单,仅有一个 analyze.dart 文件。主要用来分析 Riverpod 项目和生成 providers / Widget 之间的依赖关系图

    1. Riverpod_lint

    Riverpod_lint 也非常简单,主要在编译/构建时进行静态类型检查。

    1. riverpod_lint_flutter_test

    riverpod_lint_flutter_test 是 Riverpod_lint 的测试用例。

    这里可以得出一些结论:1. Riverpod 是一个纯 dart 实现的 package,没有任何平台相关的代码。2. Riverpod 插件其核心是 riverpod 项目下的状态管理,支持 annotation 注解hook 钩子静态检查 lint 等功能,以及 cli脚手架依赖关系图分析等插件工具组成,可以说插件的功能十分完善。

    二、状态管理实现过程分析

    看完整个项目结构,发现整个插件实现十分复杂难,让人对于源码分析无从下手。下面我们从使用一个状态管理的案例出发,通过分析其实现的底层逻辑,来推演整个状态更新的过程。

    还是从最经典的案例: 计数器开始:

    void main() {
      runApp(
        // 添加ProviderScope可以使Riverpod适用于整个项目
        const ProviderScope(child: MyApp()),
      );
    }
    
    /// provider是全局声明的,并指定如何创建一个状态
    final counterProvider = StateProvider((ref) => 0);
    
    class Home extends ConsumerWidget {
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        return Scaffold(
          body: Center(
            // Consumer是一个允许读取providers的widget
            child: Consumer(
              builder: (context, ref, _) {
                final count = ref.watch(counterProvider);
                return Text('$count');
              },
            ),
          ),
          floatingActionButton: FloatingActionButton(
            // read方法用于更新provider的值
            onPressed: () => ref.read(counterProvider.notifier).state++,
            child: const Icon(Icons.add),
          ),
        );
      }
    }
    

    1. 代码结构分析

    1. 添加 ProviderScope 使 Riverpod 适用于整个项目

    在前面我们有介绍到 ProviderScope ,是一个状态管理 Widget,位于 flutter_riverpod 项目中。ProviderScope 是一个 StatefulWidget,成员变量有一个 child 用于子类实现 Widget,observers 作为观察者的集合,用于订阅 providers 的监听;overrides 用于 provider/family 如何覆盖的信息;提供 parent 作为 ProviderContainer,暴露其他 packages 下作用域的访问。

    1. 声明全局变量StateProvider

    可以看到 StateProvider 继承 _StateProviderBase 混入 AlwaysAliveProviderBase。我们在创建实例时有传入 _createFn ,其实就是一个带 StateProviderRef 参数的 Function

     class StateProvider<T> extends _StateProviderBase<T>
        with AlwaysAliveProviderBase<T> {}
    
     final T Function(StateProviderRef<T> ref) _createFn;
    

    这里混入 AlwaysAliveProviderBase,AlwaysAliveProviderBase 又混入 ProviderListenable。 主要是为了实现 Provider 的 state 更新功能。

    1. 创建 Consumer 允许读取 providers

    这里我们分析下Consumer:

    @sealed
    class Consumer extends ConsumerWidget {
      /// {@template riverpod.consumer}
      const Consumer({super.key, required ConsumerBuilder builder, Widget? child})
          : _child = child,
            _builder = builder;
    
      final ConsumerBuilder _builder;
      final Widget? _child;
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        return _builder(context, ref, _child);
      }
    }
    

    Consumer 的实现非常简单,继承 ConsumerWidget,实现 ConsumerBuilder,完成 build 的 Widget 构建。

    我们接着往下看,ConsumerWidget 继承 ConsumerStatefulWidget,创建 _ConsumerState,并将 widget.build 添加到自身 build 实现中。

    abstract class ConsumerWidget extends ConsumerStatefulWidget {
      /// {@macro riverpod.consumerwidget}
      const ConsumerWidget({super.key});
    
        Widget build(BuildContext context, WidgetRef ref);
    
      @override
      // ignore: library_private_types_in_public_api
      _ConsumerState createState() => _ConsumerState();
    }
    
    class _ConsumerState extends ConsumerState<ConsumerWidget> {
      @override
      WidgetRef get ref => context as WidgetRef;
    
      @override
      Widget build(BuildContext context) {
        return widget.build(context, ref);
      }
    }
    

    ConsumerStatefulWidget 只是在 createElement 中将 ConsumerStatefulElement 创建。

    abstract class ConsumerStatefulWidget extends StatefulWidget {
      /// A [StatefulWidget] that can read providers.
      const ConsumerStatefulWidget({super.key});
    
      @override
      // ignore: no_logic_in_create_state
      ConsumerState createState();
    
      @override
      ConsumerStatefulElement createElement() {
        return ConsumerStatefulElement(this);
      }
    }
    

    我们接下来看一看 ConsumerStatefulElement,其继承 StatefulElement,通过在 didChangeDependencies 中判断当前的 ProviderContainer 是否是同一个。

    1. 更新counterProvider的状态
    ref.read(counterProvider.notifier).state++
    

    其中 ref 是 WidgetRef,一个抽象类,主要定义了 provider 和 Widget 之前的交互。有 watchexistslistenlistenManualreadrefreshinvalidate等方法。

    2. 执行流程分析

    1. Consumer.build 时 ConsumerStatefulElement 执行 watch 方法将 target 添加到 _dependencies 中,然后通过 ProviderContainer 的 listen 方法添加其 provider 的 addListener。最后将 listern 封装成 _ListenerEntry 添加到S _listeners 的集合中。
    /// state_notifier-0.7.2+1/lib/state_notifier.dart
    RemoveListener addListener(
        Listener<T> listener, {
        bool fireImmediately = true,
      }) {
        final listenerEntry = _ListenerEntry(listener);
        _listeners.add(listenerEntry);
        ...
        return () {
          if (listenerEntry.list != null) {
            listenerEntry.unlink();
          }
        };
      }
    
    1. 当我们点击 onPressed 时,通过 ref.read 获取到 StateController.state 对象,然后更新其 state 值。在 StateNotifier.state 中我们可以看到其代码逻辑,通过遍历 _listeners 进行回调
    /// state_notifier-0.7.2+1/lib/state_notifier.dart
    for (final listenerEntry in _listeners) {
      try {
         listenerEntry.listener(value);
      } catch (error, stackTrace) {
         ...
      }
    }
    
    1. 当我们的 listener 触发更新时,前面定义的 Consumer 的 builder 函数就会触发回调,从而更新其监听的 UI。可以看到其更新逻辑也非常简单,将 StateController 的状态 result 赋值给 _stateNotifier,然后调用 setState 触发 state 的更新。
      /// riverpod/lib/src/state_provider/base.dart
      @override
      void create({required bool didChangeDependency}) {
        final provider = this.provider as _StateProviderBase<T>;
        final initialState = provider._create(this);
    
        final controller = StateController(initialState);
        _controllerNotifier.result = Result.data(controller);
    
        _removeListener = controller.addListener(
          fireImmediately: true,
          (state) {
            _stateNotifier.result = _controllerNotifier.result;
            setState(state);
          },
        );
      }
    

    至此,我们简单的分析完了整个状态更新的执行过程。

    总结

    今天我们先介绍了Riverpod项目的整理结构及其功能,然后从计数器案例入手,分析Riverpod的文件组成和源码执行更新状态的整个过程。下一篇文章将继续深入分析Riverpod的实现源码,请大家保持关注。

    作者:Fitem
    链接:https://juejin.cn/post/7183260548711055421

    相关文章

      网友评论

          本文标题:Riverpod源码分析(二)

          本文链接:https://www.haomeiwen.com/subject/rxtkcdtx.html