Flutter 第十九章(InheritedWidget数据共享)

版权声明:本文为作者原创书籍。转载请注明作者和出处,未经授权,严禁私自转载,侵权必究!!!

情感语录: 生活可以漂泊,可以孤独,但灵魂必须有所归依。所有的不甘,都是因为还心存梦想,在你放弃之前,好好拼一把,只怕心老,不怕路长。

欢迎来到本章节,上一章节介绍了Flutter 中的 WebSocket的使用,知识点回顾 戳这里 Flutter第十九章

本章节来介绍 Flutter 中的数据共享,实际开发中很多时候共享数据都是通过传值的方式达到数据的统一,比如: A界面请求到的数据,在B界面也需要做展示。通常你会想到几种方式:1:全局静态变量、2:单例模式、3:跳转传值(构造函数 或 路由)、4:数据持久化(ShardPrefences,File,Sql)等。但是这些方式存在一个弊端就是只能单一的传值,往往都是界面跳转后才能取值,因此缺乏了何时去取值的契机。

业务开发中经常会碰到这样的情况,在收到某条数据后,多个界面都需要同时做出响应的改变,比如应用换肤等 即多个组件在收到消息后都需要进行改变颜色。 此时上面说到的几种方式都不能满足业务需求了。熟悉设计模式的童鞋会想到用观察者模式去处理,即 当点击了换肤按钮后,则向所有订阅者发送一条消息说你们都改变下自己的颜色吧,然后订阅者开始纷纷修改自己的颜色。

在原生开发中一般的实现都是观察者模式,需要开发者自行实现并维护观察者的列表。而在Flutter中,提供了用于Widget间共享数据的InheritedWidget组件,当InheritedWidget发生变化时,它的子树中所有依赖了它的数据的Widget都会收到通知并重新构建刷新。

一、案例演示

为了能更好的理解,先结合上面的情景写一个简单的案例,在主界面修改数据后,通知其他页面刷新数据。

1、主界面
    //主界面
    class DataShardPage extends StatefulWidget {
      @override
      _DataShardPageState createState() => _DataShardPageState();
    }

    class _DataShardPageState extends State<DataShardPage> {
      //用于存储数据的 Mode,初始化数据 为 0
      InheritedTestModel inheritedTestModel = InheritedTestModel(0);

      @override
      Widget build(BuildContext context) {

        //在该组件下的 子组件可以收到消息通知
        return MyInheritedWidget(

            inheritedTestModel: inheritedTestModel,

            child: Scaffold(
                floatingActionButton: FloatingActionButton(
                  elevation: 0,
                  child: Text('增加'),
                  onPressed: () {
                    setState(() {
                      //修改共享的数据,并刷新子组件
                      inheritedTestModel.count = inheritedTestModel.count + 1;
                    });
                  },
                ),
                floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
                appBar: AppBar(
                  title: Text("InheritedWidget"),
                ),
                body: Center(

                    child: ListView(
                      children: <Widget>[
                          //创建 三个 页面
                          WidgetTestPageOne(),

                          WidgetTestPageTwo(),

                          WidgetTestPageThree(),
                        ],
                ))));
      }
    }
2、MyInheritedWidget 组件
    // 组件共享数据的容器
    class MyInheritedWidget extends InheritedWidget {

      //数据的Mode
      final InheritedTestModel inheritedTestModel;

      MyInheritedWidget({
        Key key,
        @required this.inheritedTestModel,
        @required Widget child,
      }) : super(key: key, child: child);


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

        return context.inheritFromWidgetOfExactType(MyInheritedWidget);
      }


      //是否重建widget就取决于数据是否相同
      @override
      bool updateShouldNotify(MyInheritedWidget oldWidget) {

        //判断数据是否反生改变,如果为真 所有子组件的 build 方法会被调用;
        // 有状态组件 didChangeDependencies()方法会被调用

        return inheritedTestModel != oldWidget.inheritedTestModel;
      }

    }
