前面我们讲了InheritedWidget的用途和用法,还留了一个小问题。前文链接
Flutter framework是怎么知道子widget有没有依赖InheritedWidget的?
InheritedWidget 定义
首先看一下 InheritedWidget 的定义:
abstract class InheritedWidget extends ProxyWidget {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
/// Whether the framework should notify widgets that inherit from this widget.
///
/// When this widget is rebuilt, sometimes we need to rebuild the widgets that
/// inherit from this widget but sometimes we do not. For example, if the data
/// held by this widget is the same as the data held by `oldWidget`, then we
/// do not need to rebuild the widgets that inherited the data held by
/// `oldWidget`.
///
/// The framework distinguishes these cases by calling this function with the
/// widget that previously occupied this location in the tree as an argument.
/// The given widget is guaranteed to have the same [runtimeType] as this
/// object.
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
继承自 ProxyWidget, 包含createElement()
和 updateShouldNotify
两个方法。updateShouldNotify
的注释较为完整,
定义了是否需要通知子树,那么该如何通知呢?我们看一下createElement()
返回的InheritedElement
是什么:
class InheritedElement extends ProxyElement {
...
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@override
void _updateInheritance() {
assert(_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;
}
....
}
其中 _parent?._inheritedWidgets 又是什么? 继续跟踪发现, 这是Element
拥有的属性:
Map<Type, InheritedElement> _inheritedWidgets;
其中保存了祖先节点中出现的 InheritedWidget 与其对应 element 的映射关系。在 element 的 mount 阶段
和 active 阶段
,会执行 _updateInheritance() 方法更新这个映射关系。
对于普通 Element 实例,_updateInheritance() 只是单纯把父 element 的 _inheritedWidgets 属性保存在自身 _inheritedWidgets 里。从而实现映射关系的层层向下传递。
@override
void _updateInheritance() {
assert(_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;
}
此时我们再回过头来看,InheritedElement的 _updateInheritance
的不同,InheritedElement 实例会把自身的信息添加到 _inheritedWidgets 属性中,这样其子孙 element 就可以通过前面提到的 _inheritedWidgets 的传递机制获取到此 InheritedElement 的引用。
铺垫的差不多,可以看InheritedWidget如何进行更新通知了。
InheritedWidget 更新通知机制
前文提到,想要获取"最近"的InheritedElement,需要调用 BuildContext.inheritFromWidgetOfExactType
。那跟Element
有什么关系呢?
其实,Element
就是BuildContext
。源码定义如下:
abstract class Element extends DiagnosticableTree implements BuildContext
接下来看BuildContext.inheritFromWidgetOfExactType
的源码:
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
首先在 _inheritedWidget 映射中查找是否有特定类型 InheritedWidget 的实例。如果有则将该实例添加到自身的依赖列表中,同时将自身添加到对应的依赖项列表中。这样该 InheritedWidget 在更新后就可以通过其 _dependents 属性知道需要通知哪些依赖了它的 widget。
每当 InheritedElement 实例更新时,会调用 InheritedWidget
中的updated
方法:
/// Calls [Element.didChangeDependencies] of all dependent elements, if
/// [InheritedWidget.updateShouldNotify] returns true.
///
/// Called by [update], immediately prior to [build].
///
/// Calls [notifyClients] to actually trigger the notifications.
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
上面提到继承自 ProxyWidget, 我们来看 ProxyWidget的 updated 方法:
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
又回到了 InheritedWidget 中notifyClients :
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies.contains(this));
notifyDependent(oldWidget, dependent);
}
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
终于看到熟悉的didChangeDependencies
了,哈哈,终于可以解释 前文中 如果_TestWidget的build方法中没有使用ShareDataWidget的数据,那么它的didChangeDependencies()将不会被调用,因为它并没有依赖ShareDataWidget。
总结一下,首先执行相应 InheritedWidget 上的 updateShouldNotify 方法判断是否需要通知,如果该方法返回 true 则遍历 _dependents 列表中的 element 并执行他们的 didChangeDependencies() 方法。这样 InheritedWidget 中的更新就通知到依赖它的子 widget 中了。
源码分析结束了,后续可能会再讲一些 InheritedWidget
的具体应用。
如果你觉得这篇文章对你有益,还请帮忙转发和点赞,万分感谢。
![](https://img.haomeiwen.com/i730571/20b9d53effe8842b.jpg)
网友评论