flutter
/

context.read, context.watch, and context.select: Accessing Blocs in Flutter

Last Sync: Today

On this page

9
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

context.read, context.watch, and context.select: Accessing Blocs in Flutter

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

MethodPurposeRebuilds WidgetUse Case
`context.read<T>()`Get bloc without listeningNoAdding events, calling methods
`context.watch<T>()`Get bloc and listen to stateYes, on every state changeDisplaying state data
`context.select<T, R>()`Listen to a specific part of stateYes, only when selected value changesOptimized 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 initState or other lifecycle methods
  • Inside BlocListener or BlocConsumer callbacks where the state is already handled
DARTRead-only
1
ElevatedButton(
  onPressed: () {
    context.read<CounterBloc>().add(IncrementPressed());
  },
  child: Text('Increment'),
)

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 build method
  • When you need the entire state object
  • For small, simple states where performance is not a concern
DARTRead-only
1
class CounterDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final state = context.watch<CounterBloc>().state;
    return Text('Count: ${state.value}');
  }
}

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
DARTRead-only
1
class UserName extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final name = context.select((UserBloc bloc) => bloc.state.user.name);
    return Text('Hello, $name');
  }
}

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).

ApproachAdvantagesDisadvantages
`context.watch`Less boilerplate, direct accessLess explicit, can be overused
`BlocBuilder`Explicit, easy to see rebuild scopeMore nesting
`context.select`Optimised rebuilds, minimal codeRequires state equality
`BlocSelector`Explicit selection, separate builderSlightly more verbose

Common Mistakes

  • ❌ Using context.read in build to access state – The widget won't rebuild when state changes. Use watch or select instead.
  • ❌ Using context.watch for adding events – Causes unnecessary rebuilds. Use read for events.
  • ❌ Calling context.read inside a BlocBuilder that already provides the bloc – The bloc is already available via the builder parameter; no need to read again.
  • ❌ Not using Equatable with context.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.read only for events and one‑time actions – Never for displaying state.
  • Prefer context.select over context.watch for large states – Improves performance.
  • Use Equatable for all state classes – Enables correct equality checks for select and prevents unnecessary rebuilds.
  • Keep widgets that use watch or select small – Rebuilds are scoped to that widget, reducing the impact.
  • Avoid mixing watch and read for the same bloc in a widget – If you need both, consider using BlocConsumer or 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.

DARTRead-only
1
class ProfileScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Profile')),
      body: Column(
        children: [
          // Rebuilds only when name changes
          Text(
            context.select((ProfileBloc bloc) => bloc.state.user.name),
            style: TextStyle(fontSize: 24),
          ),
          // Rebuilds only when email changes
          Text(
            context.select((ProfileBloc bloc) => bloc.state.user.email),
          ),
          ElevatedButton(
            onPressed: () {
              // No rebuild needed, just add event
              context.read<ProfileBloc>().add(UpdateEmail('new@example.com'));
            },
            child: Text('Update Email'),
          ),
        ],
      ),
    );
  }
}

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.

Test Your Knowledge

Q1
of 3

Which method should you use to add an event from a button press?

A
context.watch
B
context.read
C
context.select
D
BlocBuilder
Q2
of 3

What happens if you use context.watch to display state and then call context.read for the same bloc in the same widget?

A
It works fine
B
It causes a rebuild loop
C
It throws an exception
D
It requires a MultiBlocProvider
Q3
of 3

Which method is best for optimising rebuilds when you only need one field from a large state?

A
context.read
B
context.watch
C
context.select
D
BlocBuilder

Frequently Asked Questions

Can I use context.read inside a build method?

Yes, you can use it to add events or call methods that don't require listening to state. However, if you need to display state, use watch or select instead, otherwise the widget won't update when state changes.

Does context.select cause rebuilds if the selected value is the same?

No. It compares the selected value with the previous value using ==. If they are equal, the widget does not rebuild. That's why it's important to implement proper equality (e.g., with Equatable) for complex selected values.

What is the difference between context.watch and BlocBuilder?

Both subscribe to state changes and cause rebuilds. context.watch is a method that returns the bloc's state and can be called anywhere in build. BlocBuilder is a widget that provides a builder callback with the state. Use whichever you find more readable; performance is similar.

Can I access multiple blocs in one widget?

Yes, you can call context.read, context.watch, or context.select multiple times for different bloc types. However, if the widget becomes too large, consider splitting it into smaller widgets for better separation.

Previous

bloc selector

Next

bloc navigation

Related Content

Need help?

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