I'm learning about flutter's provider and I'm suffering from one error.
The following code works.
[code1]
class Model extends ChangeNotifier {
void save() {
print('save');
}
}
class Main extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Model(),
child: Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: NewWidget(),
),
);
}
}
class NewWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
child: Text(
"test",
),
onPressed: () {
context.read<Model>().save();
},
);
}
}
But the code below does not work.
[code2]
class Model extends ChangeNotifier {
void save() {
print('save');
}
}
class Main extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Model(),
child: Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: RaisedButton(
child: Text(
"test",
),
onPressed: () {
context.read<Model>().save();
},
),
),
);
}
}
With this code, the following error is output when the button is pressed.
Error: Could not find the correct Provider<Model> above this Main Widget
This likely happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that Main is under your MultiProvider/Provider<Model>.
This usually happen when you are creating a provider and trying to read it immediatly.
For example, instead of:
I don't separate widgets, I want to write one widget like code2.
Please let me know if there is any good way.
Thanks!
In your first example, NewWidget
is build with a new BuildContext
, that already have access to it's ancestor, so this Widget can see the provider that you have created there with: context.read<Model>()
.
But, on your second example, you are creating and using your provider all in the same Widget Main
so everything on the same BuildContext
, and when you run context.read<Model>()
flutter will try to look up in your Widget tree to find the Model
, but it won't find it because you have just created it. That's a scenario where you could make use, of the Builder Widget:
class Main extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Model(),
child: Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: Builder(
// Here the magic happens
// this builder function will generate a new BuilContext for you
builder: (BuildContext newContext){
return RaisedButton(
child: Text(
"test",
),
onPressed: () {
newContext.read<Model>().save();
},
);
}
),
),
);
}
}
By using the Builder
Widget, you have the ability to create a new BuildContext
the can be used to retrieve information about the provider you have just created, that's because your Builder widget, is build after your ChangeNotifierProvider, and it is a child of it, so it can easily look up and find this information on it's parent.
Also pay attention on what errors tell you, flutter compiler is really smart with that kind of issues:
Make sure that Main is under your MultiProvider/Provider. This usually happen when you are creating a provider and trying to read it immediatly.
These lines tell you exactly what I explained before.
In addition to the above answer, you can also use the Consumer class of the Provider package.
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';
class Model extends ChangeNotifier {
void save() {
print('save');
}
}
class Main extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Model(),
child: Scaffold(
appBar: AppBar(
title: Text('test'),
),
body: Consumer<Model>(
builder: (context, model, _) {
return RaisedButton(
child: Text(
"test",
),
onPressed: () {
context.read<Model>().save();
},
);
},
),
),
);
}
}
Wrap MaterialApp from MultiProvider. and set the Provider Class(es) in value.
Ex:
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Cart(),
),
],
child: MaterialApp(
title: 'Perfumino',
home: Home(),
),
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With