本篇已同步到 個(gè)人博客 冯丙,歡迎常來(lái)船老。
本文為《Flutter Bloc Package》的譯文酥筝,原文地址滚躯,若轉(zhuǎn)載譯文請(qǐng)注明出處。
在使用Flutter工作一段時(shí)間之后嘿歌,我決定創(chuàng)建一個(gè)軟件包以幫助我經(jīng)常使用的東西:BLoC模式掸掏。
對(duì)于那些不熟悉BLoC模式的人來(lái)說(shuō),它是一種設(shè)計(jì)模式宙帝,有助于將表示層與業(yè)務(wù)邏輯分開丧凤。你在這里了解更多。
使用BLoC模式可能具有挑戰(zhàn)性,因?yàn)樾枰?duì)Streams和Reactive Programming的理解步脓。但它的核心是BLoC非常簡(jiǎn)單:
BLoC 將event流作為輸入愿待,并將它們轉(zhuǎn)換為state流作為輸出。
我們現(xiàn)在可以在bloc的dart包的幫助下使用這種強(qiáng)大的設(shè)計(jì)模式靴患。
該軟件包抽象了模式的反應(yīng)方面仍侥,允許開發(fā)人員專注于將事件(event)轉(zhuǎn)換為狀態(tài)(state)。
讓我們從定義這些術(shù)語(yǔ)開始......
詞匯表
Events 是Bloc的輸入鸳君。它們通常是UI事件农渊,例如按鈕按下。Events被分發(fā)(dispatched)并且被轉(zhuǎn)換為States或颊。
States 是Bloc的輸出砸紊。表示組件可以監(jiān)聽狀態(tài)流 并根據(jù)給定狀態(tài)重繪其自身的部分(BlocBuilder有關(guān)詳細(xì)信息,請(qǐng)參閱參考資料)囱挑。
Transitions 發(fā)生在 調(diào)用mapEventToState之后 但在更新了bloc的state之前 調(diào)度了一個(gè)Event
現(xiàn)在我們了解事件和狀態(tài)醉顽,我們可以看一下Bloc API。
BLOC API
mapEventToState
當(dāng)一個(gè)類繼承Bloc時(shí)看铆,必須實(shí)現(xiàn) mapEventToState 方法徽鼎, 該函數(shù)將傳入事件作為參數(shù)。
只要UI層觸發(fā)一個(gè)事件弹惦,就會(huì)調(diào)用 mapEventToState否淤。
mapEventToState 必須將該event轉(zhuǎn)換為新state,并以UI層使用的Stream形式返回新狀態(tài)棠隐。
dispatch
dispatch 是一個(gè) 接受 event 并觸發(fā) mapEventToState 的方法石抡。
可以從表示層調(diào)用dispatch 或 從Bloc內(nèi)部(見例子)并通知Bloc一個(gè)新 event。
initialState
initialState是處理任何事件之前的狀態(tài)(在mapEventToState被調(diào)用之前)助泽。
如果未實(shí)現(xiàn)啰扛,則為initialState null嚎京。
transform
transform是一個(gè) 在調(diào)用mapEventToState之前 可以重寫以轉(zhuǎn)換 Stream<Event> .
這允許使用distinct() 和 debounce() 的操作。
onTransition
onTransition 是一個(gè) 每次 transform 發(fā)生時(shí)都可以重寫以進(jìn)行處理 的方法隐解。
調(diào)度新event 并調(diào)用mapEventToState時(shí)發(fā)生transition鞍帝。
onTransition 在更新 bloc 狀態(tài)之前 被調(diào)用。
這是添加特定于塊的日志記錄/分析的好地方
讓我們創(chuàng)建一個(gè)counter bloc煞茫!
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}
創(chuàng)建一個(gè)BLoC 需要作如下操作:
- 定義所有 event 和 state
- 繼承Bloc
- 重寫 initialState 和 mapEventToState
在這種情況下帕涌,我們的 events 是CounterEvents ,states 是 integers
CounterBloc 轉(zhuǎn)換 CounterEvents 為 integers。
我們可以通過(guò) dispatch 來(lái) 通知CounterBloc 事件
void main() {
final counterBloc = CounterBloc();
counterBloc.dispatch(CounterEvent.increment);
counterBloc.dispatch(CounterEvent.decrement);
}
為了觀察狀state 的 轉(zhuǎn)換(Transitions)续徽,我們可以重寫onTransition蚓曼。
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc<CounterEvent, int> {
@override
int get initialState => 0;
@override
void onTransition(Transition<CounterEvent, int> transition) {
print(transition);
}
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
yield currentState - 1;
break;
case CounterEvent.increment:
yield currentState + 1;
break;
}
}
}
現(xiàn)在,每當(dāng)發(fā)出(dispatch)一個(gè)CounterEvent我們的Bloc將響應(yīng)一個(gè)新的integer狀態(tài)钦扭,我們將看到一個(gè)transition被輸出到控制臺(tái)纫版。
現(xiàn)在讓我們使用Flutter構(gòu)建一個(gè)UI,并使用flutter_bloc 包將UI連接到我們的CounterBloc客情。
BlocBuilder
BlocBuilder是一個(gè)Flutter小部件其弊,它需要一個(gè)Bloc和一個(gè)構(gòu)建器函數(shù)。
BlocBuilder處理構(gòu)建窗口小部件以響應(yīng)新state裹匙。
BlocBuilder與StreamBuilder非常相似瑞凑,但它有一個(gè)更簡(jiǎn)單的API來(lái)減少所需的樣板代碼量末秃。
BlocProvider
BlocProvider是一個(gè)Flutter小部件概页,它通過(guò) BlocProvider.of(context)為其子女提供了一個(gè)bloc。
它用作依賴注入(DI)小部件, 這樣一個(gè)bloc實(shí)例 可以被提供給子樹中的多個(gè)小部件练慕。
現(xiàn)在讓我們構(gòu)建 counter App
class App extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AppState();
}
class _AppState extends State<App> {
final CounterBloc _counterBloc = CounterBloc();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider<CounterBloc>(
bloc: _counterBloc,
child: CounterPage(),
),
);
}
@override
void dispose() {
_counterBloc.dispose();
super.dispose();
}
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc _counterBloc = BlocProvider.of<CounterBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterEvent, int>(
bloc: _counterBloc,
builder: (BuildContext context, int 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: () {
_counterBloc.dispatch(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
_counterBloc.dispatch(CounterEvent.decrement);
},
),
),
],
),
);
}
}
該App小部件是StatefulWidget惰匙,負(fù)責(zé)創(chuàng)建和銷毀 CounterBloc。
它讓 CounterBloc 使用 BlocProvider 小部件可用于 CounterPage 小部件铃将。
CounterPage小部件是StatelessWidget项鬼, 它使用BlocBuilder重建UI以響應(yīng)CounterBloc的狀態(tài)變化。
此時(shí)劲阎,我們已經(jīng)成功地將我們的表示層與業(yè)務(wù)邏輯層分開绘盟。請(qǐng)注意,CounterPage窗口小部件對(duì)用戶點(diǎn)擊按鈕時(shí)發(fā)生的情況一無(wú)所知悯仙。小部件只是告訴CounterBloc用戶按下了遞增或遞減按鈕龄毡。
有關(guān)更多示例和詳細(xì)文檔,請(qǐng)查看官方集團(tuán)文檔锡垄。
相關(guān)鏈接: