Flutter联动DropdownButtonFormField切换时出现同值错误求助
问题重现
页面有两个联动的DropdownButtonFormField,第二个下拉框的选项随第一个的选择动态加载。执行以下操作触发断言错误:
- 选择第一个下拉框的某选项
- 选择第二个下拉框的某选项
- 修改第一个下拉框的选中值
错误信息:
There should be exactly one item with [DropdownButtonFormField]'s value: 1789.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
'package:flutter/src/material/dropdown.dart':
dropdown.dart:1
Failed assertion: line 933 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItemitem) {
return item.value == value;
}).length == 1'
原代码实现
DropdownButtonFormField<int?>( value: userController.selectedReturnCity, items: List.generate( userController.storeCityFilter?.length ?? 0, (index) => DropdownMenuItem( value: userController.storeCityFilter![index]['id'], child: Text(userController.storeCityFilter![index]['value']) ) ), onChanged: (value) async { userController.selectedReturnCity = value == 0 ? null : value; // 获取第二个下拉框数据,该方法会清空原有的选项数据 await userController.getStores( city: userController.selectedReturnCity, type: "market" ); }, ), 10.v(), DropdownButtonFormField<int?>( value: userController.selectedReturnStore, items: List.generate( userController.storeList?.length ?? 0, (index) => DropdownMenuItem<int?>( value: userController.storeList?[index].id, child: Text(userController.storeList?[index].name ?? '') ) ), onChanged: (value) { userController.selectedReturnStore = value; }, )
错误根源
修改第一个下拉框选项时,第二个下拉框的storeList会被重新加载(甚至清空),但selectedReturnStore仍保留着之前选中的旧值。此时新的storeList中没有对应旧值的选项,导致DropdownButtonFormField的value不在当前items列表中,触发Flutter的断言检查失败。
修复方案
方案1:切换父选项时重置子下拉框选中值
直接在第一个下拉框的onChanged回调中,先重置第二个下拉框的选中值,再加载新数据:
onChanged: (value) async { userController.selectedReturnCity = value == 0 ? null : value; // 重置子下拉框选中值,避免旧值与新选项不匹配 userController.selectedReturnStore = null; await userController.getStores( city: userController.selectedReturnCity, type: "market" ); },
方案2:数据加载后校验选中值有效性
在getStores方法内部,数据加载完成后,校验当前selectedReturnStore是否存在于新的选项列表中,不存在则设为null:
// 假设getStores是Controller中的方法,数据加载完成后添加以下逻辑 void getStores({int? city, String type}) async { // ... 原有数据请求逻辑 ... storeList = 新加载的门店列表; // 校验选中值有效性 final validStoreIds = storeList?.map((store) => store.id).toList() ?? []; if (selectedReturnStore != null && !validStoreIds.contains(selectedReturnStore)) { selectedReturnStore = null; // 如果用了状态管理(比如GetX),需要确保状态更新通知UI update(); } }
方案1简单直接,适合大多数场景;方案2更严谨,能覆盖其他可能导致选中值失效的情况。
内容的提问来源于stack exchange,提问作者Baran Güngör

