Provider | 三步速通原理

Provider | 三步速通原理

原理:

Provider.of(listen:true)的时候,Consumer将自己的context注册进顶层的InheritedElement的依赖数据中。在InheritedElement数据改变调用notifyListeners时,rebuilt自己,并且发出通知,让所有注册依赖的组件都rebuilt。

虽然原理很简单,但是每一步,都不简单,拆分为3个步骤:

  1. consumer如何将自己的context注册进顶层InheritedElement
  2. InheritedElement是如何通过notifyListeners调用自己的build
  3. InheritedElement如何通知consumer,让它build

Consumer如何将自己的context注册进顶层InheritedElement

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


}


consumer,很简单,只有一句涉及到Provider,就看这里

	///class Provider
  static T of<T>(BuildContext context, {bool listen = true}) {
    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {

      context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();

    }

    final value = inheritedElement?.value;

    if (_isSoundMode) {

      if (value is! T) {

        throw ProviderNullException(T, context.widget.runtimeType);

      }

      return value;

    }

    return value as T;

  }

围绕标题,只看context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();,这里的T就是Consumer的目标类型,consumer要将自己注册进T类型的依赖列表里面。

///class Element
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
  final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
  if (ancestor != null) {
    return dependOnInheritedElement(ancestor, aspect: aspect) as T;

  _hadUnsatisfiedDependencies = true;
  return null;
}


先默认_inheritedWidgets![T]存在,ancestor就是目标顶层InheritedElement。

	///class Element
	@override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object? aspect}) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies!.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }


然后将Consumer的element作为参数,调用InheritedElement.updateDependencies

///class InheritedElement

@protected
  void updateDependencies(Element dependent, Object? aspect) {
    setDependencies(dependent, null);
  }


@protected
  void setDependencies(Element dependent, Object? value) {
    _dependents[dependent] = value;
  }

给依赖_dependents新增对象。

至此,完成Consumer的Element注册进顶层的依赖的Map中

InheritedElement是如何通过notifyListeners调用自己的build

要回答这个问题,肯定是从顶部开始分析,即创建ChangeNotifier对象的地方ChangeNotifierProvider.create

这部分关系较为复杂,通过图例讲解。

provider.png

图中的起点为ChangeNotifierProvider,该类的create方法使用了例子中的Count类,可以左手图例,右手源码,这样理解起来非常简单。(真的简单!)

  1. 首先是起点ChangeNotifierProvider,一直往上寻找父类,直到InheritedProvider,它有两个分支,一个是buildWithChild方法返回的的_InheritedProviderScope,另一个是持有的_delegate
  2. 左边,_InheritedProviderScope对应的_InheritedProviderScopeElement有个get value方法,是重点1。
  3. 右边,持有的_delegate有个createState方法,通过createState可以获取到一个实现了get value方法的State,是重点2。

分别查看这两个重点的代码:

class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {

  late final _DelegateState<T, _Delegate<T>> _delegateState =
      widget.owner._delegate.createState()..element = this;


	@override
	  T get value => _delegateState.value;


}


value,指向了_delegateState.value,而_delegateState又是通过

widget.owner._delegate.createState()生成,这就来到了图例中最右边节点,重点2的代码。

///class _CreateInheritedProviderState
@override


  T get value {
    element!._isNotifyDependentsEnabled = false;
    **_removeListener ??= delegate.startListening?.call(element!, _value as T);**
    element!._isNotifyDependentsEnabled = true;
    return _value as T;
  }

两个重点结合之后,落在startListening方法

///class ListenableProvider
static VoidCallback _startListening(
  InheritedContext e,
  Listenable? value,
) {
  value?.addListener(e.markNeedsNotifyDependents);
  return () => value?.removeListener(e.markNeedsNotifyDependents);
}

这里将_InheritedProviderScopeElement标脏,在下一次setState的时候rebuilt

///class _InheritedProviderScopeElement
@override

void markNeedsNotifyDependents() {
  markNeedsBuild();
}

那,是什么调用get value?

Provider.of

///class Provider
static T of<T>(BuildContext context, {bool listen = true}) {
    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {

      context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();

    }

    final value = inheritedElement?.value;

    if (_isSoundMode) {

      if (value is! T) {

        throw ProviderNullException(T, context.widget.runtimeType);

      }

      return value;

    }

    return value as T;

  }

当有地方调用Provider.of的时候,就会调用get value

InheritedElement如何通知Consumer,让它build

基于前面已经将Consumer的Element注册进上层的依赖中,以及notifyListeners()会调用_InheritedProviderScopeElement的build方法。

推测:基于问题「InheritedProvider是如何通过notifyListeners调用自己的build」,推断build方法,是刷新的关键,那就从_InheritedProviderScopeElementbuild方法入手

分析:

///class _InheritedProviderScopeElement
@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
  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();
  }

build方法中有个非常显眼的notifyClients(widget);

进入notifyClients方法

///class InheritedElement

@override

  void notifyClients(InheritedWidget oldWidget) {
    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    for (final Element dependent in _dependents.keys) {
      notifyDependent(oldWidget, dependent);
    }
  }



@protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }

最后是调用到了依赖的didChangeDependencies,即Consumer.didChangeDependencies。最后会触发ConsumersetState方法,而Consumer的build方法,会执行get value,保证自己使用最新的数据。(同样的,在_InheritedProviderScopeElementupdate方法也会调用到notifyClients,不展开说。)

小结:

_InheritedProviderScopeElementbuild以及update的时候,会遍历自己的依赖,分别调用依赖的didChangeDependencies,从而让Consumer(即依赖)rebuild,重新get value,保证本次刷新使用了最新的数据。

番外问题:

_inheritedWidgets是如何、何时,赋值的呢?

简述:

每个element在mount操作时,继承父节点的_inheritedWidgets。如果当前节点是InheritedElement类型,还会在继承的基础上,新增自身。

分析:

直接在Element类里面全局搜索「_inheritedWidgets = 」(等号前后有空格)。 在Element.mount方法中,调用了_updateInheritance,这个方法,就是继承了父节点的数据。

然后InheritedElement类override了_updateInheritance,将自己也添加了进去。

/// class Element
@override


InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
  return ancestor;
}

void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    _inheritedWidgets = _parent?._inheritedWidgets;
}

@mustCallSuper
void mount(Element? parent, dynamic newSlot) {
  _parent = parent;
  _slot = newSlot;
  _lifecycleState = _ElementLifecycle.active;
  _depth = _parent != null ? _parent!.depth + 1 : 1;
  if (parent != null) // Only assign ownership if the parent is non-null
    _owner = parent.owner;
  final Key? key = widget.key;
  if (key is GlobalKey) {
    key._register(this);
  }
  _updateInheritance();
}

/// class InheritedElement
@override
  void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets![widget.runtimeType] = this;
  }

由于ChangeNotifierProvider的实现是线性排列的,所以能保证继承父类结点能够获取到所有的InheritedElement

Untitled.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYSz9xo6' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片