3、WidgetTestPageOne 界面
    // 第一个页面
    class WidgetTestPageOne extends StatefulWidget {

      @override
      _WidgetTestPageOneState createState() => _WidgetTestPageOneState();
    }

    class _WidgetTestPageOneState extends State<WidgetTestPageOne> {


      @override
      Widget build(BuildContext context) {

        final myInheritedWidget = MyInheritedWidget.of(context);

        final inheritedTestModel = myInheritedWidget.inheritedTestModel;

        print("WidgetTestPageOne____${inheritedTestModel.count}");

        return Padding(
            padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
            child: Text("One页面获取到最新值:${inheritedTestModel.count}"));
      }

      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("WidgetTestPageOne___didChangeDependencies");
      }
    }

其它界面这里就先不贴了,基本类似。首先在 点击主界面的 增加 按钮后会触发调用 setState()方法,在该方法中将共享的数据进行了累加。在前面 StatefulWidget 的章节中就有介绍到 setState() 还会导致 _DataShardPageState 重新 build。此时创建的三个页面就重新创建了。你可能会说 既然都重新创建了,何尝不通过构造器将数据代入呢? 没错,是可以这样做,但如果有很多界面都需要该数据, 岂不是每个界面都要修改,这样做维护的代价就太大了。更加优雅的方式就是,我管你需不要该数据,反正我将数据暴露提供出来,至于你内部要不要该数据,那就交由内部自行维护了。

第一个界面 WidgetTestPageOne 中通过 继承了 InheritedWidget 的 MyInheritedWidget将共享的数据进行了取值,并刷新到界面上。 那 MyInheritedWidget 又是怎么取到值的呢? 这里先买个关子.......

下面来看下效果简版图:
InheritedWidget共享数据.gif
控制台输出结果:
    I/flutter ( 2882): WidgetTestPageOne___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageOne____1
    I/flutter ( 2882): WidgetTestPageTwo____1
    I/flutter ( 2882): WidgetTestPageThree____1
    I/flutter ( 2882): WidgetTestPageOne___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageOne____2
    I/flutter ( 2882): WidgetTestPageTwo____2
    I/flutter ( 2882): WidgetTestPageThree____2
    I/flutter ( 2882): WidgetTestPageOne___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageOne____3
    I/flutter ( 2882): WidgetTestPageTwo____3
    I/flutter ( 2882): WidgetTestPageThree____3
    .....

从控制台可以看出在每次点击事件后,都触发了每个界面的 build()didChangeDependencies() 方法。那么一个很严重的问题就来了。假如我的是 A,B,C 三个界面。 A 界面并不想修改自己,不关心共享数据的变化,即不参与订阅。B 界面可根据业务情况可参与订阅。C 界面则为订阅者。 但在每次点击后调用了 setState() 方法后导致了 节点下的页面全部重新构建了,而 A 界面根本不需要重新构建,因为它不关心数据的变化。因此这种方式将导致严重的性能问题........

二、源码分析

InheritedWidget 怎么就能从上往下共享数据呢? 下面进入到内部源码进行分析下。

    /// Base class for widgets that efficiently propagate information down the tree.
    
    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 有这么一个概述,它是用于高效的沿树传播信息的小部件的基类。它是一个抽象类,要使用它因此我们必须要去实现该类。InheritedWidget 继承至 ProxyWidget ,那先进入 ProxyWidget 中看看干了啥。

    abstract class ProxyWidget extends Widget {
        
      const ProxyWidget({ Key key, @required this.child }) : super(key: key);
      
      final Widget child;
    }

