likes
comments
collection
share

Riverpod之Provider&StateProvider(三)

作者站长头像
站长
· 阅读数 12

第二节讲解了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)的两个要点

  1. 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名字表示就是下图这样,其实是双方都记录了对方,但这个例子用不到这层关系,就简便了事

Riverpod之Provider&StateProvider(三)

随后给StateController添加了监听,这里的监听和前篇参数是不一样的,前篇的参数是StateControler自己,这里是其state值。方法的最后返回了其中状态值0

Riverpod之Provider&StateProvider(三)

那么ref.watch(counterProvider)返回的就是0,除此之外在countProvidere中添加了监听用来刷新

Riverpod之Provider&StateProvider(三)

StateProvider的更新流程

更新操作由read方法开启,read方法直接获取notifier中的state,也就是StateController对象

ref.read(counterProvider.notifier).state++

关于这个对象的介绍参考上一篇,随后state++引发的流程如下

Riverpod之Provider&StateProvider(三)

  1. 更新StateController的state值为1
  2. 依次通知监听者,调用到counterProvider的setState方法,参数state为1
  3. counterProvider发现state发生变更后,先更新然后也依次通知其监听者
  4. 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的关系也存在,但是没用上 Riverpod之Provider&StateProvider(三)

那么这次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); }),

总结

最后我们用下面这张图总结一下点击操作的流程 Riverpod之Provider&StateProvider(三)

  1. 通过read从notifier中获取到StateController
  2. StateController执行++操作,状态值发生变更通知到监听者,就会调用到ref.setState(state)
  3. counterProvider的setState操作更新自己的状态后,会通知那些对自己有依赖的小伙伴,当前就是_denpendents列表中的greetingProvider
  4. greetingProvider收到通知后会重新走create方法生成最新状态值,这个更新也会通知对自己有监听的小伙伴,也就是Consumer
  5. Consumer的监听就是重建,在build方法中再次调用watch方法获取greetingProvider的最新值
转载自:https://juejin.cn/post/7240374267695218746
评论
请登录