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

Flutter:搜索文本时如何实时更新已打开的PopupMenuButton筛选项?

解决PopupMenuButton打开时实时筛选列表的问题

问题分析

原代码存在两个核心问题:

  • 搜索逻辑错误:filteredItems.contains(query)属于循环引用,应该判断当前item是否包含搜索关键词
  • 仅调用setState更新items无法触发已打开PopupMenu的重建——因为菜单内容是在打开时由itemBuilder一次性生成的,之后不会自动响应外部状态变化

解决方案

要实现打开菜单时实时筛选,需要让PopupMenu内部内容能独立响应状态变化,这里用StatefulBuilder包裹菜单内容,同时修正搜索逻辑:

修正后的完整代码

class SearchablePopupMenu extends StatefulWidget {
  const SearchablePopupMenu({super.key});

  @override
  State<SearchablePopupMenu> createState() => _SearchablePopupMenuState();
}

class _SearchablePopupMenuState extends State<SearchablePopupMenu> {
  late TextEditingController _searchFilterController;
  List<String> items = [];
  // 保存原始列表,避免筛选后丢失完整数据
  late List<String> originalItems;

  @override
  void initState() {
    super.initState();
    originalItems = ['Item 1', 'Sample 2', 'Selection 3'];
    items = List.from(originalItems);
    _searchFilterController = TextEditingController()..addListener(_searchFilterListener);
  }

  void _searchFilterListener() {
    final query = _searchFilterController.text.trim();
    List<String> filteredItems;

    if (query.isNotEmpty) {
      // 实现不区分大小写的模糊搜索
      filteredItems = originalItems.where((item) => item.toLowerCase().contains(query.toLowerCase())).toList();
    } else {
      filteredItems = List.from(originalItems);
    }

    setState(() {
      items = filteredItems;
    });
  }

  @override
  void dispose() {
    _searchFilterController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return PopupMenuButton<String>(
      itemBuilder: (context) => StatefulBuilder(
        builder: (menuContext, menuSetState) {
          // 同步外部状态变化,触发菜单内部重建
          WidgetsBinding.instance.addPostFrameCallback((_) {
            menuSetState(() {});
          });
          return [
            PopupMenuItem<String>(
              enabled: false, // 禁用输入框项的点击事件,避免误触发
              child: TextField(
                controller: _searchFilterController,
                decoration: const InputDecoration(
                  hintText: "搜索...",
                  border: OutlineInputBorder(),
                ),
              ),
            ),
            ...items.map(
              (item) => PopupMenuItem<String>(
                value: item,
                child: Text(item),
              ),
            ),
          ];
        },
      ),
      onSelected: (value) {
        // 这里处理选中项的逻辑
        print("选中了:$value");
      },
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
        child: const Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Flexible(child: Text("选择项目")),
            Icon(Icons.arrow_drop_down),
          ],
        ),
      ),
    );
  }
}

关键改动说明

  1. 新增originalItems保存原始列表,避免每次筛选都基于已过滤的列表导致数据丢失
  2. 修正搜索逻辑:用item.toLowerCase().contains(query.toLowerCase())实现不区分大小写的模糊搜索
  3. 在itemBuilder中使用StatefulBuilder,通过WidgetsBinding同步外部状态变化,强制菜单内部重建
  4. 禁用输入框对应的PopupMenuItem点击事件,避免误触发选中操作

这样在TextField输入内容时,已打开的PopupMenu会实时更新筛选后的列表项,无需重新打开菜单。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 23:18:10