如何在Flutter中创建支持元素搜索的简易下拉列表
Flutter 可搜索下拉列表的自定义样式实现
方法一:利用 DropdownMenu 的 inputDecoration 参数自定义搜索框样式
你之前使用 DropdownMenu 时没找对样式配置入口,其实可以通过 inputDecoration 参数直接设置搜索用 TextField 的样式,包括边框、提示文本、文字样式等,示例代码如下:
DropdownMenu<String>( enableSearch: true, // 自定义搜索输入框样式 inputDecoration: InputDecoration( hintText: '搜索选项', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), hintStyle: TextStyle(color: Colors.grey[600], fontSize: 14), ), dropdownMenuEntries: const [ DropdownMenuEntry(value: '苹果', label: '苹果'), DropdownMenuEntry(value: '香蕉', label: '香蕉'), DropdownMenuEntry(value: '橙子', label: '橙子'), ], onSelected: (value) { print('选中了 $value'); }, )
方法二:使用 SearchAnchor 实现高度自定义的搜索下拉
如果需要更灵活的输入框布局(比如自定义前缀/后缀图标、复杂样式),可以用 Flutter 3.3+ 提供的 SearchAnchor 组件,完全掌控输入框和下拉菜单的样式:
SearchAnchor( builder: (BuildContext context, SearchController controller) { // 自定义输入框 return TextField( controller: controller, decoration: InputDecoration( hintText: '搜索水果', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), suffixIcon: const Icon(Icons.search), prefixIcon: const Icon(Icons.food_bank), ), onTap: () => controller.openView(), ); }, suggestionsBuilder: (BuildContext context, SearchController controller) { // 实时过滤匹配选项 final query = controller.text.toLowerCase(); final options = ['苹果', '香蕉', '橙子', '葡萄', '西瓜'] .where((item) => item.toLowerCase().contains(query)) .toList(); return options.map((option) { return ListTile( title: Text(option), onTap: () { controller.closeView(option); print('选中了 $option'); }, ); }); }, )
方法三:自定义组合 TextField + 下拉菜单
如果需要完全自定义下拉菜单的展示逻辑和样式,可以手动组合 TextField 和弹窗组件,实现最灵活的控制:
class CustomSearchDropdown extends StatefulWidget { const CustomSearchDropdown({super.key}); @override State<CustomSearchDropdown> createState() => _CustomSearchDropdownState(); } class _CustomSearchDropdownState extends State<CustomSearchDropdown> { final TextEditingController _controller = TextEditingController(); final List<String> _options = ['苹果', '香蕉', '橙子', '葡萄', '西瓜']; List<String> _filteredOptions = []; bool _isMenuOpen = false; @override void initState() { super.initState(); _filteredOptions = _options; _controller.addListener(_filterOptions); } void _filterOptions() { final query = _controller.text.toLowerCase(); setState(() { _filteredOptions = _options .where((item) => item.toLowerCase().contains(query)) .toList(); }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: _controller, decoration: InputDecoration( hintText: '输入搜索', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), suffixIcon: IconButton( icon: Icon(_isMenuOpen ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down), onPressed: () => setState(() => _isMenuOpen = !_isMenuOpen), ), ), ), if (_isMenuOpen) Container( width: double.infinity, margin: const EdgeInsets.only(top: 4), decoration: BoxDecoration( border: Border.all(color: Colors.grey[300]!), borderRadius: BorderRadius.circular(8), ), child: ListView.builder( shrinkWrap: true, itemCount: _filteredOptions.length, itemBuilder: (context, index) { return ListTile( title: Text(_filteredOptions[index]), onTap: () { setState(() { _controller.text = _filteredOptions[index]; _isMenuOpen = false; }); print('选中了 ${_filteredOptions[index]}'); }, ); }, ), ), ], ); } }
内容的提问来源于stack exchange,提问作者YUVARAAJ S
相关产品推荐
相关产品推荐

