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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 00:38:11