Flutter 狀態(tài)管理(數(shù)據(jù)共享)

所有程序里界面和數(shù)據(jù)的交互都至關(guān)重要嘲叔,直接決定了整個程序的結(jié)構(gòu),選好狀態(tài)管理方案的重要性就不言而喻了
目前為止,F(xiàn)lutter 里的狀態(tài)管理有很多的實現(xiàn)方法,官方也給出了很多的案例

官方對state的圖釋

官方給出的參考舉例

  • setState
  • ChangeNotifier
  • Delegate
  • scoped_model -InheritedWidget
  • Sigslot
  • provide
  • flutter-provide
  • rx dart, fish redux, bloc
  • EventBus

setState

最原始最基本 最重要的方式 setState,支持規(guī)模較小的程序足夠了逻淌,其它方式最終都需要調(diào)用 setState

Function callback

Flutter 內(nèi)置 ChangeNotifier, ValueNotifier 都可以認(rèn)為是類似方案

Delegate

可以認(rèn)為是多個回調(diào)函數(shù),其他語言里都有類似模式疟暖,名稱似乎來源于 Objective-C卡儒。

Sigslot

源自 Qt 里的經(jīng)典編程模式,Dart 可以輕易實現(xiàn)俐巴。這種方式在 Flutter 里可能根本不會有太多應(yīng)用

pkg:scoped_model

個人以為是最佳方案骨望,源自 Fuchsia 代碼,在其中廣泛使用欣舵,設(shè)置程序幾乎都是這個模式擎鸠,后來獨立為 package,包括注釋也只有 287 行代碼缘圈。由于 Fuchsia App 結(jié)構(gòu)都是后臺Service+前臺UI劣光,這個方案絕對是最合適的方案。使用 InheritedWidget 實現(xiàn)糟把,性能不可能更好绢涡。

pkg:provide

出自Flutter dev team,絕對的官方了遣疯,總共代碼 675行雄可。實現(xiàn)方式和 scoped_model 類似,增加 Providers缠犀,Provider 支持 Stream数苫。

flutter-provide

可以認(rèn)為這個比 provide 更早,功能更豐富辨液,實現(xiàn)依然是 InheritedWidget文判。可能不會有太廣泛使用室梅,但是在時間上有歷史意義,故列出。

rxdart, fish-redux,bloc

略……^?_?^

EventBus

分享主體:

InheritedWidget

可能對InheritedWidget 比較陌生亡鼠,但是在Flutter開發(fā)當(dāng)中赏殃,我們幾乎每個Page里都能看到的身影,并且用過它,间涵,它可以高效的將數(shù)據(jù)在Widget樹中向下傳遞仁热、共享,這在一些需要在Widget樹中共享數(shù)據(jù)的場景中非常方便勾哩,如Flutter中抗蠢,正是通過InheritedWidget來共享應(yīng)用主題(Theme)和Locale(當(dāng)前語言環(huán)境)信息的。它是自上而下的思劳。

如:

 MediaQuery.of(context).size.width
 Theme.of(context).snackBarTheme.actionTextColor

使用姿勢

  final InheritedWidgetModel model;

  //點擊+號的方法
  final Function() increment;

  //點擊-號的方法
  final Function() reduce;

  const MainInheritedWidget({Key key, Widget child, this.model, this.increment, this.reduce})
      : super(key: key, child: child);

  static MainInheritedWidget of(BuildContext context) {
    context.dependOnInheritedWidgetOfExactType<MainInheritedWidget>();
//    context.inheritFromWidgetOfExactType(MainInheritedWidget);
  }

  @override
  bool updateShouldNotify(MainInheritedWidget oldWidget) {
    return oldWidget.model != model;
  }

觸發(fā)事件

//觸發(fā)迅矛,完全是自定義
 MainInheritedWidget.of(context).increment();
//取數(shù)據(jù)
Text( "${MainInheritedWidget.of(context).model.count}",style: TextStyle(fontSize: 50 ,fontWeight: FontWeight.w900),),

scoped_model

短小精干的scoped_model,是以InheritedWidget為基礎(chǔ)的開發(fā),所以直接展示潜叛,它的讀取在一個函數(shù)體里去操作

class Counter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ScopedModelDescendant<CounterModel>(
      builder: (context, _, model) => ActionChip(
        label: Text('${model.count}'),//可以直接取
        onPressed: model.increaseCount,//可以直接改變
      ),
    );
  }
}

ChangeNotifier

通知Notification的發(fā)送是通過disPatch進(jìn)行分發(fā)的秽褒,就好像Android里面的事件分發(fā),當(dāng)NotificationListener監(jiān)聽到了通知事件威兜,這時候會走到其onNotification回調(diào)中销斟,根據(jù)回調(diào)中的返回值類型(true還是false)來決定是否還繼續(xù)向父親節(jié)點發(fā)送通知。它是自下而上椒舵。

接受數(shù)據(jù)

