129 lines
4.8 KiB
Dart
129 lines
4.8 KiB
Dart
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<AppBootstrap> createState() => _AppBootstrapState();
|
||
}
|
||
|
||
class _AppBootstrapState extends State<AppBootstrap> {
|
||
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<void> _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(),
|
||
);
|
||
}
|
||
}
|