avoid_duplicate_bloc_event_handlers
This rule flags a bloc constructor that registers the same event type with on<E> more than once.
Why use this rule
Section titled “Why use this rule”Bloc.on<E> asserts that each event type has exactly one handler. A second registration for the same type throws a StateError:
on<IncrementEvent>was called multiple times.
Because the registration happens in the constructor, the throw fires the first time the bloc is instantiated — often deep in a provider or a route builder, far from the duplicated line. Catching it at analysis time turns a runtime crash into a squiggle on the exact call.
The usual cause is a copy-pasted on<...> line where the type argument was not updated.
See also: bloc: Bloc.on
Examples
Section titled “Examples”A copy-pasted registration
Section titled “A copy-pasted registration”The usual cause: the line was duplicated to add the opposite behaviour and the type argument was never updated. It compiles; it throws the first time the bloc is constructed.
// Don'tclass CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<IncrementEvent>((event, emit) => emit(state + 1)); on<IncrementEvent>((event, emit) => emit(state - 1)); // LINT }}// Doclass CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<IncrementEvent>((event, emit) => emit(state + 1)); on<DecrementEvent>((event, emit) => emit(state - 1)); }}If two behaviours genuinely belong to one event, merge them into a single handler rather than registering twice.
A project wrapper around on
Section titled “A project wrapper around on”If your blocs register through a helper that forwards to on, name it so the same crash is caught there:
rules: avoid_duplicate_bloc_event_handlers: additional_methods: [onEvent]// Don'tclass CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { onEvent<IncrementEvent>((event, emit) => emit(state + 1)); onEvent<IncrementEvent>((event, emit) => emit(state - 1)); // LINT }
void onEvent<E extends CounterEvent>(EventHandler<E, int> handler) => on<E>(handler);}Known limitations
Section titled “Known limitations”Registrations are tracked per constructor. A bloc with two constructors that each register the same event is not flagged, because only one of them runs for any given instance.
Only an unqualified on<E>(...) or this.on<E>(...) with exactly one type argument is counted. A registration made outside the constructor body, or through a variable holding the handler, is not seen.
Turning this rule off
Section titled “Turning this rule off”This rule is in the core preset, so it is on with preset: core,
preset: recommended or preset: opinionated.
To turn it off:
rules: avoid_duplicate_bloc_event_handlers: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”many_lints: rules: avoid_duplicate_bloc_event_handlers: additional_methods: [onEvent]rules: avoid_duplicate_bloc_event_handlers: additional_methods: [onEvent]| Option | Type | Default | Description |
|---|---|---|---|
additional_methods |
list of strings | [] |
Extra registration methods treated like Bloc’s on, for a project wrapper that forwards to it |
Related rules
Section titled “Related rules”handle_bloc_event_subclasses— Register a handler for every Bloc event subclass.emit_new_bloc_state_instances— Emit a new state instance instead of the existing state object.prefer_immutable_bloc_state— Ensure Bloc and Cubit state classes are annotated with @immutable.avoid_bloc_public_methods— Prevent public methods, getters, and setters in Bloc classes.