Flutter Bloc如何通过transformEvents按事件属性排序事件队列
Flutter Bloc 自定义事件排序的实现方案
完全可以通过重写transformEvents结合rxdart、Dart内置优先队列实现,核心要解决的是流式实时调度,而不是等所有事件到齐再做全量排序——这也是很多人一开始实现失败的常见原因。
核心逻辑匹配你的示例需求
结合你给出的输入队列和目标输出队列,逻辑拆解非常明确:
- 所有带
int排序属性的MyEvent不按到达顺序立刻执行,而是先入优先队列,按属性值从小到大排序 - 遇到不参与排序的
AnotherEvent时,先把当前队列里已经攒着的所有MyEvent按升序全部执行完,再执行AnotherEvent - 事件流结束时,把队列里剩余的
MyEvent按升序依次执行
这套逻辑跑出来的结果和你给出的目标队列完全一致。
完整可运行代码示例
首先定义基础事件类:
abstract class MyBlocEvent {} class MyEvent extends MyBlocEvent { final int sortValue; MyEvent(this.sortValue); @override String toString() => 'MyEvent($sortValue)'; } class AnotherEvent extends MyBlocEvent { @override String toString() => 'AnotherEvent()'; }
Bloc类中重写transformEvents实现自定义调度:
import 'dart:collection'; import 'package:bloc/bloc.dart'; import 'package:rxdart/rxdart.dart'; class SortableBloc extends Bloc<MyBlocEvent, int> { SortableBloc() : super(0) { on<MyEvent>((event, emit) => emit(event.sortValue)); on<AnotherEvent>((event, emit) => emit(-1)); } @override Stream<MyBlocEvent> transformEvents( Stream<MyBlocEvent> events, Stream<MyBlocEvent> Function(Stream<MyBlocEvent> events) eventMapper, ) { // 初始化优先队列,规则:sortValue更小的MyEvent排在前面 final eventQueue = PriorityQueue<MyEvent>( (a, b) => a.sortValue.compareTo(b.sortValue), ); return events // 逐次处理流入的事件 .asyncExpand((event) { // 遇到非排序类事件时,先排空当前优先队列 if (event is! MyEvent) { final flushedEvents = <MyBlocEvent>[]; while (eventQueue.isNotEmpty) { flushedEvents.add(eventQueue.dequeue()); } flushedEvents.add(event); return Stream.fromIterable(flushedEvents); } // 排序类事件直接入队,不立刻触发执行 eventQueue.enqueue(event); return const Stream.empty(); }) // 流结束时,排空队列中剩余的排序事件 .concatWith([ DeferredStream(() { final remainingEvents = <MyBlocEvent>[]; while (eventQueue.isNotEmpty) { remainingEvents.add(eventQueue.dequeue()); } return Stream.fromIterable(remainingEvents); }), ]) .transform(eventMapper); } }
效果验证
当向Bloc依次送入事件[MyEvent(5), MyEvent(3), MyEvent(7), AnotherEvent(), MyEvent(2), MyEvent(1)]时:
- 前3个MyEvent依次入队,队列按排序规则存储顺序为
[MyEvent(3), MyEvent(5), MyEvent(7)] - 遇到AnotherEvent时,先依次输出队列中的3、5、7三个事件,再输出AnotherEvent
- 后续到达的MyEvent(2)、MyEvent(1)依次入队
- 事件流结束时,输出队列中剩余的
MyEvent(1)、MyEvent(2)
最终实际执行顺序和你给出的目标队列完全一致。
调整说明
- 如果需求是所有MyEvent永远比AnotherEvent优先级高(不管AnotherEvent什么时候到,都要等所有MyEvent排完再执行),只需要修改优先队列的排序规则,给AnotherEvent分配一个远大于MyEvent的排序值即可
- Bloc 8.0以下版本的
transformEvents参数命名略有差异,核心的流转换逻辑完全通用
内容的提问来源于stack exchange,提问作者Giuseppe Cianci
相关产品推荐
相关产品推荐

