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

Flutter DropdownButton选中值不显示问题求助

Flutter DropdownButton 无法显示选中值的解决方法

我正在开发一款食品杂货扫描与管理的Flutter应用,需通过DropdownButton选择杂货的存储位置:货架(Shelf)、冰箱(Fridge)或冰柜(Freezer)。以下是控制该下拉按钮的函数代码,该函数位于继承自ChangeNotifier的MyAppState类中,无法使用setState。当前遇到的问题是DropdownButton无法显示选中的值。

Product addDateAndQuantityAndStorage(BuildContext context, Product product) {
TextEditingController dateInput = TextEditingController();
int selectedValue = 0;

showDialog<String>(
  context: context,
  builder: (BuildContext context) => AlertDialog(
    title: const Text("Add Expiration Date and Quantity"),
    content: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        TextField(
          controller: dateInput,
          decoration: const InputDecoration(hintText: "Expiration Date"),
          onTap: () async {
            DateTime? pickedDate = await showDatePicker(
              context: context, initialDate: DateTime.now(),
              firstDate: DateTime(DateTime.now().year - 2),
              lastDate: DateTime(DateTime.now().year + 5),
            );

            if (pickedDate != null) {
              product.properties.expr = DateFormat('yy-MM-dd').format(pickedDate);
              dateInput.text = DateFormat('yy-MM-dd').format(pickedDate);
              notifyListeners();
            }
          }
        ),
        TextField(
          decoration: const InputDecoration(hintText: "Quantity"),
          keyboardType: TextInputType.number,
          onChanged: (value) {
            product.total = int.parse(value);
            notifyListeners();
          }
        ),
        DropdownButton(
          isExpanded: true,
          value: selectedValue,
          hint: const Text("Storage"),
          items: [
            DropdownMenuItem(
              value: 0,
              child: Text("Shelf"),
              onTap: () {
                selectedValue = 0;
                notifyListeners();
              },
            ),
            DropdownMenuItem(
              value: 1,
              child: Text("Fridge"),
              onTap: () {
                selectedValue = 1;
                notifyListeners();
              },
            ),
            DropdownMenuItem(
              value: 2,
              child: Text("Freezer"),
              onTap: () {
                selectedValue = 2;
                notifyListeners();
              },
            ),
          ],
          onChanged: (value) {
            switch(value) {
              case 0:
                product.storage = "Shelf";
                break;
              case 1:
                product.storage = "Fridge";
                break;
              case 2:
                product.storage = "Freezer";
                break;
            }
            notifyListeners();
          },
        ),
      ]
    ),
    actions: <Widget>[
      TextButton(
        onPressed: () {
          Navigator.pop(context, "Add");
        },
        child: const Text('Add'),
      ),
    ],
  ),
);
return product;
}

问题原因

  1. selectedValue是函数内的局部变量,调用notifyListeners()无法触发Dialog的UI更新——Dialog的上下文独立于ChangeNotifier监听的上下文,局部变量的变化也不会被ChangeNotifier追踪。
  2. 错误使用DropdownMenuItem的onTap回调更新选中值,这不是Dropdown组件的标准用法,选中状态应该通过DropdownButton的onChanged回调统一处理。

解决方案

方案一:将选中值设为MyAppState的成员变量

把selectedValue提升为类成员,让ChangeNotifier能追踪其变化并触发UI更新:

  1. 在MyAppState类中添加成员变量:
int selectedStorageValue = 0;
  1. 修改函数代码:
