mcpField
B
bloc

bloc

github.com/verygoodopensource开发175102 次安装3 天前更新
代码仓库

关于 bloc

用于在 Dart 和 Flutter 应用中采用 Bloc 状态管理模式的开发与测试技能,规定架构、事件与状态、组件集成及使用 `bloc_test` 和 `mocktail` 的测试方式。

主要功能

  1. 01Bloc 与 Cubit 设计指导
  2. 02Page 与 View 分离
  3. 03事件和状态约定
  4. 04组件集成模式
  5. 05使用 `bloc_test` 和 `mocktail` 测试

适用场景

  1. 01使用 Bloc 或 Cubit 组织业务逻辑
  2. 02分离表现层、业务逻辑层和数据层
  3. 03将状态连接到 Flutter 组件
  4. 04编写 Bloc 和 Cubit 测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/verygoodopensource/vgv-ai-flutter-plugin --skill bloc

安装后在 Claude Code 中输入/bloc确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
architecture.md
patterns.md
testing.md
widgets.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 的比较

方面CubitBloc
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。

其他资源

评论

支持 Markdown