Flutter InheritedWidget源码解析
InheritedWidget是一个提供数据共享的Widget,数据从上往下传递,任何子Widget都可以获取共享的数据.
在Flutter源码用了大量的InheritedWidget来实现数据从上往下的传递,比如Theme、DefaultTextStyle、MediaQuery等.
Theme并不是直接继承InheritedWidget,但是它的内部实现还是用了InheritedWidget.
InheritedWidget的创建过程
我们从Element的updateChild开始,下面我会省略很多和InheritedWidget无关的代码.
Element > updateChild
这里会调用inflateWidget创建element.
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
Element newChild;
...
newChild = inflateWidget(newWidget, newSlot);
...
return newChild;
}
Element > inflateWidget
使用newWidget的createElement创建一个新的element,并调用element的mount方法.
@protected
Element inflateWidget(Widget newWidget, dynamic newSlot) {
...
///使用newWidget的createElement创建一个新的element
final Element newChild = newWidget.createElement();
///调用newChild的mount方法
newChild.mount(this, newSlot);
return newChild;
}
Element > mount
调用_updateInheritance,这里InheritedElement重写了_updateInheritance方法.
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
...
_updateInheritance();
}
InheritedElement > _updateInheritance
这里先解释一下 _inheritedWidgets,它是在Element里定义的,Key是InheritedWidget的runtimeType,value是InheritedElement
Map<Type, InheritedElement> _inheritedWidgets;
先拿到父Widget的_inheritedWidgets,如果为null则新建一个空的HashMap,不然就拿里面的数据重新创建一个新的HashMap.
这里不直接添加到父element的_inheritedWidgets是为了只有InheritedWidget的子Widget能拿到它的数据.
void _updateInheritance() {
///拿到父element的_inheritedWidgets
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
///保存当前element到_inheritedWidgets里,key是InheritedWidget.runtimeType,value是当前的InheritedElement.
_inheritedWidgets[widget.runtimeType] = this;
}
创建过程到这里就结束了.
获取InheritedWidget的数据
子widget获取InheritedWidget有两种方法: 一个是getElementForInheritedWidgetOfExactType,这个方法当InheritedWidget触发更新的时候并不会更新当前widget.
还有一种是dependOnInheritedWidgetOfExactType,这个方法获取InheritedWidget后当InheritedWidget触发更新后会触发当前widget的更新.
这里以Theme举例
final _InheritedTheme inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
element > dependOnInheritedWidgetOfExactType
这个方法需要传入继承InheritedWidget的泛型T,在前面创建的_updateInheritance方法中说过,InheritedElement会把自己保存在_inheritedWidgets里,key就是InheritedWidget的runtimeType.
我们可以通过泛型T拿到保存在_inheritedWidgets的InheritedElement.
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
///拿到泛型T,然后从_inheritedWidgets里拿到key为T的InheritedElement
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
if (ancestor != null) {
///将当前element保存在inheritedElement的_dependents里.
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
Element > dependOnInheritedElement
将当前element保存在InheritedElement的_dependents里.这里我们先介绍接一下_dependents.
它是一个Key是Element,Value是Object的一个HashMap.它的作用就是当InheritedElement更新的时候,会调用所有_dependents里的element的didChangeDependencies方法.
class InheritedElement extends ProxyElement {
final Map<Element, Object> _dependents = HashMap<Element, Object>();
}
这里的ancestor是InheritedElement,this就是调用dependOnInheritedWidgetOfExactType方法所在的element.
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
///调用InheritedElement的updateDependencies方法.
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
InheritedElement > updateDependencies
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
InheritedElement > setDependencies
将调用的element保存在被调用的InheritedElement的_dependents里.
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
InheritedWidget的更新
当InheritedWidget发生更新时,会触发部分使用InheritedWidget的widget.之所以是部分,是因为通过getElementForInheritedWidgetOfExactType方法获取InheritedWidget的widget不会收到InheritedWidget的更新通知.
下面还是从Element的updateChild说起.
Element > updateChild
调用element的update方法.
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
...
child.update(newWidget);
...
}
InheritedElement > update
这里会调用InheritedWidget的updateShouldNotify方法,通过返回值来判断是否要发起通知.
@override
void updated(InheritedWidget oldWidget) {
///如果InheritedWidget的updateShouldNotify返回true则更新,返回false则不更新
if (widget.updateShouldNotify(oldWidget))
///这里调用ProxyElement的updated
super.updated(oldWidget);
}
ProxyElement > updated
发出通知更新.
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
InheritedElement > notifyClients
遍历_dependents.keys的所有element.并调用notifyDependent方法.
@override
void notifyClients(InheritedWidget oldWidget) {
for (final Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
InheritedElement > notifyDependent
调用element的didChangeDependencies方法来触发更新.
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
///调用element的didChangeDependencies,标记当前element是需要更新的.
///会标记StatefulElement的_didChangeDependencies为true,
///element的performRebuild会根据_didChangeDependencies判断是否触发state.didChangeDependencies
dependent.didChangeDependencies();
}
StatefulElement > didChangeDependencies
void didChangeDependencies() {
super.didChangeDependencies();
///标记_didChangeDependencies为true
_didChangeDependencies = true;
}
Element > didChangeDependencies
这里调用Element的markNeedsBuild方法来触发更新.
void didChangeDependencies() {
///这里标记改element为dirty,会在下一帧rebuildelement
markNeedsBuild();
}
到这里更新流程就结束了.