Flutter多级联动下拉选择插件multi_dropdown的使用

发布于 1周前 作者 ionicwang 来自 Flutter

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

1 回复

更多关于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结果,以避免在数据不匹配时抛出异常。

回到顶部