Reading: watch, read, select, Consumer
There are four ways to read a value with Provider, and the difference between them is subscription and rebuild scope.
context.watch<T>()— returns the value and subscribes the widget. On change, the whole `build` method re-runs. Only valid insidebuild.context.read<T>()— creates no subscription. It is for calling methods from callbacks such asonPressed.context.select<T, R>((T value) => ...)— subscribes only to the selected part: ifRis unchanged there is no rebuild.Consumer<T>/Selector<T, R>— narrow the subscription at widget level: only what the builder returns is rebuilt.
Provider.of<T>(context, listen: false) is the older, longer spelling of read; with listen: true it equals watch.
| Goal | The right tool | The typical mistake |
|---|---|---|
| Show a value in `build` | `context.watch<T>()` or `Consumer<T>` | Using `read` → the UI never updates |
| Call a method on a button press | `context.read<T>().doIt()` | Using `watch` → a needless subscription |
| Track only one field of a model | `context.select((T v) => v.field)` / `Selector` | Watching the whole model → a rebuild on every change |
| Get an initial value in `initState` | `context.read<T>()` | Calling `watch` → the framework forbids it |
A direct recommendation from the official Flutter docs: put your `Consumer` widgets as deep in the tree as possible — you don't want to rebuild large portions of the UI just because some detail changed. The Provider docs warn about the mirror image: don't use read to obtain values in build, and initState is not the place for watch.
// ❌ Bütün ekran səbətin hər dəyişikliyində rebuild olunur.
@override
Widget build(BuildContext context) {
final cart = context.watch<CartModel>();
return Column(
children: [
const ExpensiveBanner(),
HeavyProductGrid(items: allProducts),
Text('Cəmi: ${cart.totalPrice}'),
],
);
}
// ✅ Yalnız Text rebuild olunur; grid child kimi ötürülür.
@override
Widget build(BuildContext context) {
return Column(
children: [
const ExpensiveBanner(),
HeavyProductGrid(items: allProducts),
Consumer<CartModel>(
builder: (context, cart, child) => Text('Cəmi: ${cart.totalPrice}'),
),
],
);
}
// ✅ Yalnız bir sahəyə abunəlik: səbətə element əlavə olunsa da,
// totalPrice dəyişməyibsə rebuild olunmur.
final total = context.select<CartModel, double>((c) => c.totalPrice);The `child` parameter of `Consumer`: the unchanging heavy part is built once.
📚 Sources and documentation
- package:provider — reading APIsofficialpub.dev
watch, read, select, Consumer, Selector and the DO/DON'T notes are in the README.
- Simple app state: keep Consumer deepofficialdocs.flutter.dev
The official recommendation on where to place Consumer and the child optimisation.
- dependOnInheritedWidgetOfExactTypeofficialapi.flutter.dev
The call behind watch: it is O(1) but must not be called from initState.