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

Flutter中DropdownButton异常:存在重复值的DropdownMenuItem

解决DropdownButton重复值错误:Either zero or 2 or more [DropdownMenuItem]s were detected with the same value

这个错误的核心原因是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...'),
            ),
          );
  },
)

额外优化建议

  1. 避免重复请求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, ...)
  1. 初始化选中值:如果需要默认选中第一项,在第一次获取数据后主动设置:
if (snapshot.connectionState == ConnectionState.done && dropDownValue == null) {
  setState(() {
    dropDownValue = snapshot.data.first; // 方法一的情况
    // 或者 dropDownValue = snapshot.data.first.Scheme_Id; // 方法二的情况
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 19:00:07