flutter
/

Bloc Emit: Mastering State Emission in Flutter

Last Sync: Today

On this page

11
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Bloc Emit: Mastering State Emission in Flutter

In the BLoC pattern, emitting is how you publish new states from your bloc. The Emitter<State> object, passed to every event handler, is your gateway to updating the UI. This guide explains everything about emitting states correctly and efficiently.

What is Emitter?

Emitter<State> is a callback that allows you to emit one or more states from within an event handler. It is the only way to change the bloc's state. When you call emit(state), the new state flows through the bloc's state stream, and the UI is notified.

Basic Emission

The simplest use is to emit a single state in response to an event.

DARTRead-only
1
on<IncrementPressed>((event, emit) {
  emit(CounterState(state.value + 1));
});

Multiple Emissions

You can call emit multiple times within the same handler. This is useful for sequences like loading → success or for progressive updates.

DARTRead-only
1
on<LoginSubmitted>((event, emit) async {
  emit(LoginLoading());
  try {
    final user = await repository.login(event.email, event.password);
    emit(LoginSuccess(user));
  } catch (e) {
    emit(LoginFailure(e.toString()));
  }
});

Conditional Emitting

Sometimes you want to emit only if certain conditions are met. You can guard emissions with if statements.

DARTRead-only
1
on<UpdateUser>((event, emit) {
  if (event.user.id != state.user.id) {
    emit(UserUpdated(event.user));
  }
});

You can also check if the bloc is still open using the isClosed property. Avoid emitting after the bloc is closed.

DARTRead-only
1
on<LongRunningTask>((event, emit) async {
  emit(TaskLoading());
  await doWork();
  if (!isClosed) {
    emit(TaskSuccess());
  }
});

Asynchronous Emissions

Emitting inside asynchronous code works as expected, but you must be careful about timing and bloc disposal. Always check isClosed before emitting after an async gap.

DARTRead-only
1
on<FetchData>((event, emit) async {
  emit(DataLoading());
  
  final data = await repository.fetch();
  
  // The bloc might have been closed while waiting
  if (!isClosed) {
    emit(DataLoaded(data));
  }
});

Avoiding Redundant Emissions

Bloc automatically prevents emission of the same state (based on ==). To take advantage of this, ensure your state classes implement proper equality. The easiest way is to use Equatable.

DARTRead-only
1
class CounterState extends Equatable {
  final int value;
  const CounterState(this.value);

  @override
  List<Object> get props => [value];
}

// Later in the bloc:
on<IncrementPressed>((event, emit) {
  // Emits only if the new value is different
  emit(CounterState(state.value + 1));
});

Error Handling in Emissions

Always wrap code that might throw exceptions. Uncaught exceptions in handlers will propagate to the bloc's onError and may stop further processing. Emit error states instead of letting exceptions bubble.

DARTRead-only
1
on<ProcessData>((event, emit) async {
  try {
    emit(Processing());
    final result = await riskyOperation();
    emit(ProcessSuccess(result));
  } catch (e, s) {
    emit(ProcessFailure(e.toString()));
    addError(e, s); // Optional: also report to onError
  }
});

Common Emission Patterns

DARTRead-only
1
emit(Loading());
try {
  final data = await fetch();
  emit(Success(data));
} catch (e) {
  emit(Error(e.toString()));
}
DARTRead-only
1
emit(UploadProgress(0));
for (int i = 1; i <= 10; i++) {
  await Future.delayed(Duration(milliseconds: 100));
  emit(UploadProgress(i * 10));
}
emit(UploadComplete());
DARTRead-only
1
on<LoadCachedData>((event, emit) {
  if (state is DataLoaded && (state as DataLoaded).isFresh) {
    // No need to emit, state already fresh
    return;
  }
  // else load from source
  emit(DataLoading());
  // ... load and emit loaded
});

Best Practices

  • Use Equatable for all states – prevents unnecessary emissions and rebuilds.
  • Always check isClosed after async operations – avoids emitting to a closed bloc.
  • Emit loading states for async operations – gives users feedback.
  • Prefer atomic states – each state should represent a complete snapshot.
  • Never mutate a state object and emit the same instance – always create a new instance.
  • Use addError to propagate exceptions to onError – helps with logging and debugging.

Common Mistakes

  • ❌ Emitting the same instance – UI won't update; always create new instances.
  • ❌ Emitting after bloc is closed – causes an exception; check isClosed.
  • ❌ Not handling exceptions – uncaught exceptions stop the bloc; always catch and emit error states.
  • ❌ Emitting too frequently – can cause performance issues; use transformers if needed.
  • ❌ Mutating state after emission – the UI might already be using the previous state; avoid side effects after emit.

What's Next?

Now that you know how to emit states correctly, learn how to structure your events and handle more complex scenarios with event transformers.

Next, explore Bloc events and Bloc class lifecycle.

Test Your Knowledge

Q1
of 3

What does the Emitter do in a Bloc event handler?

A
Adds new events
B
Emits new states
C
Closes the bloc
D
Handles errors
Q2
of 3

Which property should you check before emitting after an async operation?

A
isDisposed
B
isActive
C
isClosed
D
isOpen
Q3
of 3

What is the recommended way to prevent duplicate states from causing rebuilds?

A
Use Equatable on states
B
Use a global flag
C
Emit only once per handler
D
Use Stream.distinct

Frequently Asked Questions

Can I emit a state that is the same as the current state?

Yes, but Bloc will ignore it because it checks equality. This is why you should use Equatable for states to avoid accidental duplicate emissions.

What happens if I emit after the bloc is closed?

It will throw a StateError because the event sink is closed. Always check isClosed before emitting after any asynchronous operation.

How many times can I call emit in a single handler?

As many times as needed. However, be aware that each emit will trigger UI rebuilds if the state changed. For very frequent emissions, consider using throttling or debouncing via event transformers.

Do I need to use Equatable for emitting to work?

No, emit works regardless. But without proper equality, Bloc may still think the new state is different and cause unnecessary rebuilds. Equatable is strongly recommended.

Previous

bloc cubit

Next

bloc equatable

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.