Flutter DropdownButton异常:显示国家名称绑定国家代码时出错
我希望Dropdown组件显示国家名称,但选中后获取对应国家代码。编写了CountryDropdown组件,初始_selectedCountry设为"Austria",但当DropdownMenuItem的value设为国家代码(如"au")时出现错误;若将value设为"Austria",或显示文本设为"au"而value为"Austria"则正常运行,请问这是什么原因?
错误截图

代码示例
class CountryDropdown extends StatefulWidget { const CountryDropdown({Key? key}) : super(key: key); @override _CountryDropdownState createState() => _CountryDropdownState(); } class _CountryDropdownState extends State<CountryDropdown> { String _selectedCountry = "Austria"; List<DropdownMenuItem<String>> get dropdownItems { List<DropdownMenuItem<String>> menuItems = [ const DropdownMenuItem(child: Text("Austria"), value: "au"), const DropdownMenuItem(child: Text("Belgium"), value: "be"), const DropdownMenuItem(child: Text("Croatia"), value: "hr"), ]; return menuItems; } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(10.0), child: DropdownButton<String>( value: _selectedCountry, hint: const Text('Select a country'), onChanged: (newValue) { setState(() { _selectedCountry = newValue!; }); }, items: dropdownItems, ), ); } }
Flutter的DropdownButton有严格的匹配规则:初始设置的value必须存在于所有DropdownMenuItem的value集合中。你这里初始_selectedCountry设为"Austria",但所有DropdownMenuItem的value都是国家代码("au"、"be"、"hr"),"Austria"不在这个集合里,因此触发断言错误:There should be exactly one item with [DropdownButton]'s value: Austria.
当你把value设为"Austria",或者把显示文本换成代码、value用国家名称时,初始值和MenuItem的value完全匹配,所以不会报错。
要实现“显示名称,选中后获取代码”的需求,有两种实用方案:
方案1:统一用国家代码作为value,调整初始值
直接把初始_selectedCountry设为对应国家的代码,保持MenuItem的child显示名称:
class _CountryDropdownState extends State<CountryDropdown> { // 初始值改为奥地利的国家代码"au" String _selectedCountry = "au"; List<DropdownMenuItem<String>> get dropdownItems { List<DropdownMenuItem<String>> menuItems = [ const DropdownMenuItem(child: Text("Austria"), value: "au"), const DropdownMenuItem(child: Text("Belgium"), value: "be"), const DropdownMenuItem(child: Text("Croatia"), value: "hr"), ]; return menuItems; } // 其余代码保持不变 }
这种方式简单直接,初始值和MenuItem的value匹配,选中后_selectedCountry就是国家代码,显示的是名称,完全符合需求。
方案2:自定义数据类绑定名称和代码
如果需要同时处理名称和代码的关联逻辑,可以创建一个数据类来封装两者:
// 定义国家数据类,绑定名称和代码 class Country { final String name; final String code; Country(this.name, this.code); }
然后修改Dropdown的泛型为Country:
class _CountryDropdownState extends State<CountryDropdown> { // 初始选中奥地利的Country对象 Country _selectedCountry = Country("Austria", "au"); List<DropdownMenuItem<Country>> get dropdownItems { List<DropdownMenuItem<Country>> menuItems = [ DropdownMenuItem(child: Text("Austria"), value: Country("Austria", "au")), DropdownMenuItem(child: Text("Belgium"), value: Country("Belgium", "be")), DropdownMenuItem(child: Text("Croatia"), value: Country("Croatia", "hr")), ]; return menuItems; } @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(10.0), child: DropdownButton<Country>( value: _selectedCountry, hint: const Text('Select a country'), // 自定义选中项的显示内容 selectedItemBuilder: (context) { return dropdownItems.map((item) => Text(item.value!.name)).toList(); }, onChanged: (newValue) { setState(() { _selectedCountry = newValue!; // 选中后可直接通过_selectedCountry.code获取国家代码 print("选中的国家代码:${_selectedCountry.code}"); }); }, items: dropdownItems, ), ); } }
这种方式扩展性更强,适合需要同时使用名称和代码的复杂场景。
内容的提问来源于stack exchange,提问作者Lee Casey

