Riverpod之Provider&StateProvider(三)
第二节讲解了StateProvider的内部流程,主要涉及的是其内部的名叫state的Provider,今天先将上个例子改一种更普遍的写法,看看流程如何。
计数器Demo
效果和之前一样,点击+1
final counterProvider = StateProvider((ref) => 0);
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('example')),
body: Center(
child: Consumer(builder: (context, ref, _) {
final count = ref.watch(counterProvider);
return Text('$count');
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).state++,
child: const Icon(Icons.add),
),
);
}
}
不同的是这次抛弃了StateProvider内部的名为state的Provider,改成notifer上位,同时直接使用counterProvider,改动如下,下面我们从counterProvider实例化开始
final count = ref.watch(counterProvider); ref.read(counterProvider.notifier).state++
StateProvider的实例化
这个比较简单,之前没提过,这里提一嘴,主要就是在此实例化了_NotifierProvider,并共用了create函数,所以notifier的create方法中使用的_create函数和StateProvider一样,都是(ref) => 0
StateProvider(
Create<State, StateProviderRef<State>> create, {
...,
}) : notifier = _NotifierProvider(
create,
...
),
super(name: name, from: from, argument: argument);
StateProvider的watch流程
final count = ref.watch(counterProvider);
根据之前的经验,先看其create方法,因为这里面涉及初始化状态值,还有一些关系的构建,是一个关键操作
State create(
ProviderElementBase<State> ref,) {
final notifier = ref.watch(this.notifier);
final removeListener = notifier.addListener(ref.setState);
ref.onDispose(removeListener);
return notifier.state;
}
这里和上篇一样,也涉及了Provider对Provider的观察,我们直接总结里面这个ref.watch(this.notifier)的两个要点
- notifier的create方法,返回的是StateController,是notifier的state,也就是watch notifier返回的值,其中_create函数来自countProvider的Create函数“(ref) => 0”,那么初始化值为0
StateController<State> create(StateProviderRef<State> ref) {
final initialState = _create(ref);
final notifier = StateController(initialState);
ref.onDispose(notifier.dispose);
return notifier;
}
2. 建立了依赖关系,其实是ProviderElementBase之间的关系,这个类一直在幕后,不好命名,所以用provider名字表示就是下图这样,其实是双方都记录了对方,但这个例子用不到这层关系,就简便了事
随后给StateController添加了监听,这里的监听和前篇参数是不一样的,前篇的参数是StateControler自己,这里是其state值。方法的最后返回了其中状态值0
那么ref.watch(counterProvider)返回的就是0,除此之外在countProvidere中添加了监听用来刷新
StateProvider的更新流程
更新操作由read方法开启,read方法直接获取notifier中的state,也就是StateController对象
ref.read(counterProvider.notifier).state++
关于这个对象的介绍参考上一篇,随后state++引发的流程如下
- 更新StateController的state值为1
- 依次通知监听者,调用到counterProvider的setState方法,参数state为1
- counterProvider发现state发生变更后,先更新然后也依次通知其监听者
- ConsumerStatefulElement收到通知后rebuild,重新触发 ref.watch(counterProvider)方法,直接读取到state值1
Ref watch Ref
在第一篇的时候说过,Riverpod强大的地方在于Provider之间可以互相观察,也就是Ref watch Ref(由ProviderElementBase实现),这点可以就将原来线性关系变成了网状关系,StateProvider虽然也涉及Provider之间的观察,但这层关系没用上,我们将之前的两个Demo中Provider和StateProvider配合使用(官方todo Demo的一个重点),看看其中的奥义所在
final greetingProvider=Provider((ref){
// 在Provider中观察StateProvider
int count =ref.watch(counterProvider);
return "welcome $count";
});
final counterProvider = StateProvider((ref) => 0);
class Home extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('example')),
body: Center(
child: Consumer(builder: (context, ref, _) {
// 改成对greetingProvider的观察
final greet = ref.watch(greetingProvider);
return Text(greet);
}),
),
floatingActionButton: FloatingActionButton(
// 不变
onPressed: () => ref.read(counterProvider.notifier).state++,
child: const Icon(Icons.add),
),
);
}
}
点击按钮的效果就是:显示welcome0、welcome1,这样依次递增。这上面涉及的Provider之前都说明过,直接给出关系图,重点只有一个
- ref.watch(counterProvider)除了获取到state值,关键是将greetingProvider添加到了countProvider的_dependents集合中,用实线add表示,虚线add的关系也存在,但是没用上
那么这次StateController +1操作引起的流程和上面相比有哪些不一样呢?区别就是上面counterProvider通知的是listeners列表,这里通知的是_dependents列表(红色字体),_dependents里面的对象就是greetingProvider(其创建的ProviderElement)
void setState(State newState) {
final previousState = getState();
final result = _state = Result.data(newState);
if (_didBuild) {
_notifyListeners(result, previousState);
}
}
void _notifyListeners(Result<State> newState,
Result<State>? previousStateResult, ) {
...
// 这次listener列表为空
final listeners = _listeners.toList(growable: false);
final subscribers = _subscribers.toList(growable: false);
...
// 通知依赖,状态有变更 需要重新获取 目前就是greetingProvider
for (var i = 0; i < _dependents.length; i++) {
_dependents[i]._didChangeDependency();
}
...
}
现在切换到greetingProvider结构中,其_didChangeDependency方法比较简单,涉及代码并不复杂,主要是安排重新计算状态值,所以拉出来说说
void _didChangeDependency() {
if (_mustRecomputeState) return;
// will notify children that their dependency may have changed
markMustRecomputeState();
}
void markMustRecomputeState() {
if (_mustRecomputeState) return;
// 标记为true 后面用到
_mustRecomputeState = true;
_mounted = false;
// 安排刷新
_container._scheduler.scheduleProviderRefresh(this);
...
}
这个scheduleProviderRefresh操作,不是调用了就立马刷新,而是先添加到一个集合
void scheduleProviderRefresh(ProviderElementBase element) {
_stateToRefresh.add(element);
_scheduleTask();
}
void _scheduleTask() {
// 不会重复安排
if (_scheduledTask) return;
_scheduledTask = true;
...
// 看名字就像和帧有关
vsync(_task);
}
// 此方法的调用时机看下面
void _task() {
...
if (_disposed) return;
// 将_stateToRefresh集合中的元素依次刷新
_performRefresh();
// 将_stateToDispose集合的元素依次dispose
_performDispose();
_scheduledTask = false;
...
}
上面的vsync函数是_flutterVsync函数赋值,此方法主要就是
- 记录_task函数
- 触发重建调用_task函数
可见_task函数最快也要到下一帧执行,这个刷新动作是按帧的周期来的
_UncontrolledProviderScopeElement
void _flutterVsync(void Function() task) {
// 先保存下来
_task = task;
if (SchedulerBinding.instance.schedulerPhase ==
SchedulerPhase.transientCallbacks) {
markNeedsBuild();
} else {
// Using microtask as Flutter otherwise Flutter tests omplains about pending timers
Future.microtask(() {
if (_mounted) markNeedsBuild();
});
}
}
// build之前先执行task函数
Widget build() {
_task?.call();
_task = null;
return super.build();
}
_task中的刷新操作就又回到了greetingProvider中,_mustRecomputeState标记位之前已修改
void flush() {
...
if (_mustRecomputeState) {
_mustRecomputeState = false;
_performBuild();
}
}
在_performBuild中,主要是两件事
void _performBuild() {
...
final previousStateResult = _state;
_buildState();
...
if (_state != previousStateResult) {
_state!.map(
data: (data) => _notifyListeners(data, previousStateResult),
error: (error) => _notifyListeners(_state!, previousStateResult),
);
}
- _buildState()重走了greetProvider的create方法,里面获取了countProvider的新值1,这正是counterProvider通知我们的意思所在,然后create方法返回“welcome1”刷新了state值
final greetingProvider=Provider((ref){
int count =ref.watch(counterProvider);
return "welcome $count";
});
- 比较新旧state发生变化后和之前一样_notifyListeners,也就调用了Consumer组件的刷新,随后页面就获取了greetingProvider中的最新state:"welcome1"
child: Consumer(builder: (context, ref, _) {
final greet = ref.watch(greetingProvider);
return Text(greet); }),
总结
最后我们用下面这张图总结一下点击操作的流程
- 通过read从notifier中获取到StateController
- StateController执行++操作,状态值发生变更通知到监听者,就会调用到ref.setState(state)
- counterProvider的setState操作更新自己的状态后,会通知那些对自己有依赖的小伙伴,当前就是_denpendents列表中的greetingProvider
- greetingProvider收到通知后会重新走create方法生成最新状态值,这个更新也会通知对自己有监听的小伙伴,也就是Consumer
- Consumer的监听就是重建,在build方法中再次调用watch方法获取greetingProvider的最新值
转载自:https://juejin.cn/post/7240374267695218746