Sparround

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 inside build.
  • context.read<T>() — creates no subscription. It is for calling methods from callbacks such as onPressed.
  • context.select<T, R>((T value) => ...) — subscribes only to the selected part: if R is 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.

GoalThe right toolThe 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.

dart
// ❌ 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