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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:55:29