Flutter_Bloc

Flutter中的Bloc

Bloc和Widget是一種強(qiáng)綁定的關(guān)系以清,下面介紹一些核心的概念典格。下面提到的狀態(tài)并不是Flutter原生的State廓潜,而是Bloc中的 概念母怜。

常用Widget

BlocBuilder

是一個widget余耽,需要一個Bloc參數(shù)和builder參數(shù)。BlocBuilder會自動響應(yīng)狀態(tài)去 build widget苹熏。根據(jù)狀態(tài)去build widget碟贾,并不是BlocBuilder的特點(diǎn),像FutureBuilder轨域、StreamBuilder都可以做到這一點(diǎn)袱耽。但是BlocBuilder是為了結(jié)合 Bloc使用。參數(shù)中的builder可能會被調(diào)用多次干发,因?yàn)橹灰辛藸顟B(tài)改變就會 觸發(fā)一次builder調(diào)用(響應(yīng)式)朱巨。

BlocBuilder<BlocA, BlocAState>(
 bloc: blocA,
  builder: (context, state) {
     //根據(jù)state 返回widget
  }
)

其中bloc參數(shù)不是必填項(xiàng),如果不傳入bloc參數(shù)枉长,那么會根據(jù)自己的context自動向上尋找 合適 的Bloc冀续。那么是如何向上尋找呢?通過Provider搀暑。

//不帶bloc參數(shù)
BlocBuilder<BlocA, BlocAState>(
  builder: (context, state) {
     //根據(jù)state 返回widget
  }
)

那么帶bloc參數(shù)和不帶bloc參數(shù)的區(qū)別是什么呢沥阳?
我們知道 bloc的作用是 將事件流 map為 狀態(tài)流。如果不帶參數(shù) 默認(rèn)是使用 父widget的 bloc自点,那就是說 子也需要這個狀態(tài)桐罕,這樣可以做到 將狀態(tài)層層下發(fā)下來,實(shí)現(xiàn)局部的widget刷新桂敛。比如將狀態(tài) 傳給了整個頁面級別的widget功炮,子widget也可以方便的獲取到這個 狀態(tài),不需要重新生成整個頁面术唬,也不需要通過構(gòu)造參數(shù)層層傳遞薪伏。

那么什么時候使用帶參數(shù)?什么時候使用不帶bloc參數(shù)呢粗仓?

第一:如果想要很好的自己控制自己嫁怀,那么就使用帶參數(shù)的设捐。

第二:如果通過context 向上尋找不到合適的bloc,那么就使用帶參數(shù)的塘淑。

我們知道Bloc是基于訂閱關(guān)系的萝招,那么就存在一種情況:每個輸入事件---都會產(chǎn)生一個狀態(tài)事件---都會調(diào)用build方法。我們知道flutter的widget是不可變的存捺,大多數(shù)情況下都會生成一個新的widget樹槐沼,這樣是十分消耗資源的。有些狀態(tài)不需要執(zhí)行build方法捌治,針對這種情況岗钩,BlocBuilder提供了一個condition方法參數(shù),他接受兩個兩個參數(shù):前一個狀態(tài)肖油、新生成的狀態(tài)兼吓,返回一個bool結(jié)果。如果true則執(zhí)行builder方法构韵,如果false則 不執(zhí)行周蹭。這樣就起到了過濾的作用。

BlocBuilder<BlocA, BlocAState>(
  condition: (previousState, state) {
    //根據(jù)返回的bool值去決定是否 指定builder方法
  },
  builder: (context, state) {
  
  }
)

BlocProvider

上面我們提到了 通過provider向上尋找到合適的Bloc疲恢。這個provider就是BlocProvider凶朗。BlocProvider也是一個widget,它可以為他的子樹提供一個Bloc显拳。子樹可以通過BlocProvider.of<T>(context)去獲取到Bloc棚愤。是不是很像InheritedWidget。

既然BlocProvider可以提供一個Bloc杂数。那么根據(jù)構(gòu)造Bloc的方式就可以分為兩類了:

一:構(gòu)造一個新的Bloc宛畦。

