About bloc
A workflow skill for building and testing Dart and Flutter applications with the Bloc state-management pattern. It sets conventions for architecture, events and states, widget integration, and tests using `bloc_test` and `mocktail`.
Key features
- 01Bloc and Cubit design guidance
- 02Page and View separation
- 03Event and state conventions
- 04Widget integration patterns
- 05Testing with `bloc_test` and `mocktail`
Use cases
- 01Structure business logic in Bloc or Cubit
- 02Separate presentation, business logic, and data layers
- 03Connect state to Flutter widgets
- 04Write Bloc and Cubit tests
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ verygoodopensource/ vgv-ai-flutter-plugin --skill blocAfter installing, type/blocin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
agents/openai.yamlreferences/architecture.mdpatterns.mdtesting.mdwidgets.mdDetails
From the project READMEBloc
State management library for Dart and Flutter using the BLoC (Business Logic Component) pattern to separate business logic from the presentation layer.
Core Standards
Apply these standards to all Bloc/Cubit work:
- Use
blocTest()frompackage:bloc_testfor all Bloc and Cubit tests — never rawtest()with manual stream assertions - Use
package:mocktailfor mocking — neverpackage:mockito build:constructs the bloc under test — return a new instance from the callback, never one built insetUpand shared, so everyblocTeststarts from a clean bloc and its lifecycle stays withblocTest- No bloc-to-bloc direct dependencies — blocs communicate through the UI or shared repositories
- Page/View separation — Page provides the Bloc/Cubit via
BlocProvider, View consumes viaBlocBuilder/BlocListener - Sealed classes for events and multi-state types — enables exhaustive pattern matching with Dart 3
switch - Equatable for all states and events — extend
Equatableand overridepropsfor value equality - Business logic in Bloc/Cubit only — never in widgets, pages, or views
- Single responsibility — one Bloc/Cubit per feature concern
emitonly inside the handler — callemitfrom theon<Event>callback or Cubit method that owns it, never from a stream subscription or callback that outlives the handler- Dart 3.13 primary constructors — on a Dart 3.13+ baseline, declare event, state, and widget fields as primary-constructor declaring parameters (
class const ProfileRequested(final String userId) extends ProfileEvent) rather thanthis.field; keep the classic form only below 3.13
Cubit vs Bloc
| Aspect | Cubit | Bloc |
|---|---|---|
| API | Functions → emit(state) | Events → on<Event> → emit(state) |
| Complexity | Low | Higher |
| Traceability | Less (no event log) | Full (events + transitions) |
| When to use | Simple state, UI-driven logic | Complex flows, event-driven, transforms |
| Testing | Call methods, assert states | Add events, assert states |
Cubit Example
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
Bloc Example
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));
}
}
Naming Conventions
Events
Pattern: BlocSubject + Noun + VerbPastTense
| Event class name | Meaning |
|---|---|
TodoListSubscriptionRequested | Subscribing to todo list stream |
TodoListTodoDeleted | Deleting a specific todo |
TodoListUndoDeletionRequested | Undoing the last deletion |
LoginFormSubmitted | Submitting the login form |
ProfilePageRefreshed | Refreshing the profile page |
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];
}
States
Subclass Approach (multiple state types)
Use when each state carries different data.
| State class name | Meaning |
|---|---|
LoginInitial | No action taken yet |
LoginInProgress | Login request in flight |
LoginSuccess | Login succeeded |
LoginFailure | Login failed |
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];
}
Single Class Approach (one state, multiple fields)
Use when all states share the same data shape: one class holding a status enum plus the
data fields, with a copyWith for transitions. See
references/patterns.md for the full shape.
Architecture
| Layer | Contains | Depends on |
|---|---|---|
| Presentation | Pages, Views, Widgets | Business Logic |
| Business Logic | Blocs, Cubits | Data |
| Data | Repositories, Data Providers | External sources |
Data Layer
Repositories abstract data sources and provide a clean API for Blocs/Cubits. Mirror the feature folder structure under test/ for all test files.
See references/architecture.md for the repository example, feature folder structure, and test directory layout.
Flutter Widgets
Use BlocProvider to supply a Bloc or Cubit to a subtree, then BlocBuilder,
BlocListener, BlocConsumer, or BlocSelector to consume it. Reach for BlocSelector
when a rebuild should depend on one field rather than the whole state, and BlocListener
for side effects such as navigation or a snackbar.
The rule that matters: context.read in callbacks (onPressed, onTap),
context.watch or BlocBuilder in build. Never call context.watch outside a
build method.
Additional Resources
- references/architecture.md — repository example, feature folder structure, test directory layout
- references/widgets.md — widget and context extension tables, Page/View pattern,
BlocListenerexample - references/testing.md —
blocTest()parameters, Cubit/Bloc test examples, mocking dependencies, widget integration tests - references/patterns.md — single-class state, adding features with Bloc/Cubit, async operations, event transformers