incomingWidgets 不为空:将父节点_inheritedWidgets 所有数据深拷贝,返回一个全新的Map实例(含有父节点 _inheritedWidgets 所有数据),赋值给父类Element的 _inheritedWidgets 变量
将自身实例赋值给父类Element的 _inheritedWidgets 变量,key为其widget的runtimeType
为什么任何一个Widget的Element实例的 _inheritedWidgets 变量,可直接拿到父节点InheritedElement实例?
Element中做了一个父节点向子节点赋值的操作:整个数据传输链清晰了
abstract class Element extends DiagnosticableTree implements BuildContext { Map<Type, InheritedElement>? _inheritedWidgets; void _updateInheritance() { assert(_lifecycleState == _ElementLifecycle.active); _inheritedWidgets = _parent?._inheritedWidgets; } ... }图示
刷新机制InheritedElement和Element之间有一些交互,实际上自带了一套刷新机制
InheritedElement存子节点Element: _dependents,这个变量是用来存储,需要刷新的子Element
class InheritedElement extends ProxyElement { InheritedElement(InheritedWidget widget) : super(widget); final Map<Element, Object?> _dependents = HashMap<Element, Object?>(); @protected void setDependencies(Element dependent, Object? value) { _dependents[dependent] = value; } @protected void updateDependencies(Element dependent, Object? aspect) { setDependencies(dependent, null); } }InheritedElement刷新子Element
notifyClients这个方法就是将 _dependents 存储的Element,全部拿了出来,传入notifyDependent
在notifyDependent方法中,传入Element调用了自身didChangeDependencies()方法
Element的didChangeDependencies() 方法会调用 markNeedsBuild() ,来刷新自身
class InheritedElement extends ProxyElement { InheritedElement(InheritedWidget widget) : super(widget); final Map<Element, Object?> _dependents = HashMap<Element, Object?>(); @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) { dependent.didChangeDependencies(); } @override void notifyClients(InheritedWidget oldWidget) { for (final Element dependent in _dependents.keys) { ... notifyDependent(oldWidget, dependent); } } } abstract class Element extends DiagnosticableTree implements BuildContext { ... @mustCallSuper void didChangeDependencies() { assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies')); markNeedsBuild(); } ... }InheritedWidget的子节点是怎么将自身Element
添加到InheritedElement的_dependents变量里的呢?
Element里面有个 dependOnInheritedElement 方法
Element中dependOnInheritedElement方法,会传入InheritedElement实例 ancestor
ancestor调用updateDependencies方法,将自身的Element实例传入
InheritedElement中就将这个Element,添加到_dependents变量中了
abstract class Element extends DiagnosticableTree implements BuildContext { ... @override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) { assert(ancestor != null); _dependencies ??= HashSet<InheritedElement>(); _dependencies!.add(ancestor); ancestor.updateDependencies(this, aspect); return ancestor.widget; } ... } class InheritedElement extends ProxyElement { InheritedElement(InheritedWidget widget) : super(widget); final Map<Element, Object?> _dependents = HashMap<Element, Object?>(); @protected void setDependencies(Element dependent, Object? value) { _dependents[dependent] = value; } @protected void updateDependencies(Element dependent, Object? aspect) { setDependencies(dependent, null); } }dependOnInheritedElement该方法的使用也很简单
一般来说在某个Widget使用 getElementForInheritedWidgetOfExactType 获取父节点的 InheritedElement
然后将其传入 dependOnInheritedElement 方法中即可
// 举例 var inheritedElement = context .getElementForInheritedWidgetOfExactType<ChangeNotifierEasyP<T>>() as EasyPInheritedElement<T>?; context.dependOnInheritedElement(inheritedElement);Provider核心原理,就是采用了InheritedWidget这种刷新机制
想详细了解Provider相关原理,可参考下面文章
源码篇:Flutter Provider的另一面(万字图文)
图示
来看下InheritedWidget刷新机制的图示
路由小知识路由Navigator中基本都是些操作路由的静态方法,NavigatorState是实现的具体逻辑
大家在使用InheritedWidget获取数据的时候,或许有过这样一种困扰:A页面 ---> B页面 ---> C页面
如果我在A页面使用InheritedWidget储存了数据,跳转到B页面或者C页面,会发现使用context获取不到A页面的InheritedElement
这侧面证明了Navigator路由跳转:A页面跳转B页面,B页面并不是A页面的子节点
大致结构:可勉强理解为,Navigator是所有页面父节点,页面与页面之间是平级关系
这里我画了下大致结构,如有偏差,请务必指出来,我会尽快修改
关于Flutter路由原理解析,可参考此文章(作者为啥现在不更文了呢 ~~):Flutter 路由原理解析
思考InheritedWidget为我们带了很多便利
可以在一个Widget树范围,获取到我们想要InheritedElement(通过泛型区分即可)
InheritedElement和Element各种交互,也实现了一套极其简洁的刷新机制
进行一些深度封装,甚至可以无缝的管理很多控件的资源释放