关于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办法。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理