Flutter:搜索文本时如何实时更新已打开的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), ], ), ), ); } }
关键改动说明
- 新增
originalItems保存原始列表,避免每次筛选都基于已过滤的列表导致数据丢失 - 修正搜索逻辑:用
item.toLowerCase().contains(query.toLowerCase())实现不区分大小写的模糊搜索 - 在
itemBuilder中使用StatefulBuilder,通过WidgetsBinding同步外部状态变化,强制菜单内部重建 - 禁用输入框对应的
PopupMenuItem点击事件,避免误触发选中操作
这样在TextField输入内容时,已打开的PopupMenu会实时更新筛选后的列表项,无需重新打开菜单。
内容的提问来源于stack exchange,提问作者N B
相关产品推荐
相关产品推荐

