Flutter 數(shù)據(jù)共享 - InheritedWidget

InheritedWidget 提供了數(shù)據(jù)在 widget 中從上到下傳遞共享的方式密浑;在根 widget 中共享一個數(shù)據(jù),便可以在任意子 widget 中獲取該共享數(shù)據(jù)县袱;對于需要在 widget 樹中共享數(shù)據(jù)的場景十分方便;Flutter SDK 中亦是通過 InheritedWidget 共享應(yīng)用的主題(theme)戳葵、語言環(huán)境(Locale) 信息瘩蚪;

InheritedWidget 和 React 中的 context 功能類似泉懦,和逐級傳遞數(shù)據(jù)相比,它們能實現(xiàn)組件跨級傳遞數(shù)據(jù)疹瘦。InheritedWidget 的在 widget 樹中數(shù)據(jù)傳遞方向是從上到下的崩哩,這和通知 Notification 的傳遞方向正好相反。

State 中的 didChangeDependencies 回調(diào)拱礁,會在父 widget 中 InheritedWidget 的數(shù)據(jù)發(fā)生變化時被 Flutter Framework 調(diào)用琢锋;如果有使用 didChangeDependencies 則表示子 widget 有依賴 InheritedWidget,沒有使用則表示沒有依賴呢灶;因此我們可以在 InheritedWidget 發(fā)生變化時對組件進行更新;當應(yīng)用的主題(theme)钉嘹、語言環(huán)境(Locale) 信息發(fā)生變化時, 子 widget 的 didChangeDependencies 方法將被調(diào)用鸯乃;

被調(diào)用當依賴發(fā)生變化后 Framework 都會調(diào)用 build 方法,因此一般情況下子 widget 很少會重寫 didChangeDependencies跋涣;但是如果需要執(zhí)行一些比較昂貴的操作(如網(wǎng)絡(luò)請求)缨睡,最好的方式就是在此方法中執(zhí)行,以避免每次 build 都觸發(fā)該操作陈辱;

??

通過繼承 InheritedWidget奖年,將當前計數(shù)器點擊次數(shù)保存在 ShareDataWidget的data 屬性中:

class ShareDataWidget extends InheritedWidget {
  ShareDataWidget({
    @required this.data,
    Widget child
  }) :super(child: child);

  final int data; //需要在子樹中共享的數(shù)據(jù),保存點擊次數(shù)

  //定義一個便捷方法沛贪,方便子樹中的widget獲取共享數(shù)據(jù)  
  static ShareDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }

  //該回調(diào)決定當data發(fā)生變化時陋守,是否通知子樹中依賴data的Widget  
  @override
  bool updateShouldNotify(ShareDataWidget old) {
    //如果返回true,則子樹中依賴(build函數(shù)中有調(diào)用)本widget
    //的子widget的`state.didChangeDependencies`會被調(diào)用
    return old.data != data;
  }
}

然后我們實現(xiàn)一個子組件_TestWidget利赋,在其build方法中引用ShareDataWidget中的數(shù)據(jù)水评。同時,在其didChangeDependencies() 回調(diào)中打印日志

class _TestWidget extends StatefulWidget {
  @override
  __TestWidgetState createState() => new __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget> {
  @override
  Widget build(BuildContext context) {
    //使用InheritedWidget中的共享數(shù)據(jù)
    return Text(ShareDataWidget
        .of(context)
        .data
        .toString());
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //父或祖先widget中的InheritedWidget改變(updateShouldNotify返回true)時會被調(diào)用媚送。
    //如果build中沒有依賴InheritedWidget中燥,則此回調(diào)不會被調(diào)用。
    print("Dependencies change");
  }
}

最后塘偎,我們創(chuàng)建一個按鈕疗涉,每點擊一次,就將ShareDataWidget的值自增:

class InheritedWidgetTestRoute extends StatefulWidget {
  @override
  _InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}

class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return  Center(
      child: ShareDataWidget( //使用ShareDataWidget
        data: count,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(bottom: 20.0),
              child: _TestWidget(),//子widget中依賴ShareDataWidget
            ),
            RaisedButton(
              child: Text("Increment"),
              //每點擊一次吟秩,將count自增咱扣,然后重新build,ShareDataWidget的data將被更新  
              onPressed: () => setState(() => ++count),
            )
          ],
        ),
      ),
    );
  }
}

