<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【源碼分析】系列之 InheritedWidget

          共 20203字,需瀏覽 41分鐘

           ·

          2021-01-20 21:09


          老孟導(dǎo)讀:這是2021年源碼系列的第一篇文章,其實(shí)源碼系列的文章不是特別受歡迎,一個(gè)原因是原理性的知識(shí)非??菰?,我自己看源碼的時(shí)候特別有感觸,二是想把源碼分析講的通俗易懂非常困難,自己明白 和 讓別人聽懂完全是兩回事。不過我依然會(huì)堅(jiān)持 Flutter 源碼系列的文章,提高自己的技術(shù)水平的同時(shí),也希望大家收獲一些知識(shí)。

          為了使源碼系列的文章不那么枯燥,文章中會(huì)有很多流程圖,流程圖比純文字更直觀,一圖勝千言。

          我也是第一次寫源碼系列的文章,如果文章哪里有不對(duì)的地方請(qǐng)告訴我,雖然我也不一定聽??,開個(gè)玩笑。

          希望大家來個(gè) ,您的 是我寫下去的巨大動(dòng)力??。

          所有源碼系列文章都會(huì)分享到我個(gè)人博客(源碼目錄下):http://laomengit.com/

          正文

          注意:使用的 Flutter 版本 和 Dart 版本如下:

          Flutter 1.22.4 ? channel stable ? https://github.com/flutter/flutter.git Framework ? revision 1aafb3a8b9 (6 weeks ago) ? 2020-11-13 09:59:28 -0800 Engine ? revision 2c956a31c0 Tools ? Dart 2.10.4

          不同的版本可能有所不同,請(qǐng)注意版本之間的區(qū)別。

          首先, InheritedWidget 是一個(gè)非常重要,非常重要非常重要的組件,重要的事情說3遍??,系統(tǒng)中很多功能都是功能型組件都是通過 InheritedWidget 實(shí)現(xiàn)的,著名的 Provider 狀態(tài)管理框架也是基于 InheritedWidget 實(shí)現(xiàn)的,因此不管是工作中,還是面試,InheritedWidget 組件的原理及使用場景都是考察的重點(diǎn)。

          此篇文章包括如下幾個(gè)部分:

          • InheritedWidget 組件簡介,是什么場景下使用 InheritedWidget?
          • InheritedWidget 基本用法。
          • InheritedWidget 源碼分析。

          InheritedWidget 組件簡介

          InheritedWidget 組件是功能型組件,提供了沿樹向下,共享數(shù)據(jù)的功能,即子組件可以獲取父組件(InheritedWidget 的子類)的數(shù)據(jù),通過BuildContext.dependOnInheritedWidgetOfExactType 獲取。

          InheritedWidget 組件的共享數(shù)據(jù)是沿著樹從上到下,是否聯(lián)想到 Notification,Notification 正好與 InheritedWidget 傳遞方向相反,Notification 是沿著樹從下到上,兩者功能的實(shí)現(xiàn)都是子節(jié)點(diǎn)主動(dòng)發(fā)起,InheritedWidget 組件的子節(jié)點(diǎn)主動(dòng)查找父節(jié)點(diǎn)上 InheritedWidget 共享的數(shù)據(jù),Notification 也是子節(jié)點(diǎn)主動(dòng)發(fā)起通知,沿著樹向上通知。

          Notification 也是 Flutter 中非常重要的,后面會(huì)有專門的文章詳細(xì)介紹,此篇不做介紹。

          那么什么樣的場景適合使用 InheritedWidget 呢? 通常App會(huì)有登錄用戶信息,登錄用戶信息為全局共享信息,想象一下,子組件要如何獲取登錄用戶信息?將上面的場景抽象一下,有一顆組件樹,A、H 組件依賴同一數(shù)據(jù),如下:

          A、H 組件要如何獲取到數(shù)據(jù)呢?

          有一種實(shí)現(xiàn)方式是 通過構(gòu)造函數(shù)透傳,數(shù)據(jù)通過A傳遞給B,B傳遞給C、E,C和E在傳遞給F、H,如下圖虛線的傳遞:

          反應(yīng)到代碼上就是:

          return?A(
          ??data:data
          ??child:B(
          ????data:data
          ????child:C(
          ??????data:data
          ??????child:F(
          ????????data:data
          ??????)???
          ????)
          ??)
          );

          這樣的實(shí)現(xiàn)缺點(diǎn)非常明顯,B、C組件不需要 data 數(shù)據(jù),如果組件樹比較深的話,那將是噩夢(mèng)。

          為了處理此問題,F(xiàn)lutter Framework 提供了 InheritedWidget 組件,InheritedWidget 組件的子組件可以直接獲取數(shù)據(jù),如下圖:

          InheritedWidget 組件的所有子組件都可以直接通過 BuildContext.dependOnInheritedWidgetOfExactType 獲取數(shù)據(jù)。

          InheritedWidget 基本用法

          上面分析了 InheritedWidget 組件的使用場景,下面用一個(gè)最簡單的 demo 展示如何使用 ?InheritedWidget 組件。

          定一個(gè)用戶信息共享數(shù)據(jù)的實(shí)體類,任何子組件都可以獲取用戶信息,用戶信息實(shí)體類:

          class?UserInfo?{
          ??String?name;
          ??int?age;

          ??UserInfo({this.name,?this.age});

          ??@override
          ??bool?operator?==(Object?other)?{
          ????if?(!(other?is?UserInfo))?{
          ??????return?false;
          ????}
          ????var?old?=?other?as?UserInfo;
          ????return?name?==?old.name?&&?age?==?old.age;
          ??}
          }

          UserInfo 類重寫了 == 操作符,是為了后面數(shù)據(jù)是否發(fā)生變化做判斷。

          定義共享 UserInfo 數(shù)據(jù)的 InheritedWidget 組件。

          class?MyInheritedWidget?extends?InheritedWidget?{
          ??final?UserInfo?userInfo;

          ??MyInheritedWidget({this.userInfo,?Widget?child}):super(child:?child);

          ??static?MyInheritedWidget?of(BuildContext?context)?{
          ????return?context.dependOnInheritedWidgetOfExactType();
          ??}

          ??@override
          ??bool?updateShouldNotify(covariant?MyInheritedWidget?oldWidget)?{
          ????return?oldWidget.userInfo?!=?userInfo;
          ??}
          }

          這里有兩個(gè)地方需要注意:

          1. 靜態(tài)(static) of 方法,這個(gè)方法不是必須的,但一般都會(huì)添加此方法,方便其子組件調(diào)用,當(dāng)然也可以直接在子組件中使用 context.dependOnInheritedWidgetOfExactType 方法,不加 of 方法,子組件調(diào)用如下:

            class?F?extends?StatelessWidget?{
            ??@override
            ??Widget?build(BuildContext?context)?{
            ????var?myInheritedWidget?=
            ????????context.dependOnInheritedWidgetOfExactType();
            ????return?Text('name:${myInheritedWidget.userInfo.name}');
            ??}
            }

            添加靜態(tài)(static) of 方法,用法如下:

            class?F?extends?StatelessWidget?{
            ??@override
            ??Widget?build(BuildContext?context)?{
            ????return?Text('name:${MyInheritedWidget.of(context).userInfo.name}');
            ??}
            }

            我們經(jīng)常使用的 MediaQuery.of(context)Theme.of(context) 方法都是系統(tǒng)封裝的此方法。

          2. updateShouldNotify 方法必須重寫,此方法是判斷新的共享數(shù)據(jù)和原數(shù)據(jù)是否一致,是否將通知傳遞給所有子組件(已注冊(cè))。重建此組件時(shí),有時(shí)需要重建繼承 InheritedWidget 組件的組件,但有時(shí)不需要。例如,如果此組件所保存的數(shù)據(jù)與“ oldWidget”所保存的數(shù)據(jù)相同,則我們無需重建繼承了“ oldWidget”所保存的數(shù)據(jù)的組件。

          使用 MyInheritedWidget 組件:

          class?InheritedWidgetDemo?extends?StatefulWidget?{
          ??@override
          ??_InheritedWidgetDemoState?createState()?=>?_InheritedWidgetDemoState();
          }

          class?_InheritedWidgetDemoState?extends?State<InheritedWidgetDemo>?{
          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?Scaffold(
          ??????appBar:?AppBar(
          ????????title:?Text('InheritedWidget?Demo'),
          ??????),
          ??????body:?Center(
          ????????child:?MyInheritedWidget(
          ??????????userInfo:?UserInfo(name:?'老孟',?age:?18),
          ??????????child:?A(
          ????????????child:?F(),
          ??????????),
          ????????),
          ??????),
          ????);
          ??}
          }

          A 組件代碼:

          class?A?extends?StatelessWidget?{
          ??final?Widget?child;

          ??const?A({Key?key,?this。child})?:?super(key:?key);

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?Center(
          ??????child:?child,
          ????);
          ??}
          }

          F 組件代碼:

          class?F?extends?StatelessWidget?{
          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?Text('name:${MyInheritedWidget.of(context).userInfo.name}');
          ??}
          }

          上面代碼構(gòu)成的組件樹為(無關(guān)的節(jié)點(diǎn)已忽略比如Scaffold、Center等):

          注意: A 組件是為了表示樹的深度,此 Demo 中將其簡化了,僅僅設(shè)置了一層,也可以設(shè)多多層。

          運(yùn)行效果:

          下面修改數(shù)據(jù)并刷新UI,下面的代碼僅能用于Demo,是為了演示方便,千萬不要用于實(shí)際項(xiàng)目,因?yàn)橄旅娴膶懛ㄓ芯薮蟮男阅軉栴},因?yàn)橹亟?InheritedWidget 組件下的所有子組件,重要的事情說3遍:下面演示Demo千萬不要用于實(shí)際項(xiàng)目千萬不要用于實(shí)際項(xiàng)目,千萬不要用于實(shí)際項(xiàng)目。文章最后我會(huì)給出正確用法。

          修改 _InheritedWidgetDemoState

          class?_InheritedWidgetDemoState?extends?State<InheritedWidgetDemo>?{
          ??UserInfo?_userInfo?=?UserInfo(name:?'老孟',?age:?18);

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?Scaffold(
          ??????appBar:?AppBar(
          ????????title:?Text('InheritedWidget?Demo'),
          ??????),
          ??????body:?Center(
          ????????child:?MyInheritedWidget(
          ??????????userInfo:?_userInfo,
          ??????????child:?A(
          ????????????child:?F(),
          ??????????),
          ????????),
          ??????),
          ??????floatingActionButton:?FloatingActionButton(
          ????????onPressed:?()?{
          ??????????setState(()?{
          ????????????_userInfo?=?UserInfo(name:?'老孟1',?age:?18);
          ??????????});
          ????????},
          ??????),
          ????);
          ??}
          }

          點(diǎn)擊按鈕的時(shí)候,UI刷新了,但請(qǐng)重點(diǎn)看右側(cè) rebuild stats 部分,每點(diǎn)擊一次按鈕,MyInheritedWidget 組件及其子組件全部 重新構(gòu)建(rebuild),但 A 組件并不依賴于 MyInheritedWidget 共享數(shù)據(jù),理想情況下不應(yīng)該 rebuild,實(shí)際項(xiàng)目中,樹的結(jié)構(gòu)會(huì)比這個(gè)復(fù)雜的多,因此全部 rebuild 會(huì)造成性能問題,這也是開頭說千萬不要將此方式用于實(shí)際項(xiàng)目,網(wǎng)上充斥著大量此種用法的文章,后面會(huì)給出正確用法,正確用法比較復(fù)雜,而且涉及其他相關(guān)知識(shí),所以此處的Demo僅用于學(xué)習(xí) InheritedWidget。

          大家是否還記得 Stateful 組件的生命周期 文章中介紹的 didChangeDependencies 生命周期,對(duì)其的介紹如下:

          didChangeDependencies 方法在 initState 之后由 Framework 立即調(diào)用。另外,當(dāng)此 State 對(duì)象的依賴項(xiàng)更改時(shí)被調(diào)用,比如其所依賴的 InheritedWidget 發(fā)生變化時(shí), Framework 會(huì)調(diào)用此方法通知組件發(fā)生變化。

          下面將 A 和 F 組件改為 StatefulWidget 組件:

          class?F?extends?StatefulWidget?{
          ??@override
          ??_FState?createState()?=>?_FState();
          }

          class?_FState?extends?State<F>?{
          ??@override
          ??void?initState()?{
          ????super.initState();
          ????print('F?initState');
          ??}

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????print('F?build');
          ????return?Text('name:${MyInheritedWidget.of(context).userInfo.name}');
          ??}

          ??@override
          ??void?didChangeDependencies()?{
          ????super.didChangeDependencies();
          ????print('F?didChangeDependencies');
          ??}

          ??@override
          ??void?dispose()?{
          ????super.dispose();
          ????print('F?dispose');
          ??}
          }

          class?A?extends?StatefulWidget?{
          ??final?Widget?child;

          ??const?A({Key?key,?this.child})?:?super(key:?key);

          ??@override
          ??_AState?createState()?=>?_AState();
          }

          class?_AState?extends?State<A>?{
          ??@override
          ??void?initState()?{
          ????super.initState();
          ????print('A?initState');
          ??}

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????print('A?build');
          ????return?Center(
          ??????child:?widget.child,
          ????);
          ??}

          ??@override
          ??void?didChangeDependencies()?{
          ????super.didChangeDependencies();
          ????print('A?didChangeDependencies');
          ??}

          ??@override
          ??void?dispose()?{
          ????super.dispose();
          ????print('A?dispose');
          ??}
          }

          給各個(gè)生命周期添加日志打印,重新運(yùn)行,點(diǎn)擊按鈕,輸出日志如下:

          flutter:?A?build
          flutter:?F?didChangeDependencies
          flutter:?F?build

          因此,依賴 MyInheritedWidget 組件的 F 組件調(diào)用 didChangeDependencies 方法,而 A 組件沒有調(diào)用 didChangeDependencies 方法,因?yàn)?A 沒有依賴 MyInheritedWidget 組件。

          下面再說一個(gè)非常容易忽略的地方 MyInheritedWidget.updateShouldNotify方法,一般這樣寫:

          @override
          bool?updateShouldNotify(covariant?MyInheritedWidget?oldWidget)?{
          ??return?oldWidget.userInfo?!=?userInfo;
          }

          這樣寫有什么問題嗎?如果數(shù)據(jù)(userInfo)是自定義的實(shí)體類且未在 UserInfo 中重寫 ==,那么極大概率出現(xiàn)有問題,因?yàn)椴恢貙?== 操作符方法,使用 != 判斷是否相等的時(shí)候判斷的是兩個(gè)對(duì)象的內(nèi)存地址,下面將 UserInfo 中 ?== 方法去掉,

          class?UserInfo?{
          ??String?name;
          ??int?age;

          ??UserInfo({this.name,?this.age});
          }

          修改 _InheritedWidgetDemoState 類:

          class?_InheritedWidgetDemoState?extends?State<InheritedWidgetDemo>?{
          ??UserInfo?_userInfo?=?UserInfo(name:?'老孟',?age:?18);

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?Scaffold(
          ??????appBar:?AppBar(
          ????????title:?Text('InheritedWidget?Demo'),
          ??????),
          ??????body:?Center(
          ????????child:?MyInheritedWidget(
          ??????????userInfo:?_userInfo,
          ??????????child:?A(
          ????????????child:?F(),
          ??????????),
          ????????),
          ??????),
          ??????floatingActionButton:?FloatingActionButton(
          ????????onPressed:?()?{
          ??????????setState(()?{
          ????????????_userInfo?=?UserInfo(name:?'老孟',?age:?18);
          ??????????});
          ????????},
          ??????),
          ????);
          ??}
          }

          修改 updateShouldNotify 方法,添加日志打?。?/p>

          @override
          bool?updateShouldNotify(covariant?MyInheritedWidget?oldWidget)?{
          ??bool?flag?=?oldWidget.userInfo?!=?userInfo;
          ??print('updateShouldNotify:$flag');
          ??return?flag;
          }

          點(diǎn)擊按鈕,_userInfo 對(duì)象引用發(fā)生了變化,但其值( name 和 age)都沒有發(fā)生變化,updateShouldNotify 應(yīng)該返回false,但實(shí)際打印的結(jié)果:

          flutter:?updateShouldNotify:true
          flutter:?A?build
          flutter:?F?didChangeDependencies
          flutter:?F?build

          實(shí)際返回了 true,因?yàn)榍昂?_userInfo 對(duì)象引用發(fā)生了變化,在 UserInfo 中重寫 ==,比較具體的name 和 age 是否相等:

          @override
          bool?operator?==(Object?other)?{
          ??if?(!(other?is?UserInfo))?{
          ????return?false;
          ??}
          ??var?old?=?other?as?UserInfo;
          ??return?name?==?old.name?&&?age?==?old.age;
          }

          再次運(yùn)行,日志如下:

          flutter:?updateShouldNotify:false
          flutter:?A?build
          flutter:?F?build

          還有一種錯(cuò)誤寫法:

          class?_InheritedWidgetDemoState?extends?State<InheritedWidgetDemo>?{
          ??UserInfo?_userInfo?=?UserInfo(name:?'老孟',?age:?18);

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?Scaffold(
          ??????appBar:?AppBar(
          ????????title:?Text('InheritedWidget?Demo'),
          ??????),
          ??????body:?Center(
          ????????child:?MyInheritedWidget(
          ??????????userInfo:?_userInfo,
          ??????????child:?A(
          ????????????child:?F(),
          ??????????),
          ????????),
          ??????),
          ??????floatingActionButton:?FloatingActionButton(
          ????????onPressed:?()?{
          ??????????setState(()?{
          ????????????_userInfo.name?=?'老孟1';
          ????????????//?_userInfo?=?UserInfo(name:?'老孟',?age:?18);
          ??????????});
          ????????},
          ??????),
          ????);
          ??}
          }

          重點(diǎn)看這部分修改:

          floatingActionButton:?FloatingActionButton(
          ????????onPressed:?()?{
          ??????????setState(()?{
          ????????????_userInfo.name?=?'老孟1';
          ????????????//?_userInfo?=?UserInfo(name:?'老孟',?age:?18);
          ??????????});
          ????????},
          ??????),

          _userInfo = UserInfo(name: '老孟', age: 18) 修改為 _userInfo.name = '老孟1',猜猜 updateShouldNotify 返回是 true or false?

          運(yùn)行日志:

          flutter:?updateShouldNotify:false
          flutter:?A?build
          flutter:?F?build

          是不是感覺非常不可思議,兩次的 name 值不一樣???

          那是因?yàn)?_userInfo.name = '老孟1' 也修改了 oldWidget 的_userInfo,前后兩次都指向了同一個(gè)對(duì)象引用。

          很多人應(yīng)該會(huì)有這樣一個(gè)疑問,假設(shè)設(shè)置 updateShouldNotify 返回false,點(diǎn)擊的時(shí)候UI也會(huì)更改,因?yàn)檎w樹都 rebuild 了,那么 updateShouldNotify 由什么意義呢?

          肯定是有意義的,看如下場景,F(xiàn) 組件使用 InheritedWidget 的共享數(shù)據(jù)訪問服務(wù)器接口,獲取服務(wù)器數(shù)據(jù)并展示,如果 updateShouldNotify 返回 false,那么 F 組件 rebuild 時(shí)只會(huì)執(zhí)行 build 函數(shù),而訪問服務(wù)器接口是一個(gè)耗時(shí)工作,考慮性能因素,不能將訪問服務(wù)器接口放在 build 函數(shù)中,那么 InheritedWidget 數(shù)據(jù)的更新就無法更新其依賴的組件,而 updateShouldNotify 返回 true時(shí), F 組件 rebuild 時(shí)會(huì)執(zhí)行 didChangeDependencies 和 ?build 函數(shù),此時(shí)可以將訪問服務(wù)器接口放在 didChangeDependencies 函數(shù)中,這也是 didChangeDependencies 生命周期存在的意義。

          下面重點(diǎn)來了,那么如何正確使用 InheritedWidget 組件,答案是 InheritedWidget + ValueNotifier,關(guān)于 ****的用法可以到我的個(gè)人博客中查看,地址:http://laomengit.com/flutter/widgets/ValueListenableBuilder.html ,這里不詳細(xì)展開介紹。

          修改 MyInheritedWidget 代碼:

          class?MyInheritedWidget?extends?InheritedWidget?{
          ??ValueNotifier?_valueNotifier;

          ??ValueNotifier?get?valueNotifier?=>?_valueNotifier;

          ??MyInheritedWidget(UserInfo?userInfo,?{Widget?child})?:?super(child:?child)?{
          ????_valueNotifier?=?ValueNotifier(userInfo);
          ??}

          ??static?MyInheritedWidget?of(BuildContext?context)?{
          ????return?context.getElementForInheritedWidgetOfExactType().widget;
          ??}

          ??void?updateData(UserInfo?info)?{
          ????_valueNotifier.value?=?info;
          ??}

          ??@override
          ??bool?updateShouldNotify(covariant?MyInheritedWidget?oldWidget)?{
          ????return?false;
          ??}
          }

          主要的變化是:

          • 共享數(shù)據(jù)由 UserInfo 類型變?yōu)榱?ValueNotifier。

          • 增加了更新數(shù)據(jù)的方法 updateData。

          • updateShouldNotify 方法直接返回了 false,因?yàn)閿?shù)據(jù)的更新通過 ValueNotifier 實(shí)現(xiàn)。

          • 靜態(tài)方法 of

            static?MyInheritedWidget?of(BuildContext?context)?{
            ?return?context.dependOnInheritedWidgetOfExactType();
            }

            改為

            static?MyInheritedWidget?of(BuildContext?context)?{
            ??return?context.getElementForInheritedWidgetOfExactType().widget;
            }

            區(qū)別是 dependOnInheritedWidgetOfExactType 注冊(cè)了依賴關(guān)系,而 getElementForInheritedWidgetOfExactType 未注冊(cè),后面的源碼部分會(huì)詳細(xì)分析。

          修改 F 組件的代碼:

          class?F?extends?StatefulWidget?{
          ??@override
          ??_FState?createState()?=>?_FState();
          }

          class?_FState?extends?State<F>?{
          ??@override
          ??void?initState()?{
          ????super.initState();
          ????print('F?initState');
          ??}

          ??@override
          ??void?didChangeDependencies()?{
          ????super.didChangeDependencies();
          ????print('F?didChangeDependencies');
          ??}

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????print('F?build');
          ????return?ValueListenableBuilder(
          ??????builder:?(context,?UserInfo?value,?child)?{
          ????????return?Text('${value.name}');
          ??????},
          ??????valueListenable:?MyInheritedWidget.of(context).valueNotifier,
          ????);
          ??}

          ??@override
          ??void?dispose()?{
          ????super.dispose();
          ????print('F?dispose');
          ??}
          }

          變化:

          • 依賴共享數(shù)據(jù)的Text組件添加了 ValueListenableBuilder 組件,數(shù)據(jù)發(fā)生變化時(shí),重新執(zhí)行 builder 。

          _InheritedWidgetDemoState 代碼修改如下:

          @override
          Widget?build(BuildContext?context)?{
          ??return?MyInheritedWidget(UserInfo(name:?'老孟',?age:?18),?child:?Builder(
          ????builder:?(context)?{
          ??????return?Scaffold(
          ????????appBar:?AppBar(
          ??????????title:?Text('InheritedWidget?Demo'),
          ????????),
          ????????body:?Center(
          ??????????child:?A(
          ????????????child:?F(),
          ??????????),
          ????????),
          ????????floatingActionButton:?FloatingActionButton(
          ??????????onPressed:?()?{
          ????????????MyInheritedWidget.of(context)
          ????????????????.updateData(UserInfo(name:?'老孟${_clickCount++}',?age:?18));
          ??????????},
          ????????),
          ??????);
          ????},
          ??));
          }

          運(yùn)行效果:

          重點(diǎn)看 ?rebuild 的組件,無關(guān)的組件(比如 A)沒有 rebuild。

          當(dāng)然也可以使用 Provider 實(shí)現(xiàn)子組件更新,增加 UserInfoModel

          class?UserInfoModel?extends?ChangeNotifier?{
          ??UserInfoModel(this._userInfo);

          ??UserInfo?_userInfo;

          ??UserInfo?get?userInfo?=>?_userInfo;

          ??void?update(UserInfo?userInfo)?{
          ????_userInfo?=?userInfo;
          ????notifyListeners();
          ??}
          }

          修改 _InheritedWidgetDemoState

          class?_InheritedWidgetDemoState?extends?State<InheritedWidgetDemo>?{
          ??int?_clickCount?=0;

          ??@override
          ??Widget?build(BuildContext?context)?{
          ????return?MultiProvider(
          ??????providers:?[
          ????????ChangeNotifierProvider(
          ????????????create:?(_)?=>?UserInfoModel(UserInfo(name:?'老孟',?age:?18))),
          ??????],
          ??????builder:?(context,?child)?{
          ????????return?Scaffold(
          ??????????appBar:?AppBar(
          ????????????title:?Text('InheritedWidget?Demo'),
          ??????????),
          ??????????body:?Center(
          ????????????child:?A(
          ??????????????child:?F(),
          ????????????),
          ??????????),
          ??????????floatingActionButton:?Consumer(
          ????????????builder:?(ctx,?userInfoModel,?child)?{
          ??????????????return?FloatingActionButton(
          ????????????????child:?child,
          ????????????????onPressed:?()?{
          ??????????????????userInfoModel.update(UserInfo(name:?'老孟${_clickCount++}',?age:?18));
          ????????????????},
          ??????????????);
          ????????????},
          ??????????),
          ????????);
          ??????},
          ????);
          ??}
          }

          InheritedWidget 源碼分析

          分析源碼的時(shí)候一定要先想想,如果是我來實(shí)現(xiàn)這個(gè)組件,要如何實(shí)現(xiàn)?InheritedWidget 組件主要實(shí)現(xiàn)了兩個(gè)功能:

          • 如何實(shí)現(xiàn)綁定依賴它的子組件
          • 如何通知子組件自己發(fā)生了更改。

          如何實(shí)現(xiàn)綁定依賴它的子組件

          依賴 ?InheritedWidget 的子組件如何獲取 InheritedWidget 組件的共享數(shù)據(jù)?首先查看獲取共享數(shù)據(jù)的方法:

          context.dependOnInheritedWidgetOfExactType();

          這段代碼獲取 MyInheritedWidget 實(shí)例,dependOnInheritedWidgetOfExactType 方法是 BuildContext 的方法,Element 實(shí)現(xiàn)了此方法:

          @override
          T?dependOnInheritedWidgetOfExactTypeextends?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;
          }

          從上面的源代碼可以看出,首先到 _inheritedWidgets 中查找指定的 InheritedElement,_inheritedWidgets 這個(gè) Map 是哪里來的?什么時(shí)候被初始化的?看下 _inheritedWidgets 屬性的定義:

          Map<Type,?InheritedElement>?_inheritedWidgets;

          查找其引用和賦值的源代碼:

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

          上面的代碼在 InheritedElement 中,但此方法在 Element 中也實(shí)現(xiàn)了:

          void?_updateInheritance()?{
          ??assert(_active);
          ??_inheritedWidgets?=?_parent?._inheritedWidgets;
          }

          上面的代碼說明了非 InheritedElement 的 Element 中 _inheritedWidgets 等于父組件的 _inheritedWidgets,而 InheritedElement 會(huì)將自身添加到 _inheritedWidgets 中,系統(tǒng)通過此方式將組件和 InheritedWidgets 的依賴關(guān)系層層向下傳遞,每一個(gè) Element 中都含有 _inheritedWidgets 集合,此集合中包含了此組件的父組件且是InheritedWidgets 組件的引用關(guān)系。

          那么是什么時(shí)候執(zhí)行的 _updateInheritance 方法的呢?通過查找其引用,發(fā)現(xiàn)在 mountactivate 中調(diào)用了 ?_updateInheritance 方法。關(guān)于 mountactivate 階段可以查看 ?Stateful 組件的生命周期 文章。

          下面查看 dependOnInheritedElement 方法,在查找到依賴的 InheritedElement 后,執(zhí)行 dependOnInheritedElement 方法,源代碼如下:

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

          updateDependencies 方法源代碼如下:

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

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

          上面的代碼就是向 _dependents 中添加注冊(cè), InheritedWidget 組件更新時(shí)可以更具此列表通知子組件。

          再來看下的代碼:

          @Deprecated(
          ??'Use?getElementForInheritedWidgetOfExactType?instead.?'
          ??'This?feature?was?deprecated?after?v1.12.1.'
          )
          @override
          InheritedElement?ancestorInheritedElementForWidgetOfExactType(Type?targetType)?{
          ??assert(_debugCheckStateIsActiveForAncestorLookup());
          ??final?InheritedElement?ancestor?=?_inheritedWidgets?==?null???null?:?_inheritedWidgets[targetType];
          ??return?ancestor;
          }

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

          在 v1.12.1 版本以前使用 ancestorInheritedElementForWidgetOfExactType 方法,現(xiàn)在使用 getElementForInheritedWidgetOfExactType 方法,此方法和 dependOnInheritedWidgetOfExactType 方法的唯一卻不就是 getElementForInheritedWidgetOfExactType 方法沒有注冊(cè),也就是使用 getElementForInheritedWidgetOfExactType 方法獲取共享數(shù)據(jù)的子組件,不會(huì)在 InheritedWidget 組件重建時(shí)調(diào)用 didChangeDependencies 方法。

          下面看看為什么 InheritedWidget 組件數(shù)據(jù)方式變化,重建時(shí)會(huì)調(diào)用其 ?didChangeDependencies 方法?

          當(dāng)組件發(fā)生變化時(shí)會(huì)調(diào)用 update方法:

          @override
          void?update(ProxyWidget?newWidget)?{
          ??final?ProxyWidget?oldWidget?=?widget;
          ??assert(widget?!=?null);
          ??assert(widget?!=?newWidget);
          ??super.update(newWidget);
          ??assert(widget?==?newWidget);
          ??updated(oldWidget);
          ??_dirty?=?true;
          ??rebuild();
          }

          InheritedElement 重寫了 updated 方法:

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

          當(dāng) updateShouldNotify 返回 true時(shí),執(zhí)行更新操作。而其父類的 updated 方法如下:

          @protected
          void?updated(covariant?ProxyWidget?oldWidget)?{
          ??notifyClients(oldWidget);
          }

          notifyClients 方法源代碼:

          @override
          void?notifyClients(InheritedWidget?oldWidget)?{
          ??assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
          ??for?(final?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);
          ??}
          }

          遍歷 _dependents,上面已經(jīng)介紹,_dependents 是依賴它的子組件集合,遍歷調(diào)用 notifyDependent 方法:

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

          這里調(diào)用了 didChangeDependencies 方法,這也是 InheritedWidget 組件發(fā)生變化,重建時(shí)執(zhí)行生命周期 didChangeDependencies。

          上面的代碼都是在 InheritedElement 中的,在看下 ?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);
          }

          這個(gè)類非常簡單,創(chuàng)建了一個(gè) InheritedElement,定義了一個(gè) updateShouldNotify 方法(上面已經(jīng)詳細(xì)介紹此方法的作用),子類需要重寫。

          通過上面的源碼解析,子組件獲取共享數(shù)據(jù)時(shí),實(shí)際是直接在 ?_inheritedWidgets 集合中匹配的,通過斷點(diǎn)也可以查看其中的內(nèi)容:

          總結(jié)

          通過上面的分析,InheritedWidget 組件流程如下:

          說明:

          • 在當(dāng)前組件的 mountactivate 階段,系統(tǒng)調(diào)用 _updateInheritance 方法,將 InheritedWidget 類型的父組件添加到 _inheritedWidgets 集合中。
          • 子組件執(zhí)行 dependOnInheritedWidgetOfExactType 方法時(shí),從 _inheritedWidgets 集合中獲取指定 InheritedWidget 類型的父組件,并將當(dāng)前組件注冊(cè)到 InheritedWidget 類型父組件的 _dependents 集合中。
          • InheritedWidget 組件數(shù)據(jù)發(fā)生變化(updateShouldNotify 方法返回true),重建時(shí),InheritedWidget 組件遍歷 _dependents 集合中所有依賴的子組件,執(zhí)行子組件的 didChangeDependencies 的方法。

          一點(diǎn)看法

          那么為什么是在當(dāng)前組件的中保存這樣一個(gè) Map 集合,而不是依次向上查找呢(我最開始的想法)?

          下面是我個(gè)人的一點(diǎn)看法,如果你有不同的看法,歡迎一起討論:

          當(dāng)前組件的中保存這樣一個(gè) Map 集合,獲取共享數(shù)據(jù)時(shí)直接定位依賴的 InheritedWidget,復(fù)雜度 O(1) 。

          而依次向上查找的復(fù)雜度是 O(n),樹的結(jié)構(gòu)越深,消耗時(shí)間越長,復(fù)雜度線性增長。



          你可能還喜歡


          關(guān)注「老孟Flutter」
          讓你每天進(jìn)步一點(diǎn)點(diǎn)


          瀏覽 35
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品久久久久久久搜平安片 | 蜜桃传媒一区二区亚洲 | 国产娇小13videos糟蹋 | 国产视频第二页 | 黄片久久久久久久 |