乐趣区

关于flutter:Flutter源码分析之InheritedWidget

InheritedWidget 介绍

在 Flutter 进行界面开发时,咱们常常会遇到数据传递的问题。因为 Flutter 采纳节点树的形式组织页面,以致于一个一般页面的节点层级会很深。此时,咱们如果还是一层层传递数据,当须要获取多层父节点的数据时,会十分麻烦。
因为呈现上述问题,Flutter 给我咱们提供一种 InheritedWidgetInheritedWidget 可能让节点下的所有子节点,拜访该节点下的数据。
对于 Scoped ModelBloCProvider 就是基于 InheritedWidget 实现的。

本文中所用到的 源码链接 视频教程

InheritedWidget 源码剖析

能够看到 InheritedWidget 的源码非常简单。

/// 抽象类,继承自 Proxywidget 继承门路 InheritedWidget => ProxyWidget => Widget
abstract class InheritedWidget extends ProxyWidget {
  /// 构造函数
  /// 因为 InheritedWidget 是没有界面的 Widget,所有须要传入理论的 Widget    
  const InheritedWidget({Key key, Widget child})
    : super(key: key, child: child);

  /// 重写了超类 Widget createElement 办法
  @override
  InheritedElement createElement() => InheritedElement(this);

  /// 父级或先人 widget 中扭转 (updateShouldNotify 返回 true) 时会被调用。@protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

InheritedWidget 示例

咱们以官网计数器的例子用 InheritedWIdget 进行革新

第一步:先定义一个 InheritedState 类

  1. 创立 InheritedState 继承自InheritedWidget
  2. 定义一个须要共享的数据count
  3. 定义一个给内部获取 InheritedState 实例的 of 办法
  4. 重写 updateShouldNotify 办法,次要目标告诉依赖该树共享数据的子widget

    import 'package:flutter/material.dart';
    
    class InheritedState extends InheritedWidget {
    
      /// 构造方法
      InheritedState({
     Key key,
     @required this.count,
     @required Widget child
      }): assert(count != null),
     super(key:key, child: child);
    
      /// 须要共享的数据
      final int count;
    
      /// 获取组件最近以后的 InheritedWidget
      static InheritedState of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<InheritedState>();
      }
    
      /// 告诉依赖该树共享数据的子 widget
      @override
      bool updateShouldNotify(covariant InheritedState oldWidget) {return count != oldWidget.count;}
    
    }

