InheritedWidget的运用与源码解析

InheritedWidget 源码定义

/// Base class for widgets that efficiently propagate 
///information down the tree.

/// To obtain the nearest instance of a particular type of inherited
 ///widget from a build context, use 
///[BuildContext.inheritFromWidgetOfExactType].

/// Inherited widgets, when referenced in this way, will cause the 
///consumer to rebuild when the inherited widget itself changes state.

大体意思如下:

InheritedWidget 是在树中高效向下传递信息的基类部件;

调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例;

在 InheritedWidget 类型的控件被引用,也就是调用过 inheritFromWidgetOfExactType 方法后,当 InheritedWidget 自身状态改变时,会导致引用了 InheritedWidget 类型的子控件重构(rebuild)。

InheritedWidget 用法案例

定义数据模型

这里随便定义一个 Person 类。

//数据模型
class Person {
  String name;

  int age;

  Person(this.name, this.age);

  @override
  String toString() {
    // TODO: implement toString
    return "我叫${this.name},今年${this.age}了";
  }

}

自定义 InheritedWidget 控件类

创建一个类继承 InheritedWidget,并实现 updateShouldNotify 方法。

class InheriedDataWidget extends InheritedWidget {
  final Person person; //需要在树中共享的数据

  InheriedDataWidget({@required this.person,Widget child})
      : super(child: child);

  //定义一个便捷方法,方便子树中的widget获取共享数据
  static InheriedDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(InheriedDataWidget);
  }

  @override
  bool updateShouldNotify(InheriedDataWidget oldWidget) {
    // TODO: implement updateShouldNotify
    //如果返回true,则子树中依赖(build函数中有调用)本widget的子 . 
    //widget的`state.didChangeDependencies`方法会被调用
    return this.person.name != oldWidget.person.name ||
        this.person.age != oldWidget.person.age;
  }
}

之前说到调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例,所以此处定义一个静态的 of 方法,通过传入的 context 获取到最近的 InheriedDataWidget 实例。

InheriedDataWidget 的使用

InheriedDataWidget 使用起来也很简单,它本身也是一个控件,只要在任意一个页面的子控件调用其构造方法就行,这里我们定义一个形如

InheritedWidget的运用与源码解析_第1张图片

的 Widget 树。

WidgetA 类
//WidgetA
class WidgetA extends StatefulWidget {
  @override
  _WidgetAState createState() => _WidgetAState();
}

class _WidgetAState extends State {
  Person person = new Person("小明", 24);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        //调用构造,在此节点共享了一个Person类型的数据
        child: InheriedDataWidget(
          person: person,
          child: WidgetA1(),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            //在setState中刷新Person的值
            this.person=new Person(person.name, person.age+1);
          });
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

WidgetA 是一个 StatefulWidget 类型的控件,可以调用 setState 刷新,如果是继承 Stateless 类型的控件,那我们也可以通过 Stream 或者其他方式刷新数据,感兴趣请看什么是 Stream? Dart

WidgetA1 类
//控件WidgetA
class WidgetA1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey,
      width: MediaQuery.of(context).size.width,
      margin: EdgeInsets.all(10),
      child: Column(
        children: [
          Text(
            "A1 Widget",
            style: TextStyle(color: Colors.white,fontSize: 20),
          ),
          //调用InheriedDataWidget.of()获取数据,其实也就是调用了 . 
         //inheritFromWidgetOfExactType方法
          Text(
            "共享数据的信息: ${InheriedDataWidget.of(context).person.toString()}",
            style: TextStyle(color: Colors.white,fontSize: 20),
          ),
          Container(
            margin: EdgeInsets.only(top: 40),
            color: Colors.deepOrangeAccent,
            height: MediaQuery.of(context).size.height*0.5,
            width: MediaQuery.of(context).size.width,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                WidgetA1_1(),
                WidgetA1_2(),
                WidgetA1_3(),
              ],
            ),
          ),

        ],
      ),
    );
  }
}

WidgetA1_1 类

class WidgetA1_1 extends StatefulWidget {
  @override
  _WidgetA1_1State createState() => _WidgetA1_1State();
}

