Flutter核心类分析深入理解数据共享InheritedWidget
Posted 牧羊人.阿标
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter核心类分析深入理解数据共享InheritedWidget相关的知识,希望对你有一定的参考价值。
背景
Flutter提供了用于Widget间共享数据的InheritedWidget,当InheritedWidget发生变化时,它的子树中所有依赖了它的数据的Widget都会进行rebuild,这使得开发者省去了维护数据同步逻辑的麻烦。InheritedWidget看名字就是一个Widget,那么它是如何做到数据共享,如何做到通知更新的呢?下面我们一起来看看。
InheritedWidget用法
InheritedWidget用法分为以下几个步骤;
- 自定义Widget继承自InheritedWidget,并且自定义static of方法,用于child获取当前实例
- 实现
InheritedWidtet
类中的updateShouldNotify
方法,用于返回update条件; - 当数据变化时,调用自定义widget的State类中的setState()方法,触发整棵InheritedWIdget tree的更新。
代码就不贴了,相当简单。下面我们来一步步分析它的原理。
原理
InheritedWidget源码
先来看看InheritedWidget的源码部分
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key? key, required Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
他继承自ProxyWidget,我们再来看看proxyWidget
abstract class ProxyWidget extends Widget {
const ProxyWidget({ Key? key, required this.child }) : super(key: key);
final Widget child;
}
通过上面的总共不超过10行代码的源码,我们看出来InheritedWidget内部除了实现createElement()创建InheritedElement之外就没有其他的操作,我们继续看InheritedElement
InheritedElement源码
class InheritedElement extends ProxyElement {
InheritedElement(InheritedWidget widget) : super(widget);
@override
InheritedWidget get 这个Set记录了所有依赖的Element => super.widget as InheritedWidget;
//这个Set记录了所有依赖的Element,这里的value值一般都是null
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
// 该方法会在Element mount和activate方法中调用
// _inheritedWidgets为基类Element中的成员,缓存父节点中所有相关的InheritedElement
@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>();
_inheritedWidgets![widget.runtimeType] = this;
}
//该方法在父类ProxyElement的update(ProxyWidget oldWidget)方法中调用,看名字就知道是通知依赖方该进行更新了,这里首先会调用重写的updateShouldNotify方法是否需要进行更新,然后遍历_dependents列表并调用didChangeDependencies方法,该方法内会调用mardNeedsBuild,于是在下一帧绘制流程中,对应的Widget就会进行rebuild,界面也就进行了更新
@override
void notifyClients(InheritedWidget oldWidget) {
for (final Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
// 更新所有的子项
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
}
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
我们看到InheritedElement
同样继承自ProxyElement
。核心方法也就三个,根据注释理解
下面我们继续看,既然Element在update的时候,会调用到notifyClients方法,从而遍历更新所有的_dependens,那么_InheritedElement
的dependents是在什么时候被添加进去的呢?
代码最后跟进到:Element.dependOnInheritedElement
方法
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
//在ancestor.updateDependencies方法中更新_dependents[dependent],以this作为key
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@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);
// 这里调用了dependOnInheritedElement方法
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
相信大家如果看过深入理解BuildContext一文或者实际使用过InheritedWidget的朋友对这两个方法肯定不陌生。对于想读取上级Widget共享的数据来说,通俗的约定就是在上级的Widget类,定义static的of方法,该方法中通过dependOnInheritedWidgetOfExactType获取parent中最近的InheritedWidget的实例并返回,并且同时将自己注册到_dependents中,这样当parent的InheritedWidget更新的时候,就会间接通过notifyCLients
方法来更新自己了。
总结
- InheritedElement的父节点们是无法查找到自己的,即InheritedWidget的数据只能由父节点向子节点传递,反之不能。
- 如果某节点的父节点有不止一个同一类型的InheritedWidget,调用
dependOnInheritedWidgetOfExactType
获取到的是离自身最近的该类型的InheritedWidget。
以上是关于Flutter核心类分析深入理解数据共享InheritedWidget的主要内容,如果未能解决你的问题,请参考以下文章
Flutter核心类分析深入理解数据共享Notification