import 'package:flutter/material.dart'; import 'package:miler/views/helpers/constants/Colorconstants.dart'; import 'package:get/get.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:miler/controllers/auth.dart'; import 'package:miler/views/introscreens/introscreen.dart'; import 'package:miler/views/onboardscreens/Mpin.dart'; import 'package:miler/views/onboardscreens/Sign_in.dart'; import 'package:miler/widget/Bottom_page.dart'; /// Decides which screen the app opens on, and nothing else. /// /// This replaces the old animated splash screen. The splash held two very /// different jobs: a four-second brand animation, and the routing rules that /// decide login vs. intro vs. home. Only the second one is load-bearing. /// /// The four seconds were pure cost for a rider who opens this app 30–50 times /// a shift — the native launch image already covers the cold-start gap, so the /// animation was time added on top of it, every single time. Removing it is /// the single biggest latency win on the whole flow. /// /// What survives is the routing, unchanged in behaviour: /// • app version changed since last run → force re-verification (MPIN if the /// rider was signed in, otherwise Sign In) /// • signed in + on duty → straight to the dashboard /// • signed in + off duty → the go-on-duty banner /// • signed out, intro already seen → Sign In /// • first ever launch → Introscreen class AppBootstrap extends StatefulWidget { const AppBootstrap({super.key}); @override State createState() => _AppBootstrapState(); } class _AppBootstrapState extends State { bool _routed = false; @override void initState() { super.initState(); // Route on the first frame — GetX needs a built navigator before `offAll`. WidgetsBinding.instance.addPostFrameCallback((_) => _resolveStartScreen()); } Future _resolveStartScreen() async { if (_routed) return; _routed = true; final prefs = await SharedPreferences.getInstance(); final bool isLoggedOut = prefs.getBool('logged_out') ?? false; final bool hasSeenIntro = prefs.getBool('has_seen_intro') ?? false; final int? savedUserId = prefs.getInt('userid'); // ── App update → force re-verification ── try { final packageInfo = await PackageInfo.fromPlatform(); final currentVersion = packageInfo.version; final lastRunVersion = prefs.getString('last_run_version'); if (lastRunVersion != null && lastRunVersion != currentVersion) { debugPrint( '[BOOTSTRAP] App update detected: $lastRunVersion -> $currentVersion. ' 'Forcing re-verification.', ); final String? savedPhone = prefs.getString('contactno'); await prefs.setString('last_run_version', currentVersion); if (savedUserId != null && savedPhone != null && savedPhone.isNotEmpty) { debugPrint( '[BOOTSTRAP] User was logged in. Redirecting to MPIN page.', ); final auth = Get.put(AuthController()); auth.currentPhone = savedPhone; await prefs.remove('userid'); await prefs.remove('partnerid'); await prefs.remove('onduty'); await prefs.setBool('logged_out', true); if (mounted) Get.offAll(() => Mpin()); return; } debugPrint( '[BOOTSTRAP] User was not logged in. Redirecting to Sign In.', ); if (mounted) Get.offAll(() => const SignIn()); return; } await prefs.setString('last_run_version', currentVersion); } catch (e) { debugPrint('[BOOTSTRAP] Version check error: $e'); } if (!mounted) return; // ── Normal launch ── // // A signed-in rider lands on the tabs whether or not he is on duty. The // duty gate that used to sit here made "open the app" and "start working" // the same decision, so a rider checking his earnings between shifts had to // pass a slider that put him back on the clock. Duty is a switch in Home's // app bar, on the screen where the work is. if (!isLoggedOut && savedUserId != null && savedUserId > 0) { Get.offAll(() => const BottomPage()); } else { Get.offAll(() => hasSeenIntro ? const SignIn() : Introscreen()); } } @override Widget build(BuildContext context) { // Held for the few milliseconds the prefs read takes. Plain white so it is // indistinguishable from the native launch image — the rider should never // perceive a screen here at all. return Scaffold( backgroundColor: ColorConstants.pureSurface, body: SizedBox.expand(), ); } }