stream_bloc

原始 BLoC 的现代实现

关于

此包包含原始的、基于 Stream/generator 的 BLoC 的现代化(bloc 包 8.0.0+ 版本)实现,并包含多项修改和便捷的附加功能。

动机

在 7.2.0 版本更新后,bloc 发生了变化。Generators 和签名方法 mapEventToState 被替换为使用 on 方法及其参数处理程序的基于方法的模式匹配。这种方法绝对可用,并且解决了 Dart 语言本身的特定 bug,但它缺少一些东西。

  1. Streams 的强大功能。它们可以使用多种转换器进行本地转换,但在新的 bloc 中,streams 被隐藏在幕后,不是“一等公民”。
  2. Generators 的强大功能。它们允许异步返回多个值,包括其他 streams。新版本使用高阶函数来模拟它们的行为。
  3. 新的 on 方法使得 freezed 基本无用。虽然可以只注册一个处理程序,但这否定了 on 处理程序的全部意义。

此包带回了原始的 bloc 及其所有优点,同时保持与 blocflutter_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 的主要区别有三点。

  1. mapEventToState 已重命名为 mapEventToStates。该方法返回一个异步状态序列,而不是单个状态。

  2. 不能在不响应特定事件的情况下发出新状态。StreamBloc 不实现 Emittable,也没有 emit 方法。原始 bloc 有其方法是因为 CubitBloc 继承自同一个基类,但在 Bloc 中不应使用 emit。它被标记为对测试可见,但始终最好测试整体而不是其部分。

  3. Bloc 可以连续发出相同的状态。StreamBloc 的输出流不区分重复状态,原因如下:

    • flutter_blocBlocListener/BlocConsumer 可能对任何新发出的状态感兴趣,即使状态没有改变。
    • 应用于 stream.distinct()stream.map(...)/stream.where(...)(本质上是 BlocBuilder 和/或 BlocSelector)消除了流中事件唯一性的保证,使得 distinct 变得多余;它应该放在最后,而不是首先应用。

附加功能

该包还提供了一个方便的 mixin BlocLifecycleMixin,可以简化 Bloc 到 Bloc 的通信。它提供了四个方法:listenToStreamlistenToStreamablereactToStreamreactToStreamable。以下是其用法示例。

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 关闭时,它也会被取消。

GitHub

查看 Github