bloc_presentation
A Flutter package for dispatching single-time side effects from Blocs and Cubits — perfect for navigation, snackbars, dialogs, and other one-shot presentation events that shouldn't live in your Bloc/Cubit state.
Built on top of flutter_bloc, bloc_presentation provides a BlocPresentationMixin for emitting events and a BlocPresentationListener widget (plus a hooks API) for reacting to them in the UI. No more clearing "consumed" flags from state or losing events on rebuilds.
Features
- BlocPresentationMixin — Add a secondary event stream to any Bloc or Cubit without touching its state
- BlocPresentationListener — A widget that listens for one-time presentation events (similar to BlocListener but for side effects)
- flutter_hooks support — Use
useOnStreamChangewithbloc.presentationfor hook-based listeners - Nullable type support — Declare
BlocPresentationListener<MyCubit?, MyEvent>for optional cubits - Testable — Companion package
bloc_presentation_testprovides mocks and stubs
When to use bloc_presentation
Use presentation events instead of state when you need to:
- Show a SnackBar, toast, or dialog after an action
- Trigger navigation (push a route, pop, open a deep link)
- Fire analytics events
- Play a sound or haptic feedback
- Any fire-and-forget side effect that isn't part of your UI state
Installation
flutter pub add bloc_presentation
Usage
First, create an event which will be emitted:
sealed class CommentCubitEvent {}
class FailedToUpvote implements CommentCubitEvent {
const FailedToUpvote(this.reason);
final String reason;
}
Next, extend your Bloc/Cubit with the presentation mixin which will give you
access to the emitPresentation method:
class CommentCubit extends Cubit<CommentState> with BlocPresentationMixin<CommentState, CommentCubitEvent> {
// body
}
Now in your methods instead of emitting new state, you can emit a single-time presentation event without overwriting your Bloc/Cubit state:
void upvote() {
// upvoting logic
if (!success) {
// we can emit it and forget about cleaning it from the state
emitPresentation(const FailedToUpvote('bad connection'));
} else {
emit(/* new state */);
}
}
In this case above, we do not want to lose our Bloc/Cubit state after a
non-fatal failure. Instead, we want to communicate this failure and not emit any
new states. Then, in the UI code one can react to such events using
BlocPresentationListener or useBlocPresentationListener:
BlocPresentationListener<CommentCubit, CommentCubitEvent>(
listener: (context, event) {
switch (event) {
case FailedToUpvote():
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.reason)));
}
},
child: MyWidget(),
)
By default, CommentCubit will be looked up using package:provider in the
widget tree. However, a bloc can be provided directly using the
BlocPresentationListener.bloc parameter (analogous to how package:bloc
listeners work). If you want the listener to be a no-op when the cubit isn't
available in the widget tree, you can declare a nullable type parameter:
BlocPresentationListener<CommentCubit?, CommentCubitEvent>(
listener: (context, event) { /* ... */ },
child: MyWidget(),
)
Example
Usage with flutter_hooks
Let's assume you have a cubit that emits presentation events:
class MyCubit extends Cubit<MyState> with BlocPresentationMixin {
(...)
}
You can listen to its events via the useOnStreamChange hook:
useOnStreamChange(
bloc.presentation,
(event) {
// Implement your listener here
},
)
Testing
In order to to make testing of this package more straightforward, please, check out the bloc_presentation_test package.
🛠️ Maintained by LeanCode
This package is built with 💙 by LeanCode. We are top-tier experts focused on Flutter Enterprise solutions.
Why LeanCode?
-
Creators of Patrol – the next-gen testing framework for Flutter.
-
Production-Ready – We use this package in apps with millions of users.
-
Full-Cycle Product Development – We take your product from scratch to long-term maintenance.

