接上一篇 Flutter狀態(tài)管理之路(二)沪伙,
此篇主要介紹Flutter_Bloc
Flutter_Bloc
版本:bloc 3.0.0 flutter_bloc 3.0.0
庫(kù)地址:https://github.com/felangel/bloc
全稱為 Business Logic Component,表示為業(yè)務(wù)邏輯組件,簡(jiǎn)稱 BLoC
概念
對(duì)象 | 說(shuō)明 |
---|---|
Event | 表示觸發(fā)某個(gè)狀態(tài)改變的事件 |
State | 狀態(tài)值 |
Stream | 用于傳輸各個(gè)時(shí)刻的狀態(tài)值的流 |
Bloc | “Bussiness Logic Component"即業(yè)務(wù)邏輯組件,響應(yīng)Action县好、作出處理围橡、通過(guò)stream輸出新的狀態(tài) |
BlocBuilder | flutter組件,封裝Bloc和組件響應(yīng)State變化更新UI的邏輯 |
BlocProvider | 利用DI注入使得子孫組件可以獲取其綁定的Bloc |
使用例子
依然是計(jì)數(shù)器例子缕贡,官方Demo : CounterPage
- counter_bloc.dart
enum CounterEvent { increment, decrement } /// 1. 定義事件
class CounterBloc extends Bloc<CounterEvent, int> { /// 2. 定義Bloc
@override
int get initialState => 0;
@override
Stream<int> mapEventToState(CounterEvent event) async* {
/// 3. 定義根據(jù)Event作出的狀態(tài)改變響應(yīng)
switch (event) {
case CounterEvent.decrement:
yield state - 1;
break;
case CounterEvent.increment:
yield state + 1;
break;
}
}
}
ps : async* yield 異步生成器語(yǔ)法某饰,用于生成異步序列:Stream
- counter_page.dart
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context); 4. 子Widget獲取
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, int>( 5. 用BlocBuilder完成狀態(tài)綁定
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: () {
counterBloc.add(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
counterBloc.add(CounterEvent.decrement); /// 6.發(fā)出事件
},
),
),
],
),
);
}
}
- app.dart
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider( /// 7. 注入Bloc實(shí)例
create: (context) => CounterBloc(),
child: CounterPage(),
),
theme: theme,
);
}
}
圖示
架構(gòu)思想:
原理:
關(guān)鍵對(duì)象
Bloc
“Bussiness Logic Component"即業(yè)務(wù)邏輯組件儒恋,響應(yīng)Action、作出處理黔漂、通過(guò)stream輸出新的狀態(tài)
構(gòu)造方法如下
abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> {
final PublishSubject<Event> _eventSubject = PublishSubject<Event>();
BehaviorSubject<State> _stateSubject;
...
Bloc() {
_stateSubject = BehaviorSubject<State>.seeded(initialState); /// 初始化狀態(tài)訂閱器
_bindStateSubject();
}
...
}
_bindStateSubject方法實(shí)現(xiàn)事件流通道處理Event并轉(zhuǎn)接到狀態(tài)流的過(guò)程
Stream<State> transformStates(Stream<State> states) => states;
Stream<State> transformEvents(
Stream<Event> events,
Stream<State> Function(Event) next,
) {
return events.asyncExpand(next); /// 將events流通道的元素一個(gè)個(gè)調(diào)用next方法并返回State的流
}
void _bindStateSubject() {
Event currentEvent;
transformStates(transformEvents(_eventSubject, (Event event) {
currentEvent = event;
return mapEventToState(currentEvent).handleError(_handleError);
})).forEach(
(State nextState) {
if (state == nextState || _stateSubject.isClosed) return;
...
_stateSubject.add(nextState); /// 往State流通道加入新的狀態(tài)元素并觸發(fā)對(duì)應(yīng)的觀察者
...
},
);
}
其余關(guān)鍵方法
@override
StreamSubscription<State> listen( /// 監(jiān)聽狀態(tài)流
void Function(State) onData, {
Function onError,
void Function() onDone,
bool cancelOnError,
}) {
return _stateSubject.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError,
);
}
Future<void> close() async {
await _eventSubject.close();
await _stateSubject.close();
}
@override
void add(Event event) {
...
_eventSubject.sink.add(event); /// 往事件流里輸入
...
}
BlocBuilder
class BlocBuilder<B extends Bloc<dynamic, S>, S> extends BlocBuilderBase<B, S> {
final BlocWidgetBuilder<S> builder;
const BlocBuilder({
Key key,
@required this.builder,
B bloc,
BlocBuilderCondition<S> condition,
}) : assert(builder != null),
super(key: key, bloc: bloc, condition: condition);
@override
Widget build(BuildContext context, S state) => builder(context, state);
}
abstract class BlocBuilderBase<B extends Bloc<dynamic, S>, S>
extends StatefulWidget {
const BlocBuilderBase({Key key, this.bloc, this.condition}) : super(key: key);
Widget build(BuildContext context, S state);
@override
State<BlocBuilderBase<B, S>> createState() => _BlocBuilderBaseState<B, S>();
}
class _BlocBuilderBaseState<B extends Bloc<dynamic, S>, S>
extends State<BlocBuilderBase<B, S>> {
StreamSubscription<S> _subscription;
S _previousState;
S _state;
B _bloc;
@override
void initState() {
super.initState();
_bloc = widget.bloc ?? BlocProvider.of<B>(context);
_previousState = _bloc?.state;
_state = _bloc?.state;
/// 訂閱
_subscribe();
}
...
@override
Widget build(BuildContext context) => widget.build(context, _state);
@override
void dispose() {
/// 取消訂閱
_unsubscribe();
super.dispose();
}
void _subscribe() {
if (_bloc != null) {
_subscription = _bloc.skip(1).listen((S state) {
if (widget.condition?.call(_previousState, state) ?? true) { /// 此處condition可作性能優(yōu)化判斷狀態(tài)改變是否需要出發(fā)build
setState(() {
_state = state;
});
}
_previousState = state;
});
}
}
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
}
BlocProvider
BlocProvider實(shí)際是利用了Provider庫(kù)的DI注入功能诫尽,并完成bloc生命周期的管理
class BlocProvider<T extends Bloc<dynamic, dynamic>>
extends SingleChildStatelessWidget {
...
BlocProvider({
Key key,
@required Create<T> create,
Widget child,
bool lazy,
}) : this._(
key: key,
create: create,
dispose: (_, bloc) => bloc?.close(),
child: child,
lazy: lazy,
);
BlocProvider.value({
Key key,
@required T value,
Widget child,
}) : this._(
key: key,
create: (_) => value,
child: child,
);
BlocProvider._({
Key key,
@required Create<T> create,
Dispose<T> dispose,
this.child,
this.lazy,
}) : _create = create,
_dispose = dispose,
super(key: key, child: child);
static T of<T extends Bloc<dynamic, dynamic>>(BuildContext context) {
...
return Provider.of<T>(context, listen: false);
...
}
@override
Widget buildWithChild(BuildContext context, Widget child) {
return InheritedProvider<T>( /// 使用Provider庫(kù)完成DI注入
create: _create,
dispose: _dispose,
child: child,
lazy: lazy,
);
}
}
進(jìn)階
bloc還提供一系列的輔助方法讓我們更好地控制數(shù)據(jù)流
-
Transition
在bloc里的回調(diào),在往狀態(tài)流通道加入數(shù)據(jù)前調(diào)用炬守,可以獲取狀態(tài)的改變情況
abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> { ... void onTransition(Transition<Event, State> transition) => null; ... }
class Transition<Event, State> { final State currentState; final Event event; final State nextState; const Transition({ @required this.currentState, /// 當(dāng)前狀態(tài) @required this.event, /// 觸發(fā)此次狀態(tài)轉(zhuǎn)換的事件 @required this.nextState, /// 將要改變成的狀態(tài) }); ... }
-
BlocDelegate
bloc的全局代理對(duì)象牧嫉,可繼承它并覆寫相應(yīng)的回調(diào)方法,如下
class SimpleBlocDelegate extends BlocDelegate { @override void onEvent(Bloc bloc, Object event) => null; /// 事件監(jiān)聽 @override void onTransition(Bloc bloc, Transition transition) => null; /// 狀態(tài)改變 @override void onError(Bloc bloc, Object error, StackTrace stacktrace) => null; /// 錯(cuò)誤捕捉 }
然后注冊(cè)
void main() { BlocSupervisor.delegate = SimpleBlocDelegate(); /// BlocSupervisor全局單例 ... }
-
MultiBlocProvider
用來(lái)將嵌套的BlocProvider給扁平化(使用了庫(kù) nested )减途,如:
BlocProvider<BlocA>( create: (BuildContext context) => BlocA(), child: BlocProvider<BlocB>( create: (BuildContext context) => BlocB(), child: BlocProvider<BlocC>( create: (BuildContext context) => BlocC(), child: ChildA(), ) ) )
變?yōu)?
MultiBlocProvider( providers: [ BlocProvider<BlocA>( create: (BuildContext context) => BlocA(), ), BlocProvider<BlocB>( create: (BuildContext context) => BlocB(), ), BlocProvider<BlocC>( create: (BuildContext context) => BlocC(), ), ], child: ChildA(), )
-
BlocListener
實(shí)際是 BlocBuilder的另一種實(shí)現(xiàn)變化酣藻,應(yīng)用緩存child的方式,每次setState只觸發(fā)listener而不重新生成child鳍置,可用于攔截狀態(tài)彈Toast辽剧,導(dǎo)航等等操作
class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S> { final Widget child; const BlocListener({ Key key, @required BlocWidgetListener<S> listener, /// 作用同 B bloc, /// 關(guān)注的bloc,不提供則會(huì)自動(dòng)用泛型of往上找 BlocListenerCondition<S> condition, /// 同BlocBuilder里的作用 this.child, /// 應(yīng)用緩存child的方式税产,每次build只觸發(fā)listener而用同一個(gè)child }) : super( key: key, child: child, listener: listener, bloc: bloc, condition: condition, ); }
總結(jié)
優(yōu)點(diǎn):
- 事件通道處理事件轉(zhuǎn)換狀態(tài)怕轿,串聯(lián)狀態(tài)通道通知外部訂閱對(duì)象
- 可實(shí)現(xiàn)局部 按需刷新
- 狀態(tài)源各自Bloc管理,實(shí)現(xiàn)分治辟拷,同時(shí)也可以利用Delegate實(shí)現(xiàn)全局的事件管理
缺點(diǎn):
- 相較于redux撞羽,更集中在"分治"的焦點(diǎn)上,但是bloc組件之間缺少有效的通信機(jī)制
- 缺少M(fèi)iddleware(AOP)模式的有效支持
- 如果父組件發(fā)生更新衫冻,子組件綁定的數(shù)據(jù)源并未發(fā)生變化诀紊,仍會(huì)導(dǎo)致子的rebuild(可利用緩存child解決)