You need to enable JavaScript to run this app. Flutter联动下拉切换一级报错,如何程序化取消次级选中项
优惠活动
大模型
产品
解决方案
定价
更多

Flutter联动下拉切换一级报错,如何程序化取消次级选中项

联动下拉报错的根因

Flutter 的DropdownButtonFormField有严格的内置校验规则:只要传入的value不为空,就必须能在当前渲染的items列表中找到完全匹配的唯一值,否则就会直接抛出渲染错误。
你当前的切换场景里刚好踩了三个规则坑:

  • 切换城市时,旧的_weight状态还保留着上一个城市对应的重量id,等新的城市重量列表拉取完成后,这个旧id根本不在新的选项列表里
  • 重量下拉的items列表里加了一个写死的DropdownMenuItem(child: Text("Select Weight")),这个项没有设置独立的value值,本身就不符合下拉组件的校验要求
  • 切换城市的逻辑只更新了重量列表,没有同步重置重量下拉的选中状态,必然触发值不匹配的报错
具体修改方案

1. 调整城市下拉的联动逻辑

将接口拉取、状态重置的逻辑统一放到下拉组件的标准onChanged回调里(不要把核心逻辑只写在单个item的onTap中,避免逻辑漏触发),选中城市时同步完成两个操作:

  • 把重量下拉的选中值重置为null,实现编程式取消选中
  • 清空旧的重量列表,避免新数据加载时残留旧选项

修改后的城市下拉代码:

// Choose City
CustomDropDownMenu(
  items: allCity.map((list) {
    return DropdownMenuItem(
      value: list["id"].toString(),
      child: Padding(
        padding: const EdgeInsets.only(left: 20.0),
        child: Text("${list["name"]}"),
      ),
    );
  }).toList(),
  value: _city,
  hint: "Choose City",
  onChanged: (selectedCityId) {
    setState(() {
      _city = selectedCityId;
      // 核心:切换城市时重置重量下拉的选中状态
      _weight = null;
      // 清空旧重量列表
      weightList = [];
    });
    // 拉取当前城市对应的重量选项数据
    getWeight(selectedCityId).then((value) {
      setState(() {
        weightList = value;
      });
    });
    FocusScope.of(context).unfocus();
  },
),

2. 修正重量下拉的结构

删掉那个没有设置value的"Select Weight"固定项——当_weight为null时,组件本来就会自动展示你设置的hint提示文本,这个多余的项不仅没有实际作用,还会因为value不唯一触发校验报错。

修改后的重量下拉代码:

// Weight
CustomDropDownMenu(
  hint: "Select Weight",
  value: _weight,
  items: weightList.map((list) {
    return DropdownMenuItem(
      value: list["id"].toString(),
      child: Padding(
        padding: const EdgeInsets.only(left: 20.0),
        child: Text("${list["weight"]}"),
      ),
    );
  }).toList(),
  onChanged: (selectedWeightId) {
    setState(() {
      _weight = selectedWeightId;
    });
    FocusScope.of(context).unfocus();
  },
),

3. 优化自定义下拉组件的类型支持

原来的CustomDropDownMenu硬写了DropdownMenuItem<dynamic>类型,类型不统一也会间接触发校验问题,改成泛型版本匹配Flutter下拉组件的原生类型逻辑:

class CustomDropDownMenu<T> extends StatelessWidget {
  final String hint;
  final T value;
  final Function(T?) onChanged;
  final Function(T?) onSaved;
  final List<DropdownMenuItem<T>> items;

  const CustomDropDownMenu({
    Key? key,
    this.hint,
    required this.onChanged,
    this.onSaved,
    required this.items,
    this.value,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        shape: BoxShape.rectangle,
        borderRadius: BorderRadius.circular(60),
      ),
      child: Card(
        shape: const StadiumBorder(),
        elevation: 5,
        child: DropdownButtonFormField<T>(
          style: const TextStyle(
            fontWeight: FontWeight.bold,
            fontSize: 18.0,
            color: Colors.black
          ),
          hint: Padding(
            padding: const EdgeInsets.only(left: 20.0),
            child: Text(hint!),
          ),
          decoration: const InputDecoration(
            enabledBorder: UnderlineInputBorder(
              borderSide: BorderSide(color: Colors.transparent),
            ),
          ),
          value: value,
          onChanged: onChanged,
          onSaved: onSaved,
          items: items,
        ),
      ),
    );
  }
}

关于取消选中的实现说明:把_weight设置为null就是Flutter下拉组件官方支持的取消选中方式——当传入的value为null时,组件不会渲染任何已选中的项,直接展示hint提示文本,完全不需要额外的自定义操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.08 22:36:04