Flutter中Firestore查询无法随用户筛选选择更新的问题
问题分析与解决方案
核心问题
你的代码存在以下几个关键问题导致查询和列表未更新:
- 错误处理Firestore Query对象:
orderBy返回的是同步的Query实例,无需使用await异步等待 - 重复修改Query实例:直接在原有
_query上追加条件会导致无效的复合查询(需配置对应索引),应从集合重新构建查询 - 未实际应用分类筛选:仅设置
sort = "yes",但未用用户选择的分类过滤数据 - setState时机错误:异步函数未完成就触发UI更新,导致最新查询未生效
修正后的完整代码
import 'package:flutter/material.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:multi_select_flutter/multi_select_flutter.dart'; class RecentPost extends StatefulWidget { const RecentPost({super.key}); @override State<RecentPost> createState() => _RecentPostState(); } class Cat { final int id; final String name; Cat({ required this.id, required this.name, }); } class _RecentPostState extends State<RecentPost> { Query _query = FirebaseFirestore.instance.collection('Posts'); String sortType = "latest"; // latest/priceAsc/priceDesc List<String> selectedCategories = []; @override void initState() { super.initState(); _getDataDb(); } final _textController = TextEditingController(); static List<Cat> _cats = [ Cat(id: 1, name: "Vehicles"), Cat(id: 2, name: "Mobile"), Cat(id: 3, name: "Property"), Cat(id: 4, name: "Electrics"), Cat(id: 5, name: "Bikes"), Cat(id: 6, name: "Services"), Cat(id: 7, name: "Fashion"), Cat(id: 8, name: "Appliances"), Cat(id: 9, name: "Stores"), Cat(id: 10, name: "Rickshaw"), ]; final _items = _cats .map((cats) => MultiSelectItem<Cat>(cats, cats.name)) .toList(); void _getDataDb() { // 从集合重新构建查询,避免重复追加条件 Query newQuery = FirebaseFirestore.instance.collection('Posts'); // 应用分类筛选 if (selectedCategories.isNotEmpty) { newQuery = newQuery.where('category', whereIn: selectedCategories); } // 应用排序规则 switch (sortType) { case "priceAsc": newQuery = newQuery.orderBy('price', descending: false); break; case "priceDesc": newQuery = newQuery.orderBy('price', descending: true); break; default: newQuery = newQuery.orderBy('publish_date', descending: true); break; } setState(() { _query = newQuery; }); } String ymwdhms(DateTime date) { // 实现你的日期格式化逻辑 return "${date.year}-${date.month}-${date.day}"; } @override Widget build(BuildContext context) { return Scaffold( drawerEnableOpenDragGesture: false, appBar: AppBar( actions: [ Builder(builder: (context) => Padding( padding: EdgeInsets.only(right: 10), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text("Sort", style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), SizedBox(width: 5), IconButton( onPressed: () { showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (BuildContext bc) { return Container( width: 100, color: Colors.transparent, child: Padding( padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), child: Container( color: Colors.white, width: 100, child: Wrap( children: <Widget>[ Padding( padding: EdgeInsets.only(top: 20, left: 20, bottom: 10, right: 20), child: Row(children: <Widget>[ Expanded(child: Divider()), SizedBox(width: 10), Text("Sort list", style: TextStyle(fontWeight: FontWeight.bold)), SizedBox(width: 10), Expanded(child: Divider()), ]), ), ListTile( leading: Wrap( spacing: 0, children: <Widget>[ Icon(Icons.arrow_downward_sharp, size: 20, color: Colors.indigoAccent), Icon(Icons.attach_money_sharp, size: 20, color: Colors.indigoAccent), ], ), title: Text('Lowest price', style: TextStyle(fontSize: 14)), onTap: () { setState(() => sortType = "priceAsc"); _getDataDb(); Navigator.pop(context); }, ), ListTile( leading: Wrap( spacing: 0, children: <Widget>[ Icon(Icons.arrow_upward_sharp, size: 20, color: Colors.indigoAccent), Icon(Icons.attach_money_sharp, size: 20, color: Colors.indigoAccent), ], ), title: Text('Highest price', style: TextStyle(fontSize: 14)), onTap: () { setState(() => sortType = "priceDesc"); _getDataDb(); Navigator.pop(context); }, ), ], ), ), ), ); } ); }, icon: Icon(Icons.sort, size: 20), ), SizedBox(width: 10), Text("|", style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), SizedBox(width: 15), Text("Filter", style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), SizedBox(width: 5), IconButton( onPressed: () { showModalBottomSheet( isScrollControlled: true, context: context, builder: (ctx) { return MultiSelectBottomSheet( title: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text("Select Category for filtering", textAlign: TextAlign.center), ], ), listType: MultiSelectListType.CHIP, items: _items, initialValue: _cats.where((cat) => selectedCategories.contains(cat.name)).toList(), onConfirm: (values) { setState(() { selectedCategories = values.map((cat) => (cat as Cat).name).toList(); }); _getDataDb(); }, maxChildSize: 0.8, ); }, ); }, icon: Icon(Icons.filter_alt_outlined, size: 20), ), ], ), )) ], ), body: Column( children: [ Expanded( child: FirestorePagination( viewType: ViewType.list, bottomLoader: const Center( child: CircularProgressIndicator( strokeWidth: 3, color: Colors.blue, ), ), isLive: false, scrollDirection: Axis.vertical, limit: 10, query: _query, itemBuilder: (context, documentSnapshot, index) { final data = documentSnapshot.data() as Map<String, dynamic>; return Card( child: InkWell( onTap: () {}, child: Container( width: 100, height: 100, padding: const EdgeInsets.only(left: 10), child: Row(children: [ Container( width: 100, height: 80, child: ClipRRect( borderRadius: BorderRadius.circular(15), child: CachedNetworkImage( imageUrl: data['photo_0'], imageBuilder: (context, imageProvider) { return Container( decoration: BoxDecoration( image: DecorationImage(image: imageProvider, fit: BoxFit.cover), ), ); }, placeholder: (context, url) => Container( height: 250, width: 164, child: Center( child: CircularProgressIndicator(color: Colors.green), ), ), errorWidget: (context, url, error) => Icon(Icons.error), ), ), ), Spacer(flex: 1), Expanded( flex: 30, child: Container( padding: const EdgeInsets.only(top: 5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Text("Rs " + data['price'].toString(), style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w700), ), SizedBox(height: 5), Row( children: <Widget>[ Flexible(child: Text( data['title'], overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600), )), ], ), SizedBox(height: 5), Row( children: <Widget>[ Text(data['address'], style: TextStyle(fontSize: 10)), ], ), SizedBox(height: 5), Row( children: [ Icon(Icons.watch_later_outlined, size: 10), SizedBox(width: 5), Text(ymwdhms(data['publish_date'].toDate()), style: TextStyle(fontSize: 8, fontWeight: FontWeight.bold), ), ], ) ], ), ), ), ]), ), ), ); }, ), ), ], ), ); } }
关键修改说明
- 重构查询逻辑:每次重新从集合构建Query,避免重复追加条件导致的错误
- 新增分类筛选:使用
whereIn条件过滤选中的分类,同时保留初始选中状态 - 清晰的排序标识:用
sortType替代模糊的sort变量,支持三种排序模式 - 正确的状态更新:在修改筛选/排序参数后立即触发查询更新,确保UI同步刷新
- 关闭弹窗优化:选择排序/筛选后自动关闭底部弹窗,提升用户体验
内容的提问来源于stack exchange,提问作者PAKdev
相关产品推荐
相关产品推荐

