使用示例
閱讀原理之前請先體驗(yàn)InheritedWidget的使用demo:數(shù)據(jù)傳遞/狀態(tài)管理 一InheritedWidget使用示例
實(shí)現(xiàn)原理分析
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);
}
InheritedWidget是一個(gè)繼承自 ProxyWidget 的抽象類。除了實(shí)現(xiàn)了一個(gè) createElement 方法之外,還定義了一個(gè) updateShouldNotify() 接口。 updateShouldNotify就是使用InheritedWidget要實(shí)現(xiàn)的接口,決定InheritedWidget變化時(shí),要不要通知子widget。
再看下ProxyWidget是什么鬼:
abstract class ProxyWidget extends Widget {
/// Creates a widget that has exactly one child widget.
const ProxyWidget({ Key key, @required this.child }) : super(key: key);
/// The widget below this widget in the tree.
///
/// {@template flutter.widgets.child}
/// This widget can only have one child. To lay out multiple children, let this
/// widget's child be a widget such as [Row], [Column], or [Stack], which have a
/// `children` property, and then provide the children to that widget.
/// {@endtemplate}
final Widget child;
}
ProxyWidget里面沒什么特別的東西
InheritedWidget 共享數(shù)據(jù)傳遞機(jī)制
既然在InheritedWidget定義上沒有收獲,我們就從InheritedWidget使用上入手,每個(gè)自定義InheritedWidget都會實(shí)現(xiàn)一個(gè) of 靜態(tài)方法,這個(gè)of靜態(tài)方法是提供給InheritedWidget的子widget來訪問自定義InheritedWidget的,先看下我們示例中of方法怎么實(shí)現(xiàn):
static ShareDataInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
of方法直接返回context的dependOnInheritedWidgetOfExactType,看下dependOnInheritedWidgetOfExactType里面是什么:
dependOnInheritedWidgetOfExactType在源碼中兩處,一處是BuildContext,一處是Element:

