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

如何将Firestore的DocumentSnapshot获取数据转换为double类型?

报错原因定位
  • 类型转换逻辑错误:你调用double.parse(documentSnapshot['price'])时,Firestore中存储的price字段本身就是double数值类型,而double.parse()方法仅支持传入字符串类型入参,直接传入double值就会触发type 'double' is not a subtype of type 'String'报错。
  • 计算位置错误:你把价格累加逻辑写在ListView.builder的itemBuilder回调中是完全错误的。itemBuilder是列表滚动时逐帧执行的渲染函数,会随滚动反复触发,放在这里做累加会出现数值重复计算、滚动时价格乱跳的问题;同时你当前代码在每个商品条目处显示累加过程中的临时值,既不是单个商品的单价,也不是最终的购物车总价,逻辑不符合业务预期。
正确实现方案

1. 安全类型转换方法

写一个通用的取值方法,兼容Firestore可能返回的num、int、double、字符串多种格式,避免类型异常崩溃:

double parsePrice(dynamic rawPrice) {
  // 如果本身就是数值类型,直接转double返回
  if (rawPrice is num) return rawPrice.toDouble();
  // 如果是字符串类型,尝试解析为double,解析失败返回0
  if (rawPrice is String) return double.tryParse(rawPrice) ?? 0.0;
  // 其他异常情况默认返回0
  return 0.0;
}

2. 逻辑调整规则

  • 总价计算逻辑挪到列表渲染前执行:拿到cart集合的全量文档后,先遍历计算总价,再传入ListView渲染,禁止在itemBuilder中做累加操作。
  • 区分单价和总价展示:每个商品条目处显示当前商品自身的单价,不要显示累加过程中的临时值;总价可以在页面底部固定区域单独展示。
修复后的完整代码
Widget bodySection(BuildContext context) {
  return StreamBuilder(
    stream: FirebaseFirestore.instance
        .collection('users')
        .doc(userid)
        .collection('cart')
        .snapshots(),
    builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) {
      if (streamSnapshot.hasData) {
        final cartDocs = streamSnapshot.data!.docs;
        // 提前计算总价,不要在itemBuilder里累加
        double totalPrice = 0;
        for (var doc in cartDocs) {
          totalPrice += parsePrice(doc['price']);
        }

        return Padding(
          padding: EdgeInsets.symmetric(
              horizontal: getProportionateScreenWidth(20)),
          child: Column(
            children: [
              // 商品列表区域
              Expanded(
                child: ListView.builder(
                    itemCount: cartDocs.length,
                    itemBuilder: (context, index) {
                      final DocumentSnapshot documentSnapshot = cartDocs[index];
                      // 这里只取当前商品的单价,用于条目展示
                      final singleItemPrice = parsePrice(documentSnapshot['price']);
                      String imgurl = documentSnapshot['imgUrl'].toString();
                      return Padding(
                          padding: EdgeInsets.symmetric(vertical: 10),
                          child: Dismissible(
                            key: Key(documentSnapshot.id),
                            direction: DismissDirection.endToStart,
                            onDismissed: (direction) {
                              // 删除商品逻辑
                              FirebaseFirestore.instance
                                  .collection('users')
                                  .doc(userid)
                                  .collection('cart')
                                  .doc(documentSnapshot.id)
                                  .delete();
                            },
                            background: Container(
                              padding: EdgeInsets.symmetric(horizontal: 20),
                              decoration: BoxDecoration(
                                color: Color(0xFFFFE6E6),
                                borderRadius: BorderRadius.circular(15),
                              ),
                              child: Row(
                                children: [
                                  Spacer(),
                                  Icon(Icons.delete),
                                ],
                              ),
                            ),
                            child: Container(
                              padding: EdgeInsets.all(
                                  getProportionateScreenWidth(8)),
                              decoration: BoxDecoration(
                                color: Colors.white ,
                                borderRadius: BorderRadius.circular(15),  
                              ),
                              child: Row(
                                children: [
                                  SizedBox(
                                    width: 88,
                                    child: AspectRatio(
                                      aspectRatio: 0.88,
                                      child: Container(
                                        padding: EdgeInsets.all(
                                            getProportionateScreenWidth(5)),
                                        decoration: BoxDecoration(
                                          color: Colors.black,
                                          borderRadius: BorderRadius.circular(10),
                                          image: DecorationImage(image: NetworkImage(imgurl), fit: BoxFit.cover)
                                        ),
                                      ),
                                    ),
                                  ),
                                  SizedBox(width: 20),
                                  Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        "${documentSnapshot['brand']} ${documentSnapshot['name']}", 
                                        style: TextStyle(
                                            color: Colors.black, fontSize: 16),
                                        maxLines: 2,
                                      ),
                                      SizedBox(height: 10),
                                      Text.rich(
                                        TextSpan(
                                          // 这里显示当前商品的单价,不是累加值
                                          text: "RM ${singleItemPrice.toStringAsFixed(2)}",
                                          style: TextStyle(
                                              fontWeight: FontWeight.w600,
                                              color: kPrimaryColor),
                                        ),
                                      )
                                    ],
                                  ),
                                  Spacer(),
                                  Column(
                                    children: [
                                      SizedBox(height: 30,),
                                      Text(
                                        "x 1", 
                                        style: TextStyle(
                                            color: Colors.black, fontSize: 16),
                                      ),
                                    ],
                                  ),
                                ],
                              ),
                            ),
                          ));
                    }
                ),
              ),
              // 底部总价展示区域
              Container(
                padding: EdgeInsets.symmetric(vertical: 15),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text("购物车总价", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
                    Text(
                      "RM ${totalPrice.toStringAsFixed(2)}",
                      style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: kPrimaryColor),
                    )
                  ],
                ),
              )
            ],
          ),
        );
      }
      return const Center(
        child: CircularProgressIndicator(),
      );
    });
}

注意:toStringAsFixed(2)是为了把价格格式化为2位小数,避免出现类似19.99999999的浮点精度显示问题。

内容的提问来源于stack exchange,提问作者Ahmad Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 21:09:58