Flutter 學(xué)習(xí)之旅(二十九) 數(shù)據(jù)共享 InheritedWidget

InheritedWidget

是Flutter 中非常重要的一個(gè)功能性組件,他的主要作用是用來(lái)提供一種從上到下的一種傳遞數(shù)據(jù)的方式,
在flutter app中的theme 和 local 就是這樣寫的,

didChangeDependencies

這個(gè)方法在前面介紹StatefulWidget 的時(shí)候介紹過(guò)該方法,表示他所依賴的父布局發(fā)現(xiàn)變化時(shí)這個(gè)方法會(huì)被framework 調(diào)用,但是如何判斷父控件是否發(fā)生變化,就是根據(jù) 子布局是否使用并注冊(cè)了父布局的 InheritedWidget,這里為什么說(shuō)是使用并注冊(cè),我們會(huì)在下面介紹一下,

先寫一個(gè)widget , 他含有一個(gè)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 共享出來(lái)的count 這個(gè)數(shù)據(jù)的使用方法就是

TsmShareInteritedWidget.of(context).data;

寫一個(gè)TsmInheritedWidget 使用共享數(shù)據(jù)

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

}

按照上面我的對(duì)InheritedWidget 這個(gè)控件介紹,他的作用是從上至下傳遞,也就是 InheritedWidget所共享的這個(gè)count 只能由他的子widget使用,最后再來(lái)一個(gè)使用他的例子

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

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(
          title: Text('Inherited 學(xué)習(xí)'),
        ),
        body: Container(
          child: Center(
              child: TsmShareInteritedWidget(
            data: count,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                TsmInheritedWidget(),
                SizedBox(
                  height: 15,
                ),
                RaisedButton(
                  child: Text('計(jì)數(shù)器'),
                  onPressed: () {
                    setState(() {
                      count++;
                    });
                  },
                )
              ],
            ),
          )),
        ),
      );
}

點(diǎn)擊按鈕的時(shí)候發(fā)現(xiàn)didChangeDependencies 和build 方法同時(shí)打印

那么為什么非要多此一舉監(jiān)聽didChangeDependencies,值機(jī)監(jiān)聽build方法不就可以了,其實(shí)如果數(shù)據(jù)發(fā)生變化是觸發(fā)請(qǐng)求接口的條件,如果該條件放在build中,則請(qǐng)求接口會(huì)發(fā)發(fā)生的很頻繁,而didChangeDependencies 則是可控制的變成,更為合理,(即didChangeDependencies 調(diào)用 build必定調(diào)用,但是build調(diào)用 didChangeDependencies 不一定被調(diào)用)

那么這里如果我只想引用數(shù)據(jù)不想要didChangeDependencies 回調(diào)被調(diào)用呢,
按照上面的說(shuō)法是,如果想要didChangeDependencies 這個(gè)回調(diào)被調(diào)用必須是使用數(shù)據(jù)并注冊(cè),
也就是說(shuō)我們不注冊(cè)就可以了,

這里看了一下源碼

  @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 ,然后再做數(shù)據(jù)變化依賴,dependOnInheritedElement

通過(guò)搜索final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; 這句代碼找到另一個(gè)只是獲取數(shù)據(jù)widgit的方法,他只有獲取數(shù)據(jù)的方法,并沒(méi)有注冊(cè)

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

下面我們?cè)赥smShareInteritedWidget 這添加一個(gè)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;
  }

  ///  源碼中方法
  ///  獲取數(shù)據(jù)同時(shí)綁定
  ///
  ///  @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>();
  }

  /// 只獲取數(shù)據(jù)
  ///
  ///
  ///
  ///源碼中的方法
  ///  @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;
  }
}

///  總結(jié),通過(guò)查看源碼發(fā)現(xiàn),dependOnInheritedWidgetOfExactType  依賴關(guān)系真正建立的時(shí)機(jī)是  dependOnInheritedElement  這個(gè)方法,除了這個(gè),其實(shí)這兩個(gè)方法都是一樣的
///
///   這就實(shí)現(xiàn)了使用   dependOnInheritedWidgetOfExactType  該方法時(shí),不僅可以使用數(shù)據(jù),同時(shí)在數(shù)據(jù)發(fā)生變更的同時(shí),didChangeDependencies  方法還是調(diào)用
///   而  getElementForInheritedWidgetOfExactType  這個(gè)方法只能使用數(shù)據(jù),
///
///   其實(shí)在使用過(guò)程中還會(huì)發(fā)生 如果  didChangeDependencies  方法發(fā)生變化,其實(shí)build 方法也會(huì)被調(diào)用,費(fèi)什么非要多此一舉呢,其實(shí)如果數(shù)據(jù)發(fā)生變化是觸發(fā)請(qǐng)求接口的條件
///   如果該條件放在build中,則請(qǐng)求接口會(huì)發(fā)發(fā)生的很頻繁,而didChangeDependencies 則是可控制的變成,更為合理
///
///

這里reload代碼發(fā)現(xiàn) didChangeDependencies 還是會(huì)打印日志,重新打包后就不打印,貌似就是在重新reload的時(shí)候,已經(jīng)注冊(cè)的方法,沒(méi)辦法取消注冊(cè)導(dǎo)致的

其實(shí)現(xiàn)在這種用法還是有弊端的,那就是我們只想更新一個(gè)TsmInheritedWidget 中的數(shù)據(jù),但是調(diào)用setState方法時(shí),整個(gè)頁(yè)面都刷新了,這樣做不是很好,這個(gè)問(wèn)題我會(huì)在下一篇Provider中解決

我學(xué)習(xí)flutter的整個(gè)過(guò)程都記錄在里面了
http://www.itdecent.cn/c/36554cb4c804

最后附上demo 地址

https://github.com/tsm19911014/tsm_flutter

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容