Flutter(GetX)应用启动仅显示Scaffold背景,点击才加载内容求助
问题描述
开发基于GetX状态管理的Flutter应用时,启动后仅显示Scaffold背景色(已改为蓝色确认),需点击屏幕才会加载并显示body内的内容,希望无需点击直接正常加载。
main函数代码
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:semis/constants/pallete.dart'; import 'package:semis/features/auth/screens/login_screen.dart'; import 'package:semis/features/child/screens/child_dashboard.dart'; import 'package:semis/features/school/screens/parent_school_dashboard.dart'; import 'package:semis/features/school/screens/select_school.dart'; import 'package:semis/features/school/screens/teacher_school_dashboard.dart'; import 'package:semis/splash_screen.dart'; import 'package:shared_preferences/shared_preferences.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); SharedPreferences prefs = await SharedPreferences.getInstance(); final isLoggedIn = prefs.getBool('isLoggedIn'); if (isLoggedIn == true) { runApp(const MyApp(isLoggedIn: true)); } else { runApp(const MyApp()); } } class MyApp extends StatelessWidget { final bool isLoggedIn; const MyApp({Key? key, this.isLoggedIn = false}) : super(key: key); @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( statusBarColor: Pallete.blueColor, statusBarIconBrightness: Brightness.light, )); return GetMaterialApp( theme: ThemeData( primarySwatch: Colors.blue, primaryColor: Pallete.blueColor, ), debugShowCheckedModeBanner: false, initialRoute: isLoggedIn ? '/splash' : '/login', getPages: [ GetPage(name: '/login', page: () => const LoginScreen()), GetPage(name: '/splash', page: () => const SplashScreen()), GetPage(name: '/select-school', page: () => const SelectSchool()), GetPage( name: '/parent-school-dashboard', page: () => const ParentSchoolDashboard()), GetPage( name: '/teacher-school-dashboard', page: () => const TeacherSchoolDashboard()), GetPage(name: '/child-dashboard', page: () => const ChildDashboard()), ], ); } }
LoginScreen代码
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:lottie/lottie.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:semis/constants/animated_loader.dart'; import 'package:semis/constants/pallete.dart'; import 'package:semis/features/auth/controllers/login_controller.dart'; import 'package:semis/features/school/screens/select_school.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State<LoginScreen> createState() => _LoginScreenState(); } class _LoginScreenState extends State<LoginScreen> { final LoginController _authController = Get.put(LoginController()); @override Widget build(BuildContext context) { return GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, child: Scaffold( backgroundColor: Pallete.blueColor, body: Obx( () => _authController.isLoading.value ? const AnimatedLoader() : SingleChildScrollView( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( width: Get.width, height: Get.height * 0.5, child: Center( child: Padding( padding: const EdgeInsets.only(top: 10), child: SizedBox( height: 350, width: 350, child: Lottie.asset( 'assets/animations/login.json', repeat: true, reverse: true, ), ), ), ), ), Container( height: Get.height * 0.5, width: Get.width, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(40), topRight: Radius.circular(40), ), ), child: Form( key: _formKey, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 30.0), child: Column( children: [ const SizedBox( height: 30, ), Text( "SEMIS - School Education Management Information System", textAlign: TextAlign.center, style: GoogleFonts.roboto( fontSize: 23, fontWeight: FontWeight.w700, color: Pallete.blueColor, ), ), const SizedBox( height: 10, ), Opacity( opacity: 0.6, child: Text( "semis Serves You Virtual Education At Your Home", textAlign: TextAlign.center, style: GoogleFonts.roboto( fontSize: 18, color: Pallete.blackColor, wordSpacing: 3, fontWeight: FontWeight.w400, ), ), ), const SizedBox( height: 50, ), TextFormField( controller: _cnicController, // CNIC Input Field decoration: const InputDecoration( hintText: "CNIC", prefixIcon: Icon(Icons.credit_card), border: OutlineInputBorder(), ), validator: (value) { if (value!.isEmpty) { return 'Please enter your CNIC'; } return null; }, ), const SizedBox( height: 10, ), ElevatedButton( onPressed: () => _handleLogin(), style: ElevatedButton.styleFrom( backgroundColor: Pallete.blueColor, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10)), ), minimumSize: Size(Get.width, 50), ), child: const Text( "Log In", style: TextStyle( fontSize: 20, fontWeight: FontWeight.w400, wordSpacing: 2, ), ), ), ], ), ), ), ), ], ), ), ), ), ); } void _handleLogin() async { try { _authController.isLoading.value = true; // Set loading state to true if (_formKey.currentState!.validate()) { // Form is valid, proceed with login final cnic = _cnicController.text; final schools = await _authController.cnicLogin(cnic); if (schools != null) { // Check if there are users associated with the CNIC if (schools.schoolCount > 0) { _saveLogin(cnic); Get.off( () => const SelectSchool(), arguments: {'schools': schools, 'cnic': cnic}, ); } else { // If no users found, display a message to the user Get.snackbar( "No Users Found", "No users are associated with this CNIC."); } } } } finally { _authController.isLoading.value = false; } } void _saveLogin(String cnic) async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString('cnic', cnic); await prefs.setBool('isLoggedIn', true); } final TextEditingController _cnicController = TextEditingController(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); }
解决方案
1. 修正LoginController的isLoading初始值
确保LoginController中的isLoading明确初始化为false.obs,因为Obx依赖响应式变量的变化触发UI更新,未正确初始化可能导致初始UI不渲染:
class LoginController extends GetxController { final RxBool isLoading = false.obs; // ...其他业务代码 }
2. 调整GestureDetector的位置
当前GestureDetector包裹整个Scaffold,其onTap逻辑可能干扰初始UI渲染。将其移到SingleChildScrollView外层,仅处理输入框失焦逻辑:
@override Widget build(BuildContext context) { return Scaffold( backgroundColor: Pallete.blueColor, body: Obx( () => _authController.isLoading.value ? const AnimatedLoader() : GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: SingleChildScrollView( // ...原有内容 ), ), ), ); }
3. 强制触发初始UI更新
如果上述方法无效,可在LoginScreen的initState中手动触发一次状态更新,确保Obx检测到变量变化:
@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { // 手动触发一次状态刷新 _authController.isLoading.value = _authController.isLoading.value; }); }
4. 排查Lottie动画加载问题
Lottie动画加载失败或耗时可能导致初始空白,添加错误监听确认是否为此原因:
Lottie.asset( 'assets/animations/login.json', repeat: true, reverse: true, errorBuilder: (context, error, stackTrace) { return const Icon(Icons.error_outline, size: 60); }, )
内容的提问来源于stack exchange,提问作者Ahmed Khan
相关产品推荐
相关产品推荐

