Flutter InheritedElement 使用源码分析

345 阅读2分钟

InheritedWidget 在很多关键的地方使用着,如 Theme、MediaQuery、Directionality 等。InheritedWidget 被设计用来沿着树向下高效的传递数据。为了获取 InheritedWidget 实例,我们需要使用 BuildContext.dependentOnInheritedWidgetOfExactType 方法。我们通常在”如果某些属性发生变化(如主题颜色、文字方向等)希望子 widget 重新构建时“使用 InheritedWidget。

1. of 方法

使用 InheritedWidget 时,惯例会提供一个静态 of 方法,内部调用 BuildContext.dependentOnInheritedWidgetOfExactType 获取具体 InheritedWidget。自己实现 of 方法有三个好处:1. 明确自己需要和什么数据关联。2. 可以返回其它自己想暴露的数据,便于直接使用。3. 可以隐藏 InheritedWidget 实现细节。我们这里直接讲BuildContext.dependentOnInheritedWidgetOfExactType。

BuildContext 的具体实现其实就是 Element 对象。所以这里说 Element.dependentOnInheritedWidgetOfExactType。这个方法通过 _inheritedWidgets(如果不为 null)找到 InheritedElement,将这个 InheritedElement 添加到自己的 _dependents 属性中,然后将自己添加到 InheritedElement 的 _dependencies 内,最后返回 InheritedElement.child。

可以看到在这里使用到 InheritedWidget 的子 Widget 它们之间互相做了关联,保存对方引用。

这里提一下 BuildContext

BuildContext 的具体实现主要在 Element。BuildContext 里声明了各种找父级节点里对应的 Widget、Element、RenderObject、State 对象的方法,原理就是沿着树向上依次判断当前 element 对象,是否是自己需要找的对象的载体,如果是就返回对应对象,否则返回 null。具体可以使用的方法可以在需要时查阅文档使用。

2. ProxyElement.update 方法

现在考虑有一颗子 Widget tree。起始 Widget 为 StatefulWidget,接着是 InheritedWidget,接着是子树其余部分,子树中有 widget 通过 of 方法引用了 InheritedWidget 的相关数据。现在假如我在 StatefulWidget 的 setState 方法中修改了一个 InheritedWidget 使用的属性,接着会发生什么?rebuild。Widget 的构建过程可以参考这篇文章。

rebuild 过程最终会走到 updateChild 方法,这次它并不需要 inflateWidget 了,而是调用 child.update 更新 child(已经有 child 了)。对于 InheritedWidget 这里显然走到了 ProxyElement.update 方法。这里继续调用 updated -> notifyClients。notifyClients 会遍历 _dependents.keys 调用 dependent(Element).didChangeDependencies,对于 StatelessElement 内部调用 markNeedsBuild 标记这个 element 对象需要重新构建。而 StatefulElement 会标记 _didChangeDependencies 为 true。当构建过程走到 StatefulElement 时会根据这个标识调用 _state.didChangeDependencies 方法。