使用GetX状态管理:首屏Controller变量显示空白,后续页面正常
首屏GetX Controller变量空白问题排查与解决
问题现象
- 首屏调用GetX Controller中的屏幕宽高变量时,变量显示无效(空白/0值)
- 通过GetX路由跳转到后续页面后,Controller的变量能正常工作
- 用MediaQuery直接获取宽高时首屏显示正常,仅通过Controller获取时出问题
- 已确认Controller状态更新正常、数据可用
核心代码
主入口代码
void main() { runApp( const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return Listener( onPointerHover: (onPointerMove){ FocusManager.instance.primaryFocus?.unfocus(); }, child: GetMaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', getPages: AppRoutes.appRoutes(), initialBinding: InitialBindings(), initialRoute: RoutesName.launchScreen, ), ); } }
首屏LaunchScreen代码
class LaunchScreen extends StatefulWidget { const LaunchScreen({super.key}); @override State<LaunchScreen> createState() => _LaunchScreenState(); } class _LaunchScreenState extends State<LaunchScreen> { final controller = Get.put(LaunchController()); final AppThemeState _appTheme = AppThemeState(); @override Widget build(BuildContext context) { return Scaffold( body: Container( padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 25), height: controller.height, width: controller.width, decoration: const BoxDecoration( image: DecorationImage( image: AssetImage(ImageAssetsPath.launchBackImage), fit: BoxFit.fill), ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [ Text("Dhulayi.com".tr, style: _appTheme.customTextStyle( fontSize: 35, fontWeightType: _appTheme.boldWeightType)), SizedBox( height: controller.height * 0.02, ), Text( "lorem Ipsum is simply dummy text of the printing and typesetting industry.LoremIpsum has been the industry's standard dummy text ever since the 1500s", style: _appTheme.customTextStyle( fontSize: 12, fontWeightType: _appTheme.regularWeightType), textAlign: TextAlign.center, ), SizedBox( height: controller.height * .05, ), MaterialButton( onPressed: () { controller.navigateToMain(); }, height: 50, color: AppColors.redBackGroundColor, minWidth: controller.width, child: Text("Swipe Here", style: _appTheme.customTextStyle( color: AppColors.buttonTextColor, fontWeightType: _appTheme.boldWeightType, fontSize: 20)), ) ], ), ), ); } }
LaunchController代码
class LaunchController extends GetxController{ double height = Get.height; double width = Get.width; void navigateToMain(){ Get.offNamed(RoutesName.verifyView); } }
问题原因
Controller初始化时机过早:在GetMaterialApp尚未完成上下文注入时,就直接在Controller的成员变量中赋值Get.height/Get.width,此时GetX还未获取到正确的屏幕上下文信息,导致宽高变量为0或无效值。
- 首屏的Controller是在
State的成员变量中直接Get.put()初始化的,这个时机早于GetMaterialApp完成上下文初始化的时机 - 后续页面通过GetX路由跳转时,
GetMaterialApp已经完成初始化,上下文信息正常,所以Get.height/Get.width能正确获取
解决方案
方案1:在Controller的onInit生命周期中初始化宽高
将宽高的赋值移到onInit方法中,该方法会在Controller绑定到GetX上下文后执行:
class LaunchController extends GetxController{ late double height; late double width; @override void onInit() { super.onInit(); height = Get.height; width = Get.width; } void navigateToMain(){ Get.offNamed(RoutesName.verifyView); } }
方案2:通过InitialBindings提前绑定Controller,在build中获取
- 在
InitialBindings中提前绑定Controller:
class InitialBindings extends Bindings { @override void dependencies() { Get.put(LaunchController()); } }
- 在首屏的build方法中用
Get.find()获取Controller:
class _LaunchScreenState extends State<LaunchScreen> { late LaunchController controller; final AppThemeState _appTheme = AppThemeState(); @override Widget build(BuildContext context) { controller = Get.find<LaunchController>(); return Scaffold( // ... 原有UI代码 ); } }
方案3:使用响应式变量(Obx)自动更新
将宽高改为响应式变量,确保上下文就绪后自动更新UI:
- 修改Controller:
class LaunchController extends GetxController{ final RxDouble height = 0.0.obs; final RxDouble width = 0.0.obs; @override void onInit() { super.onInit(); height.value = Get.height; width.value = Get.width; } void navigateToMain(){ Get.offNamed(RoutesName.verifyView); } }
- 在UI中用
Obx包裹使用变量的组件:
Obx(() => Container( padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 25), height: controller.height.value, width: controller.width.value, decoration: const BoxDecoration( image: DecorationImage( image: AssetImage(ImageAssetsPath.launchBackImage), fit: BoxFit.fill), ), child: Column( // ... 原有子组件 ), ))
额外注意事项
- 首屏的Controller推荐通过
InitialBindings提前绑定,避免在页面中直接Get.put()导致初始化时机问题 - 不要在Controller的成员变量初始化阶段直接调用
Get.height/Get.width或其他依赖GetX上下文的方法,这类操作应放在onInit或onReady生命周期中
内容的提问来源于stack exchange,提问作者Pratham Patel
相关产品推荐
相关产品推荐