可以看到 ProxyWidget 也是一个抽象类,也没干啥事,那毫无疑问它实现关键一定就是 InheritedElement 了,下面跟进 InheritedElement中,把英文注释去掉大致如下:

    class InheritedElement extends ProxyElement {
      
      InheritedElement(InheritedWidget widget) : super(widget);

      @override
      InheritedWidget get widget => super.widget;

      final Map<Element, Object> _dependents = HashMap<Element, Object>();

      @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;
      }

      @override
      void debugDeactivated() {
        assert(() {
          assert(_dependents.isEmpty);
          return true;
        }());
        super.debugDeactivated();
      }


      @protected
      Object getDependencies(Element dependent) {
        return _dependents[dependent];
      }


      @protected
      void setDependencies(Element dependent, Object value) {
        _dependents[dependent] = value;
      }


      @protected
      void updateDependencies(Element dependent, Object aspect) {
        setDependencies(dependent, null);
      }


      @protected
      void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
        dependent.didChangeDependencies();
      }


      @override
      void updated(InheritedWidget oldWidget) {
        if (widget.updateShouldNotify(oldWidget))
          super.updated(oldWidget);
      }


      @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);
        }
      }
    }

代码有点多,下面来逐步分析下。首先 InheritedElement 你可以看做是一个 InheritedWidget 的配置文件,主要记录组件树中的依赖关系。

主要方法介绍:
       // 记录所有依赖关系的子元素 集合 
      final Map<Element, Object> _dependents = HashMap<Element, Object>();

      @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;
      }

_updateInheritance() 该方法会在Element 中 mount和activate方法中被调用,_inheritedWidgets为基类Element中的成员,用于提高Widget查找父节点中的 InheritedWidget 的效率。首先判断基类中的 incomingWidgets 是否为空,如果不为则先将基类中的 InheritedWidget 添加到一个新集合中,如果为空,则先创建一个集合。 最后将自身添加到 _inheritedWidgets 中。

      @protected
      void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
        dependent.didChangeDependencies();
      }

notifyDependent() 方法会触发 didChangeDependencies()方法的调用,在第四章节中 StatefulWidget 有说到 State对象有一个didChangeDependencies回调,它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中InheritedWidget的数据。

      @override
      void updated(InheritedWidget oldWidget) {
        if (widget.updateShouldNotify(oldWidget))
          super.updated(oldWidget);
      }

updated()该方法 首先会通过 基类中的 widget 进行判断是否有发生改变,如果有则触发父类中的 updated() 方法,最终会在父类 ProxyElement 中回调走到如下方法中:

      @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);
        }
      }

notifyClients() 则比较简单,遍历出 _dependents 集合中所有依赖关系的子元素widget 进行更新,即会触发 didChangeDependencies()方法的调用。

看到这里,你可能会说: “司机,刹一脚,刹一脚!”。notifyClients 方法中在取出有依赖关系的 子元素,但咋没看到什么地方在注册依赖关系呢?

哈哈,其实这个问题就是上面 一小节中买关子的地方。回答这个问题,还是先回到源码中去。但在 InheritedElement 类中确实没发现有注册依赖关系的方法呀。那没办法了,只能向父类中探索:InheritedElement--->ProxyElement--->ComponentElement-->Element

