14
votes

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.

7
Got the same problem when updating. Thing is, what if I may or may not change the widget tree? I'm rolling back.Dpedrinha

7 Answers

8
votes

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

8
votes

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
3
votes

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)
2
votes

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

2
votes

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
1
votes

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

1
votes

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