What are Presentation Events? Why do we need them? A simple guide.
This article assumes you have prior knowledge of BLoC. But even if you don’t, this article gives you a bit of a primer. And if that is not…

What are Presentation Events in Bloc? Why do we need them? A simple guide.
Free link for readers
This article assumes you have prior knowledge of BLoC. But even if you don’t, this article gives you a bit of a primer. And if that is not enough, refer to these other articles that I have written:
To fully understand Presentation Events, we must go back in time. And recall what state machines are.

A state machine is a mathematical abstraction used to design algorithms. A state machine reads a set of inputs and changes to a different state based on those inputs. A state is a description of the status of a system waiting to execute a transition. — Wikipidea
So let’s unpack this definition:
A state machine is a system that:
- Has a finite set of states
- Starts in an initial state
- Transitions to other states based on events
- Has rules about which events cause which transitions
BLoC (Business Logic Component) is an architectural pattern where:
- Events are inputs (user actions, timers, API responses)
- States are outputs (what the UI listens to)
- A Bloc (class) reacts to events and emits new states
// Simplified
bloc.add(MyEvent());
bloc.stream.listen((state) => ...);
So, from this, we understand that at a single point in time, BLoC will hold exactly one state.
A Weather Forecast
Don’t worry, we will come to the Presentation Events, just have patience.

Introduction to the app
Let’s say we were building a weather application. We would build our BLoC events and state something like this:
abstract class WeatherEvent {}
class GetWeather extends WeatherEvent {}
sealed class WeatherState {
const WeatherState();
}
class WeatherInitial extends WeatherState {}
class WeatherLoading extends WeatherState {}
class WeatherLoaded<T> extends WeatherState {
final T data;
const WeatherLoaded(this.data);
}
class WeatherError extends WeatherState {
final String error;
const WeatherError(this.error);
}
class WeatherBloc extends Bloc<WeatherEvent, WeatherState> {
...
}
So when the app starts, it will look something like this:

This is because it will first set the state as WeatherInitial
and then as WeatherLoading
and if the call to fetch the data succeeds, we set the state as WeatherLoaded
with data. And for aesthetic reasons, let’s say our screen looks something like this:


And if an error occurs, our screen looks something like this:

Now you can imagine your state machine or BLoC has state representation, which looks something like this: whether an error has occurred or not.
WeatherInitial
> WeatherLoading
> WeatherLoaded
or
WeatherInitial
> WeatherLoading
> WeatherError
One thing to note here is that we move from Loading to eitherError
orSuccess
and when we move to either of the states, the data from the other state is lost.
This works just fine for us.
The problem statement
Now, let’s suppose you were able to load the data once, and now you want to refresh the data.

God forbid that call fails. If that happens, if you change the state to WeatherError
your screen will rebuild and your UI will change from loaded weather data to the error screen.

If that is what you want, completely fine. But what if you wanted to just show a Snackbar
while, still showing the previously loaded data.
Now we can have multiple solutions here.
1. Create a new WeatherErrorWithData state
We add a new state called WeatherErrorWithData
. This might be one way to solve this, where we add the error and the weather data to retain the state.
class WeatherError<T> extends WeatherState {
final String error;
final T? data;
const WeatherError(this.error, this.data);
}
Here, we could add a field like this, and then in the UI we could utilise the BlocListener
where if this event comes up, we show a snackbar like this:
BlocListener<WeatherBloc, WeatherState>(
listener: (context, state) {
if (state is WeatherErrorWithData) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(state.error)));
}
}
)
This works fine, but if the same error occurs when the user tries to reload the data, the state machine looks something like this:
WeatherInitial
> WeatherLoading
> WeatherLoaded
> WeatherError("Error while loading", oldData)
> WeatherError("error while loading", oldData)
But this won’t run the listener twice. This is because BLoC is change-focused. That means, only if there is a change in the state, the bloc listener and builders will be notified and called.
In this case, WeatherError has the same arguments twice, and even if we are using Equatable for equality, we won’t be able to solve this.
If we still somehow wanted to bypass this, we could fiddle with the listenWhen
and buildWhen
parameters of Bloc. But that would be controlling too much and not letting BLoC do its work.
2. Add error field in WeatherLoaded
If we modified our WeatherLoaded
state, it looks something like this:
class WeatherLoaded<T> extends WeatherState {
final T data;
final String? error
const WeatherLoaded(this.data, this.error);
}
It’s like holding the ear, but from the opposite end. Same problem, same solution. No help doing it this way.

If we do this, we are still going to fall into the same trap of overcomplicating things. We would have to either manipulate buildWhen
and listenWhen
and redundant data and states.
The solution: Presentation Events
The solution seems simple: we just need something that does one-time tasks while still retaining the state. We do not want to transition from the WeatherLoaded
state, but still show an error. This is where presentation events come to the rescue.
Extends blocs with an additional stream serving as a way of indicating single-time events (so-called “presentation events”). — Documentation
So BLoC class has a stream variable that provides us with real-time changes in state and a state variable that returns the last state.
Adding the Presentation Events mixin to the BLoC does not do anything with that Stream but rather adds another stream that pushes one-off events that the application can dispose of once utilised or can even ignore.
You would start with creating a class that denotes what kind of data you want to show for your one-time event that happens.
class WeatherRefreshError {
const OnboardingCompleteError(this.error);
final String error;
}
Add the bloc_presentation
dependency.
flutter pub add bloc_presentation
And in your BLoC, update it as:
class WeatherBloc extends Bloc<WeatherEvent, WeatherState>
with
BlocPresentationMixin<WeatherState, WeatherRefreshError> {
...
void onFetchWeatherData() async {
// fetch weather data
// if error occurs
emitPresentation(WeatherRefreshError('Failed to load weather'));
...
}
}
emitPresentation
emits the presentation event, which the UI can consume, something like this:
BlocPresentationListener<WeatherBloc, WeatherRefreshError>(
listener: (context, state) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error)),
);
}
),