接上一篇 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
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
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
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
“Bussiness Logic Component"即业务逻辑组件,响应Action、作出处理、通过stream输出新的状态
构造方法如下
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并转接到状态流的过程
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流通道加入新的状态元素并触发对应的观察者
...
},
);
}
其余关键方法
@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
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可作性能优化判断状态改变是否需要出发build
setState(() {
_state = state;
});
}
_previousState = state;
});
}
}
void _unsubscribe() {
if (_subscription != null) {
_subscription.cancel();
_subscription = null;
}
}
}
BlocProvider
BlocProvider实际是利用了Provider库的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库完成DI注入
create: _create,
dispose: _dispose,
child: child,
lazy: lazy,
);
}
}
进阶
bloc还提供一系列的辅助方法让我们更好地控制数据流
-
Transition
在bloc里的回调,在往状态流通道加入数据前调用,可以获取状态的改变情况
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, /// 当前状态 @required this.event, /// 触发此次状态转换的事件 @required this.nextState, /// 将要改变成的状态 }); ... }
-
BlocDelegate
bloc的全局代理对象,可继承它并覆写相应的回调方法,如下
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; /// 错误捕捉 }
然后注册
void main() { BlocSupervisor.delegate = SimpleBlocDelegate(); /// BlocSupervisor全局单例 ... }
-
MultiBlocProvider
用来将嵌套的BlocProvider给扁平化(使用了库 nested ),如:
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
实际是 BlocBuilder的另一种实现变化,应用缓存child的方式,每次setState只触发listener而不重新生成child,可用于拦截状态弹Toast,导航等等操作
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解决)