27

Flutter Provider 迄今为止最深、最全、最新的源码分析 - 简书

 4 years ago
source link: https://www.jianshu.com/p/bf2f33b2b5ef?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Flutter Provider 迄今为止最深、最全、最新的源码分析

Flutter Provider 迄今为止最深、最全、最新的源码分析

12020.05.31 00:51:34字数 3,620阅读 2,764

Flutter State Management状态管理全面分析
上期我们对Flutter的状态管理有了全局的认知,也知道了如何分辨是非好坏,不知道也没关系哦,我们接下来还会更加详细的分析,通过阅读Provider源码,来看看框架到底如何组织的,是如何给我们提供便利的。

通过官方我们已经知道其实Provider就是对InheritedWidget的包装,只是让InheritedWidget用起来更加简单且高可复用。我们也知道它有一些缺点,如

  • 容易造成不必要的刷新
  • 不支持跨页面(route)的状态,意思是跨树,如果不在一个树中,我们无法获取
  • 数据是不可变的,必须结合StatefulWidget、ChangeNotifier或者Steam使用

我特别想弄明白,这些缺点在Provider的设计中是如何规避的,还有一个是Stream不会主动的close掉流的通道,不得不结合StatefulWidget使用,而Provider提供了dispose回调,你可以在该函数中主动关闭,好厉害,如何做到的呢?带着这些疑问,我们去寻找答案

我们先来使用它,然后在根据用例分析源码,找到我们想要的答案,先看一个简单的例子

step 1

第一步定义一个ChangeNotifier,来负责数据的变化通知

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

}

step 2

第二步,用ChangeNotifierProvider来订阅Counter,不难猜出,ChangeNotifierProvider肯定是InheritedWidget的包装类,负责将Counter的状态共享给子Widget,我这里将ChangeNotifierProvider放到了Main函数中,并在整个Widget树的顶端,当然这里是个简单的例子,我这么写问题不大,但你要考虑,如果是特别局部的状态,请将ChangeNotifierProvider放到局部的地方而不是全局,希望你能明白我的用意

void main() {
  runApp(
    /// Providers are above [MyApp] instead of inside it, so that tests
    /// can use [MyApp] while mocking the providers
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: MyApp(),
    ),
  );
}

step 3

