InheritedWidget源码解析

875 阅读3分钟

Flutter InheritedWidget源码解析

InheritedWidget是一个提供数据共享的Widget,数据从上往下传递,任何子Widget都可以获取共享的数据. 在Flutter源码用了大量的InheritedWidget来实现数据从上往下的传递,比如ThemeDefaultTextStyleMediaQuery等.

Theme并不是直接继承InheritedWidget,但是它的内部实现还是用了InheritedWidget.

InheritedWidget的创建过程

我们从ElementupdateChild开始,下面我会省略很多和InheritedWidget无关的代码.

Element > updateChild

这里会调用inflateWidget创建element.

@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
  Element newChild;
  ...
  newChild = inflateWidget(newWidget, newSlot);
  ...
  return newChild;
}

Element > inflateWidget

使用newWidgetcreateElement创建一个新的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里定义的,KeyInheritedWidgetruntimeType,valueInheritedElement

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就是InheritedWidgetruntimeType. 我们可以通过泛型T拿到保存在_inheritedWidgetsInheritedElement.

@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. 它是一个KeyElement,ValueObject的一个HashMap.它的作用就是当InheritedElement更新的时候,会调用所有_dependents里的elementdidChangeDependencies方法.

class InheritedElement extends ProxyElement {
  final Map<Element, Object> _dependents = HashMap<Element, Object>();
}

这里的ancestorInheritedElement,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发生更新时,会触发部分使用InheritedWidgetwidget.之所以是部分,是因为通过getElementForInheritedWidgetOfExactType方法获取InheritedWidgetwidget不会收到InheritedWidget的更新通知.

下面还是从ElementupdateChild说起.

Element > updateChild

调用elementupdate方法.

@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
	...
	child.update(newWidget);
	...
}

InheritedElement > update

这里会调用InheritedWidgetupdateShouldNotify方法,通过返回值来判断是否要发起通知.

@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

调用elementdidChangeDependencies方法来触发更新.

@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

这里调用ElementmarkNeedsBuild方法来触发更新.

void didChangeDependencies() {
	///这里标记改element为dirty,会在下一帧rebuildelement
	markNeedsBuild();
}

到这里更新流程就结束了.