Flutter的狀態(tài)管理(InheritedWidget类溢,Provider)

前言

當(dāng)應(yīng)用中需要一些跨組件(包括跨路由)的狀態(tài)需要同步時百框,如果不引入狀態(tài)管理技術(shù)的話。能想到的辦法就是將狀態(tài)進(jìn)行提升寡具,放到其共同的父節(jié)點(diǎn)上秤茅,然后將父節(jié)點(diǎn)設(shè)計為有狀態(tài)組件,并提供修改狀態(tài)的方法給到子組件童叠。

根據(jù)生命周期框喳,可以得知父組件的變化會引發(fā)子組件的 build ,就算子組件沒有任何的改動厦坛。但這樣子因?yàn)橐粋€操作五垮,需要將所有子組件進(jìn)行重新 build,成本實(shí)在太高杜秸。(在 React 中可以通過 shouldComponentUpdate 來“阻止”渲染放仗,進(jìn)行性能優(yōu)化,但在 Flutter 中是沒有該功能來減少重新 build 的)

下面有兩種方法撬碟,第一個是原生所使用的 InheritedWidget诞挨,第二個是官網(wǎng)推薦使用的技術(shù) Provider 莉撇。
(InheritedWidget 在多頁面間數(shù)據(jù)共享比較麻煩,因?yàn)樾枰粋€共同的父節(jié)點(diǎn))

InheritedWidget

InheritedWidget 是 Flutter 中非常重要的一個功能型組件亭姥,它提供了一種數(shù)據(jù)在 widget 樹中從上到下傳遞稼钩、共享的方式。(InheritedWidget 和 React 中的 context 功能類似达罗,和逐級傳遞數(shù)據(jù)相比坝撑,它們能實(shí)現(xiàn)組件跨級傳遞數(shù)據(jù))

創(chuàng)建一個狀態(tài)管理類組件 ChestnutWidget:

class ChestnutWidget extends InheritedWidget {

  // 共享狀態(tài)
  final String data;
  // 修改共享狀態(tài)方法
  final Function onDataChange;

  // 生成構(gòu)造函數(shù)
  ChestnutWidget({
    Key key,
    @required Widget child,
    @required this.data,
    @required this.onDataChange,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(ChestnutWidget old) {
    //判斷依賴的 data 狀態(tài)是否變化
    return old.data != data;
  }
}

使用時,在父節(jié)點(diǎn)上粮揉,將所有子節(jié)點(diǎn)作為該節(jié)點(diǎn)狀態(tài)管理類的一個參數(shù)傳遞給 InheritedWidget巡李。
有點(diǎn)類似于 React 中的高階組件(高階組件是參數(shù)為組件,返回值為新組件的函數(shù))扶认。

  //...
 @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        ChestnutWidget(
            child: childWidget,     //子組件
            onDataChange: onDataChange,
            data: data
        ),
      ],
    );
  }

子節(jié)點(diǎn)通過 dependOnInheritedWidgetOfExactType 方法獲取 InheritedWidget 中的屬性以及方法侨拦。

context.dependOnInheritedWidgetOfExactType<ShareDataWidget>()

如果不希望共享數(shù)據(jù)改變時,調(diào)用 didChangeDependencies 回調(diào)辐宾,導(dǎo)致 build 的話狱从。用 getElementForInheritedWidgetOfExactType 方法獲取 InheritedWidget 中的屬性。

context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget

調(diào)用dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的區(qū)別就是前者會注冊依賴關(guān)系叠纹,而后者不會季研。

Provider(基于InheritedWidget)
  • ChangeNotifier 是 Flutter SDK 中的一個簡單的類,繼承自 Listenable誉察,它用于向監(jiān)聽器發(fā)送通知与涡。(和觀察者模式相類似)
    在 provider 中,ChangeNotifier 是一種能夠封裝應(yīng)用狀態(tài)的方法持偏。

創(chuàng)建一個model:

class ChestnutModel extends ChangeNotifier {
  //聲明私有變量
  String _name = 'chestnut';

  //設(shè)置get方法
  String get name => _name;

  /// 修改當(dāng)前name
  void changeName(String name) {
    _name = name
  //告訴正在監(jiān)聽此 model 的 widgets 進(jìn)行重新 build 
    notifyListeners();
  }
}