BuildContext中dependOnInheritedWidgetOfExactType其實(shí)只是定義:
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });
真正實(shí)現(xiàn)dependOnInheritedWidgetOfExactType是在Element (其實(shí)BuildContext就是Element的引用,這里就不作分析了) :
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
可以看到,子widget通過_inheritedWidgets和我們自定義的InheritedWidget拿到對應(yīng)的InheritedElement,再從InheritedElement中拿到我們自定義的InheritedWidget;
_inheritedWidgets定義如下:
Map<Type, InheritedElement> _inheritedWidgets;
_inheritedWidgets是一個(gè)Map類型,以Type為key,定義在Element中,也就是每個(gè)Elemnet都有一個(gè)_inheritedWidgets;
這里插一段小曲:
Type定義如下:
/**
* Runtime representation of a type.
*/
abstract class Type {}
可以在Object中找到它:
class Object {
...
/**
* A representation of the runtime type of the object.
*/
external Type get runtimeType;
...
}
Object本身應(yīng)該就實(shí)現(xiàn)Type,類繼承自O(shè)bject,所以類也是Object,可以直接傳給上面Map類型為Type的 key,也就是傳給Map Type key其實(shí)傳的是類的runtimeType,經(jīng)驗(yàn)證,多次以相同類名作為key保存value到Map,最后以這個(gè)類名為key取出的value為最后存儲的value,類名作為key跟定義一個(gè)字符串作為key作用類似;
回到正題,我們看下_inheritedWidgets數(shù)據(jù)怎么來,因?yàn)開inheritedWidgets在Element中定義,所以在Element中先找:
void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
發(fā)現(xiàn)_inheritedWidgets在_updateInheritance方法里初始化,_updateInheritance是在widget mounted或activate調(diào)用;知道_inheritedWidgets的數(shù)據(jù)來自父widget,那父widget的_inheritedWidgets從哪都是在來呢?
在InheritedWidget的定義中,發(fā)現(xiàn)InheritedWidget有自己的Element->InheritedElement
abstract class InheritedWidget extends ProxyWidget {
...
@override
InheritedElement createElement() => InheritedElement(this);
...
再看下InheritedElement源碼:
/// An [Element] that uses an [InheritedWidget] as its configuration.
class InheritedElement extends ProxyElement {
/// Creates an element that uses the given widget as its configuration.
InheritedElement(InheritedWidget widget) : super(widget);
@override
InheritedWidget get widget => super.widget;
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;
}
可以看到InheritedElement重寫了_updateInheritance方法,當(dāng)父widget的_inheritedWidgets為空時(shí),實(shí)例化_inheritedWidgets;當(dāng)父widget不為空時(shí),也實(shí)例化_inheritedWidgets,并把父widget的_inheritedWidgets加進(jìn)來,這里有個(gè)細(xì)節(jié),為什么不直接用父widget的_inheritedWidgets呢,這個(gè)后面會分析;最后關(guān)鍵的一步,就是將當(dāng)前自定義InheritedWidget對應(yīng)的Element保存到以自定義InheritedWidget類別作為key的_inheritedWidgets里,這樣_inheritedWidgets就保存了我們自定義InheritedWidget的數(shù)據(jù)了;InheritedWidget的_inheritedWidgets傳給子widget,子widget就可以通過_inheritedWidgets拿到我們自定義InheritedWidget的數(shù)據(jù)了;
到這里我們可以知道:(1)、每個(gè)widget對應(yīng)的Element都有一個(gè)_inheritedWidgets,widget樹自上而下,一級一級將自己的_inheritedWidgets傳給下一級,并且當(dāng)前級別為InheritedWidget時(shí),就會將當(dāng)前自定義的InheritedWidget保存在_inheritedWidgets;
(2)、通過_inheritedWidgets一級一級的向下傳遞,這樣每個(gè)子widget的_inheritedWidgets就擁有上級父widget的所有類別的InheritedWidget,就可以通過自身的_inheritedWidgets訪問到對應(yīng)的InheritedWidget;
InheritedWidget 數(shù)據(jù)更新通知機(jī)制
我們自定義的變化InheritedWidget是怎么知道到子widget的didChangeDependencies呢?
子widget跟父InheritedWidget使用關(guān)聯(lián)只有of靜態(tài)方法,所以我們再從of方法里仔細(xì)分析:
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
發(fā)現(xiàn)在子widget的dependOnInheritedElement方法里,InheritedWidget的element->InheritedElement,也就是ancestor,調(diào)用了updateDependencies方法將子widget的element傳遞過去;
來看下InheritedElement的updateDependencies的方法:
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;
}
InheritedElement的updateDependencies方法將子widget對應(yīng)的element作為key,保存在InheritedElement的_dependents中,這樣我們自定義的InheritedWidget就可以通過自身的InheritedElement,找到關(guān)聯(lián)的子widget的element了;
我們定義的的InheritedWidget有了需要通知的子widget,再看看InheritedWidget如何在變化時(shí)通知子widget的;
在自定義InheritedWidget時(shí),我們需要實(shí)現(xiàn)接口方法:updateShouldNotify,先找下updateShouldNotify在哪里調(diào)用到:
來自ProxyElement:
@override
void update(ProxyWidget newWidget) {
...
updated(oldWidget);
...
}
來自InheritedElement:
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
來自ProxyElement:
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
來自InheritedElement:
@override
void notifyClients(InheritedWidget oldWidget) {
for (Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
}
來自InheritedElement:
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
這里比較繞,需要仔細(xì)理解InheritedElement 和 ProxyElement關(guān)系(InheritedElement extends ProxyElement),update和updated方法區(qū)別;
由此可知:
- 在InheritedWidget更新變化時(shí),通過updateShouldNotify返回值決定要不要通知相關(guān)的子widget;
- 如果updateShouldNotify返回false,則相關(guān)子widget 的didChangeDependencies不會調(diào)用到;
- 如果返回true,則會遍歷_dependents,找到子widget相關(guān)的element,并調(diào)用element的didChangeDependencies方法;
自定義of靜態(tài)方法內(nèi)部實(shí)現(xiàn)用getElementForInheritedWidgetOfExactType為何子widget的didChangeDependencies就不會接收到InheritedWidget更新通知?
我們知道,自定義of靜態(tài)方法內(nèi)部實(shí)現(xiàn)用dependOnInheritedWidgetOfExactType子widget的didChangeDependencies就會接收到InheritedWidget更新,所以先看下兩個(gè)方法內(nèi)部實(shí)現(xiàn)的差異:
dependOnInheritedWidgetOfExactType:
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
getElementForInheritedWidgetOfExactType:
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
從兩個(gè)方法內(nèi)部代碼實(shí)現(xiàn)對比可知:
- dependOnInheritedWidgetOfExactType內(nèi)部實(shí)現(xiàn)比getElementForInheritedWidgetOfExactType內(nèi)部實(shí)現(xiàn)多調(diào)用了dependOnInheritedElement方法;
- 從上面分析可知,dependOnInheritedElement注冊子widget和InheritedWidget依賴關(guān)系,InheritedWidget變化時(shí)通過這層依賴關(guān)系通知子widget的didChangeDependencies;
- 所以dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的區(qū)別就是前者子widget和InheritedWidget會注冊依賴關(guān)系,而后者不會,所以用dependOnInheritedWidgetOfExactType實(shí)現(xiàn)的of方法對應(yīng)的InheritedWidget變化時(shí)會通知子widget的didChangeDependencies方法,而用getElementForInheritedWidgetOfExactType則InheritedWidget變化不會通知子widget;
Widget樹上有多個(gè)相同類別的InheritedWidget,為何子widget只會找到最近的父InheritedWidget?
從上面分析我們知道,每個(gè)子widget的_inheritedWidgets數(shù)據(jù)初始化是在_updateInheritance 方法進(jìn)行的,其實(shí)InheritedElement重寫了_updateInheritance方法,如下:,

普通widget的Element的_updateInheritance方法:
void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
InheritedWidget的InheritedElement的_updateInheritance方法:
@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;
}
對比上面代碼,我們可以看到,普通widget只是把父widget的_inheritedWidgets直接賦值到自身的_inheritedWidgets;而InheritedWidget是先創(chuàng)建新的_inheritedWidgets,再把父widget的_inheritedWidgets的數(shù)據(jù)加到自己創(chuàng)建的_inheritedWidgets中,同時(shí)以自己的類別名作為key,將自己的element實(shí)例保存到自己創(chuàng)建的_inheritedWidgets;
為什么InheritedWidget要自己創(chuàng)建_inheritedWidgets呢?
這樣做的好處是:
(1)、因?yàn)槿绻苯佑酶傅腳inheritedWidgets,當(dāng)將自身的element加到_inheritedWidgets時(shí),如果父的_inheritedWidgets數(shù)據(jù)中有和自身相同類別的InheritedWidget數(shù)據(jù),就會把父_inheritedWidgets和自身相同類別的InheritedWidget的數(shù)據(jù)替換掉,也就是一級傳一傳的_inheritedWidgets,最終只有一種類別的InheritedWidget,這樣傳遞數(shù)據(jù)就會混亂;
(2)、這也是為什么子widget只會找到最近的父InheritedWidget的原因:因?yàn)樵谶@層的InheritedWidget創(chuàng)建新的_inheritedWidgets,并以自身的類別作為key,將自己的element實(shí)例保存到自己創(chuàng)建的_inheritedWidgets,如果_inheritedWidgets中有和自身相同類別的InheritedWidget數(shù)據(jù),替換的也只是自身創(chuàng)建的_inheritedWidgets中數(shù)據(jù),不會替換掉父_inheritedWidgets中和自己相同類別保存的數(shù)據(jù);然后再將自身的_inheritedWidgets傳給子widget,這樣子widget通過of靜態(tài)方法拿到的InheritedWidget就是離它最近的父InheritedWidget的數(shù)據(jù)了。