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
相关产品推荐
相关产品推荐

