import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kmobile/features/auth/controllers/theme_mode_cubit.dart'; import 'package:kmobile/features/auth/controllers/theme_mode_state.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/splash_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: 3), () { 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) { SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.transparent, statusBarIconBrightness: Brightness.dark, ), ); return MultiBlocProvider( providers: [ BlocProvider(create: (_) => getIt()), BlocProvider(create: (_) => ThemeCubit()), BlocProvider(create: (_) => ThemeModeCubit()), ], // 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.light, // onGenerateRoute: AppRoutes.generateRoute, // initialRoute: AppRoutes.splash, // home: showSplash ? const SplashScreen() : const AuthGate(), // ); // }, // ), child: BlocBuilder( builder: (context, themeState) { return BlocBuilder( builder: (context, modeState) { 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.getLightThemeData(), darkTheme: themeState.getDarkThemeData(), themeMode: context.watch().state.mode, 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 _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(); } 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 } 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 { 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(), ), ); } }, ); } } 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, 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, ), ], ), ), ); } } // 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 (!context.mounted) { return; } if (result == true) { await _handleBiometric(context); } else { final storage = getIt(); await storage.write('biometric_enabled', 'false'); onCompleted(); } } }