BlocProvider(
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

二:使用一個已經(jīng)存在的Bloc。

BlocProvider.value(
  value: BlocProvider.of<BlocA>(context),
  child: ScreenA(),
);

那么使用這兩者有什么區(qū)別嗎揍移?誰負(fù)責(zé)創(chuàng)建了Bloc次和,那么誰就要去關(guān)閉Bloc。

MultiBlocProvider

MultiBlocProvider同樣是一個Widget那伐,它的作用是為了:將多個BlocProvider合并到一個之中踏施。因?yàn)檫@樣的可以減少多BlocProviders的嵌套層級,提高代碼的可閱讀性罕邀。如下所示使用與不使用的對比:

//層層嵌套 ----》串行
BlocProvider<BlocA>(
  create: (BuildContext context) => BlocA(),
  child: BlocProvider<BlocB>(
    create: (BuildContext context) => BlocB(),
    child: BlocProvider<BlocC>(
      create: (BuildContext context) => BlocC(),
      child: ChildA(),
    )
  )
)
//多個合并----》并行
MultiBlocProvider(
  providers: [
    BlocProvider<BlocA>(
      create: (BuildContext context) => BlocA(),
    ),
    BlocProvider<BlocB>(
      create: (BuildContext context) => BlocB(),
    ),
    BlocProvider<BlocC>(
      create: (BuildContext context) => BlocC(),
    ),
  ],
  child: ChildA(),
)

BlocListener

BlocListener也是一個Widget畅形,它需要一個BlocWidgetListener構(gòu)造參數(shù)。BlocWidgetListener的用處是響應(yīng)Bloc的狀態(tài)變化诉探。

我們知道BlocBuilder的builder也是響應(yīng)狀態(tài)變化日熬,那兩者的區(qū)別是什么呢?第一:BlocListener的listener函數(shù) 對于一種狀態(tài)只會被調(diào)用一次肾胯,而BlocBuilder的builder一種狀態(tài)可能會調(diào)用多次竖席。第二:BlocListener的listener函數(shù)是沒有返回值的耘纱,而BlocBuilder的builder需要一個Widget返回值。

和上述的其他widget相似怕敬,BlocListener也有可選的Bloc參數(shù)以及condition參數(shù)揣炕。

BlocListener<BlocA, BlocAState>(
  condition: (previousState, state) {
    //根據(jù)返回的bool值,判斷是否調(diào)用listener方法
  },
  listener: (context, state) {
  }
  child: Container(),
)

MultiBlocListener

MultiBlocListener會合并多個BlocListener到一個中东跪,用法同上述的MultiBlocProvider。

MultiBlocListener(
  listeners: [
    BlocListener<BlocA, BlocAState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocB, BlocBState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocC, BlocCState>(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)

BlocConsumer

上面我們將了BlocBuilder和BlocListener鹰溜。BlocBuilder提供了一種根據(jù)狀態(tài) 構(gòu)建 widget的能力虽填,BlocListener提供了一種 監(jiān)聽狀態(tài)的能力。 但是曹动,我們即像 構(gòu)建Ui 又像 執(zhí)行操作 怎么辦呢斋日?要么在BlocBuilder的builder代碼中 融合 操作代碼,要么在BlocListener中融合 rebuild操作墓陈,不管哪種操作恶守,都十分的怪異。
BlocConsumer就提供了解決這種怪異的能力贡必,它對外暴露了builder 和 listener兔港,兩者的方法同上述的builder和listener。除此之外仔拟,為了更加精細(xì)的控制函數(shù)的調(diào)用時機(jī)衫樊,它還提供了可選的listenWhen和buildWhen參數(shù),這兩個參數(shù)都返回bool值利花,用法同上述的condition科侈。

BlocConsumer<BlocA, BlocAState>(
  listenWhen: (previous, current) {
    //根據(jù)前一個狀態(tài)和當(dāng)前狀態(tài) 判斷 是否調(diào)用listener
  },
  listener: (context, state) {
    
  },
  buildWhen: (previous, current) {
    //根據(jù)前一個狀態(tài)和當(dāng)前狀態(tài) 判斷是否調(diào)用builder
  },
  builder: (context, state) {
    
  }
)

RepositoryProvider

我們在開發(fā)中經(jīng)常需要使用父Widget或祖Widget的某些數(shù)據(jù),RepositoryProvider就提供了這樣的能力炒事。它的子樹們可以很方便的通過RepositoryProvider.of<T>(context)獲取到它的某些信息臀栈。思路同BlocProvider,只不過BlocProvider對子樹們提供了Bloc挠乳,RepositoryProvider提供的是任意指定的信息权薯。

RepositoryProvider(
  //指定了RepositoryA()信息
  builder: (context) => RepositoryA(),
  child: ChildA(),
);

//子樹們使用的方式
RepositoryProvider.of<RepositoryA>(context)

使用

上面我們介紹了幾個常用的widget,下面我們看一下具體的使用欲侮,仍以計(jì)數(shù)器為例崭闲。

事件

具體的事件就是用戶點(diǎn)擊的加一、減一威蕉。那么就是兩種事件刁俭。

enum CounterEvent { increment, decrement }

Bloc

Bloc就是將事件流轉(zhuǎn)為 狀態(tài)流。

//范型為:點(diǎn)擊的事件韧涨、數(shù)值狀態(tài)
class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;

 //狀態(tài)的轉(zhuǎn)換
  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield state - 1;
        break;
      case CounterEvent.increment:
        yield state + 1;
        break;
    }
  }
}

