BLoC — 什么是它,Flutter 中的 Business Logic Component

作者: IT Sectr 发布日期: 2026-02-19 阅读时间: 7 分钟

BLoC(Business Logic Component)— 一种 Flutter 状态管理模式,由 Google 于 2018 年在 DartConf 上推出。BLoC 通过响应式流(Stream)将业务逻辑与用户界面分离:UI 发送 Event,BLoC 处理它并通过 Stream 返回新的 State。根据 pub.dev 的数据,flutter_bloc 包已获得超过 1.1 万个赞,并用于数千个 Flutter 应用程序。

要点

  • Event — 描述操作的输入信号:按钮点击、数据加载
  • State — UI 的输出状态:数据已加载、错误、加载中
  • Bloc — 接收 Event 并通过 Stream 返回 State 的主类
  • Cubit — 没有 Event 的简化版 Bloc,直接调用函数
  • BlocProvider — 用于将 Bloc 注入组件树的 Flutter 组件

什么是 BLoC?

BLoC(Business Logic Component)— Flutter 的架构模式,其中业务逻辑被提取到单独的类中,与 UI 隔离。BLoC 通过事件流(Event)接收输入数据,并通过状态流(State)产生输出数据。表示层(Widget)仅订阅 State 流并显示 UI,从不直接执行业务逻辑。

BLoC 概念基于响应式编程和 Observer 模式。每个 BLoC 组件都是一个独立的模块,具有明确的契约:已知 Event 集合(可能发生什么)和 State 集合(可以显示什么)。开发者无法从 UI "意外"更改状态——只能通过特定的 Event。这使得代码可预测且可测试。

根据 Flutter Community 2025 调查,BLoC 在 Flutter 状态管理解决方案中的受欢迎程度仅次于 Provider。主要优点:严格的类型化、逻辑隔离、内置 Stream 支持、丰富的工具生态系统(BlocProvider、BlocListener、BlocSelector)。

BLoC 架构:Event → Bloc → State

BLoC 架构围绕三个实体构建:Event(输入)、Bloc(处理器)和 State(输出)。Widget 通过 add() 方法发送 Event。Bloc 在 mapEventToStateon<Event> 方法中接收 Event,执行业务逻辑并通过 yield 产生新的 State。Widget 通过 Stream 接收 State 并重新渲染。

Dart
abstract class CounterEvent {}

class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    if (event is Increment) {
      yield state + 1;
    } else if (event is Decrement) {
      yield state - 1;
    }
  }
}

类型安全:Bloc 由两种类型参数化——Event 和 State。Dart 编译器检查 Widget 只调用已声明的 Event,Bloc 只返回已声明的 State。"未知操作"类型的运行时错误被排除。

Close 和 Dispose:Bloc 实现了 Closeable 接口。当组件被销毁时,Bloc 通过 close() 方法自动关闭 Stream。响应式订阅泄漏是不可能的——BlocProvider 管理 Bloc 的生命周期,将其与路由或页面绑定。

Bloc 和 Cubit:比较

Cubit — 在 flutter_bloc 6.0 包中引入的没有 Event 的简化版 Bloc 实现。Cubit 直接声明方法而不是 Event 类:increment()fetchData()。在内部,Cubit 使用相同的基于 Stream 的机制,但隐藏了 Event 层。对于简单场景,这可以将样板代码减少 40-50%。

特性BlocCubit
Event 类必需不需要
样板代码
操作追踪通过 Event 类型仅方法名称
适用于复杂场景简单状态
分析通过 Event 自动获取手动

何时选择 Cubit:有 2-3 种变体的状态(loading、loaded、error)、简单表单、计数器、UI 状态(打开/关闭)。何时选择 Bloc:涉及多个操作的复杂业务逻辑:订单结账、授权、数据同步。Bloc 通过 Event 提供每个操作的详细追踪——每次调用都记录在 BlocObserver 中。

BlocObserver — 跟踪应用程序中所有 Bloc 和 Cubit 的全局观察器。允许记录 Event、State、错误和转换。只需连接一个实例:Bloc.observer = AppBlocObserver(),整个应用程序状态追踪即可集中使用。

BlocProvider 和 BlocBuilder

BlocProvider — 来自 flutter_bloc 的 InheritedWidget,将 Bloc 提供给子组件。在组件初始化时,BlocProvider 创建一个 Bloc,并在销毁时通过 close() 自动关闭它。BlocProvider 可以放置在 MaterialApp 级别(全局 Bloc)或特定路由级别(局部 Bloc)。

Dart
BlocProvider(
  create: (context) => CounterBloc(),
  child: Column(
    children: [
      BlocBuilder<CounterBloc, int>(
        builder: (context, state) => Text('$state'),
      ),
      ElevatedButton(
        onPressed: () => context.read<CounterBloc>().add(Increment()),
        child: Text('+'),
      ),
    ],
  ),
)

