emit_new_bloc_state_instances
This rule flags an emit(state) call that hands the current state object straight back to emit. Bloc compares the incoming state with == and drops it when it is equal to the current one, so the call does nothing.
Why use this rule
Section titled “Why use this rule”BlocBase.emit short-circuits when the new state equals the current one. Passing state straight back is therefore always a no-op: no listener is notified, no widget rebuilds, and the UI keeps rendering the previous data.
Nothing throws and the handler runs to completion, so the symptom — a list that will not refresh — looks like a rendering problem and gets debugged in the widget.
See also: bloc: why doesn’t my state update?, bloc: BlocBase.emit
Examples
Section titled “Examples”Mutate-then-re-emit
Section titled “Mutate-then-re-emit”The commonest form. The list really does gain an item; no one is told:
// Don'tclass TodoCubit extends Cubit<TodoState> { TodoCubit() : super(TodoState.initial());
void add(Todo todo) { state.items.add(todo); emit(state); // LINT - same instance, `==` holds, dropped }}// Do - a new instance, so the equality check sees a changeclass TodoCubit extends Cubit<TodoState> { TodoCubit() : super(TodoState.initial());
void add(Todo todo) { emit(state.copyWith(items: [...state.items, todo])); }}Anything derived from the state is accepted - state.copyWith(...), state + 1, or a fresh construction:
void increment() => emit(state + 1);void reset() => emit(TodoState.initial());This is also why Bloc state is expected to be immutable, and what prefer_immutable_bloc_state asks for from the other end: that rule makes the state impossible to mutate in place, this one catches the mutation-and-re-emit directly.
The Bloc handler’s emit is the same
Section titled “The Bloc handler’s emit is the same”In a Bloc, emit is the handler’s Emitter parameter rather than an inherited method. Both call shapes are reported:
// Don'tclass CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<Refresh>((event, emit) => emit(state)); // LINT }}If the intent was “handle this event by doing nothing”, write nothing - an empty handler body says so, a no-op emit does not:
// Doclass CounterBloc extends Bloc<CounterEvent, int> { CounterBloc() : super(0) { on<Refresh>((event, emit) {}); }}A project wrapper around emit
Section titled “A project wrapper around emit”If your blocs emit through a helper, name it so the same no-op is caught there:
rules: emit_new_bloc_state_instances: additional_methods: [safeEmit]// Don'tclass TodoCubit extends Cubit<TodoState> { TodoCubit() : super(TodoState.initial());
void refresh() => safeEmit(state); // LINT
void safeEmit(TodoState next) { if (!isClosed) emit(next); }}Known limitations
Section titled “Known limitations”Only the bare reads state and this.state are reported. Anything built from the state is left alone, including state.copyWith() with no arguments - that returns a new instance, so emit accepts it even though the contents are identical.
A state object reached through a local variable (final s = state; emit(s);) is not reported. Tracking assignments through the method body would be needed for a shape that is rare in practice.
Only a single-argument call is checked. A wrapper invoked as safeEmit(state, force: true) is not matched.
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: emit_new_bloc_state_instances: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”many_lints: rules: emit_new_bloc_state_instances: additional_methods: [safeEmit]rules: emit_new_bloc_state_instances: additional_methods: [safeEmit]| Option | Type | Default | Description |
|---|---|---|---|
additional_methods |
list of strings | [] |
Extra methods treated like emit, for a project wrapper that forwards to it |
Related rules
Section titled “Related rules”prefer_immutable_bloc_state— Ensure Bloc and Cubit state classes are annotated with @immutable.avoid_duplicate_bloc_event_handlers— Register each bloc event type exactly once.handle_bloc_event_subclasses— Register a handler for every Bloc event subclass.avoid_bloc_public_methods— Prevent public methods, getters, and setters in Bloc classes.