• 透过源码理解Flutter InheritedWidget


     InheritedWidget的核心是保存值和保存使用这个值的widget,通过对比值的变化,来决定是否要通知那些使用了这个值的widget更新自身。

    1 updateShouldNotify和notifyClients

    InheritedWidget通过updateShouldNotify函数控制依赖其的子组件是否在InheritedWidget变化时会被重建:如果updateShouldNotify返回true,InheritedWidget变化时子组件的build会被调用,反之则不会。

    InheritedElement中的updated方法:

    1. @override
    2. void updated(InheritedWidget oldWidget) {
    3. if (widget.updateShouldNotify(oldWidget))
    4. super.updated(oldWidget);
    5. }

    InheritedElement继承于ProxyElement,而ProxyElement的updated实现为:

    1. @protected
    2. void updated(covariant ProxyWidget oldWidget) {
    3. notifyClients(oldWidget);
    4. }

    而InheritedElement的notifyClients实现是遍历_dependents中依赖自己的widget,然后调用它的didChangeDependencies进行变化通知:

    1. @override
    2. void notifyClients(InheritedWidget oldWidget) {
    3. assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    4. for (final Element dependent in _dependents.keys) {
    5. assert(() {
    6. // check that it really is our descendant
    7. Element? ancestor = dependent._parent;
    8. while (ancestor != this && ancestor != null) {
    9. ancestor = ancestor._parent;
    10. }
    11. return ancestor == this;
    12. }());
    13. // check that it really depends on us
    14. assert(dependent._dependencies!.contains(this));
    15. notifyDependent(oldWidget, dependent);
    16. }
    17. }
    18. @protected
    19. void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    20. dependent.didChangeDependencies();
    21. }
    1. Element的didChangeDependencies源码如下:
    2. void didChangeDependencies() {
    3. assert(_active); // otherwise markNeedsBuild is a no-op
    4. assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
    5. markNeedsBuild();
    6. }

    可以看到,InheritedWidget变化时,如果updateShouldNotify是true,会通过notifyClients调用子组件的didChangeDependencies函数,从而调用markNeedsBuild,将本Element加入_dirtyElements列表中。大家都知道,_dirtyElements中保存的是需要重建的Element,会在下一帧时被rebuild,因此在下一帧子组件会被重建(rebuild)。

    2 InheritedWidget的传递

    InheritedWidget能用于让用户快速从子组件获取,这是怎么实现的呢?

    其实Flutter Framework也是将InheritedWidget一层层传递下来的,只不过由于Framework层自行处理了,因此这个过程对于我们是透明的。我们现在来梳理下InheritedWidget传递的过程。

    Element中,有一个map:_inheritedWidgets。保存了所有上级节点中的InheritedElement。其源码如下:

    Map<Type, InheritedElement> _inheritedWidgets;

    其中,key中Type是InheritedWidget的子类,value是InheritedElement。为什么这里value保存的是InheritedElement而不是InheritedWidget呢?由以前的文章可以知道Element中保存着对应Widget的引用,因此可以通过InheritedElement获取对应的InheritedWidget。而且Widget在上级Widget重建时会被重建,因此保存InheritedElement更合适。

    在普通的Element中,_inheritedWidgets会直接复制其父组件中_inheritedWidgets的值,其源码如下:

    1. void _updateInheritance() {
    2. assert(_active);
    3. _inheritedWidgets = _parent?._inheritedWidgets;
    4. }

    而在InheritedElement中,_inheritedWidgets会首先复制其父组件中_inheritedWidgets的值,然后将自己添加进列表,其源码如下:

    1. @override
    2. void _updateInheritance() {
    3. assert(_active);
    4. final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
    5. if (incomingWidgets != null)
    6. _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    7. else
    8. _inheritedWidgets = HashMap<Type, InheritedElement>();
    9. _inheritedWidgets[widget.runtimeType] = this;
    10. }

    由此可以看出,InheritedElement就是这样一层层传递下来的。_inheritedWidgets赋值流程如下:

    由该流程图可以看出,_inheritedWidgets在Element被加入Element Tree时就已经被赋值,因此其在子组件的build函数中是可以访问得到的。

    3 InheritedWidget的获取及注册依赖

    我们已经知道了InheritedElement会传递到下级组件中,那怎么获取它呢?Flutter提供了专门获取某个InheritedWidget类型的函数dependOnInheritedWidgetOfExactType.其源码如下:

    1. @override
    2. T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    3. assert(_debugCheckStateIsActiveForAncestorLookup());
    4. final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    5. if (ancestor != null) {
    6. assert(ancestor is InheritedElement);
    7. return dependOnInheritedElement(ancestor, aspect: aspect) as T;
    8. }
    9. _hadUnsatisfiedDependencies = true;
    10. return null;
    11. }

    由第三行可以看出,此函数会从_inheritedWidgets中寻找对应的InheritedElement,并返回其InheritedWidget。

    除了dependOnInheritedWidgetOfExactType,Flutter还提供了另一个专门获取某个InheritedWidget类型的函数:getElementForInheritedWidgetOfExactType。其源码如下:

    1. @override
    2. InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    3. assert(_debugCheckStateIsActiveForAncestorLookup());
    4. final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    5. return ancestor;
    6. }

    对比其与dependOnInheritedWidgetOfExactType源码,可以看到dependOnInheritedWidgetOfExactType多了dependOnInheritedElement函数的调用,该函数用于创建InheritedWidget和调用dependOnInheritedWidgetOfExactType的组件的依赖关系。其有两个步骤:

    • 将依赖的InheritedElement加入本Element的_dependencies列表,该列表中保存了本Element所有依赖的InheritedElement.
    • 将本Element加入依赖的InheritedElement的_dependents map,该列表中保存了所有依赖该InheritedElement的Element。

    如果使用的是dependOnInheritedWidgetOfExactType,则当被依赖的InheritedWidget被更新时,依赖的子组件会被rebuild;而使用的是getElementForInheritedWidgetOfExactType时,由于不会建立相应的依赖关系,InheritedWidget被更新时,依赖的子组件不会被rebuild。

    4 主动调用dependOnInheritedWidgetOfExactType

     子组件需要通过调用dependOnInheritedWidgetOfExactType来获取InheritedWidget,并且将自己加入该InheritedWidget的依赖中。方便起见,一般会在InheritedWidget子类中实现of方法:

    1. class ShareDataWidget extends InheritedWidget {
    2. ShareDataWidget({
    3. @required this.data,
    4. Widget child
    5. }) :super(child: child);
    6. final int data; //需要在子树中共享的数据,保存点击次数
    7. //定义一个便捷方法,方便子树中的widget获取共享数据
    8. static ShareDataWidget of(BuildContext context) {
    9. return context.dependOnInheritedWidgetOfExactType();
    10. }
    11. //该回调决定当data发生变化时,是否通知子树中依赖data的Widget
    12. @override
    13. bool updateShouldNotify(ShareDataWidget old) {
    14. //如果返回true,则子树中依赖(build函数中有调用)本widget
    15. //的子widget的`state.didChangeDependencies`会被调用
    16. return old.data != data;
    17. }
    18. }
    19. class _TestWidget extends StatefulWidget {
    20. @override
    21. __TestWidgetState createState() => new __TestWidgetState();
    22. }
    23. class __TestWidgetState extends State<_TestWidget> {
    24. @override
    25. Widget build(BuildContext context) {
    26. print("__TestWidgetState build");
    27. //使用InheritedWidget中的共享数据
    28. return Text(ShareDataWidget
    29. .of(context)
    30. .data
    31. .toString());
    32. // return Text("tex");
    33. }
    34. @override
    35. void didChangeDependencies() {
    36. super.didChangeDependencies();
    37. //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
    38. //如果build中没有依赖InheritedWidget,则此回调不会被调用。
    39. print("Dependencies change");
    40. }
    41. }

    参考:

    Flutter框架分析 -InheritedWidget - 知乎

  • 相关阅读:
    Gbase8s数据库ALTER PROCEDURE 语句
    我的创作纪念日
    零基础学Java(3)运算符
    DJYGUI系列文章十一:GDD矩形区域运算
    Ubuntu20.04 PostgreSQL 14 安装配置记录
    Kubernetes平台部署Grafana Loki Promtail系统
    Docker容器学习笔记(看了狂神视频)
    MVCC及其原理
    Maven引用本地jar包
    Win11不识别蓝牙适配器的解决方法
  • 原文地址:https://blog.csdn.net/Mamong/article/details/132569959