I'm trying to make a favorites widget that will display items that have been added as "favorites" for the current user. So lets say I click on a favorite item called "Pizza", it will navigate me to another screen (foodDetailsScreen.dart) and lets say I remove the item from my favorites by clicking the "favorite" icon, it will successfully do it (I can see it getting removed in Firestore), but when I press back it still shows that same item card there. It will stay there until I switch tabs and come back to this tab so the initState() of FavoritesWidget is called and that food item isn't there anymore.
I tried doing .pushReplacement from FavoritesWidget to FoodDetailsScreen and I made a button (you can still see it in the code I shared) to do another .pushReplacement back to the previous screen but I don't know where to push to.
I have bottom navigation bar which has 3 tabs, 1) homeScreen, 2) cartScreen, 3) accountScreen. The FavoritesWidget is in homeScreen.
just in case its needed
"sdk: '>=3.1.3 <4.0.0';
cupertino_icons: ^1.0.2
firebase_core: ^2.24.2
cloud_firestore: ^4.14.0
firebase_auth: ^4.16.0
provider: ^6.1.1
FavoritesWidget.dart
class Favorites extends StatefulWidget {
const Favorites({super.key});
@override
State<Favorites> createState() => _FavoritesState();
}
class _FavoritesState extends State<Favorites> {
@override
void initState() {
super.initState();
Provider.of<FavoriteProvider>(context, listen: false).loadFavoriteItems();
}
@override
Widget build(BuildContext context) {
return Consumer<FavoriteProvider>(
builder: (context, favoriteProvider, child) {
return SizedBox(
height: 180,
width: double.infinity,
child: Column(
children: <Widget>[
title(),
Expanded(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: favoriteProvider.favoriteItems.length,
itemBuilder: (context, index){
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FoodDetailsScreen(foodItem: favoriteProvider.favoriteItems[index]))
);
},
child: SizedBox(
width: 220,
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(favoriteProvider.favoriteItems[index].name),
Text('\$${favoriteProvider.favoriteItems[index].price.toStringAsFixed(2)}'),
],
),
),
),
),
);
},
),
),
],
),
);
},
);
}
Widget title() {
return Container(
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Favorites", style: TextStyle(fontSize: 22)),
],
),
);
}
}
foodDetailsScreen.dart
class FoodDetailsScreen extends StatefulWidget {
final FoodItem foodItem;
const FoodDetailsScreen({super.key, required this.foodItem});
@override
State<FoodDetailsScreen> createState() => _FoodDetailsScreenState();
}
class _FoodDetailsScreenState extends State<FoodDetailsScreen> {
FavoriteProvider favoriteProvider = FavoriteProvider();
late bool isFavorited;
@override
void initState() {
super.initState();
isFavorited = favoriteProvider.checkIfFavorited(widget.foodItem);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.foodItem.name),
actions: [
IconButton(
padding: const EdgeInsets.only(right: 40),
onPressed: () async {
setState(() {
if (isFavorited) {
favoriteProvider.removeFromFavorites(widget.foodItem);
} else {
favoriteProvider.addToFavorites(widget.foodItem);
}
isFavorited = !isFavorited;
});
},
color: isFavorited ? Colors.red : Colors.black,
icon: Icon(isFavorited ? Icons.favorite : Icons.favorite_border,size: 40),
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.foodItem.name,
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
const SizedBox(height: 16),
Text(
'Price: \$${widget.foodItem.price.toStringAsFixed(2)}',
style: const TextStyle(fontSize: 18),
),
const SizedBox(height: 40),
ElevatedButton(
onPressed: () {
},
child: const Text('Go back'),
),
],
),
),
);
}
}
favoriteProvider.dart
class FavoriteProvider extends ChangeNotifier {
final FirebaseFirestore _firebaseFirestore = FirebaseFirestore.instance;
final userId = FirebaseAuth.instance.currentUser!.uid;
late CollectionReference _usersCollection;
List<FoodItem> _favoriteItems = [];
List<FoodItem> get favoriteItems => _favoriteItems;
FavoriteProvider(){
_usersCollection = _firebaseFirestore.collection('users');
}
Future<void> loadFavoriteItems() async {
try {
var snapshot = await _usersCollection.doc(userId).collection('favorites').get();
_favoriteItems = snapshot.docs.map((doc) => FoodItem.fromFirestore(doc)).toList();
notifyListeners();
} catch (error) {
print('Error loading favorite items: $error');
}
}
bool checkIfFavorited(FoodItem foodItem) {
String itemId = _favoriteItems.indexWhere((item) => item.id == foodItem.id).toString();
if(int.parse(itemId) != -1) {
return false;
} else {
return true;
}
}
Future<void> addToFavorites(FoodItem foodItem) async {
try {
if (!_favoriteItems.any((item) => item.id == foodItem.id)) {
await _usersCollection.doc(userId).collection('favorites').doc(foodItem.id).set({
'id': foodItem.id,
'name': foodItem.name,
'price': foodItem.price,
});
_favoriteItems.add(FoodItem(id: foodItem.id, name: foodItem.name, price: foodItem.price));
notifyListeners();
}
} catch (error) {
print('Error adding to favorites: $error');
}
}
Future<void> removeFromFavorites(FoodItem foodItem) async {
try {
await _usersCollection.doc(userId).collection('favorites').doc(foodItem.id).delete();
_favoriteItems.removeWhere((item) => item.id == foodItem.id);
notifyListeners();
} catch (error) {
print('Error removing to favorites: $error');
}
}
}
I tried:
WillPopScope(
onWillPop: () async {
Provider.of<FavoriteProvider>(context, listen: false).loadFavoriteItems();
return true;
}
// ...
)
I also tried so many provider state solutions, none of which worked.
So I fixed the problem and was able to do it with 2 solutions.
If I want to do do: .pushReplacement
for the FoodDetailsScreen, then I will have to return .pushReplacement
to the screen where I'm setting the bottomNavigationBar and the tabs, in my case its called "MainScreen"
If I want to use .push
then adding .then((_){ setState(() { favoriteProvider.loadFavoriteItems(); });
does the job. Before this I was just doing setState(() {});
and it wasn't working, now it does with the 2) answer.