Flutter 如何实现附图所示类型的下拉菜单(Dropdown)组件
Flutter 自定义样式下拉菜单实现方案
你需要实现的这类自定义外观下拉菜单参考效果如下:
可以通过以下两种常用方案实现,可根据你的样式复杂度选择:
方案1:基于原生DropdownButton扩展(适配简单自定义需求)
Flutter 自带的DropdownButton提供了丰富的样式配置参数,简单调整即可匹配大部分设计需求:
- 核心参数配置:
- 用
value绑定当前选中值,onChanged监听选项切换事件 - 用
items配置下拉选项列表,可自定义每个选项的内边距、文字样式 - 设置
isExpanded: true让下拉组件撑满父容器宽度
- 用
- 常见样式调整:
- 用
dropdownColor设置下拉面板背景色 - 用
icon参数替换默认的下拉箭头图标 - 用
underline参数隐藏默认下划线,或者自定义下划线样式 - 用
elevation调整下拉面板的阴影高度
- 用
示例代码参考:
String? selectedValue = '选项1'; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(8), ), child: DropdownButton<String>( value: selectedValue, icon: const Icon(Icons.keyboard_arrow_down, color: Colors.grey, size: 20), elevation: 8, style: const TextStyle(color: Colors.black, fontSize: 14), dropdownColor: Colors.white, underline: const SizedBox.shrink(), // 隐藏默认下划线 isExpanded: true, isDense: true, onChanged: (String? newValue) { setState(() { selectedValue = newValue; }); }, items: <String>['选项1', '选项2', '选项3', '选项4'] .map<DropdownMenuItem<String>>((String value) { return DropdownMenuItem<String>( value: value, child: Text(value), ); }).toList(), ), ); }
方案2:完全自定义下拉面板(适配复杂自定义需求)
如果需要实现选项分割线、选中态高亮、下拉搜索等复杂功能,原生DropdownButton的扩展性不足以满足需求,可以结合showMenu方法自定义弹出层实现:
- 用
GestureDetector包裹触发下拉的组件,点击时计算弹出位置 - 调用
showMenu弹出自定义菜单,可自由组合搜索框、分割线、自定义选项组件 - 选中选项后回传值更新触发区域的显示内容
示例代码参考:
String? selectedValue = '选项1'; // 触发自定义下拉 void _showCustomDropdown(BuildContext context) async { final renderBox = context.findRenderObject() as RenderBox; final offset = renderBox.localToGlobal(Offset.zero); // 弹出自定义菜单 final selected = await showMenu<String>( context: context, // 计算菜单弹出位置,紧跟在触发组件下方 position: RelativeRect.fromLTRB( offset.dx, offset.dy + renderBox.size.height + 4, offset.dx + renderBox.size.width, offset.dy, ), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), elevation: 8, items: [ // 可自定义添加搜索框 const PopupMenuItem( enabled: false, child: TextField( decoration: InputDecoration( hintText: '搜索选项', prefixIcon: Icon(Icons.search, size: 18), border: InputBorder.none, isDense: true, contentPadding: EdgeInsets.zero, ), ), ), const PopupMenuDivider(), PopupMenuItem(value: '1', child: const Text('选项1')), PopupMenuItem(value: '2', child: const Text('选项2')), const PopupMenuDivider(), PopupMenuItem(value: '3', child: const Text('选项3')), ], ); if (selected != null) { setState(() { selectedValue = selected; }); } } @override Widget build(BuildContext context) { return GestureDetector( onTap: () => _showCustomDropdown(context), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(selectedValue ?? '请选择', style: const TextStyle(fontSize: 14)), const Icon(Icons.keyboard_arrow_down, color: Colors.grey, size: 20), ], ), ), ); }
注意事项
- 自定义弹出层时要适配滚动场景,可计算滚动偏移调整弹出位置,避免菜单错位
- 需要支持多选的话,可以把弹出层改成StatefulWidget,记录选中状态后一次性回传结果
- 下拉面板的宽度默认和触发组件宽度一致,需要调整的话可以自定义PopupRoute实现自定义尺寸
内容的提问来源于stack exchange,提问作者kalpesh dhumal
相关产品推荐
相关产品推荐

