import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kmobile/security/secure_storage.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import './l10n/app_localizations.dart'; import 'package:kmobile/features/auth/controllers/theme_cubit.dart'; import 'package:kmobile/features/auth/controllers/theme_state.dart'; import 'config/routes.dart'; import 'di/injection.dart'; import 'features/auth/controllers/auth_cubit.dart'; import 'features/card/screens/card_management_screen.dart'; import 'features/auth/screens/welcome_screen.dart'; import 'features/auth/screens/login_screen.dart'; import 'features/service/screens/service_screen.dart'; import 'features/dashboard/screens/dashboard_screen.dart'; import 'features/auth/screens/mpin_screen.dart'; import 'package:local_auth/local_auth.dart'; import 'package:shared_preferences/shared_preferences.dart'; class KMobile extends StatefulWidget { const KMobile({super.key}); @override State createState() => _KMobileState(); static void setLocale(BuildContext context, Locale newLocale) { final _KMobileState? state = context.findAncestorStateOfType<_KMobileState>(); state?.setLocale(newLocale); } } class _KMobileState extends State { bool showSplash = true; Locale? _locale; @override void initState() { super.initState(); loadPreferences(); Future.delayed(const Duration(seconds: 2), () { setState(() { showSplash = false; }); }); } Future loadPreferences() async { final prefs = await SharedPreferences.getInstance(); // Load Locale final String? langCode = prefs.getString('locale'); if (langCode != null) { setState(() { _locale = Locale(langCode); }); } } void setLocale(Locale locale) { setState(() { _locale = locale; }); } @override Widget build(BuildContext context) { // Set status bar color and brightness SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.dark, ), ); return MultiBlocProvider( providers: [ BlocProvider(create: (_) => getIt()), BlocProvider(create: (_) => ThemeCubit()), ], child: BlocBuilder( builder: (context, themeState) { return MaterialApp( debugShowCheckedModeBanner: false, locale: _locale ?? const Locale('en'), supportedLocales: const [ Locale('en'), Locale('hi'), ], localizationsDelegates: const [ AppLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], title: 'kMobile', theme: themeState.getThemeData(), //darkTheme: themeState.getThemeData(), themeMode: ThemeMode.system, onGenerateRoute: AppRoutes.generateRoute, initialRoute: AppRoutes.splash, home: showSplash ? const SplashScreen() : const AuthGate(), ); }, ), ); } } class AuthGate extends StatefulWidget { const AuthGate({super.key}); @override State createState() => _AuthGateState(); } class _AuthGateState extends State { bool _checking = true; bool _isLoggedIn = false; bool _showWelcome = true; bool _hasMPin = false; bool _biometricEnabled = false; bool _biometricTried = false; @override void initState() { super.initState(); _checkAuth(); } Future _checkAuth() async { final storage = getIt(); final accessToken = await storage.read('access_token'); final accessTokenExpiry = await storage.read('token_expiry'); final mpin = await storage.read('mpin'); final biometric = await storage.read('biometric_enabled'); setState(() { _isLoggedIn = accessToken != null && accessTokenExpiry != null && DateTime.parse(accessTokenExpiry).isAfter(DateTime.now()); _hasMPin = mpin != null; _biometricEnabled = biometric == 'true'; _checking = false; }); } Future _tryBiometric() async { if (_biometricTried) return false; _biometricTried = true; final localAuth = LocalAuthentication(); final canCheck = await localAuth.canCheckBiometrics; if (!canCheck) return false; String localizedReason = ""; if (mounted) { localizedReason = AppLocalizations.of(context).authenticateToAccess; } try { final didAuth = await localAuth.authenticate( localizedReason: localizedReason, options: const AuthenticationOptions( stickyAuth: true, biometricOnly: true, ), ); return didAuth; } catch (_) { return false; } } @override Widget build(BuildContext context) { if (_checking) { return const SplashScreen(); } // ✅ Step 1: Show welcome screen first, only once if (_showWelcome) { return WelcomeScreen( onContinue: () { setState(() { _showWelcome = false; }); }, ); } // ✅ Step 2: Check login status if (_isLoggedIn) { if (_hasMPin) { if (_biometricEnabled) { return FutureBuilder( future: _tryBiometric(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const SplashScreen(); } if (snapshot.data == true) { return const NavigationScaffold(); // Authenticated } // ❌ Biometric failed → Show MPIN screen return MPinScreen( mode: MPinMode.enter, onCompleted: (_) { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => const NavigationScaffold(), ), ); }, ); }, ); } else { return MPinScreen( mode: MPinMode.enter, onCompleted: (_) { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => const NavigationScaffold(), ), ); }, ); } } else { // No MPIN set → show MPIN set screen + biometric dialog return MPinScreen( mode: MPinMode.set, onCompleted: (_) async { final storage = getIt(); final localAuth = LocalAuthentication(); final optIn = await showDialog( context: context, barrierDismissible: false, builder: (ctx) => AlertDialog( title: Text(AppLocalizations.of(context).enableFingerprintLogin), content: Text(AppLocalizations.of(context).enableFingerprintMessage), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(false), child: Text(AppLocalizations.of(context).no), ), TextButton( onPressed: () => Navigator.of(ctx).pop(true), child: Text(AppLocalizations.of(context).yes), ), ], ), ); if (optIn == true) { final canCheck = await localAuth.canCheckBiometrics; bool didAuth = false; String authEnable = ""; if (context.mounted) { authEnable = AppLocalizations.of(context).authenticateToEnable; } if (canCheck) { didAuth = await localAuth.authenticate( localizedReason: authEnable, options: const AuthenticationOptions( stickyAuth: true, biometricOnly: true, ), ); await storage.write( 'biometric_enabled', didAuth ? 'true' : 'false'); } else { await storage.write('biometric_enabled', 'false'); } } if (context.mounted) { Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (_) => const NavigationScaffold(), ), ); } }, ); } } // ✅ Step 3: If not logged in, show login screen return const LoginScreen(); } } class NavigationScaffold extends StatefulWidget { const NavigationScaffold({super.key}); @override State createState() => _NavigationScaffoldState(); } class _NavigationScaffoldState extends State { final PageController _pageController = PageController(); int _selectedIndex = 0; final List _pages = [ const DashboardScreen(), const CardManagementScreen(), const ServiceScreen(), ]; @override Widget build(BuildContext context) { return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (!didPop) { final shouldExit = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(AppLocalizations.of(context).exitApp), content: Text(AppLocalizations.of(context).exitConfirmation), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text(AppLocalizations.of(context).no), ), TextButton( onPressed: () => Navigator.of(context).pop(true), child: Text(AppLocalizations.of(context).yes), ), ], ), ); if (shouldExit == true) { if (Platform.isAndroid) { SystemNavigator.pop(); } exit(0); } } }, child: Scaffold( body: PageView( controller: _pageController, physics: const NeverScrollableScrollPhysics(), children: _pages, ), bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, type: BottomNavigationBarType.fixed, backgroundColor: Theme.of(context) .scaffoldBackgroundColor, // Light blue background selectedItemColor: Theme.of(context).primaryColor, unselectedItemColor: Colors.black54, onTap: (index) { setState(() { _selectedIndex = index; }); _pageController.jumpToPage(index); }, items: [ BottomNavigationBarItem( icon: const Icon(Icons.home_filled), label: AppLocalizations.of(context).home, ), BottomNavigationBarItem( icon: const Icon(Icons.credit_card), label: AppLocalizations.of(context).card, ), BottomNavigationBarItem( icon: const Icon(Icons.miscellaneous_services), label: AppLocalizations.of(context).services, ), ], ), ), ); } } class SplashScreen extends StatelessWidget { const SplashScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const CircularProgressIndicator(), const SizedBox(height: 20), Text( AppLocalizations.of(context).loading, style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), ); } } // Add this widget at the end of the file class BiometricPromptScreen extends StatelessWidget { final VoidCallback onCompleted; const BiometricPromptScreen({super.key, required this.onCompleted}); Future _handleBiometric(BuildContext context) async { final localAuth = LocalAuthentication(); final canCheck = await localAuth.canCheckBiometrics; if (!canCheck) { onCompleted(); return; } String localizedReason = ""; if (context.mounted) { localizedReason = AppLocalizations.of(context).enableFingerprintQuick; } final didAuth = await localAuth.authenticate( localizedReason: localizedReason, options: const AuthenticationOptions( stickyAuth: true, biometricOnly: true, ), ); final storage = getIt(); if (didAuth) { await storage.write('biometric_enabled', 'true'); } else { await storage.write('biometric_enabled', 'false'); } onCompleted(); } @override Widget build(BuildContext context) { Future.microtask(() => _showDialog(context)); return const SplashScreen(); } Future _showDialog(BuildContext context) async { final result = await showDialog( context: context, barrierDismissible: true, builder: (ctx) => AlertDialog( title: Text(AppLocalizations.of(context).enableFingerprintLogin), content: Text(AppLocalizations.of(context).enableFingerprintMessage), actions: [ TextButton( onPressed: () { Navigator.of(ctx).pop(false); }, child: Text(AppLocalizations.of(context).no), ), TextButton( onPressed: () { Navigator.of(ctx).pop(true); }, child: Text(AppLocalizations.of(context).yes), ), ], ), ); if (result == true) { await _handleBiometric(context); } else { final storage = getIt(); await storage.write('biometric_enabled', 'false'); onCompleted(); } } }