SuperAPI AI 工具集

bloc

编程与开发

查看项目

更新于 10-16 02:12

技能介绍

Bloc 是面向 Dart 与 Flutter 的状态管理库,采用 BLoC(Business Logic Component)模式,用于将业务逻辑与展示层分离。以下内容整理自该技能条目所描述的核心标准、对比、命名约定、架构与组件用法。

一、核心标准 适用于所有 Bloc/Cubit 工作:

  • 所有 Bloc 与 Cubit 测试使用 package:bloc_test 的 blocTest(),不要用原始 test() 加手动流断言。
  • 使用 package:mocktail 进行 mock,不要用 package:mockito。
  • blocTest 的 build 用于构造被测 bloc:从回调中返回新实例,不要使用在 setUp 中构建并共享的实例,以保证每个 blocTest 从干净的 bloc 开始,其生命周期由 blocTest 管理。
  • 不允许 bloc 与 bloc 之间直接依赖,bloc 之间通过 UI 或共享 repository 通信。
  • Page/View 分离:Page 通过 BlocProvider 提供 Bloc/Cubit,View 通过 BlocBuilder/BlocListener 消费。
  • 事件与多状态类型使用 sealed class,以便配合 Dart 3 switch 做穷尽式模式匹配。
  • 所有状态与事件使用 Equatable:继承 Equatable 并重写 props 以实现值相等。
  • 业务逻辑只放在 Bloc/Cubit 中,绝不放在 widget、page 或 view 中。
  • 单一职责:每个功能关注点对应一个 Bloc/Cubit。
  • emit 只能在 handler 内调用:从 on<Event> 回调或拥有该状态的 Cubit 方法中调用 emit,不要从流订阅或生命周期超出 handler 的回调中调用。
  • Dart 3.13 主构造函数:在 Dart 3.13+ 基线上,将事件、状态和 widget 字段声明为主构造函数的声明参数(例如 class const ProfileRequested(final String userId) extends ProfileEvent),而不是 this.field;仅在低于 3.13 时保留经典写法。

二、Cubit 与 Bloc 对比

  • API:Cubit 为函数 → emit(state);Bloc 为事件 → on<Event> → emit(state)。
  • 复杂度:Cubit 低;Bloc 较高。
  • 可追踪性:Cubit 较弱(无事件日志);Bloc 完整(事件与状态转换)。
  • 适用场景:Cubit 适合简单状态、UI 驱动逻辑;Bloc 适合复杂流程、事件驱动、需要转换的场景。
  • 测试:Cubit 调用方法并断言状态;Bloc 添加事件并断言状态。

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 + Noun + VerbPastTense。示例:TodoListSubscriptionRequested(订阅 todo 列表流)、TodoListTodoDeleted(删除特定 todo)、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]; }

状态命名有两种方式: 1)子类方式(多种状态类型),适用于每个状态携带不同数据。示例: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]; }

2)单类方式(一个状态、多个字段),适用于所有状态共享相同数据形状:一个类持有 status 枚举加数据字段,并提供 copyWith 用于状态转换。完整形态见 references/patterns.md。

四、架构

  • 展示层:包含 Pages、Views、Widgets,依赖业务逻辑层。
  • 业务逻辑层:包含 Blocs、Cubits,依赖数据层。
  • 数据层:包含 Repositories、Data Providers,依赖外部数据源。

数据层中,repository 抽象数据源并为 Bloc/Cubit 提供干净的 API。所有测试文件在 test/ 下镜像功能目录结构。repository 示例、功能目录结构与测试目录布局见 references/architecture.md。

五、Flutter 组件 使用 BlocProvider 向子树提供 Bloc 或 Cubit,再用 BlocBuilder、BlocListener、BlocConsumer 或 BlocSelector 消费。当重建只依赖某个字段而非整个状态时使用 BlocSelector;用于导航或 snackbar 等副作用时使用 BlocListener。关键规则:在回调(onPressed、onTap)中使用 context.read,在 build 中使用 context.watch 或 BlocBuilder;绝不要在 build 方法之外调用 context.watch。

六、附加资源

  • references/architecture.md:repository 示例、功能目录结构、测试目录布局。
  • references/widgets.md:widget 与 context 扩展表、Page/View 模式、BlocListener 示例。
  • references/testing.md:blocTest() 参数、Cubit/Bloc 测试示例、mock 依赖、widget 集成测试。
  • references/patterns.md:单类状态、用 Bloc/Cubit 添加功能、异步操作、事件转换器。

关于该技能的安装方式、收费与价格、支持平台等信息,资料中暂未提供。