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

Flutter中如何基于两种方案实现分组多选下拉框?

Flutter 实现带分组标题的多选下拉框

下面提供两种实现方案,分别基于第三方包和自定义实现,都能达到你需要的分组多选效果:


方案一:使用 multi_select_flutter 包

步骤1:添加依赖

在 pubspec.yaml 中引入包:

dependencies:
  multi_select_flutter: ^4.1.0 # 建议使用最新版本

步骤2:实现分组多选逻辑

通过自定义 itemBuilder 插入分组标题,示例代码如下:

import 'package:flutter/material.dart';
import 'package:multi_select_flutter/multi_select_flutter.dart';

class GroupedMultiSelectPage extends StatefulWidget {
  const GroupedMultiSelectPage({Key? key}) : super(key: key);

  @override
  State<GroupedMultiSelectPage> createState() => _GroupedMultiSelectPageState();
}

class _GroupedMultiSelectPageState extends State<GroupedMultiSelectPage> {
  // 定义分组数据结构
  final List<GroupedItem> groupedItems = [
    GroupedItem(
      groupTitle: "水果",
      items: [
        MultiSelectItem<String>("苹果", "苹果"),
        MultiSelectItem<String>("香蕉", "香蕉"),
        MultiSelectItem<String>("橙子", "橙子"),
      ],
    ),
    GroupedItem(
      groupTitle: "蔬菜",
      items: [
        MultiSelectItem<String>("白菜", "白菜"),
        MultiSelectItem<String>("胡萝卜", "胡萝卜"),
        MultiSelectItem<String>("西兰花", "西兰花"),
      ],
    ),
  ];

  List<String> selectedItems = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("分组多选下拉框")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: MultiSelectBottomSheetField(
          initialValue: selectedItems,
          title: const Text("选择选项"),
          buttonText: const Text("打开选择器"),
          items: groupedItems.expand((group) => group.items).toList(),
          listType: MultiSelectListType.CHIP,
          onConfirm: (values) {
            setState(() {
              selectedItems = values.cast<String>();
            });
          },
          // 自定义列表项,插入分组标题
          itemBuilder: (context, item, isSelected) {
            final group = groupedItems.firstWhere((g) => g.items.contains(item));
            final isFirstInGroup = group.items.first == item;

            return Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // 仅在分组第一个元素前显示标题
                if (isFirstInGroup)
                  Padding(
                    padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
                    child: Text(
                      group.groupTitle,
                      style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ),
                  ),
                MultiSelectItem(
                  item: item.value,
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
                    child: Text(item.label),
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

// 分组数据模型
class GroupedItem {
  final String groupTitle;
  final List<MultiSelectItem<String>> items;

  GroupedItem({required this.groupTitle, required this.items});
}

方案二:自定义实现分组多选

如果不想依赖第三方包,可以通过 AlertDialog + ListView 自定义实现:

import 'package:flutter/material.dart';

class CustomGroupedMultiSelect extends StatefulWidget {
  final List<GroupedOption> groupedOptions;
  final ValueChanged<List<String>> onSelectedChanged;
  final List<String> initialSelected;

  const CustomGroupedMultiSelect({
    Key? key,
    required this.groupedOptions,
    required this.onSelectedChanged,
    required this.initialSelected,
  }) : super(key: key);

  @override
  State<CustomGroupedMultiSelect> createState() => _CustomGroupedMultiSelectState();
}

class _CustomGroupedMultiSelectState extends State<CustomGroupedMultiSelect> {
  late List<String> _selectedItems;

  @override
  void initState() {
    super.initState();
    _selectedItems = List.from(widget.initialSelected);
  }

  void _toggleSelection(String item) {
    setState(() {
      _selectedItems.contains(item) ? _selectedItems.remove(item) : _selectedItems.add(item);
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _showMultiSelectDialog,
      child: Text(_selectedItems.isEmpty ? "选择选项" : "已选: ${_selectedItems.join(', ')}"),
    );
  }

  void _showMultiSelectDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text("选择选项"),
        content: SizedBox(
          width: double.maxFinite,
          height: 300,
          child: ListView.builder(
            itemCount: widget.groupedOptions.length,
            itemBuilder: (context, groupIndex) {
              final group = widget.groupedOptions[groupIndex];
              return Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // 分组标题
                  Padding(
                    padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
                    child: Text(
                      group.groupTitle,
                      style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
                    ),
                  ),
                  // 分组下的多选选项
                  ...group.options.map((option) => CheckboxListTile(
                        title: Text(option),
                        value: _selectedItems.contains(option),
                        onChanged: (checked) => checked != null ? _toggleSelection(option) : null,
                      )),
                ],
              );
            },
          ),
        ),
        actions: [
          TextButton(onPressed: () => Navigator.pop(context), child: const Text("取消")),
          TextButton(
            onPressed: () {
              widget.onSelectedChanged(_selectedItems);
              Navigator.pop(context);
            },
            child: const Text("确认"),
          ),
        ],
      ),
    );
  }
}

// 分组选项模型
class GroupedOption {
  final String groupTitle;
  final List<String> options;

  GroupedOption({required this.groupTitle, required this.options});
}

// 使用示例
class CustomMultiSelectPage extends StatelessWidget {
  const CustomMultiSelectPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final groupedOptions = [
      GroupedOption(groupTitle: "水果", options: ["苹果", "香蕉", "橙子"]),
      GroupedOption(groupTitle: "蔬菜", options: ["白菜", "胡萝卜", "西兰花"]),
    ];

    return Scaffold(
      appBar: AppBar(title: const Text("自定义分组多选下拉框")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: CustomGroupedMultiSelect(
          groupedOptions: groupedOptions,
          initialSelected: [],
          onSelectedChanged: (selected) => print("已选: $selected"),
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Khalid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:45:30