bloc
Best practices for Bloc state management in Flutter/Dart.
Works with
---
name: bloc
description: Best practices for Bloc state management in Flutter/Dart.
license: MIT
---
# Bloc
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()` from `package:bloc_test`** for all Bloc and Cubit tests — never raw `test()` with manual stream assertions
- **Use `package:mocktail` for mocking** — never `package:mockito`
- **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 via `BlocBuilder`/`BlocListener`
- **Sealed classes for events and multi-state types** — enables exhaustive pattern matching with Dart 3 `switch`
- **Equatable for all states and events** — extend `Equatable` and override `props` for value equality
- **Business logic in Bloc/Cubit only** — never in widgets, pages, or views
- **Single responsibility** — one Bloc/Cubit per feature concern
- **Emit only after async checks** — use `emit` only inside the handler callback
---
## 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
```dart
class CounterCubit extends Cubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
}
```
### Bloc Example
```dart
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 |
```dart
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];
}
```
### 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 |
```dart
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];
}
```
#### Single Class Approach (one state, multiple fields)
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 |
```dart
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];
}
```
---
## 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](references/architecture.md) for the repository example, feature folder structure, and test directory layout.
---
## Flutter Widgets
- `BlocProvider` — creates and provides a Bloc/Cubit to the subtree
- `BlocBuilder` — rebuilds widget when state changes
- `BlocListener` — executes side effects (navigation, snackbar) on state change
- `BlocConsumer` — combines `BlocBuilder` + `BlocListener`
- `BlocSelector` — rebuilds only when a selected property changes
- Use `context.read` in callbacks (`onPressed`, `onTap`), `context.watch` or `BlocBuilder` in `build` methods
- Never use `context.watch` outside of `build`
See [references/widgets.md](references/widgets.md) for the full widget and context extension tables, Page/View pattern example, and BlocListener example. See [references/testing.md](references/testing.md) for `blocTest()` parameters, Cubit/Bloc test examples, mocking dependencies, and widget integration tests. See [references/patterns.md](references/patterns.md) for adding features with Bloc/Cubit, async operations, and event transformers.More Mobile skills
animation-vocabulary
emilkowalski/skills
Reverse-lookup glossary that turns a vague description of a web animation or motion effect into its exact term ("the bouncy thing when a popover opens" → Pop in; "the iOS rubber-band scroll" → Rubber-banding). Use when the user asks "what's it called when…", or describes a motion effect without knowing its name and wants the right word to prompt an AI or designer with. For naming an effect, not designing or building one.
cross-border-ecommerce
nexscope-ai/ecommerce-skills
Cross-border e-commerce expansion advisor. Scores target markets on 8 weighted dimensions (market size, ecommerce penetration, competition, regulatory complexity, logistics infrastructure, payment ecosystem, cultural distance, IP protection), compares 5 fulfillment models with cost and transit data, provides country-by-country tax/duty compliance guides (EU VAT/IOSS, UK VAT, US sales tax, CA GST, AU GST, JP consumption tax), maps local payment preferences by market, and builds a phased expansion roadmap. No API key required.
developing-genkit-dart
firebase/agent-skills
Generates code and provides documentation for the Genkit Dart SDK. Use when the user asks to build AI agents in Dart, use Genkit flows, or integrate LLMs into Dart/Flutter applications.

