【源码篇】Flutter GetX深度剖析 | 我们终将走出自己的路(万字图文) (2)

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; } ... }

图示

InheritedWidget存取数据

刷新机制

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刷新机制的图示

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各种交互,也实现了一套极其简洁的刷新机制

进行一些深度封装,甚至可以无缝的管理很多控件的资源释放

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgyyyj.html