mcpField
B
bloc

bloc

github.com/verygoodopensourceDevelopment175102 installsUpdated 3d ago
Repository

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

  1. 01Bloc and Cubit design guidance
  2. 02Page and View separation
  3. 03Event and state conventions
  4. 04Widget integration patterns
  5. 05Testing with `bloc_test` and `mocktail`

Use cases

  1. 01Structure business logic in Bloc or Cubit
  2. 02Separate presentation, business logic, and data layers
  3. 03Connect state to Flutter widgets
  4. 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 bloc

After installing, type/blocin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

agents/
openai.yaml
references/
architecture.md
patterns.md
testing.md
widgets.md

Details

From the project README

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

AspectCubitBloc
APIFunctions → emit(state)Events → on<Event> → emit(state)
ComplexityLowHigher
TraceabilityLess (no event log)Full (events + transitions)
When to useSimple state, UI-driven logicComplex flows, event-driven, transforms
TestingCall methods, assert statesAdd 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 nameMeaning
TodoListSubscriptionRequestedSubscribing to todo list stream
TodoListTodoDeletedDeleting a specific todo
TodoListUndoDeletionRequestedUndoing the last deletion
LoginFormSubmittedSubmitting the login form
ProfilePageRefreshedRefreshing 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 nameMeaning
LoginInitialNo action taken yet
LoginInProgressLogin request in flight
LoginSuccessLogin succeeded
LoginFailureLogin 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

LayerContainsDepends on
PresentationPages, Views, WidgetsBusiness Logic
Business LogicBlocs, CubitsData
DataRepositories, Data ProvidersExternal 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

Comments

Markdown supported