接上一篇 Flutter状态管理之路(二) , 此篇主要介绍Flutter_Bloc
Flutter_Bloc 版本:bloc 3.0.0 flutter_bloc 3.0.0
库地址:https://github.com/felangel/bloc
全称为 Business Logic Component,表示为业务逻辑组件,简称 BLoC
概念
对象
说明
Event
表示触发某个状态改变的事件
State
状态值
Stream
用于传输各个时刻的状态值的流
Bloc
“Bussiness Logic Component”即业务逻辑组件,响应Action、作出处理、通过stream输出新的状态
BlocBuilder
flutter组件,封装Bloc和组件响应State变化更新UI的逻辑
BlocProvider
利用DI注入使得子孙组件可以获取其绑定的Bloc
使用例子 依然是计数器例子,官方Demo : CounterPage
counter_bloc.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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. 定义根据Event作出的状态改变响应 switch (event) { case CounterEvent.decrement: yield state - 1; break; case CounterEvent.increment: yield state + 1; break; } } }
ps : async* yield 异步生成器语法,用于生成异步序列:Stream
counter_page.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 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完成状态绑定 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.发出事件 }, ), ), ], ), ); } }
app.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', home: BlocProvider( /// 7. 注入Bloc实例 create: (context) => CounterBloc(), child: CounterPage(), ), theme: theme, ); } }
图示 架构思想:
bloc_architecture.png
原理:
bloc图示1.png
关键对象 Bloc “Bussiness Logic Component”即业务逻辑组件,响应Action、作出处理、通过stream输出新的状态
构造方法如下
1 2 3 4 5 6 7 8 9 10 11 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); /// 初始化状态订阅器 _bindStateSubject(); } ... }
_bindStateSubject方法实现事件流通道处理Event并转接到状态流的过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Stream<State> transformStates(Stream<State> states) => states; Stream<State> transformEvents( Stream<Event> events, Stream<State> Function(Event) next, ) { return events.asyncExpand(next); /// 将events流通道的元素一个个调用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流通道加入新的状态元素并触发对应的观察者 ... }, ); }
其余关键方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @override StreamSubscription<State> listen( /// 监听状态流 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 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可作性能优化判断状态改变是否需要出发build setState(() { _state = state; }); } _previousState = state; }); } } void _unsubscribe() { if (_subscription != null) { _subscription.cancel(); _subscription = null; } } }
BlocProvider BlocProvider实际是利用了Provider库的DI注入功能,并完成bloc生命周期的管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 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库完成DI注入 create: _create, dispose: _dispose, child: child, lazy: lazy, ); } }
进阶 bloc还提供一系列的辅助方法让我们更好地控制数据流
Transition
在bloc里的回调,在往状态流通道加入数据前调用,可以获取状态的改变情况
1 2 3 4 5 abstract class Bloc<Event, State> extends Stream<State> implements Sink<Event> { ... void onTransition(Transition<Event, State> transition) => null; ... }
1 2 3 4 5 6 7 8 9 10 11 12 class Transition<Event, State> { final State currentState; final Event event; final State nextState; const Transition({ @required this.currentState, /// 当前状态 @required this.event, /// 触发此次状态转换的事件 @required this.nextState, /// 将要改变成的状态 }); ... }
BlocDelegate
bloc的全局代理对象,可继承它并覆写相应的回调方法,如下
1 2 3 4 5 6 7 8 9 10 11 class SimpleBlocDelegate extends BlocDelegate { @override void onEvent(Bloc bloc, Object event) => null; /// 事件监听 @override void onTransition(Bloc bloc, Transition transition) => null; /// 状态改变 @override void onError(Bloc bloc, Object error, StackTrace stacktrace) => null; /// 错误捕捉 }
然后注册
1 2 3 4 void main() { BlocSupervisor.delegate = SimpleBlocDelegate(); /// BlocSupervisor全局单例 ... }
MultiBlocProvider
用来将嵌套的BlocProvider给扁平化(使用了库 nested ),如:
1 2 3 4 5 6 7 8 9 10 BlocProvider<BlocA>( create: (BuildContext context) => BlocA(), child: BlocProvider<BlocB>( create: (BuildContext context) => BlocB(), child: BlocProvider<BlocC>( create: (BuildContext context) => BlocC(), child: ChildA(), ) ) )
变为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 MultiBlocProvider( providers: [ BlocProvider<BlocA>( create: (BuildContext context) => BlocA(), ), BlocProvider<BlocB>( create: (BuildContext context) => BlocB(), ), BlocProvider<BlocC>( create: (BuildContext context) => BlocC(), ), ], child: ChildA(), )
BlocListener
实际是 BlocBuilder的另一种实现变化,应用缓存child的方式,每次setState只触发listener而不重新生成child,可用于拦截状态弹Toast,导航等等操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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, /// 关注的bloc,不提供则会自动用泛型of往上找 BlocListenerCondition<S> condition, /// 同BlocBuilder里的作用 this.child, /// 应用缓存child的方式,每次build只触发listener而用同一个child }) : super( key: key, child: child, listener: listener, bloc: bloc, condition: condition, ); }
总结 优点:
事件通道处理事件转换状态,串联状态通道通知外部订阅对象
可实现局部 按需刷新
状态源各自Bloc管理,实现分治,同时也可以利用Delegate实现全局的事件管理
缺点:
相较于redux,更集中在”分治”的焦点上,但是bloc组件之间缺少有效的通信机制
缺少Middleware(AOP)模式的有效支持
如果父组件发生更新,子组件绑定的数据源并未发生变化,仍会导致子的rebuild(可利用缓存child解决)