上面代碼中驼卖,和 ChangeNotifier 相關(guān)的代碼就是調(diào)用 notifyListeners(),當(dāng) model 發(fā)生改變并且需要更新 UI 的時候可以調(diào)用該方法鸿秆,其他代碼就是本身的業(yè)務(wù)邏輯酌畜。

  • ChangeNotifierProvider widget 可以向其子孫節(jié)點(diǎn)暴露一個 ChangeNotifier 實(shí)例,放在需要訪問它的 widget 之上卿叽。它屬于 provider package桥胞。

通過 ChangeNotifierProvider 讓 ChestnutModel 與 widget 相關(guān)聯(lián):

 ChangeNotifierProvider(
   create: (context) => ChestnutModel(),
   child: childWidget,    //子組件
 ),

如想提供更多狀態(tài),可以使用 MultiProvider

 MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (context) => ChestnutModel()),
      Provider(create: (context) => SomeOtherModel()),
    ],
    child: childWidget ,    //子組件
  ),
  • Consumer 只是在新的 widget 中調(diào)用 Provider.of附帽,并將其構(gòu)建實(shí)現(xiàn)委派給 builder(builder 不能為null)埠戳。
    當(dāng) ChangeNotifier 發(fā)生變化的時候會調(diào)用 builder 這個函數(shù)。(換言之蕉扮,在 model 中調(diào)用 notifyListeners() 時整胃,所有和 Consumer 相關(guān)的 builder 方法都會被調(diào)用)

必須指定要訪問的 model 類型。這里訪問 ChestnutModel 那么就寫上 Consumer<ChestnutModel>:

Consumer<ChestnutModel>(
  builder: (context, model,child)=> Stack(
        children: [
          child,
          Text("Name: ${model.name}"),
        ],
      ),
 )

第一個是 context喳钟,在每個 build 方法中都能找到這個參數(shù)屁使。
第二個參數(shù)是最開始 ChangeNotifier 的實(shí)例在岂。
第三個參數(shù)是 child,用于優(yōu)化目的蛮寂。(當(dāng) model 發(fā)生改變的時候蔽午,該子樹并不會改變

讀取值的最簡單方法是使用靜態(tài)方法 Provider.of

Provider.of<ChestnutModel>(context).name

有的時候你不需要 model 中的數(shù)據(jù)來改變 UI,但是可能還是需要訪問該數(shù)據(jù)酬蹋。將listen: false 傳遞給 Provider.of及老,當(dāng) notifyListeners 被調(diào)用的時候,并不會使 widget 被重構(gòu)范抓。

Provider.of<ChestnutModel>(context, listen: false).changeName(‘newChestnut’);

學(xué)習(xí)鏈接:
provider文檔
《Flutter實(shí)戰(zhàn)》(7.2 數(shù)據(jù)共享)
Flutter 文檔中文版(簡單的應(yīng)用狀態(tài)管理)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骄恶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子匕垫,更是在濱河造成了極大的恐慌僧鲁,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件象泵,死亡現(xiàn)場離奇詭異寞秃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)偶惠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門春寿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洲鸠,你說我怎么就攤上這事堂淡〔雒澹” “怎么了扒腕?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萤悴。 經(jīng)常有香客問我瘾腰,道長,這世上最難降的妖魔是什么覆履? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任蹋盆,我火速辦了婚禮,結(jié)果婚禮上硝全,老公的妹妹穿的比我還像新娘栖雾。我一直安慰自己,他們只是感情好伟众,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布析藕。 她就那樣靜靜地躺著,像睡著了一般凳厢。 火紅的嫁衣襯著肌膚如雪账胧。 梳的紋絲不亂的頭發(fā)上竞慢,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機(jī)與錄音治泥,去河邊找鬼筹煮。 笑死,一個胖子當(dāng)著我的面吹牛居夹,可吹牛的內(nèi)容都是我干的败潦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼准脂,長吁一口氣:“原來是場噩夢啊……” “哼变屁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起意狠,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤粟关,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后环戈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闷板,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年院塞,在試婚紗的時候發(fā)現(xiàn)自己被綠了遮晚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡拦止,死狀恐怖县遣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情汹族,我是刑警寧澤萧求,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站顶瞒,受9級特大地震影響夸政,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榴徐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一守问、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坑资,春花似錦耗帕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春探越,著一層夾襖步出監(jiān)牢的瞬間狡赐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工钦幔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枕屉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓鲤氢,卻偏偏與公主長得像搀擂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子卷玉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348