BlocBuilder — 在每个新 State 时重建 UI 的组件。BlocListener — 用于副作用(一次性处理 State,不重建 UI):显示 SnackBar、导航到其他屏幕。BlocConsumer — Builder 和 Listener 的组合,适用于同时需要重建和副作用的情况。BlocSelector — 仅在 State 的特定字段更改时选择性重建。

MultiBlocProvider — 用于嵌套 BlocProvider 的组件,不会增加嵌套级别。使用 10-15 个 Bloc 的 Flutter 应用程序在根级别使用 MultiBlocProvider 注册整个应用程序可用的所有 Bloc:AuthenticationBloc、CartBloc、SettingsBloc。

测试 BLoC

BLoC 的测试是隔离的,不需要 Flutter 组件。只需导入 Dart 包 flutter_test 和包 bloc_test。测试场景:创建 Bloc、添加 Event、检查 State。blocTest — 自动化 build → act → expect 序列的工具。

Dart
blocTest<CounterBloc, int>(
  'emits [1] when Increment is added',
  build: () => CounterBloc(),
  act: (bloc) => bloc.add(Increment()),
  expect: () => [1],
)

模拟:依赖于存储库或 API 的 BLoC 通过 mocktail 使用模拟进行测试。存储库在抽象级别被模拟,Bloc 通过构造函数获取模拟的依赖项。Hydrated Bloc — 用于在本地存储中自动保存/恢复状态的扩展。使用 HydratedBlocStorage 和临时文件存储进行测试。

生产环境中的 BLoC

文件夹和文件:使用 BLoC 的 Flutter 项目的典型结构:bloc/counter_bloc.dartbloc/counter_event.dartbloc/counter_state.dart。对于 30+ 个屏幕,建议按功能分组:features/auth/bloc/features/cart/bloc/。每个 Bloc 单独一个文件,每个 Event 和 State — 要么在单独的文件中,要么与 Bloc 放在同一个文件中。

性能:BLoC 不会在空 Stream 上产生开销。BlocBuilder 使用 buildWhen 过滤重建——组件仅在特定条件更改时更新。Close 保证非活动 Bloc 不消耗内存。根据 Flutter DevTools,BLoC 为包大小增加不到 1%。

从 Provider 迁移:BLoC 可以轻松地与 Provider 在同一项目中共存。逐步迁移:首先将最复杂的 Provider 替换为 Bloc,然后是其余的。BlocProvider 与 Provider 树兼容:旧组件可以使用 Provider,新组件使用 BlocProvider,在同一应用程序中。

常见问题解答

BLoC 与 Flutter 中的 Provider 有何不同?

BLoC 使用 Event + Stream 进行业务逻辑隔离和严格的类型化。Provider — 围绕 InheritedWidget 的包装器,用于简单的依赖注入和 ChangeNotifier。BLoC 更适合具有多种状态的复杂场景,Provider 适合本地 UI 状态。BLoC 需要更多样板代码,但通过 Event 提供完全的可追溯性。

什么是 Hydrated Bloc?

Hydrated Bloc — 来自 hydrated_bloc 包的扩展,自动将最后一个 State 保存到本地存储(默认为 Hive)。当应用程序重新启动时,Bloc 恢复保存的状态而不是初始状态。这解决了无需手动调用保存的持久性问题:登录、购物车、设置在会话之间自动保存。

如何在 BLoC 中处理错误?

BLoC 中的错误通过在 mapEventToStateon<Event> 内的 try-catch 进行处理。出错时,Bloc 返回错误 State:yield LoadError(error.message)。在 UI 上,BlocListenerBlocConsumer 检查 State 的错误类型并显示 SnackBar 或对话框。BlocObserver 全局记录所有未处理的异常。

BLoC 可以与其他框架一起使用吗?

BLoC 是 Flutter 特有的模式,因为它使用 Dart Stream 和 Flutter 组件。Event → Bloc → State 的概念可以适用于 AngularDartServer-side Dart,但主要生态系统(BlocProvider、BlocBuilder、BlocObserver)与 Flutter 绑定。对于 React Native 使用 Redux 或 MobX,对于 SwiftUI 使用 Combine + MVVM。

选择什么:Bloc 还是 Cubit?

Cubit — 用于简单状态(计数器、开关、有 2-3 个字段的表单)。Bloc — 用于复杂逻辑(新闻源、订单结账、授权)。主要规则:如果出于分析或调试目的需要跟踪每个操作(Event)——选择 Bloc。如果更改状态的方法足够——选择 Cubit。两种模式可以在同一项目中共存。

总结

  • BLoC — 通过 Event → Stream → State 实现 Flutter 状态管理的模式
  • Event — 操作(点击、加载),State — 反应(数据、错误、加载中)
  • Cubit — 没有 Event 的简化版本,样板代码减少高达 50%
  • BlocProvider — 将 Bloc 注入组件树并自动关闭
  • BlocObserver — 监控应用程序中所有 Bloc 和 Cubit 的全局观察器
  • Hydrated Bloc — 通过 Hive 实现状态的自动持久化
  • blocTest — 用于 Bloc 隔离测试的工具,与 Flutter 解耦

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读