In the BLoC pattern, not all state changes should directly modify the UI. Some changes require side effects – one‑time actions like showing a snackbar, navigating to another screen, or playing a sound. BlocListener is the dedicated widget for handling these side effects without causing unnecessary rebuilds.
What is BlocListener?
BlocListener is a Flutter widget that listens to state changes in a bloc and executes a callback in response. Unlike BlocBuilder, it does not rebuild its child. It is designed exclusively for side effects: actions that should happen once when a particular state is reached.
Basic Usage
Wrap a widget with BlocListener and provide a listener function. The listener is called whenever the state changes. You can optionally filter which states trigger the listener using the listenWhen parameter.
BlocListener vs BlocBuilder
| Feature | BlocListener | BlocBuilder |
|---|---|---|
| Primary purpose | Side effects (navigation, dialogs) | UI rebuild on state change |
| Rebuilds child | No | Yes |
| Called on every state | Yes (unless filtered) | Yes (unless filtered) |
| Common use case | Snackbars, navigation, logging | Displaying data, toggling UI |
When to Use BlocListener
- Navigation – Push a new screen when authentication succeeds.
- Snackbars / Toasts – Show feedback messages on error or success.
- Dialogs – Present a dialog when a specific state is reached.
- Analytics / Logging – Send analytics events when certain states occur.
- Focus management – Move focus to a text field after validation.
Filtering with listenWhen
By default, BlocListener calls the listener on every state change. To avoid unnecessary calls, use the listenWhen predicate. It receives the previous and current states and returns true only when you want the listener to run.
Multiple Listeners
You can use multiple BlocListener widgets for the same bloc to separate concerns. For example, one listener for navigation and another for snackbars. This keeps your code clean and focused.
BlocListener with MultiBlocProvider
When you have multiple blocs, you can nest BlocListener widgets or combine them with MultiBlocProvider. A common pattern is to wrap your UI with a MultiBlocProvider and then a BlocListener for each relevant bloc.
Testing with BlocListener
In widget tests, you can verify that the listener callback is triggered. Use tester.pumpWidget and then assert that navigation or other side effects occurred.
Best Practices
- Use
listenWhento avoid unnecessary calls – Prevents side effects from running on every state change. - Keep listener callbacks simple – Avoid heavy computation; delegate complex logic to the bloc if needed.
- Separate concerns – Use multiple
BlocListenerwidgets for different side effects. - Prefer
BlocListenerover manual checks inBlocBuilder– Avoid mixing UI rebuild logic with side effects. - Use
BlocConsumerif you need both –BlocConsumercombinesBlocListenerandBlocBuilderin one widget. - Don't trigger side effects from inside
BlocBuilder– That would run on every rebuild, leading to unexpected behaviour.
Common Mistakes
- ❌ Using
BlocBuilderfor side effects – Leads to multiple executions if the widget rebuilds for other reasons. - ❌ Not filtering with
listenWhen– The listener runs on every state change, including loading states, causing unintended actions. - ❌ Calling
setStateinside listener – The listener is called outside the build phase; usecontextto trigger navigation or show dialogs instead. - ❌ Misplacing
BlocListener– If placed too deep, the context may not have access to the required bloc or navigator. - ❌ Assuming listener runs only once per state – If you navigate and the widget is rebuilt, the listener may run again; use
listenWhento guard against that.
What's Next?
Now that you understand side effects, explore how to combine builder and listener with BlocConsumer, and learn about advanced state management patterns.
Next, explore BlocConsumer and Bloc architecture.