Flutter中如何实现多下拉菜单选项唯一选中,后选覆盖前选?
实现下拉菜单选项互斥的解决方案
要实现「同一选项只能被最后选中的下拉菜单保留,之前选中该选项的下拉菜单自动清空」的效果,核心是将所有卡片的选中状态统一移到父组件管理,通过全局状态控制互斥逻辑。以下是具体实现步骤:
1. 修改 CardWidget 组件
将 CardWidget 改为无状态组件,把选中状态、可用选项列表和选中回调交给父组件控制:
class CardWidget extends StatelessWidget { final String title; final Color color; final IconData icon; final int cardId; // 每个卡片的唯一标识 final String? selectedValue; // 父组件传递的当前选中值 final List<String> availableItems; // 可用选项(排除已被其他卡片占用的) final Function(int, String?) onSelectionChanged; // 选中变化的回调 const CardWidget({ super.key, required this.title, required this.color, required this.icon, required this.cardId, required this.selectedValue, required this.availableItems, required this.onSelectionChanged, }); @override Widget build(BuildContext context) { return SizedBox( height: 200, width: 200, child: Padding( padding: const EdgeInsets.all(8.0), child: Card( elevation: 10, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), color: color, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Icon( icon, color: Colors.white, size: 32, ), Center( child: Text( title, style: GoogleFonts.poppins( color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold), ), ), DropdownButtonHideUnderline( child: DropdownButton2( isExpanded: true, hint: const Row( children: [ Expanded( child: Center( child: Text( 'Select a Key', style: TextStyle(color: Colors.black87), overflow: TextOverflow.ellipsis, ), ), ), ], ), items: availableItems .map((item) => DropdownMenuItem<String>( value: item, child: Center( child: Text( item, style: const TextStyle(color: Colors.black87), overflow: TextOverflow.ellipsis, ), ), )) .toList(), value: selectedValue, onChanged: (value) => onSelectionChanged(cardId, value), icon: const Icon(Icons.arrow_forward_ios_outlined), iconSize: 20, iconEnabledColor: const Color.fromARGB(255, 31, 10, 88), iconDisabledColor: Colors.grey, buttonHeight: 40, buttonWidth: 135, buttonPadding: const EdgeInsets.only(left: 15, right: 15), buttonDecoration: BoxDecoration( borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.black26), color: Colors.white, ), buttonElevation: 2, itemHeight: 40, itemPadding: const EdgeInsets.only(left: 14, right: 14), dropdownMaxHeight: 200, dropdownWidth: 200, dropdownDecoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: Colors.white, ), dropdownElevation: 8, scrollbarRadius: const Radius.circular(10), scrollbarThickness: 6, scrollbarAlwaysShow: true, offset: const Offset(-20, 0), ), ), ], ), ), ), ); } }
2. 父组件(首页)实现互斥逻辑
在父组件中维护全局状态,处理选中变化时的互斥规则:
class HomePage extends StatefulWidget { const HomePage({super.key}); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { // 所有可选的选项列表 final List<String> _allItems = ['Key 1', 'Key 2', 'Key 3', 'Key 4']; // 记录每个卡片的选中值:key=卡片id,value=选中的选项 final Map<int, String?> _cardSelections = {}; // 记录每个选项的归属卡片:key=选项,value=卡片id final Map<String, int?> _optionOwners = {}; // 处理下拉菜单选中变化的核心逻辑 void _handleSelectionChanged(int cardId, String? newValue) { setState(() { // 1. 清空当前卡片之前选中选项的归属记录 final oldValue = _cardSelections[cardId]; if (oldValue != null) { _optionOwners.remove(oldValue); } // 2. 如果新选项已被其他卡片占用,清空该卡片的选中值 if (newValue != null) { final occupiedCardId = _optionOwners[newValue]; if (occupiedCardId != null && occupiedCardId != cardId) { _cardSelections[occupiedCardId] = null; _optionOwners.remove(newValue); } // 记录新选项的归属卡片 _optionOwners[newValue] = cardId; } // 3. 更新当前卡片的选中值 _cardSelections[cardId] = newValue; }); } // 为每个卡片过滤可用选项:排除被其他卡片占用的选项(自己已选中的除外) List<String> _getAvailableItems(int cardId) { final currentSelection = _cardSelections[cardId]; return _allItems.where((item) { final owner = _optionOwners[item]; return owner == null || owner == cardId; }).toList(); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ // 给每个卡片分配唯一ID(0-3) CardWidget( title: 'Mute', color: const Color.fromARGB(255, 211, 38, 25), icon: Icons.volume_off_outlined, cardId: 0, selectedValue: _cardSelections[0], availableItems: _getAvailableItems(0), onSelectionChanged: _handleSelectionChanged, ), CardWidget( title: 'Mute', color: const Color.fromARGB(255, 211, 38, 25), icon: Icons.volume_off_outlined, cardId: 1, selectedValue: _cardSelections[1], availableItems: _getAvailableItems(1), onSelectionChanged: _handleSelectionChanged, ), CardWidget( title: 'Mute', color: const Color.fromARGB(255, 211, 38, 25), icon: Icons.volume_off_outlined, cardId: 2, selectedValue: _cardSelections[2], availableItems: _getAvailableItems(2), onSelectionChanged: _handleSelectionChanged, ), CardWidget( title: 'Mute', color: const Color.fromARGB(255, 211, 38, 25), icon: Icons.volume_off_outlined, cardId: 3, selectedValue: _cardSelections[3], availableItems: _getAvailableItems(3), onSelectionChanged: _handleSelectionChanged, ), ], ), ], ), ), ); } }
核心逻辑说明
- 状态上移:将每个卡片的选中状态从组件内部移到父组件,实现全局状态统一管理。
- 双向映射:通过两个
Map分别记录「卡片→选中选项」和「选项→所属卡片」,快速定位和更新互斥关系。 - 互斥处理:当新选项被选中时,自动清空之前占用该选项的卡片选中值,并更新归属记录。
- 选项过滤:每个卡片仅显示未被其他卡片占用的选项,避免无效选择,提升交互体验。
内容的提问来源于stack exchange,提问作者ckot
相关产品推荐
相关产品推荐

