关于 bloc
用于在 Dart 和 Flutter 应用中采用 Bloc 状态管理模式的开发与测试技能,规定架构、事件与状态、组件集成及使用 `bloc_test` 和 `mocktail` 的测试方式。
主要功能
- 01Bloc 与 Cubit 设计指导
- 02Page 与 View 分离
- 03事件和状态约定
- 04组件集成模式
- 05使用 `bloc_test` 和 `mocktail` 测试
适用场景
- 01使用 Bloc 或 Cubit 组织业务逻辑
- 02分离表现层、业务逻辑层和数据层
- 03将状态连接到 Flutter 组件
- 04编写 Bloc 和 Cubit 测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ verygoodopensource/ vgv-ai-flutter-plugin --skill bloc安装后在 Claude Code 中输入/bloc确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/architecture.mdpatterns.mdtesting.mdwidgets.md详细介绍
Bloc
用于 Dart 和 Flutter 的状态管理库,使用 BLoC(业务逻辑组件)模式,将业务逻辑与展示层分离。
核心规范
所有 Bloc/Cubit 工作都应遵循以下规范:
- 所有 Bloc 和 Cubit 测试都使用
package:bloc_test中的blocTest()— 绝不使用带手动流断言的原始test() - 使用
package:mocktail进行模拟 — 绝不使用package:mockito build:用于构造待测 bloc — 从回调中返回一个新实例,绝不返回在setUp中构造并共享的实例,这样每个blocTest都会从干净的 bloc 开始,并由blocTest管理其生命周期- Bloc 之间不得直接依赖 — Bloc 通过 UI 或共享的仓库进行通信
- Page/View 分离 — Page 通过
BlocProvider提供 Bloc/Cubit,View 通过BlocBuilder/BlocListener使用它 - 事件和多状态类型使用密封类 — 支持通过 Dart 3
switch进行穷尽式模式匹配 - 所有状态和事件都使用 Equatable — 继承
Equatable并重写props,实现值相等性 - 业务逻辑只能放在 Bloc/Cubit 中 — 绝不放在组件、页面或视图中
- 单一职责 — 每个功能关注点使用一个 Bloc/Cubit
- 只能在处理器内部调用
emit— 从负责该事件的on<Event>回调或 Cubit 方法中调用emit,绝不从流订阅或生命周期超出处理器的回调中调用 - Dart 3.13 主构造函数 — 以 Dart 3.13+ 为基准时,将事件、状态和组件字段声明为主构造函数声明参数(
class const ProfileRequested(final String userId) extends ProfileEvent),而不是使用this.field;仅在低于 3.13 时使用经典形式
Cubit 与 Bloc 的比较
| 方面 | Cubit | Bloc |
|---|---|---|
| API | 函数 → emit(state) | 事件 → on<Event> → emit(state) |
| 复杂度 | 低 | 较高 |
| 可追溯性 | 较低(没有事件日志) | 完整(事件 + 状态转换) |
| 适用场景 | 简单状态、由 UI 驱动的逻辑 | 复杂流程、事件驱动、转换 |
| 测试 | 调用方法,断言状态 | 添加事件,断言状态 |
Cubit 示例
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
Bloc 示例
sealed class CounterEvent extends Equatable {
const CounterEvent();
@override
List<Object> get props => [];
}
final class CounterIncrementPressed extends CounterEvent {}
final class CounterDecrementPressed extends CounterEvent {}
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) => emit(state + 1));
on<CounterDecrementPressed>((event, emit) => emit(state - 1));
}
}
命名约定
事件
模式: BlocSubject + 名词 + 过去时动词
| 事件类名称 | 含义 |
|---|---|
TodoListSubscriptionRequested | 订阅待办事项列表流 |
TodoListTodoDeleted | 删除特定待办事项 |
TodoListUndoDeletionRequested | 撤销最近一次删除 |
LoginFormSubmitted | 提交登录表单 |
ProfilePageRefreshed | 刷新个人资料页面 |
sealed class TodoListEvent extends Equatable {
const TodoListEvent();
@override
List<Object> get props => [];
}
final class TodoListSubscriptionRequested extends TodoListEvent {}
final class const TodoListTodoDeleted({required final Todo todo})
extends TodoListEvent {
@override
List<Object> get props => [todo];
}
状态
子类方式(多种状态类型)
当每种状态携带不同数据时使用。
| 状态类名称 | 含义 |
|---|---|
LoginInitial | 尚未执行任何操作 |
LoginInProgress | 登录请求正在处理中 |
LoginSuccess | 登录成功 |
LoginFailure | 登录失败 |
sealed class LoginState extends Equatable {
const LoginState();
@override
List<Object> get props => [];
}
final class LoginInitial extends LoginState {}
final class LoginInProgress extends LoginState {}
final class const LoginSuccess({required final User user}) extends LoginState {
@override
List<Object> get props => [user];
}
final class const LoginFailure({required final String error})
extends LoginState {
@override
List<Object> get props => [error];
}
单类方式(一个状态,多个字段)
当所有状态具有相同的数据结构时使用:一个类包含 status 枚举和数据字段,并通过 copyWith 实现状态转换。完整结构请参阅
references/patterns.md。
架构
| 层级 | 包含内容 | 依赖项 |
|---|---|---|
| 展示层 | 页面、视图、组件 | 业务逻辑 |
| 业务逻辑层 | Blocs、Cubits | 数据 |
| 数据层 | 仓库、数据提供程序 | 外部来源 |
数据层
仓库对数据源进行抽象,并为 Blocs/Cubits 提供简洁的 API。对于所有测试文件,在 test/ 下镜像功能文件夹结构。
有关仓库示例、功能文件夹结构和测试目录布局,请参阅 references/architecture.md。
Flutter 组件
使用 BlocProvider 向子树提供 Bloc 或 Cubit,然后使用 BlocBuilder、
BlocListener、BlocConsumer 或 BlocSelector 来使用它。当重建应依赖某个字段而非整个状态时,使用 BlocSelector;
对于导航或弹出提示条等副作用,则使用 BlocListener。
关键规则:在回调函数(onPressed、onTap)中使用 context.read,
在 build 中使用 context.watch 或 BlocBuilder。切勿在
build 方法之外调用 context.watch。
其他资源
- references/architecture.md — 仓库示例、功能文件夹结构、测试目录布局
- references/widgets.md — 组件和上下文扩展表、Page/View 模式、
BlocListener示例 - references/testing.md —
blocTest()参数、Cubit/Bloc 测试示例、依赖模拟、组件集成测试 - references/patterns.md — 单类状态、使用 Bloc/Cubit 添加功能、异步操作、事件转换器