NotificationListener<TestNotification>(
        onNotification: (notification) {
          setState(() {
            count = notification.count;
          });
          return true;
        },

發(fā)送數(shù)據(jù)

 TestNotification(count: count).dispatch(context);

我們需要注意的地方:

  Builder(
    builder: (context) {
      return RaisedButton(
        color: Colors.blue,
        child: Text('+'),
        onPressed: () {
          count++;
          TestNotification(count: count).dispatch(context);
        },
      );
    },
  )

為什么我們一定要用Builder 取構(gòu)建一下蚂踊?
原因是通知在分發(fā)的時候,需要一個context參數(shù)笔宿,這個參數(shù)指的是Notification監(jiān)聽的子widget的context犁钟,如果直接的話,context是根widget的措伐,這樣會導(dǎo)致監(jiān)聽不到子widget了特纤。
所以需要我們通過Builder構(gòu)建出我們子widget的context,

Provide

和scoped_model一樣侥加,Provide也是借助了InheritWidget捧存,將共享狀態(tài)放到頂層MaterialApp之上。底層部件通過Provier獲取該狀態(tài)担败,并通過混合ChangeNotifier通知依賴于該狀態(tài)的組件刷新昔穴。Provide還提供了Provide.stream,讓我們能夠以處理流的方式處理數(shù)據(jù)

直接上才藝:

void main() {
 var counter = ProvideCounterModel();
 var providers = Providers();
 providers..provide(Provider<ProvideCounterModel>.value(counter));
 runApp(ProviderNode(
   child: ProvideApp(),
   providers: providers,
 ));

接受數(shù)據(jù):

child: Provide<ProvideCounterModel>(
 builder: (context, child, counter) {
   return Text(
     "${counter.count}",
     style: TextStyle(fontSize: 50, fontWeight: FontWeight.w900),
   );
 },
)

發(fā)送數(shù)據(jù):

RaisedButton(
    color: Colors.blue,
    child: Text('+'),
    onPressed: () {
      Provide.value<ProvideCounterModel>(context).increment();
    },
  )

EventBus

在APP中提前,我們經(jīng)常會需要一個廣播機(jī)制吗货,用以跨頁面事件通知,F(xiàn)lutter中我們可以使用event_bus提供的事件總線功能來實現(xiàn)一些狀態(tài)的更新狈网,其核心是基于Dart Streams(流)宙搬;事件總線通常實現(xiàn)了訂閱者模式笨腥,訂閱者模式包含發(fā)布者和訂閱者兩種角色,可以通過事件總線來觸發(fā)事件和監(jiān)聽事件

EventBus才藝展示

import 'package:event_bus/event_bus.dart';

EventBus eventBus = new EventBus();


class CounterEvent {
  int count;

  CounterEvent({this.count});
}

接受數(shù)據(jù)

  @override
  void initState() {
    super.initState();
    print("initState");
    subscription = eventBus.on<CounterEvent>().listen((event) {
      setState(() {
        count = event.count;
      });
    });
  }

  @override
  void dispose() {
    print("dispose");
    if (subscription != null) {
      subscription.cancel();
    }
    super.dispose();
  }

發(fā)送數(shù)據(jù)

RaisedButton(
color: Colors.blue,
child: Text('+'),
onPressed: () {
  count ++;
  eventBus.fire(CounterEvent(count: count));
},
)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末勇垛,一起剝皮案震驚了整個濱河市脖母,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闲孤,老刑警劉巖谆级,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異讼积,居然都是意外死亡肥照,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門勤众,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舆绎,“玉大人,你說我怎么就攤上這事决摧∫谡簦” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵掌桩,是天一觀的道長边锁。 經(jīng)常有香客問我,道長波岛,這世上最難降的妖魔是什么茅坛? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮则拷,結(jié)果婚禮上贡蓖,老公的妹妹穿的比我還像新娘。我一直安慰自己煌茬,他們只是感情好斥铺,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坛善,像睡著了一般晾蜘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眠屎,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天剔交,我揣著相機(jī)與錄音,去河邊找鬼改衩。 笑死岖常,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的葫督。 我是一名探鬼主播竭鞍,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼板惑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了笼蛛?” 一聲冷哼從身側(cè)響起洒放,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滨砍,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妖异,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡惋戏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了他膳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片响逢。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖棕孙,靈堂內(nèi)的尸體忽然破棺而出舔亭,到底是詐尸還是另有隱情,我是刑警寧澤蟀俊,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布钦铺,位于F島的核電站,受9級特大地震影響肢预,放射性物質(zhì)發(fā)生泄漏矛洞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一烫映、第九天 我趴在偏房一處隱蔽的房頂上張望沼本。 院中可真熱鬧,春花似錦锭沟、人聲如沸抽兆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辫红。三九已至,卻和暖如春瞧筛,著一層夾襖步出監(jiān)牢的瞬間厉熟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工较幌, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留揍瑟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓乍炉,卻偏偏與公主長得像绢片,于是被迫代替她去往敵國和親滤馍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355