InheritedWidget
InheritedWidget
是Flutter提供的用来从上到下传递数据的功能性组件。平常开发过程当中, 对于嵌套组件、跳转路由页面来说, 从父组件向子组件传递数据, 一般我们首先想到的是通过参数传递, 比较简单。
参数传值
class ParentWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
var value = "this is the value";
return ChildWidget(value: value);
}
}
class ChildWidget extends StatelessWidget {
String value;
ChildWidget({Key key, this.value}): super(key);
@override
Widget build(BuildContext context) {
return Text(value);
}
}
复制代码
通过参数传值的方式, 在Widget嵌套不是很多的情况下, 是很方便、简单的, 推荐使用。但是, 如果Widget嵌套层级过多, 数据一层一层一层一层一层向子组件传递, 很容易造成混乱。这个时候, 就考虑使用InheritedWidget
组件, 实现跨层级传递数据了。
InheritedWidget传递数据
下面我们一起来看一个添加数据到购物车的例子。
定义基础Inherited组件
class _BaseInheritedWidget<T> extends InheritedWidget {
_BaseInheritedWidget({
Key? key,
required Widget child,
required this.data,
}) : super(key: key, child: child);
T data;
@override
bool updateShouldNotify(_BaseInheritedWidget oldWidget) {
/// 在此简单返回true,则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。
return true;
}
}
复制代码
上面在定义InheritedWidget
的时候,重写了updateShouldNotify
方法。该方法作用是,当InheritedWidget
重新build的时候,是否要通知依赖于该Widget的子Widget也要重新build。一般是在这个方法中比较InheritedWidget
所持有的值是否变化,如果变化了,就通知子Widget重新build,以更新状态。
提供者
接下来, 我们来一起实现数据的提供者。
class CustomProviderWidget extends StatefulWidget {
CustomProviderWidget({
Key? key,
required this.child,
}): super(key: key);
final Widget child;
@override
CustomProviderState createState() => new CustomProviderState();
static CustomProviderState of(BuildContext context, [bool rebuild = true]){
return (rebuild ? context.dependOnInheritedWidgetOfExactType<_BaseInheritedWidget>() as _BaseInheritedWidget
: context.getElementForInheritedWidgetOfExactType<_BaseInheritedWidget>() as _BaseInheritedWidget).data;
}
}
复制代码
该类继承StatefulWidget
,然后定义了一个of()
静态方法供子类方便获取Widget树中的InheritedProvider
中保存的共享状态(data),下面我们实现该类对应的CustomProviderState
类:
class CustomProviderState extends State<CustomProviderWidget>{
/// List of Items
List<Item> _items = <Item>[];
/// Getter (number of items)
int get itemsCount => _items.length;
/// Helper method to add an Item
void addItem(String reference){
setState((){
_items.add(new Item(reference));
});
}
@override
Widget build(BuildContext context){
return new _BaseInheritedWidget(
data: this,
child: widget.child,
);
}
}
复制代码
消费者
class WidgetA extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CustomProviderState state = CustomProviderWidget.of(context, false);
return new Container(
child: new RaisedButton(
child: new Text('Add Item'),
onPressed: () {
state.addItem('new item');
},
),
);
}
}
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CustomProviderState state = CustomProviderWidget.of(context, true);
return new Text('I am Widget B. itemsCount: ${state.itemsCount} \n \n \n \n \n \n');
}
}
class WidgetC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Text('I am Widget C');
}
}
复制代码
我们定义了两个个消费者WidgetA
、WidgetB
, 它们都使用CustomProviderWidget.of()
方法获取父组件的state。
使用
class MyTree extends StatefulWidget {
@override
_MyTreeState createState() => new _MyTreeState();
}
class _MyTreeState extends State<MyTree> {
@override
Widget build(BuildContext context) {
/// 全局共享组件
return CustomProviderWidget(
child: new Scaffold(
appBar: new AppBar(
title: new Text('Title'),
),
body: new Column(
children: <Widget>[
new WidgetA(),
new Container(
child: new Row(
children: <Widget>[
new Icon(Icons.shopping_cart),
new WidgetB(),
new WidgetC(),
],
),
),
],
),
),
);
}
}
复制代码
当我们点击Widget A
中的Add Item
按钮时, 页面就会出现数据累加。
至此, 我们完成了一个通过共享组件InheritedWidget
层层向下传递数据的案例。
源码分析
Element
abstract class Element extends DiagnosticableTree implements BuildContext {
Map<Type, InheritedElement>? _inheritedWidgets;
Set<InheritedElement>? _dependencies;
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
assert(_lifecycleState == _ElementLifecycle.initial);
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
assert(slot == null);
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) {
// Only assign ownership if the parent is non-null. If parent is null
// (the root node), the owner should have already been assigned.
// See RootRenderObjectElement.assignOwner().
_owner = parent.owner;
}
assert(owner != null);
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
/// 调用_updateInheritance方法
_updateInheritance();
}
@mustCallSuper
void activate() {
assert(_lifecycleState == _ElementLifecycle.inactive);
assert(widget != null);
assert(owner != null);
assert(depth != null);
final bool hadDependencies = (_dependencies != null && _dependencies!.isNotEmpty) || _hadUnsatisfiedDependencies;
_lifecycleState = _ElementLifecycle.active;
// We unregistered our dependencies in deactivate, but never cleared the list.
// Since we're going to be reused, let's clear our list now.
_dependencies?.clear();
_hadUnsatisfiedDependencies = false;
/// 调用_updateInheritance方法
_updateInheritance();
if (_dirty)
owner!.scheduleBuildFor(this);
if (hadDependencies)
didChangeDependencies();
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
ancestor.updateDependencies(this, aspect);
/// 返回InheritedWidget组件
return ancestor.widget;
}
@pragma('vm:prefer-inline')
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
// When the type of a widget is changed between Stateful and Stateless via
// hot reload, the element tree will end up in a partially invalid state.
// That is, if the widget was a StatefulWidget and is now a StatelessWidget,
// then the element tree currently contains a StatefulElement that is incorrectly
// referencing a StatelessWidget (and likewise with StatelessElement).
//
// To avoid crashing due to type errors, we need to gently guide the invalid
// element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
// returns false which prevents us from trying to update the existing element
// incorrectly.
//
// For the case where the widget becomes Stateful, we also need to avoid
// accessing `StatelessElement.widget` as the cast on the getter will
// cause a type error to be thrown. Here we avoid that by short-circuiting
// the `Widget.canUpdate` check once `hasSameSuperclass` is false.
assert(() {
final int oldElementClass = Element._debugConcreteSubtype(child);
final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
hasSameSuperclass = oldElementClass == newWidgetClass;
return true;
}());
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
/// 调用update方法
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner!._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
assert(() {
if (child != null)
_debugRemoveGlobalKeyReservation(child);
final Key? key = newWidget.key;
if (key is GlobalKey) {
assert(owner != null);
owner!._debugReserveGlobalKeyFor(this, newChild, key);
}
return true;
}());
return newChild;
}
@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;
}
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
}
复制代码
ComponentElement
ComponentElement
继承 Element
, 关键是执行mount
后, 会去执行update
abstract class ComponentElement extends Element {
/// Creates an element that uses the given widget as its configuration.
ComponentElement(Widget widget) : super(widget);
Element? _child;
bool _debugDoingBuild = false;
@override
bool get debugDoingBuild => _debugDoingBuild;
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_lifecycleState == _ElementLifecycle.active);
_firstBuild();
assert(_child != null);
}
void _firstBuild() {
rebuild();
}
/// Calls the [StatelessWidget.build] method of the [StatelessWidget] object
/// (for stateless widgets) or the [State.build] method of the [State] object
/// (for stateful widgets) and then updates the widget tree.
///
/// Called automatically during [mount] to generate the first build, and by
/// [rebuild] when the element needs updating.
@override
@pragma('vm:notify-debugger-on-exception')
void performRebuild() {
if (!kReleaseMode && debugProfileBuildsEnabled)
Timeline.startSync('${widget.runtimeType}', arguments: timelineArgumentsIndicatingLandmarkEvent);
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(true));
Widget? built;
try {
assert(() {
_debugDoingBuild = true;
return true;
}());
built = build();
assert(() {
_debugDoingBuild = false;
return true;
}());
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
_debugDoingBuild = false;
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
),
);
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
assert(_debugSetAllowIgnoredCallsToMarkNeedsBuild(false));
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(
_debugReportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(this));
},
),
);
_child = updateChild(null, built, slot);
}
if (!kReleaseMode && debugProfileBuildsEnabled)
Timeline.finishSync();
}
/// Subclasses should override this function to actually call the appropriate
/// `build` function (e.g., [StatelessWidget.build] or [State.build]) for
/// their widget.
@protected
Widget build();
@override
void visitChildren(ElementVisitor visitor) {
if (_child != null)
visitor(_child!);
}
@override
void forgetChild(Element child) {
assert(child == _child);
_child = null;
super.forgetChild(child);
}
}
复制代码
ProxyElement
ProxyElement
继承ComponentElement
abstract class ProxyElement extends ComponentElement {
/// Initializes fields for subclasses.
ProxyElement(ProxyWidget widget) : super(widget);
@override
ProxyWidget get widget => super.widget as ProxyWidget;
@override
Widget build() => widget.child;
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
assert(widget != null);
assert(widget != newWidget);
super.update(newWidget);
assert(widget == newWidget);
updated(oldWidget);
_dirty = true;
rebuild();
}
/// Called during build when the [widget] has changed.
///
/// By default, calls [notifyClients]. Subclasses may override this method to
/// avoid calling [notifyClients] unnecessarily (e.g. if the old and new
/// widgets are equivalent).
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
/// Notify other objects that the widget associated with this element has
/// changed.
///
/// Called during [update] (via [updated]) after changing the widget
/// associated with this element but before rebuilding this element.
@protected
void notifyClients(covariant ProxyWidget oldWidget);
}
复制代码
ProxyElement
继承ComponentElement
使用泛型T
作为参数, 从_inheritedWidgets
字典中获取InheritedElement
类型的ancestor
对象。 如果ancestor
对象不为null
, 就调用dependOnInheritedElement
方法。
继承关系InheritedElement
-->ProxyElement
-->ComponentElement
-->Element
。
因为InheritedElement
重写了_updateInheritance
方法, 所以Element
中的mount
方法去执行_updateInheritance
时, 其实是来到了InheritedElement
中的_updateInheritance
方法。该方法中_inheritedWidgets[widget.runtimeType] = this
; 将InheritedElement
自己保存到这个Map中。
Element
中的mount
方法回去执行_updateInheritance
Element
中的activate
方法回去执行_updateInheritance
由此, 我们可以知道, 底层Element
的_inheritedWidgets
Map中包含所有类型为InheritedWidget
的祖先InheritedElement
节点信息。
InheritedElement
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 as InheritedWidget;
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
/// 重写`Element`中的方法
@override
void _updateInheritance() {
assert(_lifecycleState == _ElementLifecycle.active);
final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
/// 保存`InheritedElement`节点
_inheritedWidgets![widget.runtimeType] = this;
}
@override
void debugDeactivated() {
assert(() {
assert(_dependents.isEmpty);
return true;
}());
super.debugDeactivated();
}
@protected
Object? getDependencies(Element dependent) {
return _dependents[dependent];
}
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element? ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies!.contains(this));
notifyDependent(oldWidget, dependent);
}
}
}
复制代码
updateShouldNotify方法
InheritedWidget
重写updateShouldNotify
方法,这个方法会在InheritedEelement
中的updated
方法中被调用
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
复制代码
调用updateShouldNotify
方法,如果返回true就再调用super.updated(oldWidget)
方法,否则,不执行任何操作。
InheritedElement
的父类是ProxyElement
,ProxyElement
的updated
方法会调用notifyClinets(oldWidget)
方法
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
复制代码
notifyClinets(oldWidget)
方法是ProxyElement
定义的抽象方法,在InheritedElement
类中有具体的实现如下:
@override
void notifyClients(InheritedWidget oldWidget) {
...
for (final Element dependent in _dependents.keys) {
...
notifyDependent(oldWidget, dependent);
}
}
复制代码
会循环遍历_dependents
列表,调用notifyDependent(oldWidget, dependent)
方法
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
复制代码
notifyDependent
方法会直接调用dependent
的didChangeDependencies
方法,这个方法定义在Element
类中,实现如下
void didChangeDependencies() {
...
markNeedsBuild();
}
复制代码
didChangeDependencies
方法会调用Element的markNeedsBuild
方法,而markNeedsBuild
方法会将该element
标记为dirty,在下一帧将会重新build。
总结
InheritedWidget
是用于向下传递数据的功能性组件。自定义InheritedWidget
时, 需要重写updateShouldNotify
方法, 当InheritedWidget
重新build的时候,是否要通知依赖于该Widget的子Widget也要重新build。