BLoC(Business Logic Component)— 一种 Flutter 状态管理模式,由 Google 于 2018 年在 DartConf 上推出。BLoC 通过响应式流(Stream)将业务逻辑与用户界面分离:UI 发送 Event,BLoC 处理它并通过 Stream 返回新的 State。根据 pub.dev 的数据,flutter_bloc 包已获得超过 1.1 万个赞,并用于数千个 Flutter 应用程序。
要点
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(输出)。Widget 通过 add() 方法发送 Event。Bloc 在 mapEventToState 或 on<Event> 方法中接收 Event,执行业务逻辑并通过 yield 产生新的 State。Widget 通过 Stream 接收 State 并重新渲染。
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 的生命周期,将其与路由或页面绑定。
Cubit — 在 flutter_bloc 6.0 包中引入的没有 Event 的简化版 Bloc 实现。Cubit 直接声明方法而不是 Event 类:increment()、fetchData()。在内部,Cubit 使用相同的基于 Stream 的机制,但隐藏了 Event 层。对于简单场景,这可以将样板代码减少 40-50%。
| 特性 | Bloc | Cubit |
|---|---|---|
| Event 类 | 必需 | 不需要 |
| 样板代码 | 高 | 低 |
| 操作追踪 | 通过 Event 类型 | 仅方法名称 |
| 适用于 | 复杂场景 | 简单状态 |
| 分析 | 通过 Event 自动获取 | 手动 |
何时选择 Cubit:有 2-3 种变体的状态(loading、loaded、error)、简单表单、计数器、UI 状态(打开/关闭)。何时选择 Bloc:涉及多个操作的复杂业务逻辑:订单结账、授权、数据同步。Bloc 通过 Event 提供每个操作的详细追踪——每次调用都记录在 BlocObserver 中。
BlocObserver — 跟踪应用程序中所有 Bloc 和 Cubit 的全局观察器。允许记录 Event、State、错误和转换。只需连接一个实例:Bloc.observer = AppBlocObserver(),整个应用程序状态追踪即可集中使用。
BlocProvider — 来自 flutter_bloc 的 InheritedWidget,将 Bloc 提供给子组件。在组件初始化时,BlocProvider 创建一个 Bloc,并在销毁时通过 close() 自动关闭它。BlocProvider 可以放置在 MaterialApp 级别(全局 Bloc)或特定路由级别(局部 Bloc)。
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 的测试是隔离的,不需要 Flutter 组件。只需导入 Dart 包 flutter_test 和包 bloc_test。测试场景:创建 Bloc、添加 Event、检查 State。blocTest — 自动化 build → act → expect 序列的工具。
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 的 Flutter 项目的典型结构:bloc/counter_bloc.dart、bloc/counter_event.dart、bloc/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 使用 Event + Stream 进行业务逻辑隔离和严格的类型化。Provider — 围绕 InheritedWidget 的包装器,用于简单的依赖注入和 ChangeNotifier。BLoC 更适合具有多种状态的复杂场景,Provider 适合本地 UI 状态。BLoC 需要更多样板代码,但通过 Event 提供完全的可追溯性。
Hydrated Bloc — 来自 hydrated_bloc 包的扩展,自动将最后一个 State 保存到本地存储(默认为 Hive)。当应用程序重新启动时,Bloc 恢复保存的状态而不是初始状态。这解决了无需手动调用保存的持久性问题:登录、购物车、设置在会话之间自动保存。
BLoC 中的错误通过在 mapEventToState 或 on<Event> 内的 try-catch 进行处理。出错时,Bloc 返回错误 State:yield LoadError(error.message)。在 UI 上,BlocListener 或 BlocConsumer 检查 State 的错误类型并显示 SnackBar 或对话框。BlocObserver 全局记录所有未处理的异常。
BLoC 是 Flutter 特有的模式,因为它使用 Dart Stream 和 Flutter 组件。Event → Bloc → State 的概念可以适用于 AngularDart 和 Server-side Dart,但主要生态系统(BlocProvider、BlocBuilder、BlocObserver)与 Flutter 绑定。对于 React Native 使用 Redux 或 MobX,对于 SwiftUI 使用 Combine + MVVM。
Cubit — 用于简单状态(计数器、开关、有 2-3 个字段的表单)。Bloc — 用于复杂逻辑(新闻源、订单结账、授权)。主要规则:如果出于分析或调试目的需要跟踪每个操作(Event)——选择 Bloc。如果更改状态的方法足够——选择 Cubit。两种模式可以在同一项目中共存。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。