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

Flutter中Firestore查询无法随用户筛选选择更新的问题

问题分析与解决方案

核心问题

你的代码存在以下几个关键问题导致查询和列表未更新:

  1. 错误处理Firestore Query对象:orderBy返回的是同步的Query实例,无需使用await异步等待
  2. 重复修改Query实例:直接在原有_query上追加条件会导致无效的复合查询(需配置对应索引),应从集合重新构建查询
  3. 未实际应用分类筛选:仅设置sort = "yes",但未用用户选择的分类过滤数据
  4. 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),
                                    ),
                                  ],
                                )
                              ],
                            ),
                          ),
                        ),
                      ]),
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

关键修改说明

  1. 重构查询逻辑:每次重新从集合构建Query,避免重复追加条件导致的错误
  2. 新增分类筛选:使用whereIn条件过滤选中的分类,同时保留初始选中状态
  3. 清晰的排序标识:用sortType替代模糊的sort变量,支持三种排序模式
  4. 正确的状态更新:在修改筛选/排序参数后立即触发查询更新,确保UI同步刷新
  5. 关闭弹窗优化:选择排序/筛选后自动关闭底部弹窗,提升用户体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:44:53