You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,无法理解原因。


问题原因与解决方法

核心原因

  1. 异步操作未等待:onWishTap中调用的generateCustomerWishList()是异步函数,但未加await,导致判断收藏状态时,Firebase的数据还没拉取完成,用的是旧的列表数据。
  2. 对象引用不匹配:storedInstruments中的Map是StreamBuilder每次重建时生成的新对象,而Firebase存储的wish列表里的是旧对象,两者内容相同但内存引用不同,contains方法通过引用判断相等,因此返回false。
  3. 变量与函数位置不合理:数据拉取函数定义在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:33:17