接上一篇 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

  1. 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

  1. 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.发出事件
},
),
),
],
),
);
}
}
  1. 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_architecture.png

原理:

bloc图示1.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还提供一系列的辅助方法让我们更好地控制数据流

  1. 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, /// 将要改变成的状态
    });
    ...
    }
  2. 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全局单例
    ...
    }
  3. 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(),
    )
  4. 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,
    );
    }

总结

优点:

  1. 事件通道处理事件转换状态,串联状态通道通知外部订阅对象
  2. 可实现局部 按需刷新
  3. 状态源各自Bloc管理,实现分治,同时也可以利用Delegate实现全局的事件管理

缺点:

  1. 相较于redux,更集中在”分治”的焦点上,但是bloc组件之间缺少有效的通信机制
  2. 缺少Middleware(AOP)模式的有效支持
  3. 如果父组件发生更新,子组件绑定的数据源并未发生变化,仍会导致子的rebuild(可利用缓存child解决)