Flutter 学习之旅(二十九) 数据共享 InheritedWidget

InheritedWidget

是Flutter 中非常重要的一个功能性组件,他的主要作用是用来提供一种从上到下的一种传递数据的方式,
在flutter app中的theme 和 local 就是这样写的,

didChangeDependencies

这个方法在前面介绍StatefulWidget 的时候介绍过该方法,表示他所依赖的父布局发现变化时这个方法会被framework 调用,但是如何判断父控件是否发生变化,就是根据 子布局是否使用并注册了父布局的 InheritedWidget,这里为什么说是使用并注册,我们会在下面介绍一下,

先写一个widget , 他含有一个count 属性,

class TsmShareInteritedWidget extends InheritedWidget {


  TsmShareInteritedWidget({@required this.data, Widget child}) : super(child: child);

  int data;

  @override
  bool updateShouldNotify(TsmShareInteritedWidget oldWidget) {
    return oldWidget.data != data;
  }


  static TsmShareInteritedWidget of(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<TsmShareInteritedWidget>();
  }
}

想要使用 TsmShareInteritedWidget 共享出来的count 这个数据的使用方法就是

TsmShareInteritedWidget.of(context).data;

写一个TsmInheritedWidget 使用共享数据

class  TsmInheritedWidget extends StatefulWidget{
  @override
  State<StatefulWidget> createState() =>_TsmInheritedWidgetState();

}


class _TsmInheritedWidgetState extends State<TsmInheritedWidget>{
  @override
  Widget build(BuildContext context) {
    printString('build');
    return Text(TsmShareInteritedWidget.ofDate(context).data.toString());
  }



  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    printString('didChangeDependencies');
  }

}

按照上面我的对InheritedWidget 这个控件介绍,他的作用是从上至下传递,也就是 InheritedWidget所共享的这个count 只能由他的子widget使用,最后再来一个使用他的例子

class _TsmInheritedSendPageState extends State<TsmInheritedSendPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text('Inherited 学习'),
        ),
        body: Container(
          child: Center(
              child: TsmShareInteritedWidget(
            data: count,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                TsmInheritedWidget(),
                SizedBox(
                  height: 15,
                ),
                RaisedButton(
                  child: Text('计数器'),
                  onPressed: () {
                    setState(() {
                      count++;
                    });
                  },
                )
              ],
            ),
          )),
        ),
      );
}

点击按钮的时候发现didChangeDependencies 和build 方法同时打印

那么为什么非要多此一举监听didChangeDependencies,值机监听build方法不就可以了,其实如果数据发生变化是触发请求接口的条件,如果该条件放在build中,则请求接口会发发生的很频繁,而didChangeDependencies 则是可控制的变成,更为合理,(即didChangeDependencies 调用 build必定调用,但是build调用 didChangeDependencies 不一定被调用)

那么这里如果我只想引用数据不想要didChangeDependencies 回调被调用呢,
按照上面的说法是,如果想要didChangeDependencies 这个回调被调用必须是使用数据并注册,
也就是说我们不注册就可以了,

这里看了一下源码

  @override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect) as T;
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

源码的逻辑就是先找到InheritedElement ,然后再做数据变化依赖,dependOnInheritedElement

通过搜索final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; 这句代码找到另一个只是获取数据widgit的方法,他只有获取数据的方法,并没有注册

  @override
  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    return ancestor;
  }

下面我们在TsmShareInteritedWidget 这添加一个ofData方法,修改TsmInheritedWidget 中的 TsmShareInteritedWidget.of 改成TsmShareInteritedWidget.ofData ,

  static TsmShareInteritedWidget ofDate(BuildContext context) {
    return context
        .getElementForInheritedWidgetOfExactType<TsmShareInteritedWidget>()
        .widget;
  }

修改后的TsmShareInteritedWidget 变化为

class TsmShareInteritedWidget extends InheritedWidget {


  TsmShareInteritedWidget({@required this.data, Widget child}) : super(child: child);

  int data;

  @override
  bool updateShouldNotify(TsmShareInteritedWidget oldWidget) {
    return oldWidget.data != data;
  }

  ///  源码中方法
  ///  获取数据同时绑定
  ///
  ///  @override
  ///   T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
  ///     assert(_debugCheckStateIsActiveForAncestorLookup());
  ///     final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
  ///     if (ancestor != null) {
  ///       assert(ancestor is InheritedElement);
  ///       return dependOnInheritedElement(ancestor, aspect: aspect) as T;
  ///     }
  ///     _hadUnsatisfiedDependencies = true;
  ///     return null;
  ///    }
  ///
  ///
  static TsmShareInteritedWidget of(BuildContext context) {
    return context
        .dependOnInheritedWidgetOfExactType<TsmShareInteritedWidget>();
  }

  /// 只获取数据
  ///
  ///
  ///
  ///源码中的方法
  ///  @override
  ///  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
  ///    assert(_debugCheckStateIsActiveForAncestorLookup());
  ///    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
  ///    return ancestor;
  ///  }
  static TsmShareInteritedWidget ofData(BuildContext context) {
    return context
        .getElementForInheritedWidgetOfExactType<TsmShareInteritedWidget>()
        .widget;
  }
}

///  总结,通过查看源码发现,dependOnInheritedWidgetOfExactType  依赖关系真正建立的时机是  dependOnInheritedElement  这个方法,除了这个,其实这两个方法都是一样的
///
///   这就实现了使用   dependOnInheritedWidgetOfExactType  该方法时,不仅可以使用数据,同时在数据发生变更的同时,didChangeDependencies  方法还是调用
///   而  getElementForInheritedWidgetOfExactType  这个方法只能使用数据,
///
///   其实在使用过程中还会发生 如果  didChangeDependencies  方法发生变化,其实build 方法也会被调用,费什么非要多此一举呢,其实如果数据发生变化是触发请求接口的条件
///   如果该条件放在build中,则请求接口会发发生的很频繁,而didChangeDependencies 则是可控制的变成,更为合理
///
///

这里reload代码发现 didChangeDependencies 还是会打印日志,重新打包后就不打印,貌似就是在重新reload的时候,已经注册的方法,没办法取消注册导致的

其实现在这种用法还是有弊端的,那就是我们只想更新一个TsmInheritedWidget 中的数据,但是调用setState方法时,整个页面都刷新了,这样做不是很好,这个问题我会在下一篇Provider中解决

我学习flutter的整个过程都记录在里面了
https://www.jianshu.com/c/36554cb4c804

最后附上demo 地址

https://github.com/tsm19911014/tsm_flutter

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

推荐阅读更多精彩内容