read<T> method Null safety

T read<T>(
  1. ProviderBase<Object?, T> provider
)

Reads a provider without listening to it.

AVOID calling read inside build if the value is used only for events:

Widget build(BuildContext context) {
  // counter is used only for the onPressed of RaisedButton
  final counter = context.read(counterProvider);

  return 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 widget to use counter for other things, but forget to change read into Consumer/useProvider.

CONSIDER calling read inside event handlers:

Widget build(BuildContext context) {
  return RaisedButton(
    onPressed: () {
      // as performant as the previous solution, but resilient to refactoring
      context.read(counterProvider).increment(),
    },
  );
}

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

AVOID using read for creating widgets with a value that never changes

Widget build(BuildContext context) {
  // using read because we only use a value that never changes.
  final model = context.read(modelProvider);

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

While the idea of not rebuilding the widget if unnecessary 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 Provider or select for filtering unwanted rebuilds:

Widget build(BuildContext context) {
  // Using select to listen only to the value that used
  final valueThatNeverChanges = useProvider(modelProvider.select((model) {
    return model.valueThatNeverChanges;
  }));

  return Text('$valueThatNeverChanges');
}

While more verbose than read, using Provider/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.

Implementation

T read<T>(ProviderBase<Object?, T> provider) {
  return ProviderScope.containerOf(this, listen: false).read(provider);
}