在Flutter中,因为其激进式组合(Everything is Widget)的设计理念,多层Widget是很常见的,如何在不一样层的Widget之间传递数据,是开发者常常遇到的问题。一般作法是将数据添加到下级Widget的构造函数中。可是若是传递数据的Widget层级相隔较远,那将数据一层层传下来将是个噩梦。所以,Flutter提供了一种在Widget树中自上而下传递数据的方式:InheritedWidget。本文将深刻讲解InheritedWidget的原理,并举例说明InheritedWidget如何使用。html
updateShouldNotify用于控制依赖于该InheritedWidget的组件是否须要重建。若是updateShouldNotify的值是true,则当InheritedWidget发生变化时,依赖于该InheritedWidget的Widget会被rebuild,其Element的didChangeDependencies函数会被调用,从而更新子Widget中的显示。反之,则不会重建依赖于该InheritedWidget的Widget。markdown
流程图以下:架构
能够看到,InheritedWidget变化时,若是updateShouldNotify是true,会经过notifyClients调用子组件的didChangeDependencies函数,从而调用markNeedsBuild,将本Element加入_dirtyElements列表中。你们都知道,_dirtyElements中保存的是须要重建的Element,会在下一帧时被rebuild,所以在下一帧子组件会被重建(rebuild)。框架
InheritedElement的updated源码以下:ide
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
复制代码
Element的didChangeDependencies源码以下:函数
void didChangeDependencies() {
assert(_active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
复制代码
既然InheritedWidget能用于让用户快速从子组件获取,那必然会涉及到一个问题,它是如何传递给子组件的呢?其实Flutter Framework也是将InheritedWidget一层层传递下来的,只不过因为Framework层自行处理了,所以这个过程对于咱们是透明的。咱们如今来梳理下InheritedWidget传递的过程。oop
Element中,有一个map:_inheritedWidgets。保存了全部上级节点中的InheritedElement。其源码以下:post
Map<Type, InheritedElement> _inheritedWidgets;
复制代码
其中,key中Type是InheritedWidget的子类,value是InheritedElement。为何这里value保存的是InheritedElement而不是InheritedWidget呢?由之前的文章能够知道Element中保存着对应Widget的引用,所以能够经过InheritedElement获取对应的InheritedWidget。并且Widget在上级Widget重建时会被重建,所以保存InheritedElement更合适。
在普通的Element中,_inheritedWidgets会直接复制其父组件中_inheritedWidgets的值,其源码以下:ui
void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
复制代码
而在InheritedElement中,_inheritedWidgets会首先复制其父组件中_inheritedWidgets的值,而后将本身添加进列表,其源码以下:this
@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就是这样一层层传递下来的。_inheritedWidgets赋值流程以下:
由该流程图能够看出,_inheritedWidgets在Element被加入Element Tree时就已经被赋值,所以其在子组件的build函数中是能够访问获得的。
咱们已经知道了InheritedElement会传递到下级组件中,那怎么获取它呢?Flutter提供了专门获取某个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) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
复制代码
由第三行能够看出,此函数会从_inheritedWidgets中寻找对应的InheritedElement,并返回其InheritedWidget。
除了dependOnInheritedWidgetOfExactType,Flutter还提供了另外一个专门获取某个InheritedWidget类型的函数:getElementForInheritedWidgetOfExactType。其源码以下:
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
复制代码
对比其与dependOnInheritedWidgetOfExactType源码,能够看到dependOnInheritedWidgetOfExactType多了dependOnInheritedElement函数的调用,该函数用于建立InheritedWidget和调用dependOnInheritedWidgetOfExactType的组件的依赖关系。其有两个步骤:
若是使用的是dependOnInheritedWidgetOfExactType,则当被依赖的InheritedWidget被更新时,依赖的子组件会被rebuild;而使用的是getElementForInheritedWidgetOfExactType时,因为不会创建相应的依赖关系,InheritedWidget被更新时,依赖的子组件不会被rebuild。
下面是一个使用示例。
首先,咱们经过继承InheritedWidget,将当前计数器点击次数保存在ShareDataWidget的data属性中:
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data; //须要在子树中共享的数据,保存点击次数
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//若是返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return old.data != data;
}
}
复制代码
而后咱们实现一个子组件_TestWidget,在其build方法中引用ShareDataWidget中的数据。同时,在其didChangeDependencies回调中打印日志:
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
print("__TestWidgetState build");
//使用InheritedWidget中的共享数据
return Text(ShareDataWidget
.of(context)
.data
.toString());
// return Text("tex");
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//若是build中没有依赖InheritedWidget,则此回调不会被调用。
print("Dependencies change");
}
}
复制代码
最后,咱们建立一个按钮,每点击一次,就将ShareDataWidget的值自增:
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget( //使用ShareDataWidget
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_TestWidget(),
RaisedButton(
child: Text("Increment"),
//每点击一次,将count自增,而后从新build,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
复制代码
本文主要介绍了InheritedWidget的核心函数和核心流程,并展现了一个InheritedWidget的示例。其重点以下:
Flutter框架分析(一)--架构总览
Flutter框架分析(二)-- Widget
Flutter框架分析(三)-- Element
Flutter框架分析(四)-RenderObject
Flutter框架分析(五)-Widget,Element,RenderObject树
Flutter框架分析(六)-Constraint
Flutter框架分析(七)-relayoutBoundary
Flutter框架分析(八)-Platform Channel
Flutter框架分析- Parent Data