
Flutter单选多选、Popover的实现
大约 5 分钟
Flutter单选多选、Popover的实现
最近开发Flutter项目时发现,Flutter的组件DropdownButton使用时会有一些使用上的局限性(尤其是在Flutter Web中),所以在项目开发中,使用了两个组件做为DropdownButton组件的补充和拓展。本文旨在介绍组件,比较应用向,如果对组件想要更深入了解,请阅读官方文档。
dropdown_search
官方文档地址:https://pub.dev/packages/dropdown_search 参考资料:https://morioh.com/p/bd8d453ec55e
单选列表
| 参数名 | 类型 | 描述 |
|---|---|---|
| items | List<T> | 本地设置的选项列表 |
| popupProps | PopupProps | popup的设置class |
| itemAsString | DropdownSearchItemAsString<T> | 自定义options中显示的名称 |
| onChanged | ValueChanged<T?>? | 当一个item被选中后的回调 |
| selectedItem | T? | 单选时选中对象 |
| selectedItems | List<T> | 多选的时候选中的列表 |
Expanded(
child: DropdownSearch<LabelList>(
items: options,
popupProps: const PopupProps.menu(),
itemAsString: (option) => option.labelName,
onChanged: (value) {
setState(() {
selectedLabelList = value!;
valueOptions = value.labelValues;
});
},
selectedItem: selectedLabelList,
),)
这样你就得到了一个简单不带筛选功能的单选Select。 
多选列表
| 参数名 | 类型 | 描述 | |
|---|---|---|---|
| items | List<T> | 本地设置的选项列表 | |
| popupProps | PopupPropsMultiSelection | popup的设置class | |
| itemAsString | DropdownSearchItemAsString<T> | 自定义options中显示的名称 | |
| onChanged | ValueChanged<T?>? | 当一个item被选中后的回调 | |
| selectedItems | List<T> | 多选的时候选中的列表 | |
| compareFn | DropdownSearchCompareFn<T>? | 比对items与selectedItems的参数(用于回显选中状态) | |
Expanded(
child: DropdownSearch<Trigger>.multiSelection(
items: valueOptions,
popupProps: PopupPropsMultiSelection.menu(
showSearchBox: true,
searchFieldProps: TextFieldProps(
controller: TextEditingController(text: ''),
decoration:
const InputDecoration(hintText: '搜索标签')),
),
itemAsString: (option) => option.value,
onChanged: (selectedList) {
setState(() {
triggerSelected = [...selectedList];
});
},
selectedItems: triggerSelected,
compareFn: (item1, item2) =>
item1.valueId == item2.valueId,
),)
这样你也就获得了一个带有筛选功能多选框组。 
dropdown_button2
官方文档地址:https://pub.dev/packages/dropdown_button2
(其实官网文档写的很详细,下面提供参数翻译和一个小例子)
| Option | Description | Type | Required |
|---|---|---|---|
| items | 用户可以选择的项目列表 | List<DropdownMenuItem> | Yes |
| hint | 在用户选择一个项目之前显示的占位符 | Widget | No |
| disabledHint | 如果下拉菜单被禁用,显示的占位符。 | Widget | No |
| value | 当前选择的[DropdownMenuItem]的值 | T | No |
| onChanged | 当用户选择一个项目时被调用 | ValueChanged<T?> | No |
| onMenuStateChange | 当下拉菜单被打开或关闭时被调用 | Function(bool isOpen) | No |
| selectedItemBuilder | 所选项目将如何在按钮上显示 | DropdownButtonBuilder | No |
| buttonHeight | 按钮的高度 | double | No |
| buttonWidth | 按钮的宽度 | double | No |
| buttonPadding | 按钮的内部填充物 | EdgeInsetsGeometry | No |
| buttonDecoration | 按钮的装饰 | BoxDecoration | No |
| buttonElevation | 按钮的高度 | int | No |
| buttonSplashColor | 按钮InkWell的亮光颜色 | Color | No |
| buttonHighlightColor | 按钮InkWell的高光色 | Color | No |
| buttonOverlayColor | 按钮InkWell的叠加颜色 | MaterialStateProperty<Color?> | No |
| icon | 下拉按钮的后缀图标 | Widget | No |
| iconOnClick | 打开下拉菜单时显示不同的图标 | Widget | No |
| iconSize | 图标的大小 | double | No |
| iconEnabledColor | 如果按钮被激活,图标的颜色 | Color | No |
| iconDisabledColor | 如果按钮被禁用,图标的颜色 | Color | No |
| itemHeight | 菜单项的高度 | double | No |
| itemPadding | 菜单项的padding | EdgeInsetsGeometry | No |
| itemSplashColor | 项目的InkWell的亮色 | Color | No |
| itemHighlightColor | 项目的InkWell的高亮颜色 | Color | No |
| dropdownMaxHeight | 下拉菜单的最大高度 | double | No |
| dropdownWidth | 下拉菜单的宽度 | double | No |
| dropdownPadding | 下拉菜单的内部padding | EdgeInsetsGeometry | No |
| dropdownScrollPadding | 包括滚动条在内的下拉菜单的内部padding | EdgeInsetsGeometry | No |
| dropdownDecoration | 下拉菜单的装饰 | BoxDecoration | No |
| dropdownDirection | 下拉菜单相对于按钮的方向 | DropdownDirection | No |
| dropdownElevation | 下拉菜单的高度 | int | No |
| selectedItemHighlightColor | 当前所选项目的高亮颜色 | Color | No |
| scrollbarRadius | 滚动条边角的半径 | Radius | No |
| scrollbarThickness | 滚动条的厚度 | double | No |
| scrollbarAlwaysShow | 始终显示滚动条,即使是在没有滚动的情况下 | bool | No |
| offset | 改变下拉菜单的位置 | Offset | No |
| customButton | 使用自定义的小部件,如图标、图片等,而不是默认的按钮。 | Widget | No |
| customItemsHeights | 为菜单项使用不同的预定义高度(对添加分隔符很有用)。 | List | No |
| isExpanded | 使按钮的内部内容扩大(设置为真以避免长文本溢出)。 | bool | No |
| openWithLongPress | 在长按时打开下拉菜单,而不是轻触。 | bool | No |
| dropdownOverButton | 在按钮上方而不是下方打开下拉菜单 | bool | No |
| dropdownFullScreen | 在全屏模式下打开下拉菜单(在AppBar和TabBar上面) | bool | No |
| focusColor | 使用传统界面(键盘和鼠标)输入焦点时的按钮颜色 | Color | No |
| barrierDismissible | 你是否可以通过点击模态障碍物来取消这一途径 | bool | No |
| barrierColor | 模态屏障的颜色。如果这个颜色为空,屏障将是透明的 | Color | No |
| barrierLabel | 用于可撤销障碍物的语义标签 | String | No |
| searchController | 用于可搜索下拉菜单的控制器,如果为空,那么它将作为普通下拉菜单执行。 | TextEditingController | No |
| searchInnerWidget | 用于可搜索下拉菜单顶部的部件,用于可搜索下拉菜单的部件。 | Widget | No |
| searchMatchFn | 用于可搜索下拉菜单的匹配函数,如果为空,将使用defaultFn | SearchMatchFn | No |
DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Row(
children: const [
Expanded(
child: Text(
'更多',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.white,
), overflow: TextOverflow.ellipsis,
),),],),
items: _items .map(
(e) => DropdownMenuItem<MoreOption>(
value: e,
child: Text(
e.label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.black45,
),
overflow: TextOverflow.ellipsis,
)),).toList(),
// value: _selectedValue,
onChanged: (item) {
item?.onTap();
},
buttonHeight: 36,
buttonWidth: 80,
buttonPadding: const EdgeInsets.only(left: 12, right: 12),
buttonDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
// border: Border
color: kPrimaryColor,
),
buttonElevation: 2,
iconDisabledColor: Colors.grey,
iconEnabledColor: Colors.white,
itemHeight: 40,
itemPadding:
const EdgeInsets.only(left: 14, right: 14),
dropdownMaxHeight: 400,
dropdownWidth: 200,
dropdownPadding: null,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
color: Colors.white,
),
dropdownElevation: 8,
scrollbarRadius: const Radius.circular(40),
scrollbarThickness: 6,
scrollbarAlwaysShow: true,
offset: const Offset(-20, 0),
),)

appflowy_popover
上述两个组件,可以基本实现下拉框的功能,但是不能手动触发popover,这里推荐一个开源项目封装的popover,以供参考:
