flutter dart
npx skills add dhruvanbhalara/skills --skill flutter-bloc

BLoC Pattern

  • Sealed States & Events: Always use sealed class for both States and Events to ensure exhaustive UI handling and compile-time safety.
  • Immutability: All States, Events, and Domain Entities MUST be immutable (using final and Equatable or freezed).
  • Official BLoC Part-Part Of Pattern: Every _bloc.dart file MUST include its corresponding _event.dart and _state.dart files using part directives. Each event/state file MUST have a part of directive pointing back to the bloc file. This ensures a single library scope and shared private members.
    // auth_bloc.dart
    part 'auth_event.dart';
    part 'auth_state.dart';
    
    class AuthBloc extends Bloc<AuthEvent, AuthState> { ... }
    
    // auth_event.dart
    part of 'auth_bloc.dart';
    
    // auth_state.dart
    part of 'auth_bloc.dart';
    
  • Mandatory Directory Structure: Every BLoC feature set MUST reside in its own sub-directory within the bloc/ folder. Flat bloc/ directories are STRICTLY prohibited.
    presentation/bloc/
    └── <bloc_name>/
        ├── <bloc_name>_bloc.dart
        ├── <bloc_name>_event.dart
        └── <bloc_name>_state.dart
    
  • Loading State Mandate: ALWAYS emit Loading before async work, then Success or Error. Never skip the loading state.
  • Concurrency: Use transformers (e.g., restartable(), droppable()) for events requiring debouncing (search) or throttling (buttons).
  • Zero-Logic UI: Widgets MUST NOT contain business logic, orchestration logic, or direct calls to external services. They should ONLY dispatch events and build UI based on BLoC states.

BLoC Widget Usage

  • BlocBuilder for local UI rebuilds based on state
  • BlocListener for side effects (navigation, snackbars, dialogs)
  • BlocConsumer when both rebuild and side effects are needed
  • context.read<Bloc>().add(Event()) for dispatching events
  • context.watch<Bloc>().state for reactive rebuilds (inside build() only)

BLoC Submission Checklist

  • [ ] Events and States use Equatable with correct props
  • [ ] All async operations follow Loading → Success/Error pattern
  • [ ] No business logic in UI widgets
  • [ ] No SDK/API calls outside DataSources
  • [ ] Zero hardcoded colors, spacing, or typography \u2014 use design tokens (AppColors, AppSpacing)
  • [ ] Code formatted with dart format

Dependency Injection

  • Use injectable for dependency injection and service location
  • Standardized Injection:
    • Use @injectable for screen-specific BLoCs to ensure a fresh instance per screen access.
    • Use @lazySingleton for global or shared BLoCs (e.g., AuthBloc, ThemeBloc, SettingsBloc, PasswordBloc).
  • Organize blocs logically by feature and ensure strict separation of concerns

Navigation & Routing

  • Dynamic Routes: STRICTLY prohibit hardcoded route strings in GoRouter configuration. Use static constants in AppRoutes.
  • Centralized BLoCs: BLoC providers MUST be injected via ShellRoute or BlocProvider in app_router.dart when shared across multiple screens or within a feature branch.
  • No Local Providers: Avoid BlocProvider in individual screen build() methods if the BLoC is needed by a feature set.
  • Primitive Route Arguments: STRICTLY prohibit passing complex objects (BLoCs, ChangeNotifiers, Entity instances) as route arguments. Pass only primitive IDs/Keys and fetch data in the destination screen using Repository or Bloc injection.