Flutter/Dart代码为何重复添加元素至Firestore收藏列表而非移除?
问题描述
目标
实现点击收藏图标时的切换功能:若商品已在收藏列表则移除,否则添加。Firebase Firestore中有customers集合和instrument集合,customers文档包含名为wish的字段,该字段是instrument类型的列表。
遇到的问题
首次点击收藏图标时,商品成功添加至customerWishlist;但再次点击同一商品的收藏图标时,并未移除该商品,而是重复添加。
我的代码
class _HomeWelcomeScreenState extends State<HomeWelcomeScreen>{ final Stream<QuerySnapshot> instrumentsStream = FirebaseFirestore.instance.collection("instrument").snapshots(); final List storedInstruments = []; List customerWishlist = []; @override Widget build(BuildContext context){ final currUser = context.read<FirebaseAuthMethods>().user; Future<List> generateFutureCustomerWishlist() async { return FirebaseFirestore.instance .collection('customers') .doc(currUser!.uid) .get() .then((value) => value.get('wish')); } void generateCustomerWishList() async { customerWishlist = await generateFutureCustomerWishlist(); } return SafeArea( child: Scaffold( body: Column( //some code for UI StreamBuilder<QuerySnapshot>( stream: instrumentsStream, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { if (snapshot.hasError) { print("Someting went wrong"); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator(), ); } storedInstruments.clear(); snapshot.data!.docs .map((DocumentSnapshot document) { Map a = document.data() as Map<String, dynamic>; storedInstruments.add(a); }).toList(); return Row( children: [ Expanded( child: ListView.builder( itemCount: storedInstruments.length, scrollDirection: Axis.horizontal, //shrinkWrap: true, physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { return InstrumentCard( width: screenWidth * 0.4, height: screenWidth * 0.4 / 0.75, instrumentImageUrl: storedInstruments[index] ["img-url"], instrumentName: storedInstruments[index] ["name"], instrumentMrp: "₹${storedInstruments[index]["mrp"].toString()}", instrumentPrice: "₹${storedInstruments[index]["price"].toString()}", paddingRight: screenHeight * 0.015, innerHorizontalSymmetricPadding: screenWidth * 0.025, innerVerticalSymmetricPadding: screenHeight * 0.00, instrumentDiscount: "${(((1 - (storedInstruments[index]["price"] / storedInstruments[index]["mrp"])) * 100).round()).toString()}% off", onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => InstrumentDetail( instrument: storedInstruments[ index]))); }, onWishTap: () async { generateCustomerWishList(); if (customerWishlist.contains( storedInstruments[index])) { setState(() { customerWishlist.remove( storedInstruments[ index]); print(customerWishlist); FirebaseFirestore.instance .collection('customers') .doc(currUser!.uid) .update({ "wish": customerWishlist }); }); } else { setState(() { customerWishlist.add( storedInstruments[ index]); print(customerWishlist); FirebaseFirestore.instance .collection('customers') .doc(currUser!.uid) .update({ "wish": customerWishlist }); }); } // if (wishList.contains( // storedInstruments[index])) { // setState(() { // wishList.remove( // storedInstruments[ // index]); // }); // } else { // setState(() { // wishList.add( // storedInstruments[ // index]); // }); // } }, onCartTap: () { if (cartList.contains( instruments[index])) { setState(() { cartList.remove( instruments[index]); cartMap.remove( instruments[index]); }); } else { setState(() { cartList.add( instruments[index]); cartMap.addAll({ instruments[index]: 1 }); }); } }, isWishlisted: (wishList.contains( instruments[index])) ? true : false, isCarted: (cartList.contains( instruments[index])) ? true : false, instrument: instruments[index], ); }, ), ), ], ); }, ), ), ), ), } }
需求
首次使用Firebase Firestore,希望了解代码中导致该问题的原因及解决方法。
补充
即使首次点击收藏图标后,customerWishlist.contains(storedInstruments[index])仍返回false,无法理解原因。
问题原因与解决方法
核心原因
- 异步操作未等待:
onWishTap中调用的generateCustomerWishList()是异步函数,但未加await,导致判断收藏状态时,Firebase的数据还没拉取完成,用的是旧的列表数据。 - 对象引用不匹配:
storedInstruments中的Map是StreamBuilder每次重建时生成的新对象,而Firebase存储的wish列表里的是旧对象,两者内容相同但内存引用不同,contains方法通过引用判断相等,因此返回false。 - 变量与函数位置不合理:数据拉取函数定义在
build方法内,每次UI重建都会重新定义;全局的storedInstruments每次Stream更新都会清空重加,进一步加剧对象引用混乱。
解决步骤
1. 修正异步等待逻辑
在onWishTap中必须等待收藏列表拉取完成后再判断状态:
onWishTap: () async { await generateCustomerWishList(); // 添加await等待异步操作完成 // 后续判断逻辑不变 }
2. 用唯一标识判断收藏状态
放弃直接用Map对象判断,改用商品的唯一标识(比如Firestore文档ID)匹配:
- 先在存储商品时加入文档ID:
snapshot.data!.docs.map((DocumentSnapshot document) { Map a = document.data() as Map<String, dynamic>; a['docId'] = document.id; // 把文档ID存入Map storedInstruments.add(a); }).toList();
- 修改判断与更新逻辑:
onWishTap: () async { await generateCustomerWishList(); final currentInstrument = storedInstruments[index]; // 通过docId判断是否在收藏列表 final isInWishlist = customerWishlist.any((item) => item['docId'] == currentInstrument['docId']); setState(() { if (isInWishlist) { // 通过docId移除对应商品 customerWishlist.removeWhere((item) => item['docId'] == currentInstrument['docId']); } else { customerWishlist.add(currentInstrument); } FirebaseFirestore.instance.collection('customers').doc(currUser!.uid).update({ "wish": customerWishlist }); }); }
3. 优化变量与函数位置
把数据拉取函数移到build外,初始化时先拉取一次收藏列表,同时把storedInstruments改为局部变量避免全局污染:
class _HomeWelcomeScreenState extends State<HomeWelcomeScreen>{ final Stream<QuerySnapshot> instrumentsStream = FirebaseFirestore.instance.collection("instrument").snapshots(); List customerWishlist = []; late final currUser; @override void initState() { super.initState(); currUser = context.read<FirebaseAuthMethods>().user; // 初始化时拉取一次收藏列表 generateCustomerWishList(); } Future<List> generateFutureCustomerWishlist() async { final doc = await FirebaseFirestore.instance.collection('customers').doc(currUser!.uid).get(); return doc.get('wish') ?? []; // 处理wish字段为空的情况 } Future<void> generateCustomerWishList() async { customerWishlist = await generateFutureCustomerWishlist(); setState(() {}); // 更新UI } @override Widget build(BuildContext context){ return SafeArea( child: Scaffold( body: Column( // ... 其他UI代码 StreamBuilder<QuerySnapshot>( stream: instrumentsStream, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { if (snapshot.hasError) { print("Something went wrong"); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 改为局部变量,避免全局修改 final List storedInstruments = []; snapshot.data!.docs.map((DocumentSnapshot document) { Map a = document.data() as Map<String, dynamic>; a['docId'] = document.id; storedInstruments.add(a); }).toList(); return Row( // ... ListView.builder代码不变 ); }, ), ), ), ); } }
4. 修正收藏状态显示逻辑
把isWishlisted的判断也改为用文档ID匹配,同时修正变量名笔误:
isWishlisted: customerWishlist.any((item) => item['docId'] == storedInstruments[index]['docId']),
额外建议
- 可以给Firebase操作添加成功/失败回调,避免网络问题导致的状态不一致:
await FirebaseFirestore.instance.collection('customers').doc(currUser!.uid).update({ "wish": customerWishlist }).then((_) { print("收藏列表更新成功"); }).catchError((e) { print("更新失败: $e"); // 可在此回滚状态,比如撤销刚才的添加/移除操作 });
内容的提问来源于stack exchange,提问作者Ayan Dasgupta
相关产品推荐
相关产品推荐

