GetXController继承失效求助:Flutter新手遇控制器继承异常
问题描述
我是Flutter新手,我有一个名为LoginController的控制器,它继承自BaseController,但运行应用时出现报错。我原本以为调用Get.put(LoginController())会因为继承关系自动初始化BaseController,但实际并非如此。
相关代码如下:
BasePage代码
class BasePage extends StatelessWidget { var baseController = Get.find<BaseController>(); BasePage({Key? key}) : super(key: key); late Widget child; @override Widget build(BuildContext context) { return Obx(() { return baseController.IsNetworkAvailable.value?child:const NoInternetPage(); }); } Widget GetWidget() { if (baseController.IsNetworkAvailable.value) { return child; } else { return const NoInternetPage(); } } }
LoginPage代码片段
class LoginPage extends BasePage { LoginPage({Key? key}) : super(key: key) { loginController =Get.put(LoginController()); child=RenderView(); } late LoginController loginController; TextStyle GetHeadingStyle() { return const TextStyle( color: Colors.grey, fontSize: 20 ); } Widget RenderView() { return Scaffold( body: Padding( padding: const EdgeInsets.fromLTRB(30,0,30,0), child: Obx((){ return loginController.IsBusy.value? const Center( child: SizedBox( height: 60, width: 60, child: CircularProgressIndicator( strokeWidth: 5.0, valueColor: AlwaysStoppedAnimation<Color>(Colors.blue), ), ), ): Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ TextField( controller: loginController.domainController, decoration: const InputDecoration(labelText: 'Domain', border: OutlineInputBorder()) ), SizedBox(height: 20), TextField( controller: loginController.usernameController, decoration: const InputDecoration(labelText: 'Username', border: OutlineInputBorder()) ), const SizedBox(height: 20), TextField( controller: loginController.passwordController, obscureText: true, decoration: const InputDecoration(labelText: 'Password', border: OutlineInputBorder()) ), const SizedBox(height: 20), SizedBox( width: 150, height: 40, child: ElevatedButton(onPressed: ()=>{ loginController.doLogin(LoginSuccess,LoginFailed) }, child:const Text('Login' ,style: TextStyle()), ), ), ], ); }) ), ); } void LoginSuccess() { loginController.IsBusy.value = false; Get.snackbar("Success", "Login Success",backgroundColor: Colors.green,colorText: Colors.white); var storage = GetStorage(); storage.write("IsLoggedIn", true); Get.off(MainPage()); } void LoginFailed() { loginController.IsBusy.value=false; Get.snackbar("Failed", "Login Failed",backgroundColor: Colors.red,colorText: Colors.white); } }
Login Controller代码
class LoginController extends BaseController { TextEditingController domainController = TextEditingController(); TextEditingController usernameController = TextEditingController(); TextEditingController passwordController = TextEditingController(); LoginController() {} void doLogin(Function loginSuccess, Function loginFailed) async { if (await checkConnection()) { //await RestService().PostRequest(Resources.LOGIN_URL,loginData , loginSuccess, loginFailed); } } }
Base Controller代码
class BaseController extends GetxController { var IsBusy = false.obs; var IsNetworkAvailable = false.obs; var IsServerDown = false.obs; BaseController() {} Future<bool> checkConnection( {bool setIsBusy = true, bool setNetworkAvailable = true}) async { var result = await ConnectivityService.checkConnection(); bool isConnected = result != ConnectivityResult.none; if (setIsBusy) { IsBusy.value = isConnected; } if (setNetworkAvailable) { IsNetworkAvailable.value = isConnected; } return isConnected; } }
报错原因分析
- GetX类型匹配规则限制:GetX的
Get.find<T>()是严格按类型查找实例的。你注入了子类LoginController,但父类BaseController并未被注册到GetX依赖容器,因此Get.find<BaseController>()会找不到对应实例,抛出控制器未找到的错误。 - 初始化顺序错误:
LoginPage继承BasePage时,会先执行父类BasePage的构造方法,此时BasePage中的Get.find<BaseController>()已执行,但LoginPage里的Get.put(LoginController())要等父类构造完成后才运行,导致查找操作早于注入操作,必然报错。
解决方案
方案一:调整初始化顺序并注册父类类型
通过初始化列表先完成控制器注入,同时指定注册类型为父类BaseController,让BasePage能正常查找实例:
class LoginPage extends BasePage { late LoginController loginController; // 初始化列表中先注入控制器,再调用父类构造 LoginPage({Key? key}) : loginController = Get.put<BaseController>(LoginController()) as LoginController, super(key: key) { child = RenderView(); } // 其余代码保持不变 }
方案二:构造参数传递控制器(解耦更优)
让BasePage通过构造参数接收BaseController实例,避免直接在父类中使用Get.find,降低耦合:
修改BasePage
class BasePage extends StatelessWidget { final BaseController baseController; late Widget child; // 通过构造参数接收控制器 BasePage({required this.baseController, Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Obx(() { return baseController.IsNetworkAvailable.value ? child : const NoInternetPage(); }); } Widget GetWidget() { if (baseController.IsNetworkAvailable.value) { return child; } else { return const NoInternetPage(); } } }
修改LoginPage构造
class LoginPage extends BasePage { late LoginController loginController; LoginPage({Key? key}) : loginController = Get.put(LoginController()), // 将子类实例作为父类类型传递给父类 super(baseController: Get.find<LoginController>(), key: key) { child = RenderView(); } // 其余代码保持不变 }
额外优化建议
- 遵循Dart命名规范:变量名改为小驼峰格式,比如
IsBusy改为isBusy,IsNetworkAvailable改为isNetworkAvailable。 - 自动监听网络状态:在
BaseController的onInit方法中初始化网络监听,无需手动调用更新:
class BaseController extends GetxController { var isBusy = false.obs; var isNetworkAvailable = false.obs; var isServerDown = false.obs; @override void onInit() { super.onInit(); _initNetworkListener(); } Future<void> _initNetworkListener() async { // 初始检查网络状态 var initialResult = await Connectivity().checkConnectivity(); isNetworkAvailable.value = initialResult != ConnectivityResult.none; // 监听网络状态变化 Connectivity().onConnectivityChanged.listen((result) { isNetworkAvailable.value = result != ConnectivityResult.none; }); } // 其余代码保持不变 }
内容的提问来源于stack exchange,提问作者Dinesh Falwadiya
相关产品推荐
相关产品推荐

