Flutter中DropdownButton异常:存在重复值的DropdownMenuItem
这个错误的核心原因是Flutter默认用对象引用判定相等,而你的ClsSchemes类没有重写==和hashCode方法。每次API返回的新列表里的对象,哪怕内容和之前的选中项完全一致,也会被判定为不同的对象;当列表更新后,原来的dropDownValue不在新的items的value集合中,Flutter就会误触发“重复值或无匹配值”的错误——哪怕列表只有一项。
下面是两种直接可行的解决方法:
方法一:重写ClsSchemes类的相等判定逻辑
这是最规范的做法,让对象根据业务唯一标识判断相等(比如假设Scheme_Id是每个方案的唯一ID):
class ClsSchemes { final String Scheme_Id; final String Scheme_Name; final double Total_Dues; ClsSchemes({ required this.Scheme_Id, required this.Scheme_Name, required this.Total_Dues, }); // 重写==运算符,以Scheme_Id作为相等判定依据 @override bool operator ==(Object other) => identical(this, other) || other is ClsSchemes && runtimeType == other.runtimeType && Scheme_Id == other.Scheme_Id; // 重写hashCode,基于Scheme_Id生成 @override int get hashCode => Scheme_Id.hashCode; }
修改后,DropdownButton就能正确识别新旧列表中内容相同的对象,不会再误判重复值。
方法二:改用唯一标识字段作为Dropdown的泛型类型
如果不想修改ClsSchemes类,可以把Dropdown的泛型从ClsSchemes改成字符串(比如用Scheme_Id),直接对比基础类型:
// 先调整状态变量的类型 String? dropDownValue; double? TotalDues; // 再修改FutureBuilder中的DropdownButton代码 FutureBuilder( future: Api.getSchemes(context), builder: (BuildContext context, AsyncSnapshot snapshot) { return snapshot.hasData ? Container( padding: EdgeInsets.all(5), decoration: BoxDecoration( color: Color(0xffF3D876), borderRadius: BorderRadius.circular(10), ), child: DropdownButton<String>( alignment: AlignmentDirectional.centerStart, isExpanded: true, value: dropDownValue, // 根据选中的ID获取对应的名称作为提示 hint: Text(dropDownValue != null ? snapshot.data.firstWhere((item) => item.Scheme_Id == dropDownValue).Scheme_Name : 'Make a selection'), items: snapshot.data.map<DropdownMenuItem<String>>((item) { return DropdownMenuItem<String>( value: item.Scheme_Id, child: Text(item.Scheme_Name), ); }).toList(), onChanged: (value) { setState(() { dropDownValue = value!; // 通过ID找到对应的对象,更新TotalDues final selectedScheme = snapshot.data.firstWhere((item) => item.Scheme_Id == value); TotalDues = selectedScheme.Total_Dues; }); }, ), ) : Container( child: Center( child: Text('Loading...'), ), ); }, )
额外优化建议
- 避免重复请求API:不要直接在
FutureBuilder的future参数里写Api.getSchemes(context),每次build都会重新发起请求。应该把Future存到State变量中:
late Future<List<ClsSchemes>> _schemesFuture; @override void initState() { super.initState(); _schemesFuture = Api.getSchemes(context); } // 后续FutureBuilder使用_schemesFuture FutureBuilder(future: _schemesFuture, ...)
- 初始化选中值:如果需要默认选中第一项,在第一次获取数据后主动设置:
if (snapshot.connectionState == ConnectionState.done && dropDownValue == null) { setState(() { dropDownValue = snapshot.data.first; // 方法一的情况 // 或者 dropDownValue = snapshot.data.first.Scheme_Id; // 方法二的情况 }); }
内容的提问来源于stack exchange,提问作者FinAcc
相关产品推荐
相关产品推荐