class _WidgetA1_1State extends State {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      height: 140,
      width: MediaQuery.of(context).size.width/3-20,
      //引用共享数据
      child: Text("WidgetA1_1ful\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
    );
  }


  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    //当WidgetA中Person数据发生改变时,此方法会被调用,
  //前提是updateShouldNotify返回true
    print("WidgetA1_1ful===>didChangeDependencies");
  }
}

WidgetA1_2 类

class WidgetA1_2 extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      height: 140,
      width: MediaQuery.of(context).size.width/3-20,
      child: Text("WidgetA1_2less\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
    );;
  }

}

WidgetA1_3 类


class WidgetA1_3 extends StatefulWidget {
  @override
  _WidgetA1_3State createState() => _WidgetA1_3State();
}

class _WidgetA1_3State extends State {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      height: 140,
      width: MediaQuery.of(context).size.width/3-20,
      child: Text("WidgetA1_3ful\n\n未引用共享数据信息"),
    );
  }


  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    print("WidgetA1_3===>didChangeDependencies");
  }
}

运行结果

InheritedWidget的运用与源码解析_第2张图片

当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,
当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,都会输出:

WidgetA1_1ful===>didChangeDependencies

如果我们试图在和 WidgetA 的同一层级的兄弟节点去访问 InheriedDataWidget 的 Person 数据,是不行的,因为父节点中并没有插入 InheriedDataWidget。

  • Widget B

把 WidgetB 和 WidgetA 保持同一节点

//WidgetB
class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //此处将报错,因为WidgetB 与WidgetA 是兄弟节点,
   //B中并未找到A中 InheriedDataWidget 的数据
    return Text(InheriedDataWidget.of(context).person.toString());
  }
}

这也体现了 Inheried(遗传) 这一单词的特性,遗传只存在于父子。兄弟不存在遗传的关系。

一张图总结

InheritedWidget的运用与源码解析_第3张图片

这种数据共享的方式在某些场景还是很有用的,就比如说全局主题,字体大小,字体颜色的变更,只要在 App 根层级共享出这些配置数据,然后在触发数据改变之后,所有引用到这些共享数据的地方都会刷新,这换主题,字体是不是就很轻松,事实上 Theme.of(context).primaryColor 之流就是这么干的。。。

InheriedWidget 的源码浅析

InheritedWidget 源码

abstract class InheritedWidget extends ProxyWidget {

  const InheritedWidget({ Key key, Widget child })
    : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

可见 InheritedWidget 是一个抽象类,其子类必须实现 updateShouldNotify 方法,之前的 InheriedDataWidget 也实现了该方法,还可以看到它继承自 ProxyWidget。

ProxyWidget 源码

abstract class ProxyWidget extends Widget {
  /// Creates a widget that has exactly one child widget.
  const ProxyWidget({ Key key, @required this.child }) : super(key: key);


  final Widget child;
}

可见 ProxyWidget 中也没什么,最主要的还是 InheritedWidget 中 的 InheritedElement createElement() 方法,它返回了一个 的 InheritedElement 实例。

InheritedElement 源码

class InheritedElement extends ProxyElement {

  InheritedElement(InheritedWidget widget) : super(widget);

  @override
  InheritedWidget get widget => super.widget;

   // 记录了所有依赖的对象
  final Map _dependents = HashMap();
  // 该方法是基类Element 的重写,会在Element mount和activate方 
 //法中调用,基类的该方法实现待会贴出。该方法的作用就是维护
 //基类的_inheritedWidgets,其类型为 Map,
 //在此处它除了保存父类的依赖映射关系之外,还将自身添加到
 //_inheritedWidgets中。这也使得查找InheritedWidget的效率大大的
 //提高,因为他都是从最近的节点去查找的
  @override
  void _updateInheritance() {
    assert(_active);
    final Map incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap();
    _inheritedWidgets[widget.runtimeType] = this;
  }

  @override
  void debugDeactivated() {
    assert(() {
      assert(_dependents.isEmpty);
      return true;
    }());
    super.debugDeactivated();
  }
  //返回对应的依赖,通常是在updateDependencies方法中调用
  @protected
  Object getDependencies(Element dependent) {
    return _dependents[dependent];
  }

  //设置或者更新依赖,通常是在updateDependencies方法中调用
  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }

  //Called by [inheritFromWidgetOfExactType] when a new [dependent] is added
  //当调用inheritFromWidgetOfExactType时候,
  //inheritFromWidgetOfExactType会调用 updateDependencies更新依赖
  @protected
  void updateDependencies(Element dependent, Object aspect) {
    setDependencies(dependent, null);
  }

  // 被notifyClients 通知调用,通知相关依赖的Widget调用didChangeDependencies
  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }


  @override
  void updated(InheritedWidget oldWidget) {
    //如果 updateShouldNotify 返回true 则通知更新
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
  }


  //通知依赖了 InheritedWidget 的控件调用 didChangeDependencies
  @override
  void notifyClients(InheritedWidget oldWidget) {
    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    for (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);
    }
  }
}
其中 _updateInheritance 在 Element 基类中的实现如下:
 void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

只是单纯把父 element 的 _inheritedWidgets 属性保存在自身 _inheritedWidgets 里。从而实现映射关系的层层向下传递。

总结来说就是 Element 在 mount 的过程中,如果不是 InheritedElement,就简单的将缓存指向父节点的缓存,如果是 InheritedElement,就创建一个缓存的副本,然后将自身添加到该副本中,这样做会有以下几点:

  • 查找 InheritedWidget 的效率比较高,不用一层层向上找。
  • InheritedElement 的父节点们是无法查找到自己的,即 InheritedWidget 的数据只能由父节点向子节点传递,反之不能。

  • 如果某节点的父节点有不止一个同一类型的 InheritedWidget,调用 inheritFromWidgetOfExactType 获取到的是离自身最近的该类型的 InheritedWidget。

之前 WidgetA1,WidgetA1_1 之类的依赖是如何添加到 _dependents 中,在共享数据改变时能调用到 didChangeDependencies 的呢?

WidgetA1,WidgetA1_1 之中用引用的就是调用 InheriedDataWidget.of(context)方法,而其中 of 方法调用的还是:

 static InheriedDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(InheriedDataWidget);
  }

inheritFromWidgetOfExactType 源码

 @override
  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet();
    _dependencies.add(ancestor);
    //将该实例添加到自身的依赖列表中,同时将自身添加到对应的依赖项列表中,
    //updateDependencies 这个方法在之前的 InheritedElement 出现过
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

  @override
  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    //通过 _inheritedWidgets  映射中查找是否有指定类型的 InheritedElement
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
    if (ancestor != null) {
      //如果 InheritedElement 不为空
      assert(ancestor is InheritedElement);
      //调用 inheritFromElement
      return inheritFromElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

其作用就是在 _inheritedWidget 映射中查找是否有特定类型 InheritedWidget 的实例。如果有则将该实例添加到自身的依赖列表中,同时将自身添加到对应的依赖项列表中。这样该 InheritedWidget 在更新后就可以通过其 _dependents 属性知道需要通知哪些依赖了它的 widget。

inheritFromWidgetOfExactType 由 BuildContext 的实例调用,但它却能直接访问 Element 中的_inheritedWidgets 这个属性? 这是因为 BuildContext 是接口,而 Element 实现了 BuildContext 接口,inheritFromWidgetOfExactType 方法的具体实现就是在 Element 类里。

至此,你是否也想看看 Flutter 中 Theme 的源码呢?

最后

最后厚颜贴一张自己学习Flutter的公众号,感兴趣的小伙伴可以一起学习哦。。。

InheritedWidget的运用与源码解析_第4张图片

本文实例代码

import 'package:flutter/material.dart';

class InheriedHomePage extends StatefulWidget {
  @override
  _InheriedHomePageState createState() => _InheriedHomePageState();
}

class _InheriedHomePageState extends State {
  int _curIndex = 0;

  PageController _pageController;

  List _tabs = ["View1", "View2"];

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _pageController = new PageController(initialPage: _curIndex);
  }

  @override
  Widget build(BuildContext context) {

    return new Scaffold(
      appBar: AppBar(
        title: Text("InheriedDemo"),
      ),
      body: PageView(
        children: [
          WidgetA(),
          WidgetB(),
        ],
        controller: _pageController,
        onPageChanged: (index) {
          setState(() {
            _curIndex = index;
          });
        },
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          BottomNavigationBarItem(
              icon: new Icon(Icons.tablet_android), title: new Text(_tabs[0])),
          BottomNavigationBarItem(
              icon: new Icon(Icons.tablet_mac), title: new Text(_tabs[1])),
        ],
        currentIndex: _curIndex,
        onTap: (index) {
          _curIndex = index;
          _pageController.animateToPage(index,
              duration: Duration(milliseconds: 100), curve: Curves.linear);
        },
      ),
    );
  }
}

