Provider | 三步速通原理
原理:
Provider.of(listen:true)
的时候,Consumer将自己的context注册进顶层的InheritedElement的依赖数据中。在InheritedElement数据改变调用notifyListeners
时,rebuilt自己,并且发出通知,让所有注册依赖的组件都rebuilt。
虽然原理很简单,但是每一步,都不简单,拆分为3个步骤:
- consumer如何将自己的context注册进顶层InheritedElement
- InheritedElement是如何通过notifyListeners调用自己的build
- 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
这部分关系较为复杂,通过图例讲解。
图中的起点为ChangeNotifierProvider
,该类的create
方法使用了例子中的Count
类,可以左手图例,右手源码,这样理解起来非常简单。(真的简单!)
- 首先是起点
ChangeNotifierProvider
,一直往上寻找父类,直到InheritedProvider
,它有两个分支,一个是buildWithChild
方法返回的的_InheritedProviderScope
,另一个是持有的_delegate
。 - 左边,
_InheritedProviderScope
对应的_InheritedProviderScopeElement
有个get value
方法,是重点1。 - 右边,持有的
_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方法,是刷新的关键,那就从_InheritedProviderScopeElement
的build
方法入手
分析:
///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
。最后会触发Consumer
的setState
方法,而Consumer的build方法,会执行get value,保证自己使用最新的数据。(同样的,在_InheritedProviderScopeElement
的update
方法也会调用到notifyClients
,不展开说。)
小结:
_InheritedProviderScopeElement
在build
以及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
。