You need to enable JavaScript to run this app. Flutter从Firebase Cloud Firestore获取产品数据后网格视图重复显示首个产品
优惠活动
大模型
产品
解决方案
定价
更多

Flutter从Firebase Cloud Firestore获取产品数据后网格视图重复显示首个产品

问题场景
  • 已向Cloud Firestore的kollamProducts集合上传2条产品详情,计划以网格瓦片形式做产品展示
  • 已编写getData方法从Firestore拉取数据,打印文档长度可正确得到数值2,对应实现代码:
void getData() async {
    QuerySnapshot snapshot =
        await FirebaseFirestore.instance.collection('kollamProducts').get();
    snapshot.docs.forEach((document) {
      category = document['category'];
      id = document['id'];
      name = document['name'];
      shop = document['shop'];
      quantity = document['quantity'];
      images = List.from(document['images']);
      sizes = List.from(document['sizes']);
    });

    if (snapshot.docs.length != 0) {
      setState(() {
        prodL = snapshot.docs.length;
      });
    }
  }
现有展示组件实现

网格布局组件

组件通过GridView.builder实现网格渲染,在itemBuilder逻辑中调用SingleProduct组件渲染每个网格项,对应代码:

Widget build(BuildContext context) {
    return GridView.builder(
      physics: NeverScrollableScrollPhysics(),
      shrinkWrap: false,
      primary: false,
      itemCount: prodL,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2,
      ),
      itemBuilder: (BuildContext context, int index) {
        return SingleProdc(
          product_name: name,
          product_picture: images[0],
          product_image1: images[1],
          product_image2: images[2],
          product_shop: shop,
        );
      },
    );
  }

单个产品卡片组件

SingleProdc组件将产品信息整合在Card卡片中,适配网格瓦片的展示逻辑,对应代码:

class SingleProdc extends StatelessWidget {
  final product_name;
  final product_picture;
  final product_image1;
  final product_image2;
  final product_shop;

  SingleProdc(
      {this.product_name,
      this.product_picture,
      this.product_image1,
      this.product_image2,
      this.product_shop});
  @override
  Widget build(BuildContext context) {
    return Card(
      child: Hero(
        tag: product_name,
        child: Material(
          borderRadius: BorderRadius.circular(50),
          child: InkWell(
            borderRadius: BorderRadius.circular(50),
            onTap: () => Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => ProductDetails(
                      product_detail_name: product_name,
                      product_detail_picture: product_picture,
                      product_detail_image1: product_image1,
                      product_detail_image2: product_image2,
                      product_detail_shop: product_shop,
                    ))),
            child: GridTile(
              header: Container(
                height: 30,
                color: Colors.black38,
                child: Center(
                    child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(
                      Icons.home,
                      color: Colors.white,
                    ),
                    Text(
                      product_shop,
                      style: GoogleFonts.poppins(color: Colors.white),
                    ),
                  ],
                )),
              ),
              footer: Container(
                  height: 50,
                  color: Colors.black38,
                  child: Column(
                    children: [
                      Text(
                        product_name,
                        style: GoogleFonts.poppins(
                          color: Colors.white,
                          fontSize:
                              product_name.toString().length > 9 ? 10 : 15,
                        ),
                      ),
                      Text(
                        "\u{20B9} 500",
                        style: GoogleFonts.poppins(
                            color: Colors.white, fontSize: 15),
                      ),
                    ],
                  )),
              child: Image.network(
                product_picture,
                fit: BoxFit.cover,
              ),
            ),
          ),
        ),
      ),
    );
  }
}
异常现象
  • 网格渲染环节没有正确展示2款不同产品,反而出现同一款产品被重复渲染的问题
  • 日志已确认snapshot.docs.length正确识别到2条产品数据,但所有网格项的内容完全一致
问题根因
  1. 数据存储逻辑错误:getData方法中通过forEach遍历文档时,直接把每条文档的赋值给全局单变量(category、id、name、shop、images、sizes),每一次循环都会覆盖上一次循环的赋值,遍历结束后这些全局变量仅保留最后一次循环写入的内容,没有把多条产品数据单独留存。
  2. 组件渲染取数逻辑错误:GridView的itemBuilder构建每个网格项时,没有根据传入的index参数获取对应下标的产品数据,反而直接引用上述全局变量,导致不管渲染第几个网格项,拿到的都是同一组被覆盖后的全局值,最终出现同一款产品重复渲染的效果。
修复方案
  1. 新增产品数据模型类,规范化存储单条产品的全量信息,同时在页面状态中定义产品列表变量,替代原来的全局单变量:
    // 产品数据模型
    class Product {
      final String category;
      final String id;
      final String name;
      final String shop;
      final int quantity;
      final List<String> images;
      final List<String> sizes;
    
      Product({
        required this.category,
        required this.id,
        required this.name,
        required this.shop,
        required this.quantity,
        required this.images,
        required this.sizes,
      });
    }
    
    // 页面中定义的状态变量
    List<Product> productList = [];
    
  2. 重写getData方法的遍历逻辑,把每条文档转成Product实例存入产品列表,替换原有的全局单变量赋值逻辑:
    void getData() async {
      QuerySnapshot snapshot =
          await FirebaseFirestore.instance.collection('kollamProducts').get();
      
      productList.clear();
      snapshot.docs.forEach((document) {
        final docData = document.data() as Map<String, dynamic>;
        productList.add(Product(
          category: docData['category'],
          id: docData['id'],
          name: docData['name'],
          shop: docData['shop'],
          quantity: docData['quantity'],
          images: List.from(docData['images']),
          sizes: List.from(docData['sizes']),
        ));
      });
    
      if (productList.isNotEmpty) {
        setState(() {});
      }
    }
    
  3. 调整GridView的渲染逻辑,将itemCount替换为productList.length,同时在itemBuilder中根据index取对应下标的产品数据传给SingleProdc组件:
    itemCount: productList.length,
    itemBuilder: (BuildContext context, int index) {
      final currentProduct = productList[index];
      return SingleProdc(
        product_name: currentProduct.name,
        product_picture: currentProduct.images[0],
        product_image1: currentProduct.images[1],
        product_image2: currentProduct.images[2],
        product_shop: currentProduct.shop,
      );
    },
    
额外注意点
  • Hero组件要求tag全局唯一,原代码直接使用product_name作为tag,遇到同名产品时会触发报错,建议拼接产品唯一id生成tag,比如'product_${currentProduct.id}'
  • 取图片数组下标时增加边界判断,避免部分产品的images数组长度不足3时抛出数组越界错误

内容的提问来源于stack exchange,提问作者AliMohammed Manzoor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.02 12:24:04