im creating a favorite function using provider to favorite an item to the favorite page. Here is my FavoriteProvider
code.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../model/market_model.dart';
class FavoriteProvider extends ChangeNotifier {
final List<CryptoItemModel> _favoriteItems = [];
List<CryptoItemModel> get favoriteItems => _favoriteItems;
FavoriteProvider() {
fetchFavorites();
}
Future<void> toggleFavorite(CryptoItemModel item) async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) {
print('User is not logged in.');
return;
}
final userEmail = user.email;
final userRef =
FirebaseFirestore.instance.collection('userData').doc(userEmail);
try {
final index =
_favoriteItems.indexWhere((favItem) => favItem.name == item.name);
if (index != -1) {
_removeFavoriteByIndex(index);
await userRef.collection('favorites').doc(item.name).delete();
} else {
_favoriteItems.add(item);
await userRef.collection('favorites').doc(item.name).set(item.toMap());
}
notifyListeners();
} catch (e) {
print('Error toggling favorite: $e');
}
}
Future<void> fetchFavorites() async {
try {
final user = FirebaseAuth.instance.currentUser;
if (user == null) {
print('User is not logged in.');
return;
}
final userEmail = user.email;
final userRef =
FirebaseFirestore.instance.collection('userData').doc(userEmail);
final snapshot = await userRef.collection('favorites').get();
final favoriteList = snapshot.docs
.map((doc) => CryptoItemModel.fromMap(doc.data()))
.toList();
_favoriteItems.clear();
_favoriteItems.addAll(favoriteList);
} catch (e) {
print('Error retrieving favorites: $e');
}
notifyListeners();
}
bool isExist(CryptoItemModel item) {
final isExist = _favoriteItems.contains(item);
return isExist;
}
void _removeFavoriteByIndex(int index) {
if (index >= 0 && index < _favoriteItems.length) {
_favoriteItems.removeAt(index);
notifyListeners();
}
}
static FavoriteProvider of(BuildContext context, {bool listen = true}) {
return Provider.of<FavoriteProvider>(context, listen: listen);
}
}
and here is where i call the function to favorite inside a different file.
Consumer<FavoriteProvider>(
builder: (context, provider, _) {
return IconButton(
iconSize: 35,
onPressed: () {
Provider.of<FavoriteProvider>(context,
listen: false)
.toggleFavorite(item);
},
icon: provider.isExist(item)
? const Icon(Icons.star,
color: Colors.yellow)
: const Icon(Icons.star_outline,
color: Colors.grey),
);
},
this is the page where the favorite item will be stored.
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import '../body/market_body.dart';
import '../cubit/bottom_navigation_cubit.dart.dart';
import '../provider/watchlist_provider.dart';
import '../widget/bottom_navigation.dart';
import '../widget/market_card.dart';
class WatchList extends StatefulWidget {
const WatchList({Key? key}) : super(key: key);
@override
_WatchListState createState() => _WatchListState();
}
class _WatchListState extends State<WatchList> {
@override
void initState() {
super.initState();
final favoriteProvider =
Provider.of<FavoriteProvider>(context, listen: false);
favoriteProvider.fetchFavorites().then((_) {
if (mounted) {
setState(() {});
}
});
}
@override
Widget build(BuildContext context) {
return BlocProvider<BottomNavigationBarCubit>(
create: (context) => BottomNavigationBarCubit(3),
child: Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: const Color(0xFF0D0D2B),
title: Image.asset(
'assets/light_logo.png',
width: 150.0,
),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(top: 18.0, left: 12.0),
child: Column(
children: const [
Text(
'Watchlist',
textAlign: TextAlign.start,
style:
TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
],
),
),
Padding(
padding: EdgeInsets.only(top: 10.0, left: 12.0),
child: Column(
children: const [
Text(
'View your favorite coins here!',
style: TextStyle(fontSize: 15, color: Color(0xFFBB0163)),
),
],
),
),
const SizedBox(
height: 10,
),
Expanded(
child: Consumer<FavoriteProvider>(
builder: (context, provider, _) {
final favoriteItems = provider.favoriteItems;
if (favoriteItems.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(18.0),
child: Text(
"Oops.. you have nothing on your watchlist",
style: TextStyle(fontSize: 16),
),
),
);
}
return ListView.builder(
itemCount: favoriteItems.length,
itemBuilder: (context, index) {
final item = favoriteItems[index];
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CryptoDescr(
currency: item,
),
),
);
},
child: MarketCard(
name: item.name,
symbol: item.symbol,
price: item.price,
change: item.change,
percent: item.percent,
imageUrl: item.imageUrl,
marketCap: item.marketCap,
),
);
},
);
},
),
),
],
),
),
bottomNavigationBar: const BottomNavigation(),
),
);
}
}
i can get the item to be stored inside firebase okay, and when you click on the star iconButton to favorite it, the color will change to yellow as intended. But when i leave the page and come back to it, the iconButton will change back to it previous position as if the item weren't added to the favorite but it was indeed added.
Is there a way that I can fix this problem and if so can someone please help me.
I finally fix the problem!
The problem that I'm facing occurs because the isExist
method uses the contains method, which checks for object equality only and not comparing the properties of the items.
So what I did is, I update the isExist
method in FavoriteProvider
class to compare the properties of the items instead of using the contains method.
here how it looks now:
bool isExist(CryptoItemModel item) {
final isExist = _favoriteItems.any(
(favItem) => favItem.name == item.name,
);
return isExist;
}
now whenever I leave that page contain the iconButton and then come back, the iconButton will stay as favorite!