Flutter GetX封装自定义DropDownButton时Obx报错、移除后无法选值
问题根因
- Obx报错是因为包裹的
DropdownButton内部没有读取任何.obs响应式变量,GetX检测到Obx作用域内没有绑定可观察对象就会抛出使用不当错误。代码里selectedValue是StatefulWidget的普通State属性,不是GetX响应式变量,无法触发Obx更新检测。 - 移除Obx后无法选中选项是两个低级错误导致:第一
onChanged回调写法错误,onChanged: (value) => widget.onChanged只是返回了函数引用,根本没有实际执行回调;第二选中值后没有调用setState刷新组件,DropdownButton的value属性一直是初始null,自然无法显示选中结果。 - 原代码还存在冗余实例、语法笔误、参数传错、响应式文本未包裹Obx等小问题。
修复步骤
- 删掉
DropdownButton外层没用的Obx包裹,组件内部选中状态是State管理的普通变量,用setState刷新即可,不需要Obx。 - 修正
onChanged逻辑:选中时先更新本地selectedValue值,调用setState触发UI刷新,再执行外部传入的回调函数。 - 删除
CustomDropDown里冗余的PurchasePackageController实例,整个组件没有用到这个控制器。 - 修正视图层参数传错问题:第二个下拉框的列表应该传
MDPackageName,显示选中值的Text组件外层包裹Obx,响应控制器里的obs值变化。 - 删除原代码里多余的
buildDropdownMenuItems()笔误代码。
修复后完整代码
自定义DropDown组件
class CustomDropDown extends StatefulWidget { final String hintName; final List<String> listName; final void Function(String?)? onChanged; CustomDropDown( {Key? key, required this.hintName, required this.listName, required this.onChanged}) : super(key: key); @override State<CustomDropDown> createState() => _CustomDropDownState(); } class _CustomDropDownState extends State<CustomDropDown> { String? selectedValue; List<DropdownMenuItem<String>> buildDropdownMenuItems(List list) { List<DropdownMenuItem<String>> dropDownItems = []; list.forEach((value) { dropDownItems.add(DropdownMenuItem<String>( value: value, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Text( value, style: TextStyle(fontSize: 16.0, color: colorPrimary), ), ), )); }); return dropDownItems; } @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.symmetric(horizontal: 10.0.h), child: InputDecorator( decoration: InputDecoration( enabledBorder: UnderlineInputBorder( borderSide: BorderSide(color: colorPrimary), ), ), isEmpty: selectedValue == null, child: DropdownButtonHideUnderline( child: DropdownButton<String>( iconSize: 30.0.sp, iconEnabledColor: colorPrimary, hint: Padding( padding: EdgeInsets.symmetric(horizontal: 12.0.h), child: Text( widget.hintName, style: TextStyle(fontSize: 18.0.sp, color: colorPrimary), ), ), value: selectedValue, isDense: true, onChanged: (value) { setState(() { selectedValue = value; }); if (widget.onChanged != null) { widget.onChanged!(value); } }, items: buildDropdownMenuItems(widget.listName), ), ), ), ); } }
视图层代码
class LocationView extends GetView<LocationController> { LocationView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Location'), centerTitle: true, ), body: Column( children: [ CustomDropDown( hintName: "Package Type", listName: controller.MDPackageType, onChanged: (newValue) => controller.onChangedPackageType(newValue!), ), CustomDropDown( hintName: "Package Name", listName: controller.MDPackageName, onChanged: (newValue) => controller.onChangedPackageName(newValue!)), Container( height: MediaQuery.of(context).size.height * 0.50, width: MediaQuery.of(context).size.width * 0.95, child: Column(children: [ Obx(() => Text(controller.selectedPackageName.value)), Obx(() => Text(controller.selectedPackageType.value)) ]), ) ], )); } }
控制器层代码
原控制器代码无需修改,保持原有逻辑即可。
注意事项
- Obx只能包裹内部实际读取了
.obs响应式变量的组件,不要随意给不需要响应式更新的组件套Obx,否则必然触发使用不当报错。 - StatefulWidget内部的普通状态更新必须调用
setState,不要和GetX响应式状态的更新逻辑混淆。
内容的提问来源于stack exchange,提问作者Nishu
相关产品推荐
相关产品推荐

