This commit is contained in:
2025-08-06 17:26:25 +05:30
parent 2fdef7c850
commit c4d4261afc
33 changed files with 772 additions and 935 deletions

View File

@@ -6,6 +6,10 @@ import 'package:kmobile/security/secure_storage.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import './l10n/app_localizations.dart';
import 'config/themes.dart';
import 'config/theme_controller.dart';
import 'config/theme_mode_controller.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';
@@ -17,7 +21,6 @@ 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';
//import 'package:flutter_neumorphic/flutter_neumorphic.dart';
class KMobile extends StatefulWidget {
const KMobile({super.key});
@@ -32,42 +35,43 @@ class KMobile extends StatefulWidget {
}
class _KMobileState extends State<KMobile> {
bool _showSplash = false;
Locale? _locale;
bool showSplash = true;
Locale? _locale;
@override
void initState() {
super.initState();
_loadLocale();
// Simulate a splash screen delay
loadPreferences();
Future.delayed(const Duration(seconds: 2), () {
setState(() {
_showSplash = false;
showSplash = false;
});
});
}
Future<void> _loadLocale() async{
final prefs = await SharedPreferences.getInstance();
Future<void> 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
/* @override
Widget build(BuildContext context) {
// Set status bar color
// Set status bar color
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
@@ -75,8 +79,12 @@ class _KMobileState extends State<KMobile> {
),
);
if (_showSplash) {
return MaterialApp(
return MultiBlocProvider(
providers: [
BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()),
BlocProvider<ThemeCubit>(create: (_) => getIt<ThemeCubit>()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
locale: _locale,
supportedLocales: const [
@@ -89,69 +97,67 @@ class _KMobileState extends State<KMobile> {
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
home: const SplashScreen(),
);
}
return MultiBlocProvider(
providers: [
BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()),
],
child: MaterialApp(
title: 'kMobile',
// debugShowCheckedModeBanner: false,
theme: AppThemes.lightTheme,
// darkTheme: AppThemes.darkTheme,
themeMode: ThemeMode.system, // Use system theme by default
// Dynamic Theme and ThemeMode
theme: AppThemes.getLightTheme(_themeController.currentTheme),
darkTheme: AppThemes.getDarkTheme(_themeController.currentTheme),
themeMode: _themeModeController.currentThemeMode,
onGenerateRoute: AppRoutes.generateRoute,
initialRoute: AppRoutes.splash,
home: const AuthGate(),
home: showSplash ? const SplashScreen() : const AuthGate(),
),
);
}
*/
@override
}*/
@override
Widget build(BuildContext context) {
// Set status bar color
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
);
// Set status bar color and brightness
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
),
);
return MultiBlocProvider(
providers: [
BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
locale: _locale, // Dynamic locale
supportedLocales: const [
Locale('en'),
Locale('hi'),
],
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
title: 'kMobile',
theme: AppThemes.lightTheme,
// darkTheme: AppThemes.darkTheme,
themeMode: ThemeMode.system,
onGenerateRoute: AppRoutes.generateRoute,
initialRoute: AppRoutes.splash,
home: _showSplash ? const SplashScreen() : const AuthGate(),
),
);
}
return MultiBlocProvider(
providers: [
BlocProvider<AuthCubit>(create: (_) => getIt<AuthCubit>()),
BlocProvider<ThemeCubit>(create: (_) => getIt<ThemeCubit>()),
],
child: BlocBuilder<ThemeCubit, ThemeState>(
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.lightTheme,
//theme: AppThemes.getLightTheme(themeState.themeType),
theme: themeState.lightTheme,
darkTheme: AppThemes.getDarkTheme(themeState.themeType),
themeMode: themeState.themeMode,
onGenerateRoute: AppRoutes.generateRoute,
initialRoute: AppRoutes.splash,
home: showSplash ? const SplashScreen() : const AuthGate(),
);
},
),
);
}
}
class AuthGate extends StatefulWidget {
const AuthGate({super.key});
@@ -210,105 +216,6 @@ class _AuthGateState extends State<AuthGate> {
}
}
/* @override
Widget build(BuildContext context) {
if (_checking) {
return const SplashScreen();
}
if (_isLoggedIn) {
if (_hasMPin) {
if (_biometricEnabled) {
return FutureBuilder<bool>(
future: _tryBiometric(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SplashScreen();
}
if (snapshot.data == true) {
// Authenticated with biometrics, go to dashboard
return const NavigationScaffold();
}
// If not authenticated or user dismissed, 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 {
return MPinScreen(
mode: MPinMode.set,
onCompleted: (_) async {
final storage = getIt<SecureStorage>();
final localAuth = LocalAuthentication();
// 1) Prompt user to optin for biometric
final optIn = await showDialog<bool>(
context: context,
barrierDismissible: false, // force choice
builder: (ctx) => AlertDialog(
title: const Text('Enable Fingerprint Login?'),
content: const Text(
'Would you like to enable fingerprint authentication for faster login?',
),
actions: [
TextButton(
onPressed: () => Navigator.of(ctx).pop(false),
child: const Text('No'),
),
TextButton(
onPressed: () => Navigator.of(ctx).pop(true),
child: const Text('Yes'),
),
],
),
);
// 2) If opted in, perform biometric auth
if (optIn == true) {
final canCheck = await localAuth.canCheckBiometrics;
bool didAuth = false;
if (canCheck) {
didAuth = await localAuth.authenticate(
localizedReason: 'Authenticate to enable fingerprint login',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true,
),
);
}
await storage.write(
'biometric_enabled', didAuth ? 'true' : 'false');
} else {
await storage.write('biometric_enabled', 'false');
}
// 3) Finally go to your main scaffold
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const NavigationScaffold()),
);
},
);
}
}
return const LoginScreen();
}
}*/
@override
Widget build(BuildContext context) {
@@ -430,124 +337,6 @@ class _AuthGateState extends State<AuthGate> {
}
}
/*@override
Widget build(BuildContext context) {
if (_checking) {
return const SplashScreen();
}
if (_isLoggedIn) {
if (_hasMPin) {
if (_biometricEnabled) {
return FutureBuilder<bool>(
future: _tryBiometric(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const SplashScreen();
}
if (snapshot.data == true) {
return const NavigationScaffold(); // Authenticated
}
// Failed or dismissed biometric → Show MPIN
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<SecureStorage>();
final localAuth = LocalAuthentication();
final optin = await showDialog<bool>(
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;
if (canCheck) {
didAuth = await localAuth.authenticate(
localizedReason: AppLocalizations.of(
context,
).authenticateToEnable,
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true,
),
);
}
await storage.write(
'biometric_enabled',
didAuth ? 'true' : 'false',
);
} else {
await storage.write('biometric_enabled', 'false');
}
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (_) => const NavigationScaffold()),
);
},
);
}
}
// 🔻 Show Welcome screen before login if not logged in
if (_showWelcome) {
return WelcomeScreen(
onContinue: () {
setState(() {
_showWelcome = false;
});
},
);
}
return const LoginScreen();
}
}*/
class NavigationScaffold extends StatefulWidget {
const NavigationScaffold({super.key});
@@ -612,8 +401,8 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed,
backgroundColor: const Color(0xFFE0F7FA), // Light blue background
selectedItemColor: Colors.blue[800],
backgroundColor: Theme.of(context).scaffoldBackgroundColor, // Light blue background
selectedItemColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.black54,
onTap: _onItemTapped,
items: [