如何在Flutter中从Firestore获取并展示去重的商品分类列表
解决Firestore商品分类去重并生成TabBar的问题
原代码核心问题
你当前的代码在itemBuilder里每次创建新的Set,每个列表项单独处理分类,完全起不到去重作用,所以会重复显示同一分类。另外where("category")写法错误,where需要指定匹配条件,直接去掉这部分才能正常获取所有商品数据。
方案一:客户端本地去重(适合小数据量场景)
先从所有商品文档中提取分类,通过Set自动去重后转成列表,再基于这个列表生成TabBar:
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance.collection('products').snapshots(), builder: (_, snapshot) { if (snapshot.hasError) return Text('Error = ${snapshot.error}'); if (snapshot.hasData) { final docs = snapshot.data!.docs; // 提取所有分类并去重 final categories = docs .map((doc) => doc.data()['category'] as String) .toSet() .toList(); // 用DefaultTabController包裹TabBar和对应内容 return DefaultTabController( length: categories.length, child: Scaffold( appBar: AppBar( bottom: TabBar( isScrollable: true, // 分类过多时允许横向滚动 tabs: categories.map((category) => Tab(text: category)).toList(), ), ), body: TabBarView( children: categories.map((category) { // 每个Tab对应显示该分类下的商品列表 return ListView.builder( itemCount: docs.where((doc) => doc.data()['category'] == category).length, itemBuilder: (_, i) { final product = docs.where((doc) => doc.data()['category'] == category).elementAt(i); return ListTile(title: Text(product.data()['name'])); }, ); }).toList(), ), ), ); } return Center(child: CircularProgressIndicator()); }, )
方案二:Firestore端优化(推荐大数据量场景)
如果商品数据量较大,客户端本地去重效率低,可以用以下两种方式优化:
方式1:使用Firestore的distinct查询
部分地区的Firestore支持distinct参数,直接在查询阶段按分类去重:
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance .collection('products') .distinct(['category']) // 按category字段去重 .snapshots(), builder: (_, snapshot) { if (snapshot.hasError) return Text('Error = ${snapshot.error}'); if (snapshot.hasData) { final categories = snapshot.data!.docs .map((doc) => doc.data()['category'] as String) .toList(); return DefaultTabController( length: categories.length, child: Scaffold( appBar: AppBar( bottom: TabBar( isScrollable: true, tabs: categories.map((cat) => Tab(text: cat)).toList(), ), ), body: TabBarView( children: categories.map((cat) { // 单独监听对应分类的商品数据 return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance .collection('products') .where('category', isEqualTo: cat) .snapshots(), builder: (_, snap) { if (snap.hasError) return Text('Error'); if (!snap.hasData) return CircularProgressIndicator(); final products = snap.data!.docs; return ListView.builder( itemCount: products.length, itemBuilder: (_, i) => ListTile(title: Text(products[i].data()['name'])), ); }, ); }).toList(), ), ), ); } return Center(child: CircularProgressIndicator()); }, )
方式2:维护独立的categories集合
在添加商品时,自动同步维护分类集合,避免重复分类:
// 添加商品时的逻辑 Future<void> addProduct(Map<String, dynamic> product) async { final db = FirebaseFirestore.instance; // 写入商品数据 await db.collection('products').add(product); // 检查分类是否已存在,不存在则写入分类集合 final category = product['category']; final categoryDoc = await db.collection('categories').doc(category).get(); if (!categoryDoc.exists) { await db.collection('categories').doc(category).set({'name': category}); } }
之后直接监听categories集合生成TabBar:
StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance.collection('categories').snapshots(), builder: (_, snapshot) { if (snapshot.hasError) return Text('Error = ${snapshot.error}'); if (snapshot.hasData) { final categories = snapshot.data!.docs .map((doc) => doc.data()['name'] as String) .toList(); return DefaultTabController( length: categories.length, child: Scaffold( appBar: AppBar( bottom: TabBar( isScrollable: true, tabs: categories.map((cat) => Tab(text: cat)).toList(), ), ), body: TabBarView( children: categories.map((cat) { return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>( stream: FirebaseFirestore.instance .collection('products') .where('category', isEqualTo: cat) .snapshots(), builder: (_, snap) { if (snap.hasError) return Text('Error'); if (!snap.hasData) return CircularProgressIndicator(); final products = snap.data!.docs; return ListView.builder( itemCount: products.length, itemBuilder: (_, i) => ListTile(title: Text(products[i].data()['name'])), ); }, ); }).toList(), ), ), ); } return Center(child: CircularProgressIndicator()); }, )
内容的提问来源于stack exchange,提问作者AsiF SHaik
相关产品推荐
相关产品推荐