第三步,接收数据通过Consumer<Counter>,Consumer是个消费者,它负责消费ChangeNotifierProvider生产的数据

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('MyHomePage build');
    return Scaffold(
      appBar: AppBar(
        title: const Text('Example'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('You have pushed the button this many times:'),

            /// Extracted as a separate widget for performance optimization.
            /// As a separate widget, it will rebuild independently from [MyHomePage].
            ///
            /// This is totally optional (and rarely needed).
            /// Similarly, we could also use [Consumer] or [Selector].
            Consumer<Counter>(
              builder: (BuildContext context, Counter value, Widget child) {
                return Text('${value.count}');
              },
            ),
            OtherWidget(),
            const OtherWidget2()
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        /// Calls `context.read` instead of `context.watch` so that it does not rebuild
        /// when [Counter] changes.
        onPressed: () => context.read<Counter>().increment(),
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

通过这个例子,可以判断出Provider封装的足够易用,而且Counter作为Model层使用的with ChangeNotifier 而不是extends ,所以说侵入性也比较低,感觉还不错,那么InheritedWidget的缺点它规避了吗?

  1. 容易造成不必要的刷新(解决了吗?)

我们多加两个子WIdget进去,排在Consumer的后面,OtherWidget什么都不干,不去订阅Counter,OtherWidget2通过context.watch<Counter>().count函数监听而不是Consumer,来看下效果一样不,然后在build函数中都加入了print

class OtherWidget extends StatelessWidget {
  const OtherWidget({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('OtherWidget build');
//    Provider.of<Counter>(context);
    return Text(
        /// Calls `context.watch` to make [MyHomePage] rebuild when [Counter] changes.
        'OtherWidget',
        style: Theme.of(context).textTheme.headline4);
  }
}

class OtherWidget2 extends StatelessWidget {
  const OtherWidget2({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('OtherWidget2 build');
    return Text(
      /// Calls `context.watch` to make [MyHomePage] rebuild when [Counter] changes.
        '${context.watch<Counter>().count}',
        style: Theme.of(context).textTheme.headline4);
  }
}

项目运行看下效果,跑起来是这样的


print日志
点击刷新后

分析结论如下:

  • Consumer、context.watch都可以监听Counter变化
  • Consumer只会刷新自己
  • context.watch所在子widget不管是否是const都被重建后刷新数据
  • OtherWidget并没有被重建,因为它没有订阅Counter

局部刷新确实实现了但要通过Consumer,第二个问题不支持跨页面(route)的状态,这个可以确定的说不支持,第三个问题数据是不可变的(只读),经过这个例子可以分辨出数据确实是可变的对吧,那么数据是如何变化的呢?留个悬念,下面分析源码中来看本质。

当然要想更完整的理解ChangeNotifier、ChangeNotifierProvider、Consumer的关系

请看图
设计模式真是无处不在哈,ChangeNotifier与ChangeNotifierProvider实现了观察者模式,ChangeNotifierProvider与Consumer又实现了生产者消费者模式,这里不具体聊这俩个模式,如果还不了解,请你自行搜索学习哦。下面直接源码分析

ChangeNotifier

在包package:meta/meta.dart下,是flutter sdk的代码,并不属于Provider框架的一部分哦,通过下方代码可以看出,这是一个标准的观察者模型,而真正的监听者就是typedef VoidCallback = void Function(); 是dart.ui包下定义的一个函数,没人任何返回参数的函数。ChangerNotifier实现自抽象类Listenable,通过源码的注释我们看到Listenable是一个专门负责维护监听列表的一个抽象类。

ChangeNotifierProvider

class ChangeNotifierProvider<T extends ChangeNotifier>
    extends ListenableProvider<T> {
  static void _dispose(BuildContext context, ChangeNotifier notifier) {
    notifier?.dispose();
  }

  /// 使用`create`创建一个[ChangeNotifier]
  /// 当ChangeNotifierProvider从树中被移除时,自动取消订阅通过
  /// notifier?.dispose();
  ChangeNotifierProvider({
    Key key,
    @required Create<T> create,
    bool lazy,
    TransitionBuilder builder,
    Widget child,
  }) : super(
          key: key,
          create: create,
          dispose: _dispose,
          lazy: lazy,
          builder: builder,
          child: child,
        );

  /// 生成一个已存在ChangeNotifier的Provider
  ChangeNotifierProvider.value({
    Key key,
    @required T value,
    TransitionBuilder builder,
    Widget child,
  }) : super.value(
          key: key,
          builder: builder,
          value: value,
          child: child,
        );
}

分析下构造

  • Create<T> create
    是个通用函数typedef Create<T> = T Function(BuildContext context)用于创建T类,这里负责创建ChangeNotifier
  • bool lazy
    是否懒加载
  • TransitionBuilder builder

    当builder存在时将不会用child做为子Widget,追踪到源码实现可以看到如下图
  • Widget child
    builder不存在时就用child

继承自ListenableProvider<T>,来继续分析它的源码

class ListenableProvider<T extends Listenable> extends InheritedProvider<T> {
  ///  使用 [create] 创建一个 [Listenable] 订阅它
  /// [dispose] 可以选择性的释放资源当 [ListenableProvider] 被移除树的时候
  /// [create] 不能为空
  ListenableProvider({
    Key key,
    @required Create<T> create,
    Dispose<T> dispose,
    bool lazy,
    TransitionBuilder builder,
    Widget child,
  })  : assert(create != null),
        super(
          key: key,
          startListening: _startListening,
          create: create,
          dispose: dispose,
          debugCheckInvalidValueType: kReleaseMode
              ? null
              : (value) {
                  if (value is ChangeNotifier) {
                    // ignore: invalid_use_of_protected_member
                  ...
                  }
                },
          lazy: lazy,
          builder: builder,
          child: child,
        );

  /// 生成已存在 [Listenable] 的Provider
  ListenableProvider.value({
    Key key,
    @required T value,
    UpdateShouldNotify<T> updateShouldNotify,
    TransitionBuilder builder,
    Widget child,
  }) : super.value(
          key: key,
          builder: builder,
          value: value,
          updateShouldNotify: updateShouldNotify,
          startListening: _startListening,
          child: child,
        );

  static VoidCallback _startListening(
    InheritedContext<Listenable> e,
    Listenable value,
  ) {
    value?.addListener(e.markNeedsNotifyDependents);
    return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}
  • Listenable 上面已经分析,它是负责管理观察者列表的抽象
  • 它比子类ChangeNotifierProvider多了一个构造参数dispose,这个函数是typedef Dispose<T> = void Function(BuildContext context, T value); 是个回调,应该是当页面被销毁时触发(等再深入了源码才能认证,目前只是猜测,我们继续看)

又继承自InheritedProvider<T> ,别放弃,来跟我一起往下看

class InheritedProvider<T> extends SingleChildStatelessWidget {
  /// 创建数据value并共享给子Widget
  /// 当 [InheritedProvider] 从树中被释放时,将自动释放数据value
  InheritedProvider({
    Key key,
    Create<T> create,
    T update(BuildContext context, T value),
    UpdateShouldNotify<T> updateShouldNotify,
    void Function(T value) debugCheckInvalidValueType,
    StartListening<T> startListening,
    Dispose<T> dispose,
    TransitionBuilder builder,
    bool lazy,
    Widget child,
  })  : _lazy = lazy,
        _builder = builder,
        _delegate = _CreateInheritedProvider(
          create: create,
          update: update,
          updateShouldNotify: updateShouldNotify,
          debugCheckInvalidValueType: debugCheckInvalidValueType,
          startListening: startListening,
          dispose: dispose,
        ),
        super(key: key, child: child);

  /// 暴漏给子孙一个已存在的数据value
  InheritedProvider.value({
    Key key,
    @required T value,
    UpdateShouldNotify<T> updateShouldNotify,
    StartListening<T> startListening,
    bool lazy,
    TransitionBuilder builder,
    Widget child,
  })  : _lazy = lazy,
        _builder = builder,
        _delegate = _ValueInheritedProvider(
          value: value,
          updateShouldNotify: updateShouldNotify,
          startListening: startListening,
        ),
        super(key: key, child: child);

  InheritedProvider._constructor({
    Key key,
    _Delegate<T> delegate,
    bool lazy,
    TransitionBuilder builder,
    Widget child,
  })  : _lazy = lazy,
        _builder = builder,
        _delegate = delegate,
        super(key: key, child: child);

  final _Delegate<T> _delegate;
  final bool _lazy;
  final TransitionBuilder _builder;

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    _delegate.debugFillProperties(properties);
  }

  @override
  _InheritedProviderElement<T> createElement() {
    return _InheritedProviderElement<T>(this);
  }

  @override
  Widget buildWithChild(BuildContext context, Widget child) {
    assert(
      _builder != null || child != null,
      '$runtimeType used outside of MultiProvider must specify a child',
    );
    return _InheritedProviderScope<T>(
      owner: this,
      child: _builder != null
          ? Builder(
              builder: (context) => _builder(context, child),
            )
          : child,
    );
  }
}

构造中多出来的参数

  • T update(BuildContext context, T value) 该函数返回数据变更值value,具体实现在_CreateInheritedProvider类中,说白了InheritedProvider<T>是个无状态组件对吗?那么它要变更状态肯定要依赖于别人,而它创建出一个_CreateInheritedProvider类,_CreateInheritedProvider是_Delegate的实现类,_Delegate就是一个状态的代理类,来看下_Delegate具体实现
@immutable
abstract class _Delegate<T> {
  _DelegateState<T, _Delegate<T>> createState();

  void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
}

abstract class _DelegateState<T, D extends _Delegate<T>> {
  _InheritedProviderScopeElement<T> element;

  T get value;

  D get delegate => element.widget.owner._delegate as D;

  bool get hasValue;

  bool debugSetInheritedLock(bool value) {
    return element._debugSetInheritedLock(value);
  }

  bool willUpdateDelegate(D newDelegate) => false;

  void dispose() {}

  void debugFillProperties(DiagnosticPropertiesBuilder properties) {}

  void build(bool isBuildFromExternalSources) {}
}

这是用到了委托模式,这里就有点类似StatefulWidget和State的关系,同样的_DelegateState提供了类似生命周期的函数,如willUpdateDelegate更新新的委托,dispose注销等

  • UpdateShouldNotify<T> updateShouldNotify,
    void Function(T value) debugCheckInvalidValueType,
    StartListening<T> startListening,
    Dispose<T> dispose, 这些函数全部交给了委托类
  • 最关键的实现来了,到目前位置还没看到InheritedWidget的逻辑对吧,它来了Widget buildWithChild(BuildContext context, Widget child),我们传入的Widget就被叫_InheritedProviderScope的类给包裹了,看下源码
class _InheritedProviderScope<T> extends InheritedWidget {
  _InheritedProviderScope({
    this.owner,
    @required Widget child,
  }) : super(child: child);

  final InheritedProvider<T> owner;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }

  @override
  _InheritedProviderScopeElement<T> createElement() {
    return _InheritedProviderScopeElement<T>(this);
  }
}

至此你有没有发现一个特点,所有的函数都被_Delegate带走了,剩下的只有Widget交给了_InheritedProviderScope,这里设计的也很好,毕竟InheritedWidget其实也就只能做到数据共享,跟函数并没有什么关系对吧。唯一有关系的地方,我猜测就是在InheritedWidget提供的Widget中调用

一个细节 owner: this 在 buildWithChild函数中,将InheritedProvider本身传递给InheritedWidget,应该是为了方便调用它的_Delegate委托类,肯定是用来回调各种函数。

... 快一点了,睡了,明天再更

继续分享,_InheritedProviderScope唯一特殊的地方,我们发现它自己创建了一个Element实现通过覆盖createElement函数,返回_InheritedProviderScopeElement实例,flutter三板斧 Widget、Element、RenderObject,该框架自己实现一层Element,我们都知道Widget是配置文件只有build和rebuild以及remove from the tree,而Element作为一层虚拟Dom,主要负责优化,优化页面刷新的逻辑,那我们来详细的分析一下_InheritedProviderScopeElement,看它都做了什么?

/// 继承自InheritedElement,因为InheritedWidget对应的Element就是它
/// 实现 InheritedContext,InheritedContext继承自BuildContext,多了个T范型
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {

/// 构造函数,将Element对应的widget传进来
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget);
/// 是否需要通知依赖的Element变更
  bool _shouldNotifyDependents = false;
/// 是否允许通知变更
  bool _isNotifyDependentsEnabled = true;
/// 第一次构建
  bool _firstBuild = true;
/// 是否更新newWidget的Delegate委托
  bool _updatedShouldNotify = false;
/// 这个变量就是控制的数据变更,在Widget变更和Element依赖变更的时候都会被设置为true
  bool _isBuildFromExternalSources = false;
/// 委托类的状态(我们猜测对了, owner: this 就是为了拿到上层的委托类)
  _DelegateState<T, _Delegate<T>> _delegateState;

  @override
  _InheritedProviderScope<T> get widget =>
      super.widget as _InheritedProviderScope<T>;

  @override
  void updateDependencies(Element dependent, Object aspect) {
    final dependencies = getDependencies(dependent);
    // once subscribed to everything once, it always stays subscribed to everything
    if (dependencies != null && dependencies is! _Dependency<T>) {
      return;
    }

    if (aspect is _SelectorAspect<T>) {
      final selectorDependency =
          (dependencies ?? _Dependency<T>()) as _Dependency<T>;

      if (selectorDependency.shouldClearSelectors) {
        selectorDependency.shouldClearSelectors = false;
        selectorDependency.selectors.clear();
      }
      if (selectorDependency.shouldClearMutationScheduled == false) {
        selectorDependency.shouldClearMutationScheduled = true;
        SchedulerBinding.instance.addPostFrameCallback((_) {
          selectorDependency
            ..shouldClearMutationScheduled = false
            ..shouldClearSelectors = true;
        });
      }
      selectorDependency.selectors.add(aspect);
      setDependencies(dependent, selectorDependency);
    } else {
      // subscribes to everything
      setDependencies(dependent, const Object());
    }
  }

  @override
  void notifyDependent(InheritedWidget oldWidget, Element dependent) {
    final dependencies = getDependencies(dependent);

    var shouldNotify = false;
    if (dependencies != null) {
      if (dependencies is _Dependency<T>) {
        for (final updateShouldNotify in dependencies.selectors) {
          try {
            assert(() {
              _debugIsSelecting = true;
              return true;
            }());
            shouldNotify = updateShouldNotify(value);
          } finally {
            assert(() {
              _debugIsSelecting = false;
              return true;
            }());
          }
          if (shouldNotify) {
            break;
          }
        }
      } else {
        shouldNotify = true;
      }
    }

    if (shouldNotify) {
      dependent.didChangeDependencies();
    }
  }

  @override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false;
      _delegateState = widget.owner._delegate.createState()..element = this;
    }
    super.performRebuild();
  }

  @override
  void update(_InheritedProviderScope<T> newWidget) {
    _isBuildFromExternalSources = true;
    _updatedShouldNotify =
        _delegateState.willUpdateDelegate(newWidget.owner._delegate);
    super.update(newWidget);
    _updatedShouldNotify = false;
  }

  @override
  void updated(InheritedWidget oldWidget) {
    super.updated(oldWidget);
    if (_updatedShouldNotify) {
      notifyClients(oldWidget);
    }
  }

  @override
  void didChangeDependencies() {
    _isBuildFromExternalSources = true;
    super.didChangeDependencies();
  }

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

  @override
  void unmount() {
    _delegateState.dispose();
    super.unmount();
  }

  @override
  bool get hasValue => _delegateState.hasValue;

  @override
  void markNeedsNotifyDependents() {
    if (!_isNotifyDependentsEnabled) return;

    markNeedsBuild();
    _shouldNotifyDependents = true;
  }

  @override
  T get value => _delegateState.value;

  @override
  InheritedWidget dependOnInheritedElement(
    InheritedElement ancestor, {
    Object aspect,
  }) {
    return super.dependOnInheritedElement(ancestor, aspect: aspect);
  }
}
  • void update(_InheritedProviderScope<T> newWidget) 让页面重新build的是在这里,因为InheritedElement 继承自ProxyElement,而ProxyElement的update函数调用了两个函数updated(已更新完成),rebuild函数触发重新build逻辑,下面为跟踪到的代码
abstract class ProxyElement extends ComponentElement {
  @override
  void update(ProxyWidget newWidget) {
    final ProxyWidget oldWidget = widget;
    assert(widget != null);
    assert(widget != newWidget);
    super.update(newWidget);
    assert(widget == newWidget);
    updated(oldWidget);
    _dirty = true;
    rebuild();
  }
}
  • performRebuild() 是在update触发真正调用rebuild之后被调用
  • updateDependencies、notifyDependent处理Element依赖逻辑
  • update、updated处理的widget更新逻辑
  • didChangeDependencies当此State对象的依赖项更改时调用,子类很少重写此方法,因为框架总是在依赖项更改后调用build。一些子类确实重写了此方法,因为当它们的依存关系发生变化时,它们需要做一些昂贵的工作(例如,网络获取),并且对于每个构建而言,这些工作将太昂贵。
  • build() 构建需要的widget,Element在调用build的时候也会触发Widget的build
  • void unmount() 这里看到了_delegateState.dispose();的调用,现在找到了吧,当Element从树中移除的时候,回掉了dispose函数。

来看一个生命周期的图,辅助你理解源码的调用关系

此图引自大佬Reactive,他记录了很详细的生命周期图,感谢作者的贡献

  • notifyClients 这个函数干嘛的?它是InheritedElement中实现的函数,通过官方文档了解到,它是通过调用Element.didChangeDependencies通知所有从属Element此继承的widget已更改,此方法只能在构建阶段调用,通常,在重建inherited widget时会自动调用此方法,还有就是InheritedNotifier,它是InheritedWidget的子类,在其Listenable发送通知时也调用此方法。

  • markNeedsNotifyDependents 如果你调用它,会强制build后 通知所以依赖Element刷新widget,看下面代码,发现该函数在InheritedContext中定义,所以我们可以通过InheritedContext上下文来强制页面的构建

abstract class InheritedContext<T> extends BuildContext {
  ///  [InheritedProvider] 当前共享的数据
  /// 此属性是延迟加载的,第一次读取它可能会触发一些副作用,
  T get value;

  /// 将[InheritedProvider]标记为需要更新依赖项
  /// 绕过[InheritedWidget.updateShouldNotify]并将强制rebuild
  void markNeedsNotifyDependents();

  /// setState是否至少被调用过一次
  /// [DeferredStartListening]可以使用它来区分
  /// 第一次监听,在“ controller”更改后进行重建。
  bool get hasValue;
}

小结一下
我们先回顾一下我们是如何使用InheritedWidget的,为了能让InheritedWidget的子Widget能够刷新,我们不得不依赖于Statefulwidget,并通过State控制刷新Element,调用setState刷新页面,其实底层是调用的_element.markNeedsBuild() 函数,这样我们明白了,其实最终控制页面的还是Element,那么Provider 它也巧妙的封装了自己的_delegateState,是私有的,并没有给我们公开使用,也没有提供类似setState,但可以通过markNeedsNotifyDependents函数达到了和setState一样的调用效果,一样的都是让所有子Widget进行重建,可我们要的局部刷新呢?是在Consumer里?,来吧,不要走开,没有广告,精彩继续,接下来研究Consumer源码

Consumer

class Consumer<T> extends SingleChildStatelessWidget {
 
/// 构造函数,必传builder
  Consumer({
    Key key,
    @required this.builder,
    Widget child,
  })  : assert(builder != null),
        super(key: key, child: child);

  /// 根据 [Provider<T>] 提供的value,构建的widget
  final Widget Function(BuildContext context, T value, Widget child) builder;

  @override
  Widget buildWithChild(BuildContext context, Widget child) {
    return builder(
      context,
      Provider.of<T>(context),
      child,
    );
  }
}
  • 这里源码稍微有一点绕,Widget child传给了父类SingleChildStatelessWidget,最终通过buildWithChild函数的参数child传递回来,而builder函数有收到了此child,然后再组合child和需要刷新的widget组合一个新的widget给Consumer。一句话就是说Consumer的构造函数可以传两个widget一个是builder,一个是child,最终是通过builder构建最终的widget,如果child不为空,那么你需要自己组织child和builder中返回widget的关系。
  • Provider.of<T>(context) 获取了共享数据value

Provider.of<T>(context) 是如何获取数据的呢?继续看源码

/// 调用_inheritedElementOf函数
static T of<T>(BuildContext context, {bool listen = true}) {
    assert(context != null);
    
    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {
      context.dependOnInheritedElement(inheritedElement);
    }

    return inheritedElement.value;
  }

static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(
      BuildContext context) {
  
    _InheritedProviderScopeElement<T> inheritedElement;

    if (context.widget is _InheritedProviderScope<T>) {
      // An InheritedProvider<T>'s update tries to obtain a parent provider of
      // the same type.
      context.visitAncestorElements((parent) {
        inheritedElement = parent.getElementForInheritedWidgetOfExactType<
            _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>;
        return false;
      });
    } else {
      inheritedElement = context.getElementForInheritedWidgetOfExactType<
          _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>;
    }

    if (inheritedElement == null) {
      throw ProviderNotFoundException(T, context.widget.runtimeType);
    }

    return inheritedElement;
  }

  • 通过 visitAncestorElements 往父级查找_InheritedProviderScope的实现类也就是InheritedWidget,当找到是就返回_InheritedProviderScopeElement,而_InheritedProviderScopeElement正好可以拿到value,这个value也就是 _delegateState的value
@override
  T get value => _delegateState.value;

走到这其实只是实现了读取数据,那么数据到底是如何刷新的呢?我们回过头来看下面几段代码

  1. Model数据调用ChangeNotifier提供的函数notifyListeners
  void notifyListeners() {
    assert(_debugAssertNotDisposed());
    if (_listeners != null) {
      final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
      for (final VoidCallback listener in localListeners) {
        try {
          if (_listeners.contains(listener))
            listener();
        } catch (exception, stack) {
          FlutterError.reportError(FlutterErrorDetails(
            exception: exception,
            stack: stack,
            library: 'foundation library',
            context: ErrorDescription('while dispatching notifications for $runtimeType'),
            informationCollector: () sync* {
              yield DiagnosticsProperty<ChangeNotifier>(
                'The $runtimeType sending notification was',
                this,
                style: DiagnosticsTreeStyle.errorProperty,
              );
            },
          ));
        }
      }
    }
  }

这个时候遍历所有的监听,然后执行函数listener(),这里其实等于执行VoidCallback的实例,那这个listener到底是哪个函数?

  1. 在ChangeNotifierProvider父类ListenableProvider的静态函数中,自动订阅了为观察者
    前面说了观察者就是个普通函数,而e.markNeedsNotifyDependents就是InheritedContext的一个函数,当你notifyListeners的时候执行的就是它markNeedsNotifyDependents,上面我们知道markNeedsNotifyDependents类似setState效果,就这样才实现了UI的刷新。
/// ListenableProvider 的静态函数
 static VoidCallback _startListening(
    InheritedContext<Listenable> e,
    Listenable value,
  ) {
    value?.addListener(e.markNeedsNotifyDependents); /// 添加观察者
    return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
  /// InheritedContext 上下文
 abstract class InheritedContext<T> extends BuildContext {
  ...
  void markNeedsNotifyDependents();
  ...
}

到此位置局部刷新是不是还没揭开面纱?到底是如何做的呢?跟我一起寻找,首先我们来看一个东西

  @override
  Widget buildWithChild(BuildContext context, Widget child) {
    return builder(
      context,
      Provider.of<T>(context),
      child,
    );
  }

Consumer通过Provider.of<T>(context)这句话我们才能监听到数据的对吧,而且刷新的内容也只是这一部分,我们再看下它的实现发现了另一个细节

  static T of<T>(BuildContext context, {bool listen = true}) {
    assert(context != null);
    final inheritedElement = _inheritedElementOf<T>(context);
    if (listen) {
      context.dependOnInheritedElement(inheritedElement);
    }
    return inheritedElement.value;
  }

它调用了BuildContext的dependOnInheritedElement函数,这个函数做了啥?

 @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    ...
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
 @override
  void updateDependencies(Element dependent, Object aspect) {
    print("updateDependencies===================dependent ${dependent.toString()}");
    final dependencies = getDependencies(dependent);
  ...
     setDependencies(dependent, const Object());
  ...
}
  ///    to manage dependency values.
  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }
  final Map<Element, Object> _dependents = HashMap<Element, Object>();

触发updateDependencies,通过setDependencies,将Element缓存到_dependents Map中

最后通过如下代码更新

 @override
  void notifyDependent(InheritedWidget oldWidget, Element dependent) {
    print("notifyDependent===================oldWidget ${oldWidget.toString()}");
    final dependencies = getDependencies(dependent);

    var shouldNotify = false;
    if (dependencies != null) {
      if (dependencies is _Dependency<T>) {
        for (final updateShouldNotify in dependencies.selectors) {
          try {
            assert(() {
              _debugIsSelecting = true;
              return true;
            }());
            shouldNotify = updateShouldNotify(value);
          } finally {
            assert(() {
              _debugIsSelecting = false;
              return true;
            }());
          }
          if (shouldNotify) {
            break;
          }
        }
      } else {
        shouldNotify = true;
      }
    }

    if (shouldNotify) {
      dependent.didChangeDependencies();  /// 更新方法
    }
  }

所以说整体流程是这样当notifyListeners的时候其实是触发了InheritedWidget的performRebuild,再到 build ,build后触发 notifyClients,notifyClients触发notifyDependent,notifyDependent这个时候通过getDependencies获取缓存好的Element,最终确定是否需要刷新然后调用dependent.didChangeDependencies();更新,哈哈,终于明白了,只要widget中通过Provider.of函数订阅后,就会被InheritedWidget缓存在一个Map中,然后刷新页面的时候,如果子Widget不在缓存的Map中,根本不会走刷新,而且如果shouldNotify变量是false也不会刷新,这个控制肯定是虽然子Widget订阅了,但它自己就是不刷新,可以更加细粒度的控制。

源码分析总结

  • Provider 通过缓存 inheritedElement 实现局部刷新
  • 通过控制自己实现的Element 层来 更新UI
  • 通过Element提供的unmount函数回调dispose,实现选择性释放

厉害吗?还不错哦。

其实我们明白了它的核心原理之后,剩下的就是扩展该框架了,我目前只分析了ChangeNotifierProvider、Consumer,其实它还有很多很多,来一张图吓吓你

http://jetpack.net.cn/provider.jpg

图片很大,请看原图哦
看到这个图,是不是觉得冰山一角呢?哈哈,不过还好,核心原理就是在InheritedProvider里面,我已经带你趟了一遍,剩下的就靠你自己了,加油。

大家还有没有喜欢的Flutter状态管理框架,如果你想看到更多的状态管理框架源码分析,请你关注我哦,如果你读到最后,如果你觉得还不错,也请你点个赞,感谢🙏

禁止转载,如需转载请通过简信或评论联系作者。
19人点赞
"小礼物走一走,来简书关注我"
还没有人赞赏,支持一下
i校长抽象化是一种非常的不同于模糊化的东西 … 抽象的目的并不是为了模糊,而是为了创造出一种能让我们...
总资产316 (约14.28元)共写了10.1W字获得717个赞共821个粉丝

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK