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

Flutter中如何基于GetX实现支持任意控制器传入的自定义加载按钮

GetX 通用加载状态按钮实现方案

原有代码核心问题

  • 泛型无约束:泛型T未做类型边界限定,Dart静态分析无法识别T类型下存在isLoading字段,触发属性访问报错
  • 响应式监听失效:传入的参数是控制器实例本身,没有直接访问响应式的isLoading变量,Obx无法追踪状态变更;且加载/非加载分支返回的UI完全一致,没有对应加载态效果
  • 交互逻辑缺失:加载状态下没有禁用点击,容易触发重复请求,也没有加载指示器的UI反馈
  • 代码冗余:非断网场景下两个分支的按钮样式完全重复,没有做差异化处理

具体实现

第一步:定义统一控制器基类

给所有带加载状态的业务控制器抽离统一基类,做泛型类型约束,保证所有传入的控制器都存在规范的isLoading响应式字段:

// 加载状态控制器基类
abstract class BaseLoadController extends GetxController {
  // 统一的响应式加载标记
  final RxBool isLoading = false.obs;
}

// 业务控制器示例,继承基类即可
class UserController extends BaseLoadController {
  Future<void> fetchUser(UserRequest req) async {
    isLoading.value = true;
    try {
      // 此处写接口请求、数据处理逻辑
    } catch (e) {
      // 错误处理逻辑
    } finally {
      isLoading.value = false;
    }
  }
}
// DepartmentController、SubjectController 按照同样规则继承BaseLoadController即可

第二步:重构通用按钮组件

给泛型加上BaseLoadController边界约束,修正状态监听逻辑,补充加载态UI和防重复点击逻辑:

class BtnAction<T extends BaseLoadController> extends GetView<ConnectivityController> {
  final String text;
  final Future<void> Function() press;
  final Color bgColor;
  final Color textColor;
  final T bizController;

  const BtnAction({
    super.key,
    required this.text,
    required this.press,
    required this.bgColor,
    required this.bizController,
    required this.textColor,
  });

  @override
  Widget build(BuildContext context) {
    return Obx(() {
      // 无网络状态
      if (controller.connectionStatus.value.name == 'none') {
        return TextButton.icon(
          onPressed: null,
          style: TextButton.styleFrom(backgroundColor: Colors.blueGrey),
          label: Text(text, style: TextStyle(color: textColor)),
          icon: Icon(Icons.wifi_off_sharp, color: textColor),
        );
      }

      // 接口加载中状态
      if (bizController.isLoading.value) {
        return TextButton(
          onPressed: null,
          style: TextButton.styleFrom(
            splashFactory: NoSplash.splashFactory,
            backgroundColor: bgColor.withOpacity(0.7),
            foregroundColor: textColor,
            padding: EdgeInsets.zero,
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            mainAxisSize: MainAxisSize.min,
            children: [
              SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(
                  strokeWidth: 2,
                  color: textColor,
                ),
              ),
              const SizedBox(width: 8),
              const Text("加载中..."),
            ],
          ),
        );
      }

      // 正常可点击状态
      return TextButton(
        onPressed: () async {
          if (bizController.isLoading.value) return;
          await press();
        },
        style: TextButton.styleFrom(
          splashFactory: NoSplash.splashFactory,
          backgroundColor: bgColor,
          foregroundColor: textColor,
          padding: EdgeInsets.zero,
        ),
        child: Text(text),
      );
    });
  }
}

组件调用示例

直接传入对应业务控制器实例即可,组件会自动监听加载状态切换UI,不需要手动控制加载效果:

SizedBox(
  width: double.infinity,
  child: Padding(
    padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 15),
    child: BtnAction<UserController>(
      text: "Filter",
      textColor: Colors.white,
      bgColor: Colors.black,
      bizController: Get.find<UserController>(),
      press: () async {
        await Get.find<UserController>().fetchUser(UserRequest());
      },
    ),
  ),
)

注意:如果需要切换加载指示器样式、修改加载中文字,直接在组件里新增对应可选参数即可,所有继承BaseLoadController的控制器都可以直接传入该组件使用,不需要重复写加载状态判断逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 00:48:25