40

It's stated in the docs that these are the same, and context.read is just a shortcut for Provider.of<x>(context, listen: false). There's also an error in the console if I try to use context.read in a build method, but it doesn't explain the reason.

I also found this topic: Is Provider.of(context, listen: false) equivalent to context.read()? But it doesn't answer "why".

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Sergey Molchanovsky
  • 587
  • 1
  • 4
  • 10
  • Does this answer your question? [Is Provider.of(context, listen: false) equivalent to context.read()?](https://stackoverflow.com/questions/62257064/is-provider-ofcontext-listen-false-equivalent-to-context-read) – iDecode Jun 19 '20 at 20:04
  • @iLoveFlutterDocs yes. I moved my additional question to the separate topic: https://stackoverflow.com/questions/62539885/flutter-provider-what-is-the-best-way-to-dispatch-the-action-properly – Sergey Molchanovsky Jun 23 '20 at 16:53

2 Answers2

58
  • context.read is not allowed inside build because it is very dangerous to use there, and there are much better solutions available.

  • Provider.of is allowed in build for backward-compatibility.

Overall, the reasoning behind why context.read is not allowed inside build is explained in its documentation:

DON'T call [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<Counter>();

  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 [watch].

CONSIDER calling [read] inside event handlers:

Widget build(BuildContext context) {
  return RaisedButton(
    onPressed: () {
      // as performant as the previous 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 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<Model>();

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

While the idea of not rebuilding the widget 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

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

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

Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 1
    Personally I'm using the combination of provider and mobx, thus there will be never any reaction based on the `provider watch mechanism`. These considerations just forces people like me to follow the patterns in a none sense manner. – Hamed Hamedi Jul 27 '20 at 07:07
  • However, I'm using the mobx just because it's not dependent on calling something like `notifyListeners` in its actions. If Provider provides the same ability, I would drop mobx_flutter! – Hamed Hamedi Jul 27 '20 at 07:18
  • It seems to me that the usability of Provider.of with listen true/false is superior to the usability of context.watch/read because the former works in all situations and is therefore easier to learn whereas the latter, while seeming slightly more compact and elegant, can't be used as freely. So, for what reason is context.read/watch recommended? – Alan M Dec 22 '20 at 19:35
  • @HamedHamedi try GetX, it's has the similar functionality to MobX, but doesn't require codegeneration and is not context dependent (it uses its own mechanism based on statics and not on InheritedWidget). – Sergey Molchanovsky May 09 '21 at 16:45
  • 1
    @SergeyMolchanovsky About one month after the comment I moved to GetX. Thanks for recommendation btw – Hamed Hamedi May 12 '21 at 08:00
  • ```context.read().getNews();``` - This should happen when building the widgets. How to do it? That is, I cannot call it when the button is clicked – Daniil Sep 30 '22 at 08:23
5

The problem is that you try to call context before the widget has finished building, to run your code after the widget has finished building provide your code to the post frame callback function.

For Example:

WidgetsBinding.instance.addPostFrameCallback((_) {
    // your code in here
});
EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
Yasser Benmman
  • 151
  • 2
  • 2