Flutter app crash after converting Provider 3 to 4

2020-08-17 07:09发布

问题:

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.

回答1:

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



回答2:

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



回答3:

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)


回答4:

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>() // Provider.of<T>(context, listen: false)
context.watch<T>() // Provider.of<T>(context)```

Ref :- https://github.com/rrousselGit/provider/issues/313


标签: flutter