最终发现 Element 实现了 BuildContext 接口,在 BuildContext 中定义了如下三个方法。

      /// Registers this build context with [ancestor] such that when
      /// [ancestor]'s widget changes this build context is rebuilt.
      ///
      /// Returns `ancestor.widget`.
      ///
      /// This method is rarely called directly. Most applications should use
      /// [inheritFromWidgetOfExactType], which calls this method after finding
      /// the appropriate [InheritedElement] ancestor.
      ///
      /// All of the qualifications about when [inheritFromWidgetOfExactType] can
      /// be called apply to this method as well.
      InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect });

      /// Obtains the nearest widget of the given type, which must be the type of a
      /// concrete [InheritedWidget] subclass, and registers this build context with
      /// that widget such that when that widget changes (or a new widget of that
      /// type is introduced, or the widget goes away), this build context is
      /// rebuilt so that it can obtain new values from that widget.
      ///
      /// This is typically called implicitly from `of()` static methods, e.g.
      /// [Theme.of].
      ///
      /// This method should not be called from widget constructors or from
      /// [State.initState] methods, because those methods would not get called
      /// again if the inherited value were to change. To ensure that the widget
      /// correctly updates itself when the inherited value changes, only call this
      /// (directly or indirectly) from build methods, layout and paint callbacks, or
      /// from [State.didChangeDependencies].
      ///
      /// This method should not be called from [State.dispose] because the element
      /// tree is no longer stable at that time. To refer to an ancestor from that
      /// method, save a reference to the ancestor in [State.didChangeDependencies].
      /// It is safe to use this method from [State.deactivate], which is called
      /// whenever the widget is removed from the tree.
      ///
      /// It is also possible to call this method from interaction event handlers
      /// (e.g. gesture callbacks) or timers, to obtain a value once, if that value
      /// is not going to be cached and reused later.
      ///
      /// Calling this method is O(1) with a small constant factor, but will lead to
      /// the widget being rebuilt more often.
      ///
      /// Once a widget registers a dependency on a particular type by calling this
      /// method, it will be rebuilt, and [State.didChangeDependencies] will be
      /// called, whenever changes occur relating to that widget until the next time
      /// the widget or one of its ancestors is moved (for example, because an
      /// ancestor is added or removed).
      ///
      /// The [aspect] parameter is only used when [targetType] is an
      /// [InheritedWidget] subclasses that supports partial updates, like
      /// [InheritedModel]. It specifies what "aspect" of the inherited
      /// widget this context depends on.
      InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect });

      /// Obtains the element corresponding to the nearest widget of the given type,
      /// which must be the type of a concrete [InheritedWidget] subclass.
      ///
      /// Calling this method is O(1) with a small constant factor.
      ///
      /// This method does not establish a relationship with the target in the way
      /// that [inheritFromWidgetOfExactType] does.
      ///
      /// This method should not be called from [State.dispose] because the element
      /// tree is no longer stable at that time. To refer to an ancestor from that
      /// method, save a reference to the ancestor by calling
      /// [inheritFromWidgetOfExactType] in [State.didChangeDependencies]. It is
      /// safe to use this method from [State.deactivate], which is called whenever
      /// the widget is removed from the tree.
      InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType);

看到 inheritFromElement()对该方法的描述就知道了,子类肯定是实现了该方法后对子元素进行了依赖添加。 这里直接回到 Element 中看它是如何实现的:

      @override
      InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
        assert(ancestor != null);
        _dependencies ??= HashSet<InheritedElement>();
        _dependencies.add(ancestor);
        ancestor.updateDependencies(this, aspect);
        return ancestor.widget;
      }

      @override
      InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
        assert(_debugCheckStateIsActiveForAncestorLookup());
        final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
        if (ancestor != null) {
          assert(ancestor is InheritedElement);
          return inheritFromElement(ancestor, aspect: aspect);
        }
        _hadUnsatisfiedDependencies = true;
        return null;
      }

      @override
      InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
        assert(_debugCheckStateIsActiveForAncestorLookup());
        final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
        return ancestor;
      }

可以看到 inheritFromElement 方法中对 _dependencies 集合中添加了依赖关系,并触发子类 InheritedElement 中的 _dependents集合进行依赖更新。

inheritFromWidgetOfExactType()ancestorInheritedElementForWidgetOfExactType() 两个方法有啥作用呢? 看上去都差不多。区别就是前者会注册依赖关系,而后者不会,所以在调用inheritFromWidgetOfExactType()时,InheritedWidget和依赖它的子孙组件关系便完成了注册,之后当InheritedWidget发生变化时,就会更新依赖它的子孙组件,也就是会调这些子孙组件的didChangeDependencies()方法和build()方法。而当调用的是 ancestorInheritedElementForWidgetOfExactType()时,由于没有注册依赖关系,所以之后当InheritedWidget发生变化时,就不会触发子孙组件的重建了。

看到这里是不是对 一小节中提出面临的性能问题是不是就有解决的思路了呢?

