使用示例
阅读原理之前请先体验InheritedWidget的使用demo:数据传递/状态管理 一InheritedWidget使用示例
实现原理分析
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是一个继承自 ProxyWidget 的抽象类。除了实现了一个 createElement 方法之外,还定义了一个 updateShouldNotify() 接口。 updateShouldNotify就是使用InheritedWidget要实现的接口,决定InheritedWidget变化时,要不要通知子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 共享数据传递机制
既然在InheritedWidget定义上没有收获,我们就从InheritedWidget使用上入手,每个自定义InheritedWidget都会实现一个 of 静态方法,这个of静态方法是提供给InheritedWidget的子widget来访问自定义InheritedWidget的,先看下我们示例中of方法怎么实现:
static ShareDataInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
of方法直接返回context的dependOnInheritedWidgetOfExactType,看下dependOnInheritedWidgetOfExactType里面是什么:
dependOnInheritedWidgetOfExactType在源码中两处,一处是BuildContext,一处是Element:
BuildContext中dependOnInheritedWidgetOfExactType其实只是定义:
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });
真正实现dependOnInheritedWidgetOfExactType是在Element (其实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拿到对应的InheritedElement,再从InheritedElement中拿到我们自定义的InheritedWidget;
_inheritedWidgets定义如下:
Map<Type, InheritedElement> _inheritedWidgets;
_inheritedWidgets是一个Map类型,以Type为key,定义在Element中,也就是每个Elemnet都有一个_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本身应该就实现Type,类继承自Object,所以类也是Object,可以直接传给上面Map类型为Type的 key,也就是传给Map Type key其实传的是类的runtimeType,经验证,多次以相同类名作为key保存value到Map,最后以这个类名为key取出的value为最后存储的value,类名作为key跟定义一个字符串作为key作用类似;
回到正题,我们看下_inheritedWidgets数据怎么来,因为_inheritedWidgets在Element中定义,所以在Element中先找:
void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
发现_inheritedWidgets在_updateInheritance方法里初始化,_updateInheritance是在widget mounted或activate调用;知道_inheritedWidgets的数据来自父widget,那父widget的_inheritedWidgets从哪都是在来呢?
在InheritedWidget的定义中,发现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方法,当父widget的_inheritedWidgets为空时,实例化_inheritedWidgets;当父widget不为空时,也实例化_inheritedWidgets,并把父widget的_inheritedWidgets加进来,这里有个细节,为什么不直接用父widget的_inheritedWidgets呢,这个后面会分析;最后关键的一步,就是将当前自定义InheritedWidget对应的Element保存到以自定义InheritedWidget类别作为key的_inheritedWidgets里,这样_inheritedWidgets就保存了我们自定义InheritedWidget的数据了;InheritedWidget的_inheritedWidgets传给子widget,子widget就可以通过_inheritedWidgets拿到我们自定义InheritedWidget的数据了;
到这里我们可以知道:(1)、每个widget对应的Element都有一个_inheritedWidgets,widget树自上而下,一级一级将自己的_inheritedWidgets传给下一级,并且当前级别为InheritedWidget时,就会将当前自定义的InheritedWidget保存在_inheritedWidgets;
(2)、通过_inheritedWidgets一级一级的向下传递,这样每个子widget的_inheritedWidgets就拥有上级父widget的所有类别的InheritedWidget,就可以通过自身的_inheritedWidgets访问到对应的InheritedWidget;
InheritedWidget 数据更新通知机制
我们自定义的变化InheritedWidget是怎么知道到子widget的didChangeDependencies呢?
子widget跟父InheritedWidget使用关联只有of静态方法,所以我们再从of方法里仔细分析:
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
发现在子widget的dependOnInheritedElement方法里,InheritedWidget的element->InheritedElement,也就是ancestor,调用了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对应的element作为key,保存在InheritedElement的_dependents中,这样我们自定义的InheritedWidget就可以通过自身的InheritedElement,找到关联的子widget的element了;
我们定义的的InheritedWidget有了需要通知的子widget,再看看InheritedWidget如何在变化时通知子widget的;
在自定义InheritedWidget时,我们需要实现接口方法:updateShouldNotify,先找下updateShouldNotify在哪里调用到:
来自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();
}
这里比较绕,需要仔细理解InheritedElement 和 ProxyElement关系(InheritedElement extends ProxyElement),update和updated方法区别;
由此可知:
- 在InheritedWidget更新变化时,通过updateShouldNotify返回值决定要不要通知相关的子widget;
- 如果updateShouldNotify返回false,则相关子widget 的didChangeDependencies不会调用到;
- 如果返回true,则会遍历_dependents,找到子widget相关的element,并调用element的didChangeDependencies方法;
自定义of静态方法内部实现用getElementForInheritedWidgetOfExactType为何子widget的didChangeDependencies就不会接收到InheritedWidget更新通知?
我们知道,自定义of静态方法内部实现用dependOnInheritedWidgetOfExactType子widget的didChangeDependencies就会接收到InheritedWidget更新,所以先看下两个方法内部实现的差异:
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;
}
从两个方法内部代码实现对比可知:
- dependOnInheritedWidgetOfExactType内部实现比getElementForInheritedWidgetOfExactType内部实现多调用了dependOnInheritedElement方法;
- 从上面分析可知,dependOnInheritedElement注册子widget和InheritedWidget依赖关系,InheritedWidget变化时通过这层依赖关系通知子widget的didChangeDependencies;
- 所以dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的区别就是前者子widget和InheritedWidget会注册依赖关系,而后者不会,所以用dependOnInheritedWidgetOfExactType实现的of方法对应的InheritedWidget变化时会通知子widget的didChangeDependencies方法,而用getElementForInheritedWidgetOfExactType则InheritedWidget变化不会通知子widget;
Widget树上有多个相同类别的InheritedWidget,为何子widget只会找到最近的父InheritedWidget?
从上面分析我们知道,每个子widget的_inheritedWidgets数据初始化是在_updateInheritance 方法进行的,其实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是先创建新的_inheritedWidgets,再把父widget的_inheritedWidgets的数据加到自己创建的_inheritedWidgets中,同时以自己的类别名作为key,将自己的element实例保存到自己创建的_inheritedWidgets;
为什么InheritedWidget要自己创建_inheritedWidgets呢?
这样做的好处是:
(1)、因为如果直接用父的_inheritedWidgets,当将自身的element加到_inheritedWidgets时,如果父的_inheritedWidgets数据中有和自身相同类别的InheritedWidget数据,就会把父_inheritedWidgets和自身相同类别的InheritedWidget的数据替换掉,也就是一级传一传的_inheritedWidgets,最终只有一种类别的InheritedWidget,这样传递数据就会混乱;
(2)、这也是为什么子widget只会找到最近的父InheritedWidget的原因:因为在这层的InheritedWidget创建新的_inheritedWidgets,并以自身的类别作为key,将自己的element实例保存到自己创建的_inheritedWidgets,如果_inheritedWidgets中有和自身相同类别的InheritedWidget数据,替换的也只是自身创建的_inheritedWidgets中数据,不会替换掉父_inheritedWidgets中和自己相同类别保存的数据;然后再将自身的_inheritedWidgets传给子widget,这样子widget通过of静态方法拿到的InheritedWidget就是离它最近的父InheritedWidget的数据了。