stream_bloc
原始 BLoC 的现代实现
关于
此包包含原始的、基于 Stream/generator 的 BLoC 的现代化(bloc 包 8.0.0+ 版本)实现,并包含多项修改和便捷的附加功能。
动机
在 7.2.0 版本更新后,bloc 发生了变化。Generators 和签名方法 mapEventToState 被替换为使用 on 方法及其参数处理程序的基于方法的模式匹配。这种方法绝对可用,并且解决了 Dart 语言本身的特定 bug,但它缺少一些东西。
- Streams 的强大功能。它们可以使用多种转换器进行本地转换,但在新的 bloc 中,streams 被隐藏在幕后,不是“一等公民”。
- Generators 的强大功能。它们允许异步返回多个值,包括其他 streams。新版本使用高阶函数来模拟它们的行为。
- 新的
on方法使得freezed基本无用。虽然可以只注册一个处理程序,但这否定了on处理程序的全部意义。
此包带回了原始的 bloc 及其所有优点,同时保持与 bloc 和 flutter_bloc 包的 100% 兼容性。StreamBlocs 可以与所有 flutter_bloc 小部件一起使用;它们实现了相同的接口。
概述
如果您熟悉 8.0.0/7.2.0 版本之前的 bloc,那么您就熟悉 StreamBloc——这个包的核心类。之前 bloc 版本的文档也可用于这些包,除了下一节列出的一些修改。
StreamBloc 使用一个名为 mapEventToStates 的中央事件处理方法,将单个事件转换为异步发出的状态流。官方 Bloc 可以直接转换为 StreamBloc,如下所述。
强烈建议使用 freezed 包。以下示例仅作演示,不应被视为 StreamBloc 的“最佳实践”。
abstract class CounterEvent {} // Shared counter event type
class Increment implements CounterEvent {} // Increment counter event
class Decrement implements CounterEvent {} // Decrement counter event
class OnCounterBloc extends Bloc<CounterEvent, int> { // Official Bloc – `on`s
OnCounterBloc() : super(0) {
on<Increment>((event, emit) => emit(state + 1));
on<Decrement>((event, emit) => emit(state - 1));
}
}
class StreamCounterBloc extends StreamBloc<CounterEvent, int> { // StreamBloc – `mapEventToStates`
StreamCounterBloc() : super(0);
@override
Stream<int> mapEventToStates(CounterEvent event) async* {
if (event is Increment) {
yield state + 1;
} else if (event is Decrement) {
yield state - 1;
}
}
}
修改
与原始 bloc 的主要区别有三点。
-
mapEventToState已重命名为mapEventToStates。该方法返回一个异步状态序列,而不是单个状态。 -
不能在不响应特定事件的情况下发出新状态。
StreamBloc不实现Emittable,也没有emit方法。原始 bloc 有其方法是因为Cubit和Bloc继承自同一个基类,但在Bloc中不应使用emit。它被标记为对测试可见,但始终最好测试整体而不是其部分。 -
Bloc 可以连续发出相同的状态。
StreamBloc的输出流不区分重复状态,原因如下:flutter_bloc的BlocListener/BlocConsumer可能对任何新发出的状态感兴趣,即使状态没有改变。- 应用于
stream.distinct()的stream.map(...)/stream.where(...)(本质上是BlocBuilder和/或BlocSelector)消除了流中事件唯一性的保证,使得distinct变得多余;它应该放在最后,而不是首先应用。
附加功能
该包还提供了一个方便的 mixin BlocLifecycleMixin,可以简化 Bloc 到 Bloc 的通信。它提供了四个方法:listenToStream、listenToStreamable、reactToStream 和 reactToStreamable。以下是其用法示例。
enum EventA { eventA }
class BlocA extends StreamBloc<EventA, int> {
BlocA() : super(0);
@override
Stream<int> mapEventToStates(EventA event) => Stream.value(1);
}
enum EventB { eventB }
class BlocB extends StreamBloc<EventB, int> with BlocLifecycleMixin<EventB> {
BlocB(BlocA blocA) : super(0) {
/// Will print every new state of this Bloc to the console.
listenToStream(stream, print);
/// Will add [EventB.eventB] to this bloc every time BlocA emits any state.
reactToStreamable<int>(blocA, (blocAState) => EventB.eventB);
}
@override
Stream<int> mapEventToStates(EventB event) => Stream.value(1);
}
所有方法都返回一个 StreamSubscription 实例,可以手动取消,但这是可选的——在混合了 BlocLifecycleMixin 的 Bloc 关闭时,它也会被取消。