read<T> method

T read<T>()

Obtain a value from the nearest ancestor provider of type T.

This method is the opposite of watch.
It will not make component rebuild when the value changes and cannot be called inside StatelessComponent.build/State.build.
On the other hand, it can be freely called outside of these methods.

If that is incompatible with your criteria, consider using Provider.of(context, listen: false).
It does the same thing, but without these added restrictions (but unsafe).

DON'T call read inside build if the value is used only for events:

Iterable<Component> build(BuildContext context) sync* {
  // counter is used only for the onPressed of RaisedButton
  final counter = context.read<Counter>();

  yield RaisedButton(
    onPressed: () => counter.increment(),
  );
}

While this code is not bugged in itself, this is an anti-pattern. It could easily lead to bugs in the future after refactoring the component to use counter for other things, but forget to change read into watch.

CONSIDER calling read inside event handlers:

Iterable<Component> build(BuildContext context) sync* {
  yield RaisedButton(
    onPressed: () {
      // as performant as the previous solution, but resilient to refactoring
      context.read<Counter>().increment(),
    },
  );
}

This has the same efficiency as the previous anti-pattern, but does not suffer from the drawback of being brittle.

DON'T use read for creating components with a value that never changes

Iterable<Component> build(BuildContext context) sync* {
  // using read because we only use a value that never changes.
  final model = context.read<Model>();

  yield Text('${model.valueThatNeverChanges}');
}

While the idea of not rebuilding the component if something else changes is good, this should not be done with read. Relying on read for optimisations is very brittle and dependent on an implementation detail.

CONSIDER using select for filtering unwanted rebuilds

Iterable<Component> build(BuildContext context) sync* {
  // Using select to listen only to the value that used
  final valueThatNeverChanges = context.select((Model model) => model.valueThatNeverChanges);

  yield Text('$valueThatNeverChanges');
}

While more verbose than read, using select is a lot safer. It does not rely on implementation details on Model, and it makes impossible to have a bug where our UI does not refresh.

Using read to simplify objects depending on other objects

This method can be freely passed to objects, so that they can read providers without having a reference on a BuildContext.

For example, instead of:

class Model {
  Model(this.context);

  final BuildContext context;

  void method() {
    print(Provider.of<Whatever>(context));
  }
}

// ...

Provider(
  create: (context) => Model(context),
  child: ...,
)

we will prefer to write:

class Model {
  Model(this.read);

  // `Locator` is a typedef that matches the type of `read`
  final Locator read;

  void method() {
    print(read<Whatever>());
  }
}

// ...

Provider(
  create: (context) => Model(context.read),
  child: ...,
)

Both snippets behaves the same. But in the second snippet, Model has no dependency on Flutter/BuildContext/provider.

See also:

  • WatchContext and its watch method, similar to read, but will make the component tree rebuild when the obtained value changes.
  • Locator, a typedef to make it easier to pass read to objects.

Implementation

T read<T>() {
  return Provider.of<T>(this, listen: false);
}