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; }
问题原因
selectedValue是函数内的局部变量,调用notifyListeners()无法触发Dialog的UI更新——Dialog的上下文独立于ChangeNotifier监听的上下文,局部变量的变化也不会被ChangeNotifier追踪。- 错误使用
DropdownMenuItem的onTap回调更新选中值,这不是Dropdown组件的标准用法,选中状态应该通过DropdownButton的onChanged回调统一处理。
解决方案
方案一:将选中值设为MyAppState的成员变量
把selectedValue提升为类成员,让ChangeNotifier能追踪其变化并触发UI更新:
- 在MyAppState类中添加成员变量:
int selectedStorageValue = 0;
- 修改函数代码:
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
相关产品推荐
相关产品推荐

