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.dart
├── _event.dart
└── _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().add(Event())
for dispatching events
context.watch().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.