下面来将上面的代码进行修改,首先要让参不差与重构的界面变得可配置。

  //定义一个方法,方便子树中的widget获取共享数据
  static MyInheritedWidget of(BuildContext context, {bool rebuild = true}) {
    return rebuild
        ? (context.inheritFromWidgetOfExactType(MyInheritedWidget) as MyInheritedWidget)
        : ((context.ancestorInheritedElementForWidgetOfExactType(MyInheritedWidget).widget) as MyInheritedWidget);
  }

其次,为了方便数据的获取和通知子组件的刷新,应该再建立一个 管理者封装到 StatefulWidget 中去。

    class InheritedWidgetManger extends StatefulWidget {

      InheritedWidgetManger({Key key, this.child, this.data}) : super(key: key);

      final Widget child;

      InheritedTestModel data;

      @override
      InheritedWidgetMangerState createState() => InheritedWidgetMangerState();

      //定义一个方法,方便子树中的widget获取共享数据
      static MyInheritedWidget of(BuildContext context, {bool rebuild = true}) {
        return rebuild
            ? (context.inheritFromWidgetOfExactType(MyInheritedWidget) as MyInheritedWidget)
            : ((context.ancestorInheritedElementForWidgetOfExactType(MyInheritedWidget).widget) as MyInheritedWidget);
      }

    }

    class InheritedWidgetMangerState extends State<InheritedWidgetManger> {
      @override
      Widget build(BuildContext context) {

        //将最新的数据往下传递
        return MyInheritedWidget(
          data: widget.data,
          child: widget.child,
          mangerState: this,
        );
      }

      //提供一个方法修改数据,并通知子组件 刷新
      void updateData(InheritedTestModel model) {
        setState(() {
          widget.data = model;
        });
      }
    }

然后 MyInheritedWidget 组件也可以相应的简化了。

    // 组件共享数据的容器
    class MyInheritedWidget extends InheritedWidget {

      //数据的Mode
      InheritedTestModel data;

      InheritedWidgetMangerState mangerState;

      MyInheritedWidget({
        Key key,
        @required this.data,
        @required Widget child,
        this.mangerState
      }) : super(key: key, child: child);


      //是否重建widget就取决于数据是否相同
      @override
      bool updateShouldNotify(MyInheritedWidget oldWidget) {

        //判断数据是否反生改变,如果为真 所有子组件的 build 方法会被调用;
        // 有状态组件 didChangeDependencies()方法会被调用
        return data != oldWidget.data;
      }
    }

在需要共享的组件树根部因此不再使用 MyInheritedWidget ,而是用最新创建的 InheritedWidgetManger。代码如下:

    //主界面
    class DataShardPage extends StatefulWidget {
      @override
      _DataShardPageState createState() => _DataShardPageState();
    }

    class _DataShardPageState extends State<DataShardPage> {
      //用于存储数据的 Mode,初始化数据 为 0
      InheritedTestModel inheritedTestModel = InheritedTestModel(0);

      @override
      Widget build(BuildContext context) {
        //在该组件下的 子组件可以收到消息通知
        return InheritedWidgetManger(
            data: inheritedTestModel,
            child: Scaffold(
                floatingActionButton: FloatingActionButton(
                  elevation: 0,
                  child: WidgetTestPageButton(),
                ),
                floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
                appBar: AppBar(
                  title: Text("InheritedWidget"),
                ),
                body: Center(
                    child: ListView(
                  children: <Widget>[
                    //创建 三个 页面
                    WidgetTestPageOne(),

                    WidgetTestPageTwo(),

                    WidgetTestPageThree(),
                  ],
                ))));
      }
    }

第一个界面做如下修改:

    // 第一个页面
    class WidgetTestPageOne extends StatefulWidget {

      @override
      _WidgetTestPageOneState createState() => _WidgetTestPageOneState();
    }

    class _WidgetTestPageOneState extends State<WidgetTestPageOne> {


      @override
      Widget build(BuildContext context) {

        print("WidgetTestPageOne___build");

        return Padding(
            padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
            child: Text("One页面不参与订阅:"));
      }

      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("WidgetTestPageOne___didChangeDependencies");
      }

    }

