Reputation: 1528
I tried to upgrade my Flutter app to use Provider 4.0.1
today and the following code crashed on assigning a value to null.
Here is the code I am attempting to convert. I only changed SingleChildCloneableWidget
to SingleChildStatelessWidget
which compiled OK.
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
List<SingleChildStatelessWidget> providers = [
...independentServices,
...dependentServices,
...uiConsumableProviders
];
List<SingleChildStatelessWidget> independentServices = [
Provider.value(value: Api()),
Provider.value(value: Tbl()),
Provider.value(value: Bill()),
Provider.value(value: Sale()),
Provider.value(value: Category()),
Provider.value(value: Menu()),
];
List<SingleChildStatelessWidget> dependentServices = [
ProxyProvider<Api, AuthenticationService>(
update: (context, api, authenticationService) => AuthenticationService(api: api),
),
];
List<SingleChildStatelessWidget> uiConsumableProviders = [
StreamProvider<User>(
create: (context) => Provider.of<AuthenticationService>(context, listen: false).user,
),
lazy: false
];
I implemented it like this:
StreamController<User> _userController = StreamController<User>();
Stream<User> get user => _userController.stream;
The crash occurred at this line:
Future<void> _setFixedLanguageStrings(BuildContext context) async {
User _user = Provider.of<User>(context);
_user.homeString = await translate(context, 'Home');
The getter 'language' was called on null. Receiver: null
This was working fine with Provider 3.0.3
but obviously I need to do more.
My original code came from this tutorial.
edit: I fixed that problem by adding lazy: false
in the stream provider create method but then another error later in this code.
Future<String> translate(BuildContext context, _term) async {
final String _languageCode = Provider.of<User>(context).language;
which produced this error:
Exception has occurred. _AssertionError ('package:provider/src/provider.dart': Failed assertion: line 213 pos 7: 'context.owner.debugBuilding || listen == false || _debugIsInInheritedProviderUpdate': Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called Provider.of without passing
listen: false
.To fix, write: Provider.of(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the event handler, when the widget tree doesn't care about the value. )
I added listen: false
to the line above which seems to have fixed that problem, however the next provider I attempted to use produced this error:
Tried to listen to a value exposed with provider, from outside of the widget tree.
This is likely caused by an event handler (like a button's onPressed) that called Provider.of without passing
listen: false
.To fix, write: Provider.of(context, listen: false);
It is unsupported because may pointlessly rebuild the widget associated to the event handler, when the widget tree doesn't care about the value. 'package:provider/src/provider.dart': Failed assertion: line 213 pos 7: 'context.owner.debugBuilding || listen == false || _debugIsInInheritedProviderUpdate'
Should I now go to every instance where I call a provider and add listen: false
? I need somebody to explain what has changed and why as I am fairly new at Flutter and the docs are sparse for Provider
. There are many times where I call Provider in my code and this last error did not return a code location.
Is listen: false
now always required when it wasn't before or have I missed something else? I am starting to add listen: false to every call to instantiate a Provider variable and it appears to be working but is this the correct approach? Should I just add listen: false
to every call to Provider.of
and call it a day?
edit: The error arises whenever the provider is called from outside the visible part of the widget tree. This distinction is important.
Upvotes: 19
Views: 20296
Reputation: 21
@Jaime saved me a lot of time
context.read<T>() // Provider.of<T>(context, listen: false)
context.watch<T>() // Provider.of<T>(context)
I just changed from .watch() to .read()
from Provider pub dev
"Reading a value The easiest way to read a value is by using the extension methods on [BuildContext]:
context.watch(), which makes the widget listen to changes on T context.read(), which returns T without listening to it context.select<T, R>(R cb(T value)), which allows a widget to listen to only a small part of T. One can also use the static method Provider.of(context), which will behave similarly to watch. When the listen parameter is set to false (as in Provider.of(context, listen: false)), then it will behave similarly to read.
It's worth noting that context.read() won't make a widget rebuild when the value changes and it cannot be called inside StatelessWidget.build/State.build. On the other hand, it can be freely called outside of these methods.
These methods will look up in the widget tree starting from the widget associated with the BuildContext passed and will return the nearest variable of type T found (or throw if nothing is found).
This operation is O(1). It doesn't involve walking in the widget tree."
Upvotes: 0
Reputation: 624
If we use,
Provider.of<T>(context, listen: true).method();
inside build method and if we have
notifyListeners();
in that method(), then it causes infinite recursion , as on each
notifyListeners();
invoke ,all provider calls with listen : true will execute , i.e build method is re executed, that means
method(),
is called again and again and result in error
Upvotes: 2
Reputation: 347
In my case I was getting the following error:-
I/flutter ( 7206): Tried to listen to a value exposed with provider, from outside of the widget tree.
I/flutter ( 7206):
I/flutter ( 7206): This is likely caused by an event handler (like a button's onPressed) that called
I/flutter ( 7206): Provider.of without passing `listen: false`.
I/flutter ( 7206):
I/flutter ( 7206): To fix, write:
I/flutter ( 7206): Provider.of<AstroDetailsProvider>(context, listen: false);
I/flutter ( 7206):
I/flutter ( 7206): It is unsupported because may pointlessly rebuild the widget associated to the
I/flutter ( 7206): event handler, when the widget tree doesn't care about the value.
As you can see that solution is present in the error message itself.
Hence, we are not supposed to use provider with default (listen:true) inside event handlers.
Alternatively,
context.read<T>() is same as Provider.of<T>(context, listen: false) And
context.watch<T>() is same as Provider.of<T>(context)```
Ref :- https://github.com/rrousselGit/provider/issues/313
Upvotes: 3
Reputation: 656
If you are using provider outside the build without listen: false
then, of course, you can't listen to changes as it didn't build the widgets again for changes. this doesn't come as a default value because the provider is not supposed to use outside the build as it used as a state management tool and to inject dependencies. But however, if you are using outside the build you have to use listen: false
Upvotes: 2
Reputation: 399
On Event Handlers like onPressed, OnTap, onLongPressed etc. we must use
Provider.of<T>(context,listen:false)
reason being that they will not listen for any update changes, instead are responsible for making changes.
whereas widgets like Text etc. are responsible for displaying...hence need to be updated on every change made....therefore use
Provider.of<T>(context,listen:true) //by default is listen:true
Upvotes: 14
Reputation: 191
listen:true
being the default is logical.
It's not specifying inside an event handler that is not logical.listen: false
Also, 4.1.0 will somehow have a shorter alternative to Provider.of:
context.read<T>() // Provider.of<T>(context, listen: false)
context.watch<T>() // Provider.of<T>(context)
Upvotes: 7
Reputation: 117
listen : false
called when the data wouldn't updating any thing in the UI, and should be used, like removing all cards in a widget when button clicked.
For more info's, read this go_to_link
Upvotes: 3
Reputation: 1430
I have the same "problem", if i add listen: false
everywhere i call Provider the problem is gone but i dont know if thats the right solution...?
Upvotes: 11