Flutter中InheritedWidget的原理
Flutter的响应式开发与React类似,数据都是自顶向下的。
假设有祖先组点A,中间经过结点B, C,然后到结点D,D需要从A中获取数据f,那按照自顶向下数据流转,f需要依次传递给B及C,最后才到C。这样开发极为不灵活,成本也比较高。所有Flutter需要有跨结点(只能是祖先后代节点,不能跨兄弟节点)高效传递数据的方案。
InheritedWidget
前面讲过InheritedWidget,它是Flutter用来向后代结点高效传递数据用的。
当调用BuildContext.inheritFromWidgetOfExactType
时会建立两个结点的依赖关系,每次当祖先组点数据改变后,会主动通知到后代结点,导致后代结点的重建(如果后代结点是StatefulWidget,还是调用State.didChangeDependencies
)。
那这种依赖关系是怎么建立起来的?
每个Element维护了所有祖先遗传结点(遗传结点即InheritedWidget
对应的InheritedElement
,后面会沿用)的表_inheritedWidgets
,_inheritedWidgets
该表记录了该结点至根结点所有的遗传节点。
每个Element维护了自己依赖的遗传结点的表_dependencies
。
_inheritedWidgets
记录所有的祖先遗传结点列表,而_dependencies
只记录自己依赖的祖先遗传结点列表。
InheritedElement
则维护了依赖自己的后代节点列表_dependents
。
当调用BuildContext.inheritFromWidgetOfExactType
方法时,如果找到相应类型的最近的遗传节点,则将该遗传结点添加到自己的依赖表中(_dependencies
),同时会调用祖先遗传结点的InheritedElement.updateDependencies
方法,将自己注册到InheritedElement
的_dependents
表中。
此时BuildContext.inheritFromWidgetOfExactType
已经完成依赖关系的构建,后面描述InheritedElement
的InheirtedWidget
发生变动后,InheritedElement
会遍历_dependents
表并调用Element.didChangeDependencies
。
这节我们从源码入手,搞清楚这种依赖关系建立起来的原理。
Element怎么维护所有祖先遗传结点表_inheritedWidgets
abstract class Element extends DiagnosticableTree implements BuildContext {
Map<Type, InheritedElement> _inheritedWidgets;
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
...
_updateInheritance();
...
}
@mustCallSuper
void activate() {
...
_updateInheritance();
}
@mustCallSuper
void deactivate() {
...
_inheritedWidgets = null;
...
}
void _updateInheritance() {
_inheritedWidgets = _parent?._inheritedWidgets;
}
}
class InheritedElement extends ProxyElement {
@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;
}
}
在Element树第首次构建的过程中,都会调用Element.mount
,Element.mount
又会调用Element._updateInheritance
,如果该结点是InheritedElement
类型,则将自己添加到_inheritedWidgets
表中,否则,_inheritedWidgets
引用父结点的_inheritedWidgets
。随着Element的递归构建,每个Element._inheritedWidgets
也被正确填充。
随后,当该Element被从树是移除时,会调用Element.deactivate
,会将_inheritedWidgets
置空。在动画帧结束之前,如果该Element被重新整个进树中(因为GlobalKey),会重新调用Element.activate
,进而重新填充_inheritedWidgets
。
Element怎么维护自己依赖的所有祖先遗传结点表_dependencies
abstract class Element extends DiagnosticableTree implements BuildContext {
Set<InheritedElement> _dependencies;
@mustCallSuper
void activate() {
...
_dependencies?.clear();
...
}
@mustCallSuper
void deactivate() {
...
if (_dependencies != null && _dependencies.isNotEmpty) {
for (InheritedElement dependency in _dependencies)
dependency._dependents.remove(this);
}
_inheritedWidgets = null;
...
}
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@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;
}
}
当调用BuildContext.inheritFromWidgetOfExactType
时,如果在所有祖选遗传表中找到相应遗传结点,就将该遗传结点添加到自己的依赖表中_dependencies
,同时将自己注册到相应遗传结点(调用InheritedElement.updateDependencies
注册到InheritedElement.dependents
表中)。
当Element从树中移除是,会调用Element.deactivate
,会遍历所有自己注册的依赖祖先遗传结点,从遗传结点中移除自己,这样,与遗传结点的依赖便取消了,而后将_inheritedWidgets
置空。
当Element重新整合进树中时,会再次清理一下_inheritedWidgets
(以防从树中移除后,仍然调用BuildContext.inheritFromWidgetOfExactType
)。
InheritedElement
的更新机制
class InheritedElement extends ProxyElement {
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);///super.updated会调用notifyClients
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
@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);
}
}
}
当InheritedElement需要更新时(相同位置再次配置了相同runtimeType及key的InheritedWidget)时,会再次调用InheritedWidget.updateShouldNotify
确认一下是否需要通知,如果确实需要通知,则会遍历_denpendents
并调用Element.didChangeDependencies
。