Guides Bloc state management in Flutter/Dart with best practices for Cubit vs Bloc decisions, naming conventions, and testing using blocTest and mocktail.
How this skill is triggered — by the user, by Claude, or both
Slash command
/vgv-ai-flutter-plugin:blocWhen to use
Use when writing, modifying, or reviewing code that uses package:bloc, package:flutter_bloc, or package:bloc_test.
This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
State management library for Dart and Flutter using the BLoC (Business Logic Component) pattern to separate business logic from the presentation layer.
State management library for Dart and Flutter using the BLoC (Business Logic Component) pattern to separate business logic from the presentation layer.
Apply these standards to ALL Bloc/Cubit work:
blocTest() from package:bloc_test for all Bloc and Cubit tests — never raw test() with manual stream assertionspackage:mocktail for mocking — never package:mockitoBlocProvider, View consumes via BlocBuilder/BlocListenerswitchEquatable and override props for value equalityemit only inside the handler callback| 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 |
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
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));
}
}
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 TodoListTodoDeleted extends TodoListEvent {
const TodoListTodoDeleted({required this.todo});
final Todo todo;
@override
List<Object> get props => [todo];
}
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 LoginSuccess extends LoginState {
const LoginSuccess({required this.user});
final User user;
@override
List<Object> get props => [user];
}
final class LoginFailure extends LoginState {
const LoginFailure({required this.error});
final String error;
@override
List<Object> get props => [error];
}
Use when all states share the same data shape.
| Field | Type | Purpose |
|---|---|---|
status | enum | Current loading status |
items | List<Item> | Loaded data |
error | String? | Error message if failed |
enum TodoListStatus { initial, loading, success, failure }
class TodoListState extends Equatable {
const TodoListState({
this.status = TodoListStatus.initial,
this.todos = const [],
this.error,
});
final TodoListStatus status;
final List<Todo> todos;
final String? error;
TodoListState copyWith({
TodoListStatus? status,
List<Todo>? todos,
String? error,
}) {
return TodoListState(
status: status ?? this.status,
todos: todos ?? this.todos,
error: error ?? this.error,
);
}
@override
List<Object?> get props => [status, todos, error];
}
| Layer | Contains | Depends on |
|---|---|---|
| Presentation | Pages, Views, Widgets | Business Logic |
| Business Logic | Blocs, Cubits | Data |
| Data | Repositories, Data Providers | External sources |
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.
BlocProvider — creates and provides a Bloc/Cubit to the subtreeBlocBuilder — rebuilds widget when state changesBlocListener — executes side effects (navigation, snackbar) on state changeBlocConsumer — combines BlocBuilder + BlocListenerBlocSelector — rebuilds only when a selected property changescontext.read in callbacks (onPressed, onTap), context.watch or BlocBuilder in build methodscontext.watch outside of buildSee references/widgets.md for the full widget and context extension tables, Page/View pattern example, and BlocListener example. See references/testing.md for blocTest() parameters, Cubit/Bloc test examples, mocking dependencies, and widget integration tests. See references/patterns.md for adding features with Bloc/Cubit, async operations, and event transformers.
npx claudepluginhub verygoodopensource/very-good-claude-code-marketplace --plugin vgv-ai-flutter-pluginProvides production-ready Dart/Flutter patterns for null safety, state management (BLoC, Riverpod, Provider), GoRouter navigation, Dio networking, Freezed code generation, and testing.
Provides expert Flutter/Dart patterns for cross-platform mobile apps including feature-first project structure, const widget best practices, and Riverpod/Bloc state management.
Provides production-ready Dart and Flutter code patterns for null safety, sealed classes, async composition, widget architecture, state management (BLoC, Riverpod, Provider), GoRouter navigation, Dio HTTP, Freezed codegen, and clean architecture.