如果_TestWidget的build方法中沒有使用ShareDataWidget的數(shù)據(jù),那么它的didChangeDependencies()將不會被調(diào)用峰尝,因為它并沒有依賴ShareDataWidget

class __TestWidgetState extends State<_TestWidget> {
  @override
  Widget build(BuildContext context) {
    // 使用InheritedWidget中的共享數(shù)據(jù)
    //    return Text(ShareDataWidget
    //        .of(context)
    //        .data
    //        .toString());
     return Text("text");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // build方法中沒有依賴InheritedWidget偏窝,此回調(diào)不會被調(diào)用。
    print("Dependencies change");
  }
}

如果只想在 __TestWidgetState 中引用 ShareDataWidget 數(shù)據(jù),但卻不希望在 ShareDataWidget 發(fā)生變化時調(diào)用 __TestWidgetState的didChangeDependencies() 方法
唯一的改動就是獲取 ShareDataWidget 對象的方式祭往,把 inheritFromWidgetOfExactType() 方法換成了
context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget

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

@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
  final InheritedElement ancestor = _inheritedWidgets == null ? null :   _inheritedWidgets[targetType];
  //多出的部分
  if (ancestor != null) {
    assert(ancestor is InheritedElement);
    return inheritFromElement(ancestor, aspect: aspect);
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}
  • 注 inheritFromElement 主要是注冊依賴關(guān)系 因此調(diào)用 inheritFromWidgetOfExactType 后伦意,子孫組件便完成了注冊, InheritedWidget 發(fā)生變化后, 就會更新依賴它的子孫組件硼补,同時調(diào)用子孫組件的 didChangeDependencies 和 build 方法驮肉; 而使用 ancestorInheritedElementWidgetOfExactType 由于沒有注冊依賴關(guān)系,因此當 inheritedWidget 發(fā)生變化時就不會更新子孫組件
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
  //注冊依賴關(guān)系
  _dependencies ??= HashSet<InheritedElement>();
  _dependencies.add(ancestor);
  ancestor.updateDependencies(this, aspect);
  return ancestor.widget;
}
資料

《Flutter 實戰(zhàn)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末已骇,一起剝皮案震驚了整個濱河市离钝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褪储,老刑警劉巖卵渴,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲤竹,居然都是意外死亡浪读,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門辛藻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碘橘,“玉大人,你說我怎么就攤上這事吱肌《徊穑” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵氮墨,是天一觀的道長纺蛆。 經(jīng)常有香客問我,道長勇边,這世上最難降的妖魔是什么犹撒? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮粒褒,結(jié)果婚禮上识颊,老公的妹妹穿的比我還像新娘。我一直安慰自己奕坟,他們只是感情好祥款,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著月杉,像睡著了一般刃跛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苛萎,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天桨昙,我揣著相機與錄音检号,去河邊找鬼。 笑死蛙酪,一個胖子當著我的面吹牛齐苛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桂塞,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼凹蜂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阁危?” 一聲冷哼從身側(cè)響起玛痊,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎狂打,沒想到半個月后擂煞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡菱父,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年颈娜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浙宜。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蛹磺,靈堂內(nèi)的尸體忽然破棺而出粟瞬,到底是詐尸還是另有隱情,我是刑警寧澤萤捆,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布裙品,位于F島的核電站,受9級特大地震影響俗或,放射性物質(zhì)發(fā)生泄漏市怎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一辛慰、第九天 我趴在偏房一處隱蔽的房頂上張望区匠。 院中可真熱鬧,春花似錦帅腌、人聲如沸驰弄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽戚篙。三九已至,卻和暖如春溺职,著一層夾襖步出監(jiān)牢的瞬間岔擂,已是汗流浹背位喂。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乱灵,地道東北人塑崖。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像阔蛉,于是被迫代替她去往敵國和親弃舒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容