Provider: WatchContext extension cannot be used in didChangeDependencies with AssertionException

Created on 25 Nov 2020  ยท  19Comments  ยท  Source: rrousselGit/provider

From comment:

Calling this method is equivalent to calling:

Provider.of<T>(context)

WatchContext extension is shortcut for Provider.of(context), but nope, WatchContext extension cannot be used in didChangeDependencies, something like this:

class _ScreenState extends State<Screen> {
  int _currentData;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _currentData = context.watch<int>();
  }

  @override
  Widget build(BuildContext context) {
    //final _currentData = context.watch<int>();
    return Text((_currentData ?? 0).toString());
  }
}

This lines
https://github.com/rrousselGit/provider/blob/9c4087abaa753ba5bc15492cf639d97720048cfb/lib/src/provider.dart#L639-L651
must be like this:

assert(
            owner.debugBuilding || // <= ADDED
            widget is LayoutBuilder ||
            widget is SliverWithKeepAliveWidget ||
            debugDoingBuild ||
            debugIsInInheritedProviderUpdate,
        '...');

Exception listing:

โ•โ•โ•โ•โ•โ•โ•โ• Exception caught by widgets library โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
The following assertion was thrown building _InheritedProviderScope<int>:
Tried to use `context.watch<int>` outside of the `build` method or `update` callback of a provider.

This is likely a mistake, as it doesn't make sense to rebuild a widget when the value
obtained changes, if that value is not used to build other widgets.

Consider using `context.read<int> instead.
'package:provider/src/provider.dart':
Failed assertion: line 620 pos 9: 'widget is LayoutBuilder ||
            widget is SliverWithKeepAliveWidget ||
            debugDoingBuild ||
            debugIsInInheritedProviderUpdate'

The relevant error-causing widget was: 
  Provider<int> file:///C:/git/flutter/provider_watch_feature/lib/main.dart:27:49
When the exception was thrown, this was the stack: 
#2      WatchContext.watch (package:provider/src/provider.dart:620:9)
#3      _ScreenState.didChangeDependencies (package:provider_watch_feature/main.dart:69:28)
#4      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4833:11)
#5      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4649:5)
#6      Element.inflateWidget (package:flutter/src/widgets/framework.dart:3615:14)

Most helpful comment

Works great!

image

All 19 comments

        context.owner.debugBuilding || // <= ADDED

No. This flag is true for the entire time the widget tree is building, which is not correct

There is no way to allow didChangeDependencies without allowing everything and that is not worth it imo.
You can use build instead of didChangeDependencies. They are basically the same thing

@rrousselGit but i can use Provider.of(context) something like this.
Why i can't watch shortcut?

Provider.of shouldn't allow it, but it is a breaking change to disallow it.

Provider.of shouldn't allow it, but it is a breaking change to disallow it.

Some people and I use a provider in conjunction with InheritedWidget.
Interaction through didChangeDependencies is the right way to track dependencies and context-based initialization.

Maybe it's worth adding a similar functionality anyway?

https://api.flutter.dev/flutter/widgets/State/didChangeDependencies.html

didChangeDependencies method

Called when a dependency of this State object changes.

For example, if the previous call to build referenced an InheritedWidget that later changed, the framework would call this method to notify this object about the change.

This method is also called immediately after initState. It is safe to call BuildContext.dependOnInheritedWidgetOfExactType from this method.

Subclasses rarely override this method because the framework always calls build after a dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.

Works great!

image

You can use build instead of didChangeDependencies. They are basically the same thing

image

didChangeDependencies is flawed and doesn't do what it claims to do.

There's no guarantee that didChangeDependencies won't be called even if the dependency didn't change. So you cannot safely write:

didChangeDependencies() {
  http.get(Something.of(context));
}

Instead, you need to do:

String lastSomething;

didChangeDependencies() {
  final something = Something.of(context);
  if (lastSomething != something) {
    lastSomething = something;
    http.get(something);
  }
}

This completely kills the purpose of using didChangeDependencies, and build can do the exact same thing:

String lastSomething;

build(context) {
  final something = Something.of(context);
  if (lastSomething != something) {
    lastSomething = something;
    http.get(something);
  }
}

Similarly, as I said previously, owner.debugBuilding is not a working fix.

I've used it before. You cannot make all of Provider's tests pass with this flag.

@rrousselGit
Do you mean didChangeDependencies deprecated and will be removed from Flutter framework as it has no sense?
In this case it is good idea to make didChangeDependencies case working as described in this issue till didChangeDependencies had not been removed from Flutter framework.

AFAIK didChangeDependencies isn't deprecated. I explained previously how it doesn't do what it is supposed to do, and how therefore supporting didChangeDependencies brings little to no value

@rrousselGit
Thanks for clarification
So didChangeDependencies have sense and use cases - so it have sense to fix this issues as @PlugFox described from my point of view
In other case nice to see new Flutter framework issue to remove didChangeDependencies from the framework
More over current provider issue confuse many developers
and workaround from above is not really solution for this case.

What scenario requires you to use didChangeDependencies? As I said, build is identical to didChangeDependencies in the end.

Supporting didChangeDependencies is not without drawbacks. That would require removing the current assert in its entirety.

@rrousselGit
didChangeDependencies called less times + build is good place for the widgets only, you provided very bad workaround with over-complicate build method.

@rrousselGit

Instead, you need to do:
I can do this through ProxyProvider, IheritedWidget and many other ways, this is not about me.

String lastSomething;

build(context) {
  final something = Something.of(context);
  if (lastSomething != something) {
    lastSomething = something;
 ...

You are extremely wrong and teach people extremely bad things.

build method is pure function
https://api.flutter.dev/flutter/widgets/State/build.html
https://en.wikipedia.org/wiki/Pure_function

image

What scenario requires you to use didChangeDependencies? As I said, build is identical to didChangeDependencies in the end.

didChangeDependencies is not identical build, if it was, it would not exist.

https://flutter.dev/docs/resources/architectural-overview

image

You are extremely wrong and teach people extremely bad things.

build method is pure function

Google themselves starts side-effects inside build sometimes, such as for list pagination.
The example I gave is a legitimate usage that does not violate how build works.

I've already debated with Googlers about didChangeDependencies too (during the implementation of this assertion in fact) and we agreed that it is flawed. didChangeDependencies is an easy source of bug


Again, as I mentioned before, there is no way to enable didChangeDependencies without breaking some tests.

As proven in https://github.com/rrousselGit/provider/pull/577, the CI does not pass.

@rrousselGit
But Google still keep didChangeDependencies, so it could be nice to take it into account and fix this issue as it break current Flutter framework rules.
Then we could wait until Google remove didChangeDependencies, happy to look into related issue if you already created it.

Closing in favor of #585

I won't be working on #585 soon, but I'm happy to merge a PR that removes the asserts and fix the tests

Was this page helpful?
0 / 5 - 0 ratings

Related issues

usherwalce picture usherwalce  ยท  5Comments

prasadsunny1 picture prasadsunny1  ยท  6Comments

ykaito21 picture ykaito21  ยท  3Comments

arnoutvandervorst picture arnoutvandervorst  ยท  4Comments

guopeng1994 picture guopeng1994  ยท  4Comments