美文网首页
Provider详解

Provider详解

作者: 晨曦中的花豹 | 来源:发表于2023-02-06 11:34 被阅读0次

    在flutter开发中状态管理框架Provider应该大家都接触过,这里我就结合我在使用过程中,出现的疑惑,以及一步一步找到答案的过程,来详细的介绍一下Provider框架实现的原理
    大家应该都知道Provider是基于InheritedWidget实现的,所以我最开始的思路完全是按照InheritedWidget来理解这个框架的,如果对InheritedWidget不是很了解,可以看看之前的文章InheritedWidget
    Provider中value是一个ChangeNotifier,然后在需要刷新的时候调用notifyListeners来刷新,结合InheritedWidget,我的思路是ChangeNotifier监听InheritedWidget的父Element,notifyListeners来调用父Element的markNeedsBuild,实现数据刷新

    但是通过源码发现,Provider内部并没有给InheritedWidget包裹Widget,而是直接监听的InheritedWidget(InheritedElement),调用的也是InheritedElement的markNeedsBuild

    class ChangeNotifierProvider<T extends ChangeNotifier?>
        extends ListenableProvider<T>
    

    在ListenableProvider的构造函数中,有一个_startListening,这个方法中实现了回调监听,一旦ChangeNotifier执行了notifyListeners,_startListening就会被调用

    class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
      /// Creates a [Listenable] using [create] and subscribes to it.
      ///
      /// [dispose] can optionally passed to free resources
      /// when [ListenableProvider] is removed from the tree.
      ///
      /// [create] must not be `null`.
      ListenableProvider({
        Key? key,
        required Create<T> create,
        Dispose<T>? dispose,
        bool? lazy,
        TransitionBuilder? builder,
        Widget? child,
      }) : super(
              key: key,
              startListening: _startListening,
              create: create,
              dispose: dispose,
              lazy: lazy,
              builder: builder,
              child: child,
            );
    
      static VoidCallback _startListening(
        InheritedContext e,
        Listenable? value,
      ) {
        value?.addListener(e.markNeedsNotifyDependents);
        return () => value?.removeListener(e.markNeedsNotifyDependents);
      }
    

    而markNeedsNotifyDependents本质就是调用了markNeedsBuild

    @override
      void markNeedsNotifyDependents() {
        if (!_isNotifyDependentsEnabled) {
          return;
        }
    
        markNeedsBuild();
        _shouldNotifyDependents = true;
      }
    

    因为最"正宗"的使用InheritedWidget,是通过setState这个方法去刷新,会调用InheritedWidget(InheritedElement)的update方法,这个方法会将使用InheritedWidget的element设置成_dirty,从而实现引用的组件刷新,这里没有父组件的刷新,因此也不会执行update方法,因此Provider一定是在强制刷新的方法中做了手脚,果不其然,就在performRebuild执行build中

    void performRebuild() {
        Widget? built;
        try {
          assert(() {
            _debugDoingBuild = true;
            return true;
          }());
          built = build();
          assert(() {
            _debugDoingBuild = false;
            return true;
          }());
          debugWidgetBuilderValue(widget, built);
        } catch (e, stack) {
          _debugDoingBuild = false;
          built = ErrorWidget.builder(
            _reportException(
              ErrorDescription('building $this'),
              e,
              stack,
              informationCollector: () => <DiagnosticsNode>[
                if (kDebugMode)
                  DiagnosticsDebugCreator(DebugCreator(this)),
              ],
            ),
          );
        } finally {
          // We delay marking the element as clean until after calling build() so
          // that attempts to markNeedsBuild() during build() will be ignored.
          super.performRebuild(); // clears the "dirty" flag
        }
        try {
          _child = updateChild(_child, built, slot);
          assert(_child != null);
        } catch (e, stack) {
          built = ErrorWidget.builder(
            _reportException(
              ErrorDescription('building $this'),
              e,
              stack,
              informationCollector: () => <DiagnosticsNode>[
                if (kDebugMode)
                  DiagnosticsDebugCreator(DebugCreator(this)),
              ],
            ),
          );
          _child = updateChild(null, built, slot);
        }
      }
    

    Provider 重写了build方法,这里边执行了notifyClients,去将监听的element强制刷新的

    @override
      Widget build() {
        if (widget.owner._lazy == false) {
          value; // this will force the value to be computed.
        }
        _delegateState.build(
          isBuildFromExternalSources: _isBuildFromExternalSources,
        );
        _isBuildFromExternalSources = false;
        if (_shouldNotifyDependents) {
          _shouldNotifyDependents = false;
          notifyClients(widget);
        }
        return super.build();
      }
    

    到这里其实大概的原理就解释通了,

    • ChangeNotifier负责数据以及刷新InheritedElement
    • InheritedElement负责向下传递数据以及更新使用数据的Element,但是是对InheritedElement做了小小的改动的

    补充:
    其实这样做还有一个好处,就是本身InheritedElement重构逻辑会导致下面所有的element重构,但是Provider直接通过_dirty来刷新,避免了这一重构灾难(虽然创建widget tree 不是很消耗资源和性能),可以尽量做到那里需要刷新就刷新那里,也算是一个亮点吧

    相关文章

      网友评论

          本文标题:Provider详解

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