Flutter多级联动下拉选择插件multi_dropdown的使用
Flutter多级联动下拉选择插件multi_dropdown的使用
简介
multi_dropdown
是一个功能强大且可高度定制的Flutter插件,用于管理并搜索下拉列表中的多个项目。你可以根据需要自定义下拉框样式,并通过控制器以编程方式控制下拉框。
安装
在 pubspec.yaml
文件中添加依赖:
dependencies:
multi_dropdown: ^3.0.0 # 根据实际版本号进行修改
然后执行 flutter pub get
命令来安装包。
使用方法
引入包
在 Dart 文件顶部引入 multi_dropdown
包:
import 'package:multi_dropdown/multi_dropdown.dart';
创建 MultiDropdown 组件
以下是一个完整的示例代码,展示了如何创建和配置 MultiDropdown<User>
组件。在这个例子中,我们定义了一个简单的用户模型 User
,并用它作为下拉选项的值类型。
import 'package:flutter/material.dart';
import 'package:multi_dropdown/multi_dropdown.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Multiselect dropdown demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.green,
),
home: const MyHomePage(),
);
}
}
class User {
final String name;
final int id;
User({required this.name, required this.id});
@override
String toString() {
return 'User(name: $name, id: $id)';
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _formKey = GlobalKey<FormState>();
final controller = MultiSelectController<User>();
@override
Widget build(BuildContext context) {
var items = [
DropdownItem(label: 'Nepal', value: User(name: 'Nepal', id: 1)),
DropdownItem(label: 'Australia', value: User(name: 'Australia', id: 6)),
DropdownItem(label: 'India', value: User(name: 'India', id: 2)),
DropdownItem(label: 'China', value: User(name: 'China', id: 3)),
DropdownItem(label: 'USA', value: User(name: 'USA', id: 4)),
DropdownItem(label: 'UK', value: User(name: 'UK', id: 5)),
DropdownItem(label: 'Germany', value: User(name: 'Germany', id: 7)),
DropdownItem(label: 'France', value: User(name: 'France', id: 8)),
];
return Scaffold(
appBar: AppBar(
title: const Text('MultiDropdown Example'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MultiDropdown<User>(
items: items,
controller: controller,
enabled: true,
searchEnabled: true,
chipDecoration: const ChipDecoration(
backgroundColor: Colors.yellow,
wrap: true,
runSpacing: 2,
spacing: 10,
),
fieldDecoration: FieldDecoration(
hintText: 'Countries',
hintStyle: const TextStyle(color: Colors.black87),
prefixIcon: const Icon(Icons.flag),
showClearIcon: false,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(
color: Colors.black87,
),
),
),
dropdownDecoration: const DropdownDecoration(
marginTop: 2,
maxHeight: 500,
header: Padding(
padding: EdgeInsets.all(8),
child: Text(
'Select countries from the list',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
dropdownItemDecoration: DropdownItemDecoration(
selectedIcon: const Icon(Icons.check_box, color: Colors.green),
disabledIcon: const Icon(Icons.lock, color: Colors.grey),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please select a country';
}
return null;
},
onSelectionChange: (selectedItems) {
debugPrint("OnSelectionChange: $selectedItems");
},
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
children: [
ElevatedButton(
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
final selectedItems = controller.selectedItems;
debugPrint(selectedItems.toString());
}
},
child: const Text('Submit'),
),
ElevatedButton(
onPressed: () {
controller.selectAll();
},
child: const Text('Select All'),
),
ElevatedButton(
onPressed: () {
controller.clearAll();
},
child: const Text('Unselect All'),
),
ElevatedButton(
onPressed: () {
controller.addItems([
DropdownItem(
label: 'France',
value: User(name: 'France', id: 8)),
]);
},
child: const Text('Add Items'),
),
ElevatedButton(
onPressed: () {
controller.selectWhere((element) =>
element.value.id == 1 ||
element.value.id == 2 ||
element.value.id == 3);
},
child: const Text('Select Where'),
),
ElevatedButton(
onPressed: () {
controller.selectAtIndex(0);
},
child: const Text('Select At Index'),
),
ElevatedButton(
onPressed: () {
controller.openDropdown();
},
child: const Text('Open/Close dropdown'),
),
],
)
],
),
),
),
);
}
}
参数说明
MultiDropdown
Parameter | Type | Description | Default |
---|---|---|---|
items | List<DropdownItem<T>> | 下拉项列表 | Required |
singleSelect | bool | 是否为单选模式 | false |
chipDecoration | ChipDecoration | 芯片装饰配置 | ChipDecoration() |
fieldDecoration | FieldDecoration | 字段装饰配置 | FieldDecoration() |
dropdownDecoration | DropdownDecoration | 下拉菜单装饰配置 | DropdownDecoration() |
searchDecoration | SearchFieldDecoration | 搜索字段装饰配置 | SearchFieldDecoration() |
dropdownItemDecoration | DropdownItemDecoration | 下拉项装饰配置 | DropdownItemDecoration() |
itemBuilder | DropdownItemBuilder<T>? | 下拉项构建器 | null |
selectedItemBuilder | SelectedItemBuilder<T>? | 已选项构建器 | null |
itemSeparator | Widget? | 下拉项分隔符 | null |
validator | String? Function(List<DropdownItem<T>>) | 验证函数 | null |
autovalidateMode | AutovalidateMode | 自动验证模式 | AutovalidateMode.disabled |
controller | MultiSelectController<T>? | 控制器 | null |
maxSelections | int | 最大选择数量 | 0 |
enabled | bool | 是否启用 | true |
searchEnabled | bool | 是否启用搜索 | false |
focusNode | FocusNode? | 焦点节点 | null |
future | FutureRequest<List<DropdownItem<T>>>? | 数据获取的异步请求 | null |
onSelectionChange | OnSelectionChanged<T>? | 选择变化回调 | null |
closeOnBackButton | bool | 返回按钮关闭下拉菜单 | false |
onSearchChange | ValueChanged<String>? | 搜索文本变化回调 | null |
DropdownItem
Parameter | Type | Description | Default |
---|---|---|---|
label | String | 下拉项标签 | Required |
value | T | 下拉项关联值 | Required |
disabled | bool | 是否禁用 | false |
selected | bool | 是否已选中 | false |
ChipDecoration
Parameter | Type | Description | Default |
---|---|---|---|
deleteIcon | Icon | 删除芯片图标 | Icon(Icons.close) |
backgroundColor | Color | 背景色 | Colors.blue |
labelStyle | TextStyle | 标签样式 | TextStyle() |
padding | EdgeInsets | 内边距 | EdgeInsets.all(8.0) |
border | BoxBorder | 边框 | Border() |
spacing | double | 间距 | 8.0 |
runSpacing | double | 行间距 | 8.0 |
borderRadius | BorderRadiusGeometry | 圆角半径 | BorderRadius.circular(12) |
wrap | bool | 是否换行 | true |
FieldDecoration
Parameter | Type | Description | Default |
---|---|---|---|
labelText | String? | 标签文本 | null |
hintText | String? | 提示文本 | null |
border | InputBorder? | 边框 | null |
focusedBorder | InputBorder? | 聚焦时边框 | null |
disabledBorder | InputBorder? | 禁用时边框 | null |
errorBorder | InputBorder? | 错误时边框 | null |
suffixIcon | Widget? | 后缀图标 | Icon(Icons.arrow_drop_down) |
prefixIcon | Widget? | 前缀图标 | null |
labelStyle | TextStyle? | 标签样式 | null |
hintStyle | TextStyle? | 提示样式 | null |
borderRadius | double | 圆角半径 | 8.0 |
animateSuffixIcon | bool | 是否动画后缀图标 | true |
padding | EdgeInsets? | 内边距 | null |
showClearIcon | bool | 是否显示清除图标 | true |
DropdownDecoration
Parameter | Type | Description | Default |
---|---|---|---|
backgroundColor | Color | 背景色 | Colors.white |
elevation | double | 抬升高度 | 1.0 |
maxHeight | double | 最大高度 | 400.0 |
borderRadius | BorderRadius | 圆角半径 | BorderRadius.circular(12.0) |
marginTop | double | 上边距 | 0.0 |
header | Widget? | 头部组件 | null |
footer | Widget? | 尾部组件 | null |
SearchFieldDecoration
Parameter | Type | Description | Default |
---|---|---|---|
hintText | String | 提示文本 | ‘Search’ |
border | InputBorder? | 边框 | null |
focusedBorder | InputBorder? | 聚焦时边框 | null |
searchIcon | Icon | 搜索图标 | Icon(Icons.search) |
DropdownItemDecoration
Parameter | Type | Description | Default |
---|---|---|---|
backgroundColor | Color? | 背景色 | null |
disabledBackgroundColor | Color? | 禁用背景色 | null |
selectedBackgroundColor | Color? | 选中背景色 | null |
selectedTextColor | Color? | 选中文本颜色 | null |
textColor | Color? | 文本颜色 | null |
disabledTextColor | Color? | 禁用文本颜色 | null |
selectedIcon | Icon? | 选中图标 | null |
disabledIcon | Icon? | 禁用图标 | null |
控制器(Controller)
MultiSelectController<T>
提供了对下拉框的选择、取消选择、清空选择等操作的支持。以下是常用的控制器方法:
clearAll()
:清空所有选择。selectWhere(bool Function(DropdownItem<T>) predicate)
:根据条件选择项。setItems(List<DropdownItem<T>> options)
:设置下拉项。addItem(DropdownItem<T> option)
:添加单项。addItems(List<DropdownItem<T>> options)
:添加多项。disableWhere(bool Function(DropdownItem<T>) predicate)
:根据条件禁用项。selectAll()
:全选。selectAtIndex(int index)
:选择指定索引项。deselectWhere(bool Function(DropdownItem<T>) predicate)
:根据条件取消选择项。items
:获取所有项。selectedItems
:获取已选项。disabledItems
:获取禁用项。openDropdown()
:打开下拉菜单。closeDropdown()
:关闭下拉菜单。
通过以上内容,你应该能够轻松地将 multi_dropdown
插件集成到你的 Flutter 应用中,并实现多级联动下拉选择功能。如果你有任何问题或需要进一步的帮助,请随时查阅官方文档或访问 GitHub 仓库获取更多信息。
更多关于Flutter多级联动下拉选择插件multi_dropdown的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter多级联动下拉选择插件multi_dropdown的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter中使用multi_dropdown
插件来实现多级联动下拉选择的示例代码。multi_dropdown
插件允许你创建多层级的下拉选择菜单,其中每一级的选项都依赖于前一级的选择。
首先,确保你已经在pubspec.yaml
文件中添加了multi_dropdown
依赖:
dependencies:
flutter:
sdk: flutter
multi_dropdown: ^x.y.z # 请替换为最新版本号
然后,运行flutter pub get
来安装依赖。
接下来,在你的Dart文件中使用MultiDropdown
组件。以下是一个简单的示例,演示如何实现三级联动下拉选择:
import 'package:flutter/material.dart';
import 'package:multi_dropdown/multi_dropdown.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Multi Dropdown Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MultiDropdownDemo(),
);
}
}
class MultiDropdownDemo extends StatefulWidget {
@override
_MultiDropdownDemoState createState() => _MultiDropdownDemoState();
}
class _MultiDropdownDemoState extends State<MultiDropdownDemo> {
// 定义三级联动的数据
final List<Map<String, dynamic>> countryData = [
{
'name': 'USA',
'states': [
{'name': 'California', 'cities': ['Los Angeles', 'San Francisco']},
{'name': 'Texas', 'cities': ['Houston', 'Austin']},
],
},
{
'name': 'Canada',
'states': [
{'name': 'Ontario', 'cities': ['Toronto', 'Ottawa']},
{'name': 'Quebec', 'cities': ['Montreal', 'Quebec City']},
],
},
];
String? selectedCountry;
String? selectedState;
String? selectedCity;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Multi Dropdown Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Country:'),
SizedBox(height: 8.0),
MultiDropdown<String>(
items: countryData.map((e) => e['name'] as String).toList(),
onChanged: (value) {
setState(() {
selectedCountry = value;
selectedState = null;
selectedCity = null;
});
_updateStatesAndCities(value);
},
value: selectedCountry,
),
SizedBox(height: 16.0),
Text('State:'),
SizedBox(height: 8.0),
MultiDropdown<String>(
items: _getStates(selectedCountry ?? ''),
onChanged: (value) {
setState(() {
selectedState = value;
selectedCity = null;
});
_updateCities(value);
},
value: selectedState,
isEnabled: selectedCountry != null,
),
SizedBox(height: 16.0),
Text('City:'),
SizedBox(height: 8.0),
MultiDropdown<String>(
items: _getCities(selectedState ?? ''),
onChanged: (value) {
setState(() {
selectedCity = value;
});
},
value: selectedCity,
isEnabled: selectedState != null,
),
],
),
),
);
}
// 根据选择的国家更新州列表
void _updateStatesAndCities(String? country) {
List<String> states = countryData
.firstWhereOrNull((element) => element['name'] == country)
?.['states']
?.map((e) => e['name'] as String)
?.toList() ?? [];
setState(() {
// 这里实际上不需要重新设置selectedState和selectedCity为空,因为在onChanged中已经处理
// 但为了清晰起见,可以保留这行代码,确保当国家改变时,州和城市重置
if (selectedState != null && !_contains(states, selectedState!)) {
selectedState = null;
selectedCity = null;
}
});
}
// 根据选择的州更新城市列表
void _updateCities(String? state) {
List<String> cities = countryData
.firstWhereOrNull((element) => element['name'] == selectedCountry)
?.['states']
?.firstWhereOrNull((e) => e['name'] == state)
?.['cities']
?.map((e) => e as String)
?.toList() ?? [];
setState(() {
if (selectedCity != null && !_contains(cities, selectedCity!)) {
selectedCity = null;
}
});
}
// 获取州的列表
List<String> _getStates(String country) {
return countryData
.firstWhereOrNull((element) => element['name'] == country)
?.['states']
?.map((e) => e['name'] as String)
?.toList() ?? [];
}
// 获取城市的列表
List<String> _getCities(String state) {
return countryData
.firstWhereOrNull((element) => element['name'] == selectedCountry)
?.['states']
?.firstWhereOrNull((e) => e['name'] == state)
?.['cities']
?.map((e) => e as String)
?.toList() ?? [];
}
// 辅助函数,检查列表中是否包含某个元素
bool _contains(List<String> list, String item) {
return list.contains(item);
}
}
// 辅助扩展函数,用于处理可能为null的firstWhere结果
extension NullableFirstWhereExtension<T> on Iterable<T> {
T? firstWhereOrNull(bool Function(T element) test) {
try {
return firstWhere(test);
} catch (_) {
return null;
}
}
}
在这个示例中,我们定义了一个包含国家和州/城市数据的列表。我们使用MultiDropdown
组件来创建三个级别的下拉选择菜单,每一级的选项都依赖于前一级的选择。通过onChanged
回调,我们更新选中的值,并相应地更新下一级的选项列表。
注意,我们使用了一个辅助扩展函数firstWhereOrNull
来处理可能为空的firstWhere
结果,以避免在数据不匹配时抛出异常。