//WidgetA
class WidgetA extends StatefulWidget {
  @override
  _WidgetAState createState() => _WidgetAState();
}

class _WidgetAState extends State {
  Person person = new Person("小明", 24);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        //调用构造,在此节点共享了一个Person类型的数据
        child: InheriedDataWidget(
          person: person,
          child: WidgetA1(),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            //在setState中刷新Person的值
            this.person=new Person(person.name, person.age+1);
          });
        },
        child: Icon(Icons.add),
      ),
    );
  }
}



//控件WidgetA
class WidgetA1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey,
      width: MediaQuery.of(context).size.width,
      margin: EdgeInsets.all(10),
      child: Column(
        children: [
          Text(
            "A1 Widget",
            style: TextStyle(color: Colors.white,fontSize: 20),
          ),
          //调用InheriedDataWidget.of()获取数据,其实也就是调用了inheritFromWidgetOfExactType方法
          Text(
            "共享数据的信息: ${InheriedDataWidget.of(context).person.toString()}",
            style: TextStyle(color: Colors.white,fontSize: 20),
          ),
          Container(
            margin: EdgeInsets.only(top: 40),
            color: Colors.deepOrangeAccent,
            height: MediaQuery.of(context).size.height*0.5,
            width: MediaQuery.of(context).size.width,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                WidgetA1_1(),
                WidgetA1_2(),
                WidgetA1_3(),
              ],
            ),
          ),

        ],
      ),
    );
  }
}


class WidgetA1_1 extends StatefulWidget {
  @override
  _WidgetA1_1State createState() => _WidgetA1_1State();
}

class _WidgetA1_1State extends State {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      height: 140,
      width: MediaQuery.of(context).size.width/3-20,
      //引用共享数据
      child: Text("WidgetA1_1ful\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
    );
  }


  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    //当WidgetA中Person数据发生改变时,此方法会被调用,前提是updateShouldNotify返回true
    print("WidgetA1_1ful===>didChangeDependencies");
  }
}


class WidgetA1_2 extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      height: 140,
      width: MediaQuery.of(context).size.width/3-20,
      child: Text("WidgetA1_2less\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
    );;
  }

}


class WidgetA1_3 extends StatefulWidget {
  @override
  _WidgetA1_3State createState() => _WidgetA1_3State();
}

class _WidgetA1_3State extends State {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      height: 140,
      width: MediaQuery.of(context).size.width/3-20,
      child: Text("WidgetA1_3ful\n\n未引用共享数据信息"),
    );
  }


  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    super.didChangeDependencies();
    print("WidgetA1_3===>didChangeDependencies");
  }
}

//WidgetB
class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {

    //此处将报错,因为WidgetB 与WidgetA 是兄弟节点,B中并未找到A中 InheriedDataWidget 的数据
    return Text(InheriedDataWidget.of(context).person.toString());
  }
}

//数据模型
class Person {
  String name;

  int age;

  Person(this.name, this.age);

  @override
  String toString() {
    // TODO: implement toString
    return "我叫${this.name},今年${this.age}了";
  }

}

class InheriedDataWidget extends InheritedWidget {
  final Person person; //需要在树中共享的数据

  InheriedDataWidget({@required this.person,Widget child})
      : super(child: child);

  //定义一个便捷方法,方便子树中的widget获取共享数据
  static InheriedDataWidget of(BuildContext context) {

    return context.inheritFromWidgetOfExactType(InheriedDataWidget);
  }

  @override
  bool updateShouldNotify(InheriedDataWidget oldWidget) {
    // TODO: implement updateShouldNotify
    //如果返回true,则子树中依赖(build函数中有调用)本widget的子widget的`state.didChangeDependencies`方法会被调用
    return this.person.name != oldWidget.person.name ||
        this.person.age != oldWidget.person.age;
  }
}


你可能感兴趣的:(InheritedWidget的运用与源码解析)