SuperAPI AI Directory

bloc

Coding & development

View repository

Updated 10-16 02:12

About this skill

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
  • build: constructs the bloc under test — return a new instance from the callback, never one built in setUp and shared, so every blocTest starts from a clean bloc and its lifecycle stays with blocTest
  • 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 inside the handler — call emit from the on<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 than this.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