So I'm new to Flutter learning about the Provider package. I want to toggle the bool isFavourite
and the Icon
when the FloatingActionButton
is pressed. But with using Provider, Consumer I'm getting an error saying:
Error: Could not find the correct Provider above this Consumer 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 Consumer is under your MultiProvider/Provider. This usually happens when you are creating a provider and trying to read it immediately.
Below is my code for course.dart
which is Provider:
import 'package:flutter/material.dart';
class Course with ChangeNotifier {
final String id;
final String title;
final String description;
final double rating;
final String imageUrl;
bool isFavourite;
final bool isFeatured;
Course({
required this.id,
required this.title,
required this.description,
required this.rating,
required this.imageUrl,
this.isFavourite = false,
required this.isFeatured,
});
void toggleFavouriteStatus() {
isFavourite = !isFavourite;
notifyListeners();
}
}
Below is my code for courses.dart
:
import 'package:flutter/material.dart';
import 'course.dart';
class Courses with ChangeNotifier {
final List<Course> _items = [
Course(
id: 'c1',
title: 'HTML & CSS',
description: 'This is the description of HTML & CSS',
rating: 4.5,
imageUrl: 'http://placeimg.com/640/480/tech',
isFeatured: true,
),
Course(
id: 'c2',
title: 'JAVASCRIPT',
description: 'This is the description of JAVASCRIPT',
rating: 4.9,
imageUrl: 'http://placeimg.com/1000/800/tech',
isFeatured: true,
),
Course(
id: 'c3',
title: 'WEB DEVELOPMENT',
description: 'This is the description of WEB DEVELOPMENT',
rating: 5,
imageUrl: 'http://placeimg.com/640/480/tech',
isFeatured: false,
),
Course(
id: 'c4',
title: 'APP DEVELOPMENT',
description: 'This is the description of APP DEVELOPMENT',
rating: 3.8,
imageUrl: 'http://placeimg.com/1000/800/tech',
isFeatured: false,
),
Course(
id: 'c5',
title: 'MACHINE LEARNING',
description: 'This is the description of MACHINE LEARNING',
rating: 5,
imageUrl: 'http://placeimg.com/640/480/tech',
isFeatured: false,
),
];
List<Course> get items {
return [..._items];
}
List<Course> get favouriteItems {
return _items.where((course) => course.isFavourite).toList();
}
Course findById(String id) {
return items.firstWhere((course) => course.id == id);
}
}
Below is my code for category_course_screen.dart
which is Consumer:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '/providers/course.dart';
class CategoryCourseScreen extends StatelessWidget {
CategoryCourseScreen({Key? key}) : super(key: key);
static const routeName = '/category-course-screen';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
floatingActionButton: Consumer<Course>(
builder: (_, course, child) => FloatingActionButton(
child: Icon(
course.isFavourite ? Icons.favorite : Icons.favorite_border,
),
onPressed: () {
course.toggleFavouriteStatus();
},
),
),
);
}
}
Below is my code for main.dart
file:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '/providers/courses.dart';
import '/screens/tabs_screen.dart';
import '/screens/enrolled_screen.dart';
import '/screens/favourites_screen.dart';
import '/screens/profile_screen.dart';
import '/screens/category_course_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => Courses(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Course App',
theme: ThemeData(
fontFamily: 'Lato',
primarySwatch: Colors.cyan,
accentColor: Colors.white,
textTheme: ThemeData.light().textTheme.copyWith(
subtitle1: const TextStyle(
fontSize: 30,
fontFamily: 'Lato-Bold',
fontWeight: FontWeight.w900,
),
bodyText1: const TextStyle(
fontSize: 20,
fontFamily: 'Lato-Thin',
),
bodyText2: const TextStyle(
fontSize: 20,
fontFamily: 'Lato-Bold',
color: Colors.white,
fontWeight: FontWeight.w900,
),
),
),
// home: HomeScreen(),
initialRoute: '/',
routes: {
'/': (context) => TabsScreen(),
EnrolledScreen.routeName: (context) => EnrolledScreen(),
FavouritesScreen.routeName: (context) => FavouritesScreen(),
ProfileScreen.routeName: (context) => ProfileScreen(),
CategoryCourseScreen.routeName: (context) => CategoryCourseScreen(),
},
),
);
}
}
I would say you dont need to notify the Course class, else create a simple class like this with copyWith constructor for more benifit.
class Course {
final String id;
final String title;
final String description;
final double rating;
final String imageUrl;
bool isFavourite;
final bool isFeatured;
Course({
required this.id,
required this.title,
required this.description,
required this.rating,
required this.imageUrl,
this.isFavourite = false,
required this.isFeatured,
});
Course copyWith({
String? id,
String? title,
String? description,
double? rating,
String? imageUrl,
bool? isFavourite,
bool? isFeatured,
}) {
return Course(
id: id ?? this.id,
title: title ?? this.title,
description: description ?? this.description,
rating: rating ?? this.rating,
imageUrl: imageUrl ?? this.imageUrl,
isFavourite: isFavourite ?? this.isFavourite,
isFeatured: isFeatured ?? this.isFeatured,
);
}
}
Now Courses
class will have an extra method to change the favorite option
void toggleFavorite(String id) {
final item = findById(id);
_items.remove(item);
_items.add(item.copyWith(isFavourite: !item.isFavourite));
notifyListeners();
}
And update sample cases
class CategoryCourseScreen extends StatelessWidget {
const CategoryCourseScreen({Key? key}) : super(key: key);
static const routeName = '/category-course-screen';
@override
Widget build(BuildContext context) {
final id = 'c1'; //example
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
title: Text('Title'),
),
floatingActionButton: Consumer<Courses>(
builder: (_, courses, child) => FloatingActionButton(
backgroundColor:
courses.findById(id).isFavourite ? Colors.pink : Colors.amber,
onPressed: () {
courses.toggleFavorite(id);
},
),
),
);
}
}