When using flutter_bloc, you'll often need to access a bloc from within your widgets. The library provides three extension methods on BuildContext: read, watch, and select. Each serves a different purpose, and using them correctly is crucial for performance and maintainability. This guide explains the differences and when to use each.
The Three Methods at a Glance
| Method | Purpose | Rebuilds Widget | Use Case |
|---|---|---|---|
| `context.read<T>()` | Get bloc without listening | No | Adding events, calling methods |
| `context.watch<T>()` | Get bloc and listen to state | Yes, on every state change | Displaying state data |
| `context.select<T, R>()` | Listen to a specific part of state | Yes, only when selected value changes | Optimized state display |
context.read<T>()
context.read<T>() retrieves the nearest bloc of type T without establishing a subscription. The widget will not rebuild when the bloc's state changes. Use this for one‑time actions like adding events or calling methods that don't require the UI to update based on state.
- Adding events in response to user actions (e.g., button presses)
- Calling methods on the bloc that don't affect the UI directly
- Accessing the bloc in
initStateor other lifecycle methods - Inside
BlocListenerorBlocConsumercallbacks where the state is already handled
context.watch<T>()
context.watch<T>() retrieves the nearest bloc and subscribes to its state changes. The widget will rebuild whenever the bloc's state changes. Use this to display state data in your UI.
- Displaying state values directly in the
buildmethod - When you need the entire state object
- For small, simple states where performance is not a concern
context.select<T, R>()
context.select<T, R>() listens to the bloc but rebuilds only when the selected part of the state changes. It takes a selector function that extracts a value from the state. The widget rebuilds when the selected value changes (based on == equality).
- When state objects are large and you only need a small part
- To improve performance by avoiding unnecessary rebuilds
- When different widgets depend on different parts of the same state
Comparison with BlocBuilder / BlocSelector
You might wonder: why use these methods instead of BlocBuilder or BlocSelector? Both approaches are valid. The context methods are convenient for simple cases, while the dedicated widgets are more explicit and can be used when you need more control (e.g., multiple children).
| Approach | Advantages | Disadvantages |
|---|---|---|
| `context.watch` | Less boilerplate, direct access | Less explicit, can be overused |
| `BlocBuilder` | Explicit, easy to see rebuild scope | More nesting |
| `context.select` | Optimised rebuilds, minimal code | Requires state equality |
| `BlocSelector` | Explicit selection, separate builder | Slightly more verbose |
Common Mistakes
- ❌ Using
context.readinbuildto access state – The widget won't rebuild when state changes. Usewatchorselectinstead. - ❌ Using
context.watchfor adding events – Causes unnecessary rebuilds. Usereadfor events. - ❌ Calling
context.readinside aBlocBuilderthat already provides the bloc – The bloc is already available via the builder parameter; no need to read again. - ❌ Not using
Equatablewithcontext.select– Leads to unnecessary rebuilds because the selected value may be considered changed even when it isn't. - ❌ Accessing bloc from a context that doesn't have it – Always ensure the bloc is provided above the widget.
Best Practices
- Use
context.readonly for events and one‑time actions – Never for displaying state. - Prefer
context.selectovercontext.watchfor large states – Improves performance. - Use
Equatablefor all state classes – Enables correct equality checks forselectand prevents unnecessary rebuilds. - Keep widgets that use
watchorselectsmall – Rebuilds are scoped to that widget, reducing the impact. - Avoid mixing
watchandreadfor the same bloc in a widget – If you need both, consider usingBlocConsumeror separate widgets. - Test your widgets with mocked blocs – Ensure that rebuilds happen only when expected.
Real-World Example
Consider a profile screen that shows user name and email, with a button to update the email. The state object contains many fields, but only some are displayed.
What's Next?
Now that you understand how to access blocs, explore how to provide them with BlocProvider and MultiBlocProvider, and learn about optimising rebuilds with BlocSelector.
Next, explore BlocProvider and BlocSelector.