App Functionality

This commit is contained in:
2025-10-15 12:30:01 +05:30
parent 4d19bf6146
commit df025babd5
5 changed files with 71 additions and 22 deletions

View File

@@ -20,6 +20,7 @@ import 'features/dashboard/screens/dashboard_screen.dart';
import 'features/auth/screens/mpin_screen.dart'; import 'features/auth/screens/mpin_screen.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'dart:async';
class KMobile extends StatefulWidget { class KMobile extends StatefulWidget {
const KMobile({super.key}); const KMobile({super.key});
@@ -34,13 +35,15 @@ class KMobile extends StatefulWidget {
} }
} }
class _KMobileState extends State<KMobile> { class _KMobileState extends State<KMobile> with WidgetsBindingObserver {
Timer? _backgroundTimer;
bool showSplash = true; bool showSplash = true;
Locale? _locale; Locale? _locale;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this);
loadPreferences(); loadPreferences();
Future.delayed(const Duration(seconds: 3), () { Future.delayed(const Duration(seconds: 3), () {
setState(() { setState(() {
@@ -49,10 +52,32 @@ class _KMobileState extends State<KMobile> {
}); });
} }
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_backgroundTimer?.cancel();
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
_backgroundTimer?.cancel();
break;
case AppLifecycleState.paused:
_backgroundTimer = Timer(const Duration(minutes: 2), () {
SystemNavigator.pop();
});
break;
default:
break;
}
}
Future<void> loadPreferences() async { Future<void> loadPreferences() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
// Load Locale
final String? langCode = prefs.getString('locale'); final String? langCode = prefs.getString('locale');
if (langCode != null) { if (langCode != null) {
setState(() { setState(() {
@@ -117,7 +142,6 @@ class _KMobileState extends State<KMobile> {
class AuthGate extends StatefulWidget { class AuthGate extends StatefulWidget {
const AuthGate({super.key}); const AuthGate({super.key});
@override @override
State<AuthGate> createState() => _AuthGateState(); State<AuthGate> createState() => _AuthGateState();
} }
@@ -180,7 +204,6 @@ class _AuthGateState extends State<AuthGate> {
if (_checking) { if (_checking) {
return const SplashScreen(); return const SplashScreen();
} }
if (_isLoggedIn) { if (_isLoggedIn) {
if (_hasMPin) { if (_hasMPin) {
if (_biometricEnabled) { if (_biometricEnabled) {
@@ -190,11 +213,9 @@ class _AuthGateState extends State<AuthGate> {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const SplashScreen(); return const SplashScreen();
} }
if (snapshot.data == true) { if (snapshot.data == true) {
return const NavigationScaffold(); // Authenticated return const NavigationScaffold();
} }
return MPinScreen( return MPinScreen(
mode: MPinMode.enter, mode: MPinMode.enter,
onCompleted: (_) { onCompleted: (_) {
@@ -225,7 +246,6 @@ class _AuthGateState extends State<AuthGate> {
onCompleted: (_) async { onCompleted: (_) async {
final storage = getIt<SecureStorage>(); final storage = getIt<SecureStorage>();
final localAuth = LocalAuthentication(); final localAuth = LocalAuthentication();
final optIn = await showDialog<bool>( final optIn = await showDialog<bool>(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
@@ -246,7 +266,6 @@ class _AuthGateState extends State<AuthGate> {
], ],
), ),
); );
if (optIn == true) { if (optIn == true) {
final canCheck = await localAuth.canCheckBiometrics; final canCheck = await localAuth.canCheckBiometrics;
bool didAuth = false; bool didAuth = false;
@@ -254,7 +273,6 @@ class _AuthGateState extends State<AuthGate> {
if (context.mounted) { if (context.mounted) {
authEnable = AppLocalizations.of(context).authenticateToEnable; authEnable = AppLocalizations.of(context).authenticateToEnable;
} }
if (canCheck) { if (canCheck) {
didAuth = await localAuth.authenticate( didAuth = await localAuth.authenticate(
localizedReason: authEnable, localizedReason: authEnable,
@@ -269,7 +287,6 @@ class _AuthGateState extends State<AuthGate> {
await storage.write('biometric_enabled', 'false'); await storage.write('biometric_enabled', 'false');
} }
} }
if (context.mounted) { if (context.mounted) {
Navigator.of(context).pushReplacement( Navigator.of(context).pushReplacement(
MaterialPageRoute( MaterialPageRoute(
@@ -287,7 +304,6 @@ class _AuthGateState extends State<AuthGate> {
class NavigationScaffold extends StatefulWidget { class NavigationScaffold extends StatefulWidget {
const NavigationScaffold({super.key}); const NavigationScaffold({super.key});
@override @override
State<NavigationScaffold> createState() => _NavigationScaffoldState(); State<NavigationScaffold> createState() => _NavigationScaffoldState();
} }
@@ -295,7 +311,6 @@ class NavigationScaffold extends StatefulWidget {
class _NavigationScaffoldState extends State<NavigationScaffold> { class _NavigationScaffoldState extends State<NavigationScaffold> {
final PageController _pageController = PageController(); final PageController _pageController = PageController();
int _selectedIndex = 0; int _selectedIndex = 0;
final List<Widget> _pages = [ final List<Widget> _pages = [
const DashboardScreen(), const DashboardScreen(),
const CardManagementScreen(), const CardManagementScreen(),
@@ -372,11 +387,9 @@ class _NavigationScaffoldState extends State<NavigationScaffold> {
} }
} }
// Add this widget at the end of the file
class BiometricPromptScreen extends StatelessWidget { class BiometricPromptScreen extends StatelessWidget {
final VoidCallback onCompleted; final VoidCallback onCompleted;
const BiometricPromptScreen({super.key, required this.onCompleted}); const BiometricPromptScreen({super.key, required this.onCompleted});
Future<void> _handleBiometric(BuildContext context) async { Future<void> _handleBiometric(BuildContext context) async {
final localAuth = LocalAuthentication(); final localAuth = LocalAuthentication();
final canCheck = await localAuth.canCheckBiometrics; final canCheck = await localAuth.canCheckBiometrics;
@@ -444,4 +457,4 @@ class BiometricPromptScreen extends StatelessWidget {
onCompleted(); onCompleted();
} }
} }
} }

View File

@@ -8,8 +8,8 @@ class LogoutDialog extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
title: Text(AppLocalizations.of(context).logout), title: Text(AppLocalizations.of(context).deregister),
content: Text(AppLocalizations.of(context).logoutCheck), content: Text(AppLocalizations.of(context).deregistercheck),
actions: [ actions: [
TextButton( TextButton(
onPressed: () => Navigator.pop(context, false), // dismiss onPressed: () => Navigator.pop(context, false), // dismiss

View File

@@ -1,4 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:kmobile/data/repositories/auth_repository.dart'; import 'package:kmobile/data/repositories/auth_repository.dart';
import 'package:kmobile/features/profile/change_password/change_password_screen.dart'; import 'package:kmobile/features/profile/change_password/change_password_screen.dart';
import 'package:kmobile/features/profile/logout_dialog.dart'; import 'package:kmobile/features/profile/logout_dialog.dart';
@@ -70,9 +72,39 @@ class _ProfileScreenState extends State<ProfileScreen> {
// onTap: () async { // onTap: () async {
// }, // },
// ), // ),
ListTile(
leading: const Icon(Icons.exit_to_app),
title: Text(AppLocalizations.of(context).logout),
onTap: () async {
final shouldExit = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: Text(AppLocalizations.of(context).logout),
content: Text(AppLocalizations.of(context).logoutCheck),
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);
}
},
),
ListTile( ListTile(
leading: const Icon(Icons.logout), leading: const Icon(Icons.logout),
title: Text(AppLocalizations.of(context).logout), title: Text(AppLocalizations.of(context).deregister),
onTap: () async { onTap: () async {
final shouldLogout = await showDialog<bool>( final shouldLogout = await showDialog<bool>(
context: context, context: context,

View File

@@ -326,5 +326,7 @@
"editLimit": "Edit Limit", "editLimit": "Edit Limit",
"removeLimit": "Remove Limit", "removeLimit": "Remove Limit",
"limitAmount": "Limit Amount", "limitAmount": "Limit Amount",
"save": "Save" "save": "Save",
"deregister": "De-Register",
"deregistercheck": "Are you sure you want to De-Register?"
} }

View File

@@ -327,5 +327,7 @@
"editLimit": "सीमा संपादित करें", "editLimit": "सीमा संपादित करें",
"removeLimit": "सीमा हटाएँ", "removeLimit": "सीमा हटाएँ",
"limitAmount": "सीमा राशि", "limitAmount": "सीमा राशि",
"save": "जमा करें" "save": "जमा करें",
"deregister": "अपंजीकृत",
"deregistercheck": "क्या आप वाकई पंजीकरण रद्द करना चाहते हैं??"
} }