Product addDateAndQuantityAndStorage(BuildContext context, Product product) {
  TextEditingController dateInput = TextEditingController();
  // 根据当前product的存储位置初始化选中索引
  selectedStorageValue = switch(product.storage) {
    "Shelf" => 0,
    "Fridge" => 1,
    "Freezer" => 2,
    _ => 0,
  };

  showDialog<String>(
    context: context,
    builder: (BuildContext context) => AlertDialog(
      title: const Text("添加过期日期与数量"),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextField(
            controller: dateInput,
            decoration: const InputDecoration(hintText: "过期日期"),
            onTap: () async {
              DateTime? pickedDate = await showDatePicker(
                context: context, initialDate: DateTime.now(),
                firstDate: DateTime(DateTime.now().year - 2),
                lastDate: DateTime(DateTime.now().year + 5),
              );

              if (pickedDate != null) {
                product.properties.expr = DateFormat('yy-MM-dd').format(pickedDate);
                dateInput.text = DateFormat('yy-MM-dd').format(pickedDate);
                notifyListeners();
              }
            }
          ),
          TextField(
            decoration: const InputDecoration(hintText: "数量"),
            keyboardType: TextInputType.number,
            onChanged: (value) {
              product.total = int.parse(value);
              notifyListeners();
            }
          ),
          DropdownButton(
            isExpanded: true,
            value: selectedStorageValue,
            hint: const Text("存储位置"),
            items: [
              const DropdownMenuItem(value: 0, child: Text("货架")),
              const DropdownMenuItem(value: 1, child: Text("冰箱")),
              const DropdownMenuItem(value: 2, child: Text("冰柜")),
            ],
            onChanged: (value) {
              if (value == null) return;
              selectedStorageValue = value;
              switch(value) {
                case 0: product.storage = "Shelf"; break;
                case 1: product.storage = "Fridge"; break;
                case 2: product.storage = "Freezer"; break;
              }
              notifyListeners();
            },
          ),
        ]
      ),
      actions: <Widget>[
        TextButton(
          onPressed: () => Navigator.pop(context, "添加"),
          child: const Text('添加'),
        ),
      ],
    ),
  );
  return product;
}

方案二:使用StatefulBuilder管理Dialog内部状态

如果不想将选中值提升为类成员,可通过StatefulBuilder在Dialog内部维护局部状态,无需依赖ChangeNotifier:

Product addDateAndQuantityAndStorage(BuildContext context, Product product) {
  TextEditingController dateInput = TextEditingController();
  int selectedValue = switch(product.storage) {
    "Shelf" => 0,
    "Fridge" => 1,
    "Freezer" => 2,
    _ => 0,
  };

  showDialog<String>(
    context: context,
    builder: (BuildContext context) => AlertDialog(
      title: const Text("添加过期日期与数量"),
      content: StatefulBuilder(
        builder: (context, setState) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: dateInput,
                decoration: const InputDecoration(hintText: "过期日期"),
                onTap: () async {
                  DateTime? pickedDate = await showDatePicker(
                    context: context, initialDate: DateTime.now(),
                    firstDate: DateTime(DateTime.now().year - 2),
                    lastDate: DateTime(DateTime.now().year + 5),
                  );

                  if (pickedDate != null) {
                    product.properties.expr = DateFormat('yy-MM-dd').format(pickedDate);
                    dateInput.text = DateFormat('yy-MM-dd').format(pickedDate);
                    notifyListeners();
                  }
                }
              ),
              TextField(
                decoration: const InputDecoration(hintText: "数量"),
                keyboardType: TextInputType.number,
                onChanged: (value) {
                  product.total = int.parse(value);
                  notifyListeners();
                }
              ),
              DropdownButton(
                isExpanded: true,
                value: selectedValue,
                hint: const Text("存储位置"),
                items: [
                  const DropdownMenuItem(value: 0, child: Text("货架")),
                  const DropdownMenuItem(value: 1, child: Text("冰箱")),
                  const DropdownMenuItem(value: 2, child: Text("冰柜")),
                ],
                onChanged: (value) {
                  if (value == null) return;
                  setState(() => selectedValue = value);
                  switch(value) {
                    case 0: product.storage = "Shelf"; break;
                    case 1: product.storage = "Fridge"; break;
                    case 2: product.storage = "Freezer"; break;
                  }
                  notifyListeners();
                },
              ),
            ]
          );
        }
      ),
      actions: <Widget>[
        TextButton(
          onPressed: () => Navigator.pop(context, "添加"),
          child: const Text('添加'),
        ),
      ],
    ),
  );
  return product;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 03:24:58