class FrogColor extends InheritedWidget {
const FrogColor({Key key, @required Widget child, @required this.color})
: assert(child != null),
super(key: key, child: child);
final Color color;
static FrogColor of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<FrogColor>();
}
@override
bool updateShouldNotify(FrogColor old) {
return old.color != color;
}
}
我们带着两个疑问来总结一下知识
- 为什么获取InheritedWidget的context必须是它的子节点
- 为什么InheritedWidget所有的子节点都可以访问它
前置知识
Element基类中的字段
_inheritedWidgets:存储了当前树上所有的InheritedElement,
-
_inheritedWidget是一个Map<Type, InheritedElement>,它管理了该树上所有的InheritedWidget对应的Element, 对应的Key是widget的runtimeType- 该Map是从根节点沿着子节点往下传递的,传递时机是Element对象mount时
_dependencies:存储了当前Element依赖了哪些InheritedElement
-
- _dependencies类型是Set
- 在触发
dependOnInheritedWidgetOfExactType方法时才说明当前节点对该InheritedElement有依赖,才需要加入_dependencies中
Map<Type, InheritedElement>? _inheritedWidgets;
Set<InheritedElement>? _dependencies;
InheritedElement中的字段
_dependents:存储了依赖该InheritedElement的Element
-
_dependents是一个Map<Element, Object?>,添加到_dependents的方法调用顺序是dependOnInheritedWidgetOfExactType->InheritedElement.updateDependencie->setDependencies_dependents有为什么用呢?在InheritedElement更新时会调用notifyClients方法(这是Element的知识,可以查看这里),在notifyClients里面遍历_dependents的元素执行didChangeDependencies()方法进行rebuild,如果是StatefulElement,还会触发State.didChangeDependencies,方法执行顺序是:ProxyElement.update->InheritedElement.updated->ProxyElement.updated->InheritedElement.notifyClients-> (遍历执行)notifyDependent->Element.didChangeDependencies
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
将_inheritedWidgets传递给子节点
既然子节点都可以访问InheritedWidget,那么就需要子节点能存储inheritedWidget的信息,前面说了,_inheritedWidgets是个map,是从根节点沿着树往下传递的。
- 在Element基类中, Element对象被mount(挂载)时,调用了
_updateInheritance()方法
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();
}
- 当该Element是非InheritedElement时,那么该方法只获取了parent的
_inheritedWidget
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
- 而如果当前Element是InheritedElement, 在获取parent的
_inheritedWidget同时,他也会把自己加入到_inheritedWidget中.
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; //将自己添加进去
}
_inheritedWidget会沿着子树一层层的传递,如果是InheritedElement, 还会把自己加入,这也是解释了child为什么都可以通过自身的context获取InheritedWidget的值;也解释了为什么传入父节点到InheritedWidget中会报错,因为是沿着子树传递的,父节点是没法访问InheritedWidget;
从子节点中获取inheritedWidget
我们知道,Element实现了BuildContext,所以context就是访问element,通过静态函数of调用context.context.dependOnInheritedWidgetOfExactType<FrogColor>()从_inheritedWidgets中获取对应的InheritedWidget,并且返回
static FrogColor of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<FrogColor>();
}
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
//我们知道,_inheritedWidgets存储的时候使用widget的runtimeType当作key
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
_dependencies ??= HashSet<InheritedElement>();
//将InheritedElement添加进_dependencies,前置知识说了它的作用
_dependencies!.add(ancestor);
//这个方法是将当前Element添加进InheritedElement管理的_dependents中,前面也说了
ancestor.updateDependencies(this, aspect);
return ancestor.widget; //返回InheritedWidget
}
最后回答上面的问题
因为InheritedElement的传递方向是从上往下的,所以只有子节点能访问