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

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等小问题。

修复步骤
  1. 删掉DropdownButton外层没用的Obx包裹,组件内部选中状态是State管理的普通变量,用setState刷新即可,不需要Obx。
  2. 修正onChanged逻辑:选中时先更新本地selectedValue值,调用setState触发UI刷新,再执行外部传入的回调函数。
  3. 删除CustomDropDown里冗余的PurchasePackageController实例,整个组件没有用到这个控制器。
  4. 修正视图层参数传错问题:第二个下拉框的列表应该传MDPackageName,显示选中值的Text组件外层包裹Obx,响应控制器里的obs值变化。
  5. 删除原代码里多余的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 13:21:21