前沿
上一篇《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。下面我们依次来进行介绍:
- flutter_riverpod
可以看到该目录建构还是比较简单的,主要定义了 ProviderScope 和 Consumer 这样提供状态管理的 Widget
,还定义了 ChangeNotifierProvider 的相关 builders
和调用。
- hooks_riverpod
可以看到 hook_riverpod 只有一个 consumer.dart
文件有代码,主要定义了 HookConsumerWidget
和 HookConsumer
两个既可以使用钩子又可以侦听 providers 的 Widget
。其中,HookConsumer 继承 HookConsumerWidget 提供 ConsumerBuilder
回调支持。
- riverpod
riverpod 目录是 Riverpod 插件的最核心的目录,其定义的类特别多,这里先不做过多介绍,只需要记住其是实现状态管理的核心 package。
- 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();
- Riverpod_cli
Riverpod_cli 顾名思义是 Riverpod 的脚手架定义,主要是一些 Riverpod 的命令行,帮助升级到 Riverpod 的新版本。
- riverpod_generator
riverpod_generator 该项目是 Riverpod 的一个附带包,旨在通过依赖代码生成来提供不同的定义 “providers” 的语法。
- riverpod_graph
riverpod_graph 项目非常简单,仅有一个 analyze.dart
文件。主要用来分析 Riverpod 项目和生成 providers / Widget 之间的依赖关系图。
- Riverpod_lint
Riverpod_lint 也非常简单,主要在编译/构建时进行静态类型检查。
- 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. 代码结构分析
- 添加 ProviderScope 使 Riverpod 适用于整个项目
在前面我们有介绍到 ProviderScope ,是一个状态管理 Widget,位于 flutter_riverpod 项目中。ProviderScope 是一个 StatefulWidget,成员变量有一个 child 用于子类实现 Widget,observers 作为观察者的集合,用于订阅 providers 的监听;overrides 用于 provider/family 如何覆盖的信息;提供 parent 作为 ProviderContainer,暴露其他 packages 下作用域的访问。
- 声明全局变量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 更新功能。
- 创建 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 是否是同一个。
- 更新counterProvider的状态
ref.read(counterProvider.notifier).state++
其中 ref
是 WidgetRef,一个抽象类,主要定义了 provider 和 Widget 之前的交互。有 watch
、exists
、listen
、listenManual
、read
、refresh
、invalidate
等方法。
2. 执行流程分析
- 在
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();
}
};
}
- 当我们点击
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) {
...
}
}
- 当我们的
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
网友评论