project-bloc

Guide for creating BLoC patterns with events, states, and proper structure in app_bloc packages (project)

SKILL.md

GitHub

Flutter BLoC Development Skill

This skill guides the creation of BLoC (Business Logic Component) packages following this project's conventions.

When to Use

Trigger this skill when:

  • Creating a new BLoC package
  • Adding events or states to an existing BLoC
  • Implementing business logic that manages application state
  • User asks to "create a bloc", "add state management", or "implement feature with BLoC"

Mason Templates Available

Always use Mason templates first:

# Simple BLoC (basic state management)
mason make simple_bloc -o app_bloc/feature_name --name=feature_name

# List BLoC (for managing lists with CRUD operations)
mason make list_bloc -o app_bloc/feature_name --name=feature_name

# Form BLoC (for form validation and submission)
mason make form_bloc --name Login --field_names "email,password"

Project Structure

BLoC packages MUST be created in app_bloc/:

app_bloc/
└── feature_name/
    ├── lib/
    │   ├── feature_name_bloc.dart  # Barrel export file
    │   └── src/
    │       ├── bloc.dart           # BLoC class with event handlers
    │       ├── event.dart          # Event definitions
    │       └── state.dart          # State class with status enum
    ├── test/
    │   └── feature_name_bloc_test.dart
    └── pubspec.yaml

BLoC Pattern Conventions

Event Naming

  • Events are named as actions: LoadData, SubmitForm, ChangeTheme
  • Use past tense for completed actions: DataLoaded, FormSubmitted

State Structure

  • States use a status enum: initial, loading, completed, error
  • Use copyWith() for immutable state updates
  • Extend Equatable for proper state comparison

Event Handlers

  • Prefix handlers with _on: _onLoadData, _onSubmitForm
  • Use Emitter<State> for state emissions
  • Handle errors with try-catch and emit error states

Reference Implementation

Note: DmThemeBloc is a pre-built BLoC provided by duskmoon_theme_bloc (re-exported via duskmoon_ui). It is NOT a custom BLoC that users subclass or create — use it directly from the package. It is shown here as a reference for the BLoC pattern used in this project.

DmThemeBloc (from package:duskmoon_ui/duskmoon_ui.dart) manages app theme state with automatic SharedPreferences persistence:

// Import
import 'package:duskmoon_ui/duskmoon_ui.dart';

// Provide it near the root of your widget tree
BlocProvider(
  create: (context) => DmThemeBloc(sharedPreferences),
  child: BlocBuilder<DmThemeBloc, DmThemeState>(
    builder: (context, state) {
      // state.themeName  — active theme name
      // state.themeMode  — ThemeMode (light/dark/system)
      // state.entry      — DmThemeEntry with .light and .dark ThemeData
      return MaterialApp(
        theme: state.entry.light,
        darkTheme: state.entry.dark,
        themeMode: state.themeMode,
      );
    },
  ),
)

// Dispatch events
context.read<DmThemeBloc>().add(DmSetTheme('ocean'));
context.read<DmThemeBloc>().add(DmSetThemeMode(ThemeMode.dark));

Dependencies

In pubspec.yaml, use workspace resolution:

dependencies:
  bloc: any
  flutter_bloc: any
  equatable: any

Testing Requirements

Every BLoC MUST have tests in test/ directory:

  • Test initial state
  • Test each event handler
  • Test state transitions
  • Test error handling

Run tests: cd app_bloc/feature_name && flutter test

Integration with UI

Use BlocProvider and BlocBuilder from flutter_bloc:

BlocProvider(
  create: (context) => FeatureBloc(),
  child: BlocBuilder<FeatureBloc, FeatureState>(
    builder: (context, state) {
      // Build UI based on state
    },
  ),
)