Reputation: 503
I am building a setting widget in my flutter app and I am getting this error :
Error: Could not find the correct Provider above this SettingsForm Widget
Update Adding whole debug snippet:
Launching lib\main.dart on AOSP on IA Emulator in debug mode...
════════ Exception caught by widgets library ═══════════════════════════════════
The following ProviderNotFoundException was thrown building SettingsForm(dirty, state: _SettingsFormState#c73b8):
Error: Could not find the correct Provider<MyUser> above this SettingsForm Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- 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 SettingsForm is under your MultiProvider/Provider<MyUser>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
```
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The relevant error-causing widget was
SettingsForm
When the exception was thrown, this was the stack
#0 Provider._inheritedElementOf
#1 Provider.of
#2 _SettingsFormState.build
#3 StatefulElement.build
#4 ComponentElement.performRebuild
...
════════════════════════════════════════════════════════════════════════════════
i UPDATED IT AND ADDED SOME EXTRA CODE SO YOU CAN SEE BETTER
setting.dart:
class SettingsForm extends StatefulWidget {
@override
_SettingsFormState createState() => _SettingsFormState();
}
class _SettingsFormState extends State<SettingsForm> {
final _formKey = GlobalKey<FormState>();
final List<String> sugars = ['0', '1', '2', '3', '4'];
final List<int> strengths = [100, 200, 300, 400, 500, 600, 700, 800, 900];
// form values
String? _currentName;
String? _currentSugars;
int? _currentStrength;
@override
Widget build(BuildContext context) {
MyUser user = Provider.of<MyUser>(context);
return StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData? userData = snapshot.data;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Text(
'Update your brew settings.',
style: TextStyle(fontSize: 18.0),
),
SizedBox(height: 20.0),
TextFormField(
initialValue: userData!.name,
decoration: textInputDecoration,
validator: (val) =>
val!.isEmpty ? 'Please enter a name' : null,
onChanged: (val) => setState(() => _currentName = val),
),
SizedBox(height: 10.0),
DropdownButtonFormField<String>(
value: _currentSugars ?? userData.sugars,
decoration: textInputDecoration,
items: sugars.map((sugar) {
return DropdownMenuItem(
value: sugar,
child: Text('$sugar sugars'),
);
}).toList(),
onChanged: (val) => setState(() => _currentSugars = val),
),
SizedBox(height: 10.0),
Slider(
value: (_currentStrength ?? userData.strength).toDouble(),
activeColor:
Colors.brown[_currentStrength ?? userData.strength],
inactiveColor:
Colors.brown[_currentStrength ?? userData.strength],
min: 100.0,
max: 900.0,
divisions: 8,
onChanged: (val) =>
setState(() => _currentStrength = val.round()),
),
ElevatedButton(
style:
ElevatedButton.styleFrom(primary: Colors.pink[400]),
child: Text(
'Update',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if (_formKey.currentState!.validate()) {
await DatabaseService(uid: user.uid).updateUserData(
_currentSugars ?? snapshot.data!.sugars,
_currentName ?? snapshot.data!.name,
_currentStrength ?? snapshot.data!.strength);
Navigator.pop(context);
}
}),
],
),
);
} else {
return Loading();
}
});
}
}
UPDATE: I AM INCLUDING THE HOME.DART FILE THAT INCLUDES THE 'SETTINGFORM' WIDGET home.dart :
class Home extends StatelessWidget {
final AuthService _auth = AuthService();
@override
Widget build(BuildContext context) {
void _showSettingsPanel() {
showModalBottomSheet(context: context, builder: (context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 60.0),
child: SettingsForm(), <-- Here
);
});
}
return StreamProvider<List<Brew>?>.value(
value: DatabaseService(uid: '').brews,
initialData: null,
child: Scaffold(
backgroundColor: Colors.brown[50],
appBar: AppBar(
title: Text('Brew Crew'),
backgroundColor: Colors.brown[400],
elevation: 0.0,
actions: <Widget>[
TextButton.icon(
icon: Icon(Icons.person),
label: Text('logout'),
onPressed: () async {
await _auth.signOut();
},
),
TextButton.icon(
icon: Icon(Icons.settings),
label: Text('settings'),
onPressed: () => _showSettingsPanel(),
)
],
),
body: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/coffee_bg.png'),
fit: BoxFit.cover,
),
),
child: BrewList()
),
),
);
}
}
user.dart:
class MyUser {
final String uid;
MyUser({ required this.uid });
}
class UserData {
final String uid;
final String name;
final String sugars;
final int strength;
UserData({ required this.uid, required this.sugars, required this.strength, required this.name });
}
Upvotes: 4
Views: 3407
Reputation: 916
You have to warp the parent class with the provider class you are using inside. For doing so the easiest way is to add a static method in widget havingMaterialPageRoute
which helps to navigate to SettingsForm
screen.
class SettingsForm extends StatefulWidget {
static Widget getWidget() {
return new Provider(
create: (_) => MyUser(),
child: ChangeNotifierProvider(
create: (BuildContext context) => MyUser(),
builder: (_,_) => SettingsForm()
),
);
}
@override
_SettingsFormState createState() => _SettingsFormState();
}
To open SettingsForm
screen just call getRoute
function on button pressed. Check the below code.
void _showSettingsPanel() {
showModalBottomSheet(context: context, builder: (context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 60.0),
child: SettingsForm.getWidget(), <-- Here
);
});
}
Upvotes: 1
Reputation: 570
Because you have to declare the provider class above the class were your using it , if u find this ans crt mark it as crt
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (ctx) => MyUser(),),
],
child: MaterialApp());
Upvotes: 2