flutter Bloc 更新后事件同步与异步详解
李小轰_Rex 人气:0前言
最近,小轰参与了公司 flutter 项目关于 Dart 2.0 的空安全升级工作。我们升级了所有依赖的三方库,其中就包括有 Bloc 库。作为一款使用率颇高的状态管理框架, Bloc 在版本迭代中进行了少许结构和细节的优化,下面是小轰对于 Bloc 新版本的使用总结。
使用方式
小轰使用的 Bloc 版本如下
flutter_bloc: ^7.3.1
通过最简单的例子来学习新知识
- 创建一个包含 加 减 操作的页面,使用 bloc 来操作 自增 自减 事件。
class _TestBlocPageState extends State<TestBlocPage> { late TestDartBloc _bloc; @override void initState() { super.initState(); _bloc = TestDartBloc(TestDartState(0)); } @override Widget build(BuildContext context) { return BlocBuilder<TestDartBloc, TestDartState>( bloc: _bloc, builder: (context, state) { return Column( children: [ //显示当前的数字结果 Text(state.num.toString()), TextButton( onPressed: () { //进行自增操作 _bloc.add(IncreaseEvent()); }, child: Text('add')), TextButton( onPressed: () { //进行自减操作 _bloc.add(ReduceEvent()); }, child: Text('reduce')), ], ); }, ); } }
Bloc 中 state 和 event 模型定义如下
part of 'test_dart_bloc.dart'; /// Bloc state class TestDartState { int num = 0; TestDartState(this.num); }
part of 'test_dart_bloc.dart'; /// Bloc event @immutable abstract class TestDartEvent {} //自增事件 class IncreaseEvent extends TestDartEvent{} //自减事件 class ReduceEvent extends TestDartEvent{}
创建 Bloc ,重写 mapEventToState 接收事件流转状态流
import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; part 'test_dart_event.dart'; part 'test_dart_state.dart'; class TestDartBloc extends Bloc<TestDartEvent, TestDartState> { TestDartBloc(TestDartState state) : super(state); @override Stream<TestDartState> mapEventToState(TestDartEvent event) async* { if (event is IncreaseEvent) { await Future.delayed(Duration(seconds: 10)); yield TestDartState(state.num + 1); } else if (event is ReduceEvent) { yield TestDartState(state.num - 1); } } }
好了,直接运行如上代码,我们的demo就能完整的跑起来。但,以上的写法和低版本的 Bloc 完全一致,在经历高版本迭代后,Bloc 到底做了哪些优化呢?
Bloc 新形态用法
小轰在源码里看到,重写 mapEventToState 的使用方式已经被弃用了,会在将来的某个版本中彻底删除该 API
/// **@Deprecated - Use on<Event> instead. Will be removed in v8.0.0** /// /// Must be implemented when a class extends [Bloc]. /// [mapEventToState] is called whenever an [event] is [add]ed /// and is responsible for converting that [event] into a new [state]. /// [mapEventToState] can `yield` zero, one, or multiple states for an event. @Deprecated('Use on<Event> instead. Will be removed in v8.0.0') Stream<State> mapEventToState(Event event) async* {}
新用法推荐使用 on 来进行事件注册,我们将上面demo中的 bloc 进行写法改造:
class TestDartBloc extends Bloc<TestDartEvent, TestDartState> { TestDartBloc(TestDartState state) : super(state) { init(); } void init() { on<IncreaseEvent>((event, emit) async { await Future.delayed(Duration(seconds: 10)); emit(TestDartState(state.num + 1)); }); on<ReduceEvent>((event, emit) { state.num - 1; emit(state); }); } }
替换代码后运行demo,直接成功。
事件队列的阻塞属性?
目前最新版本的 Bloc 同时支持 on 与 mapEventToState 两种写法,那么这两种写法有实际区别吗?
小轰在使用老版本(空安全之前)bloc 时总结过一篇文章 聊聊 Bloc event 的队列属性。
在 bloc 的 mapEventToState 方法中,event 队列是一个阻塞性队列,先进先出,只有当上一个事件消费完毕后,才会响应队列中的下一个事件。
如上demo( mapEventToState 方式): 自增事件中模拟耗时了10s,当依次点击 自增 和 自减 按钮后,由于事件队列的阻塞特性,自增事件消费10秒后,自减事件才会被 mapEventToState 响应。
那么,这个特性在 bloc 迭代新版本后还存在吗?在最新版本的bloc中,小轰通过demo测试得出结论:
- 当使用 mapEventToState 方式进行事件捕获时,event 队列 保持 阻塞 特性。
- 而使用 on 方式进行注册监听时,event 队列 默认是异步非阻塞的,是互不干扰的。如果把 bloc 当作事件总线来使用,小轰认为 异步非阻塞 这样的设计更为合理。
提问:如果想使用 on 的方式进行注册,还想事件队列保证顺序执行即保持阻塞特性,应该怎么办呢? 解答:使用 自定义 transformer,这样就实现了事件同步队列。
on<IncreaseEvent>((event, emit) async { await Future.delayed(Duration(seconds: 10)); emit(TestDartState(state.num + 1)); }, transformer: (events, mapper) => events.asyncExpand(mapper), );
参考链接: pub.dev/packages/bl…
加载全部内容