第二个界面做如下修改:

    //第二个页面
    class WidgetTestPageTwo extends StatefulWidget {
      @override
      _WidgetTestPageTwoState createState() => _WidgetTestPageTwoState();
    }

    class _WidgetTestPageTwoState extends State<WidgetTestPageTwo> {
      @override
      Widget build(BuildContext context) {

        //可控制的
        final inheritedWidgetManger = InheritedWidgetManger.of(context,rebuild: true);

        final inheritedTestModel = inheritedWidgetManger.data;

        print("WidgetTestPageTwo____${inheritedTestModel.count}");

        return Padding(

            padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),

            child: Text("Two页面获取到最新值:${inheritedTestModel.count}"));
      }


      @override
      void didChangeDependencies() {
        super.didChangeDependencies();

        print("WidgetTestPageTwo___didChangeDependencies");
      }

    }

第三个界面代码如下:

    //第三个页面
    class WidgetTestPageThree extends StatefulWidget {
      @override
      _WidgetTestPageThreeState createState() => _WidgetTestPageThreeState();
    }

    class _WidgetTestPageThreeState extends State<WidgetTestPageThree> {

      @override
      Widget build(BuildContext context) {

        final myInheritedWidget = InheritedWidgetManger.of(context);
        final inheritedTestModel = myInheritedWidget.data;

        print("WidgetTestPageThree____${inheritedTestModel.count}");

        return Padding(
            padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
            child: Text("Three页面获取到最新值:${inheritedTestModel.count}"));
      }

      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print("WidgetTestPageThree___didChangeDependencies");
      }
    }

最终效果如下:

InheritedWidget共享数据2.gif

控制台输出如下:

    I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageTwo____1
    I/flutter ( 2882): WidgetTestPageThree____1
    I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageTwo____2
    I/flutter ( 2882): WidgetTestPageThree____2
    I/flutter ( 2882): WidgetTestPageTwo___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageThree___didChangeDependencies
    I/flutter ( 2882): WidgetTestPageTwo____3
    I/flutter ( 2882): WidgetTestPageThree____3

可以看出第一个界面 在没引用 InheritedWidget 的情况,界面不在发生重建,第二个界面中 InheritedWidgetManger 中提供的方法 可配置 rebuild 参数是否可以重建该界面。

1、当 rebuild 为 true 时会将共享的组件树 中注册依赖关系,即将Widget添加到订阅者列表中。
2、当 rebuild 为 false 时仍然可以访问数据,但不使用 InheritedWidget 的内部实现。

本次修改完美规避了上面提到的性能问题,但是还有很多值得优化的地方,比如案例中使用的 InheritedTestModel 在实际开发中为了更加灵活可用 建议使用 泛型代替。

写在最后

当组件树中的依赖发生变化时 不仅会触发 didChangeDependencies() 方法的调用,在依赖改变后 Framework 还会调用 build()方法,因此在实际开发中 不建议将耗时类的操作放到 build() 方法中去,如需耗时类工作 建议复写 didChangeDependencies() 方法在这里面完成。这样可以避免每次build()都执行这些耗时类工作带来的性能问题。

[注意:] 笔者在写文章时是用的早期flutter 2.0以前版本,2.0后请将 文章中的 inheritFromWidgetOfExactType 替换成 dependOnInheritedWidgetOfExactType, ancestorInheritedElementForWidgetOfExactType 替换成 findAncestorWidgetOfExactType新的api使用。

好了本章节到此结束,又到了说再见的时候了,如果你喜欢请留下你的小红星;你们的支持才是创作的动力,如有错误,请热心的你留言指正, 谢谢大家观看,下章再会 O(∩_∩)O

实例源码地址:https://github.com/zhengzaihong/flutter_learn/blob/master/lib/page/Inherited/DataShardPage.dart

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345