頁面


class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //構(gòu)造bloc
    CounterBloc counterBloc = CounterBloc();
    return BlocProvider(
      create: (BuildContext context) => counterBloc,
      child: Scaffold(
        appBar: AppBar(title: Text('Counter')),
        body: BlocBuilder<CounterBloc, int>(
          //指定builder需要的bloc
          bloc: counterBloc,
          //每次狀態(tài)變化 都會執(zhí)行builder
          builder: (context, count) {
            return Center(
              child: Text(
                '$count',
                style: TextStyle(fontSize: 24.0),
              ),
            );
          },
        ),
        floatingActionButton: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5.0),
              child: FloatingActionButton(
                child: Icon(Icons.add),
                onPressed: () {
                 //將事件 添加至 bloc
                 counterBloc.add(CounterEvent.increment);
                },
              ),
            ),
            Padding(
              padding: EdgeInsets.symmetric(vertical: 5.0),
              child: FloatingActionButton(
                child: Icon(Icons.remove),
                onPressed: () {
                 //將事件添加至 bloc
                 counterBloc.add(CounterEvent.decrement);
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

我們發(fā)現(xiàn)Bloc就是 一個中轉(zhuǎn)站牍戚,將事件轉(zhuǎn)為狀態(tài)侮繁。那么為什么可以將狀態(tài)轉(zhuǎn)完更新UI呢?其實(shí)是流的訂閱如孝。

總結(jié)

image

UI組件將事件分派給Bloc宪哩,Bloc內(nèi)部做一個轉(zhuǎn)換,將狀態(tài)發(fā)送給UI第晰,UI再去更新局部或者全部自己锁孟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市茁瘦,隨后出現(xiàn)的幾起案子品抽,更是在濱河造成了極大的恐慌,老刑警劉巖甜熔,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圆恤,死亡現(xiàn)場離奇詭異,居然都是意外死亡腔稀,警方通過查閱死者的電腦和手機(jī)盆昙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焊虏,“玉大人淡喜,你說我怎么就攤上這事】换矗” “怎么了拆火?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涂圆。 經(jīng)常有香客問我们镜,道長,這世上最難降的妖魔是什么润歉? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任模狭,我火速辦了婚禮,結(jié)果婚禮上踩衩,老公的妹妹穿的比我還像新娘嚼鹉。我一直安慰自己,他們只是感情好驱富,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布锚赤。 她就那樣靜靜地躺著,像睡著了一般褐鸥。 火紅的嫁衣襯著肌膚如雪线脚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機(jī)與錄音浑侥,去河邊找鬼姊舵。 笑死,一個胖子當(dāng)著我的面吹牛寓落,可吹牛的內(nèi)容都是我干的括丁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伶选,長吁一口氣:“原來是場噩夢啊……” “哼史飞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仰税,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤祸憋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肖卧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掸鹅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年塞帐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巍沙。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡葵姥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出句携,到底是詐尸還是另有隱情榔幸,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布矮嫉,位于F島的核電站削咆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蠢笋。R本人自食惡果不足惜拨齐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昨寞。 院中可真熱鬧瞻惋,春花似錦、人聲如沸援岩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽享怀。三九已至羽峰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背限寞。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工忍啸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人履植。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓计雌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親玫霎。 傳聞我的和親對象是個殘疾皇子凿滤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353