第二步:编写布局并用 InheritedWidget 实现计数器成果

  1. 首先创立了一个 InheritedCount 组件以及子组件 WidgetAWidgetB
  2. InheritedCount 中定义一个 count 变量,用于子 widget 获取该 count 数据。
  3. 应用 InheritedState 组件并传入 count 值,以及子组件。
  4. 子组件中应用 InheritedState 中的共享数据。
  5. InheritedCount 按钮点击扭转 count 值,子组件数据将被刷新。

    import 'package:flutter/material.dart';
    import 'package:flutter_code/InheritedWidget/InheritedState.dart';
    
    class InheritedCount extends StatefulWidget {
      @override
      _InheritedCountState createState() => _InheritedCountState();
    }
    
    class _InheritedCountState extends State<InheritedCount> {
    
      int _count = 0;
    
      @override
      Widget build(BuildContext context) {
     return Scaffold(
       appBar: AppBar(title: Text("InheritedDemo"),
       ),
       floatingActionButton: FloatingActionButton(onPressed: () {setState(() {_count++;});
         },
         child: Icon(Icons.add, color: Colors.white,),
       ),
       body: Center(
         child: InheritedState(
             count: _count,
             child: Column(
               mainAxisAlignment: MainAxisAlignment.spaceAround,
               crossAxisAlignment: CrossAxisAlignment.center,
               children: [WidgetA(),
                 WidgetB()],
             )
         ),
       ),
     );
      }
    }
    
    
    class WidgetA extends StatelessWidget {
      @override
      Widget build(BuildContext context) {return Text("widget text");
      }
    }
    
    class WidgetB extends StatelessWidget {
      @override
      Widget build(BuildContext context) {return Text(InheritedState.of(context)?.count.toString(),
       style: TextStyle(
         color: Colors.green,
         fontSize: 50
       ),  
     );
      }
    }

    InheritedWidget 源码剖析

    在下面的计数器示例代码中,WidgetBInheritedWidget 产生关联的就是 InheritedState.of(context)?.count.toString(),其中最要害的形式是context.dependOnInheritedWidgetOfExactType(),咱们查看dependOnInheritedWidgetOfExactType()Element中的源码如下:该代码是在 framework.dart 第 3960 行

    Map<Type, InheritedElement> _inheritedWidgets;
    
    @override
      T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
        /// 断言,用于在调试状态下检测是否有正在应用(激活)的先人
     assert(_debugCheckStateIsActiveForAncestorLookup());
     /// 获取到_inheritedWidgets 数组数据
     final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
     if (ancestor != null) {
       // 断言,判断以后 ancestor 是否是 InheritedElement 类型
       assert(ancestor is InheritedElement);
       // 返回并调用更新办法
       return dependOnInheritedElement(ancestor, aspect: aspect) as T;
     }
     _hadUnsatisfiedDependencies = true;
     return null;
      }

    咱们不难看出,每一个 Element 实例都会持有一个 _inheritedWidgets,调用次用该办法时会从改汇合对象中取出相干类型的InheritedElement 实例,那么在这个办法中咱们没有看到设置 _inheritedWidgets 的办法,咱们来查看一下 _inheritedWidgets 是如何赋值的。

    // Element  
    void _updateInheritance() {assert(_active);
     _inheritedWidgets = _parent?._inheritedWidgets;
      }

    咱们找到赋值是在 _updateInheritance 办法中,首先断言以后节点是否激活,而后通过父节点的 _inheritedWidgets 进行赋值,咱们持续来看_updateInheritance 什么状况下会调用:

      @mustCallSuper
      void mount(Element parent, dynamic newSlot) {
        ......
     _updateInheritance();
         ......
      }
    
     @mustCallSuper
      void activate() {
     ......
     _updateInheritance();
     ......
      }

    咱们能够看到在 Element 中它在 mountactivate函数执行了调用,也就是说 element 每次挂载和从新时,会调用该办法。那么当该办法执行的时候,element就会从下层中拿到所有的 InheritedElement。而InheritedElement 他最终继承了 Element,并能够看到InheritedElement 重写了 _updateInheritance 办法:

      @override
      void _updateInheritance() {assert(_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;
      }
    

    InheritedWidget 是如何进行刷新的

    后面咱们剖析到 InheritedElement 会拿到父类的所有的 InheritedElment 并向下传递,而 InheritedWidget 正是通过这种办法能力让上面的子 Widget 能拜访的下层中所有的 InheritedWidget,那么它是如何进行刷新的呢?咱们在ElementdependOnInheritedWidgetOfExactType办法中调用了 dependOnInheritedElement 办法,代码如下:

    Set<InheritedElement> _dependencies;
    
    @override
    InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect}) {assert(ancestor != null);
      _dependencies ??= HashSet<InheritedElement>();
      _dependencies.add(ancestor);
      ancestor.updateDependencies(this, aspect);
      return ancestor.widget;
    }
    
    @protected
    void updateDependencies(Element dependent, Object aspect) {setDependencies(dependent, null);
    }
    
    @protected
    void setDependencies(Element dependent, Object value) {_dependents[dependent] = value;
    }

    能够看到 InheritedElement 实例调用本人的 updateDependencies 办法并将以后的 Element 实例传递过来

      /// Called during build when the [widget] has changed.
      ///
      /// By default, calls [notifyClients]. Subclasses may override this method to
      /// avoid calling [notifyClients] unnecessarily (e.g. if the old and new
      /// widgets are equivalent).
      @protected
      void updated(covariant ProxyWidget oldWidget) {notifyClients(oldWidget);
      }
    
      @override
      void notifyClients(InheritedWidget oldWidget) {assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
     for (final Element dependent in _dependents.keys) {assert(() {
         // check that it really is our descendant
         Element ancestor = dependent._parent;
         while (ancestor != this && ancestor != null)
           ancestor = ancestor._parent;
         return ancestor == this;
       }());
       // check that it really depends on us
       assert(dependent._dependencies.contains(this));
       notifyDependent(oldWidget, dependent);
     }
      }
    }
    
      @protected
      void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {dependent.didChangeDependencies();
      }

    因为当 InheritedElement 更新时,会执行 updated 办法,而后持续调用 notifyClients,遍历所有的element 并调用 didChangeDependencies 办法。

退出移动版