Reputation: 45
I'm working on an app in Flutter (which I'm still kinda new too) and I'm stuck with the following error:
Error: Could not find the correct Provider<List<Category>> above this Exercises 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 Exercises is under your MultiProvider/Provider<List<Category>>.
This usually happen 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>()),
}
),
I've been looking online and it clearly has to do with me not being able to get the correct 'context' when calling Provider.of<List<Category>>(context) in exercises_add.dart, and I don't really understand why. Because as you can see in my exercises.dart I have 'body: ExerciseList()', in which I am able to get the categories from the StreamProvider, but when I try to access it by clicking on my 'floatingActionButton' and then attempting to open my ExerciseAdd() page, it throws that error.
I would really appreciate a solution (+ explanation) on how to fix my code and why it isn't working.
exercises.dart
Widget build(BuildContext context) {
return _isLoading
? Loading()
: MultiProvider(
providers: [
StreamProvider<List<Exercise>>(
create: (context) => DatabaseService().exercises,
),
StreamProvider<List<Category>>(
create: (context) => DatabaseService().categories,
),
ChangeNotifierProvider<ExerciseFilter>(
create: (context) => ExerciseFilter(isActive: true),
)
],
child: Scaffold(
appBar: AppBar(
title: Text('Exercises'),
elevation: 0.0,
actions: _buildActions(),
),
body: ExerciseList(),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.black,
child: Icon(Icons.add, color: Colors.white),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ExercisesAdd(),
),
);
},
),
),
);
}
}
exercises_add.dart
@override
Widget build(BuildContext context) {
final cats = Provider.of<List<Category>>(context);
print(cats.length);
return Scaffold(
appBar: AppBar(
title: Text('Add Exercise'),
elevation: 0.0,
),
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
decoration:
textInputDecoration.copyWith(hintText: 'Exercise Name'),
validator: (value) {
if (value.isEmpty) {
return 'Exercise name is required';
}
return null;
},
onChanged: (value) {
setState(() {
exerciseName = value;
});
},
),
SizedBox(height: 20.0),
Theme(
data: Theme.of(context).copyWith(canvasColor: Colors.white),
child: DropdownButtonFormField<String>(
decoration: dropdownDecoration,
value: exerciseCategory,
onChanged: (value) {
setState(() {
exerciseCategory = value;
});
},
items: categories.map<DropdownMenuItem<String>>((value) {
return DropdownMenuItem<String>(
value: value.name,
child: Text(value.name),
);
}).toList(),
),
),
SizedBox(height: 20.0),
RaisedButton(
elevation: 0,
color: Colors.black,
child: Text(
'Add Exercise',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if (_formKey.currentState.validate()) {
bool failed = false;
String uid = await _auth.getCurrentUser();
if (uid != null) {
dynamic result = DatabaseService(uid: uid)
.addExercise(exerciseName, exerciseCategory);
if (result != null) {
Navigator.pop(context);
} else {
failed = true;
}
} else {
failed = true;
}
if (failed) {
setState(() {
error = 'Failed to add exercise. Please try again';
});
}
}
},
),
SizedBox(height: 12.0),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 14.0),
),
],
),
),
),
),
);
DatabaseService().exercises
List<Category> _categoryListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Category(name: doc.data['category'] ?? '');
}).toList();
}
Stream<List<Category>> get categories {
return categoryCollection
.orderBy('category')
.snapshots()
.map(_categoryListFromSnapshot);
}
NOTE: the StreamProvider & MultiProvider etc.. are all part of the 'provider' package (I use the most recent version)
Upvotes: 3
Views: 5301
Reputation: 17141
The error you received describes the scenario you are currently in.
- 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're navigating to a different route and trying to access the provider, but it's no longer in the widget tree.
You simply need to move your MultiProvider
above whatever navigator you're using in your widget tree. You're likely using a MaterialApp
to do this, so move MultiProvider
and make MaterialApp
it's child
.
Upvotes: 3