InheritedWidget介绍

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

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

InheritedWidget源码剖析

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

/// 抽象类,继承自Proxywidget 继承门路InheritedWidget => ProxyWidget => Widgetabstract 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;@overrideInheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {  assert(ancestor != null);  _dependencies ??= HashSet<InheritedElement>();  _dependencies.add(ancestor);  ancestor.updateDependencies(this, aspect);  return ancestor.widget;}@protectedvoid updateDependencies(Element dependent, Object aspect) {  setDependencies(dependent, null);}@protectedvoid 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办法。