307 lines
10 KiB
Dart
307 lines
10 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:kmobile/data/repositories/auth_repository.dart';
|
|
import 'package:kmobile/features/profile/daily_transaction_limit.dart';
|
|
import 'package:kmobile/features/profile/logout_dialog.dart';
|
|
import 'package:kmobile/features/profile/security_settings_screen.dart';
|
|
import 'package:kmobile/security/secure_storage.dart';
|
|
import 'package:local_auth/local_auth.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import '../../di/injection.dart';
|
|
import '../../l10n/app_localizations.dart';
|
|
import 'package:kmobile/features/profile/preferences/preference_screen.dart';
|
|
|
|
class ProfileScreen extends StatefulWidget {
|
|
final String mobileNumber;
|
|
const ProfileScreen({super.key, required this.mobileNumber});
|
|
|
|
@override
|
|
State<ProfileScreen> createState() => _ProfileScreenState();
|
|
}
|
|
|
|
class _ProfileScreenState extends State<ProfileScreen> {
|
|
bool _isBiometricEnabled = false;
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadBiometricStatus();
|
|
}
|
|
|
|
Future<String> _getAppVersion() async {
|
|
final PackageInfo info = await PackageInfo.fromPlatform();
|
|
return 'Version ${info.version} (${info.buildNumber})';
|
|
}
|
|
|
|
Future<void> _loadBiometricStatus() async {
|
|
final storage = getIt<SecureStorage>();
|
|
final enabled = await storage.read('biometric_enabled');
|
|
setState(() {
|
|
_isBiometricEnabled = enabled ?? false;
|
|
});
|
|
}
|
|
|
|
Future<void> _handleLogout(BuildContext context) async {
|
|
final auth = getIt<AuthRepository>();
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.clear(); // clear saved session/token
|
|
await auth.clearAuthTokens();
|
|
// Navigate to login and remove all previous routes
|
|
Navigator.pushNamedAndRemoveUntil(context, '/login', (route) => false);
|
|
}
|
|
|
|
Future<void> _handleBiometricToggle(bool enable) async {
|
|
final localAuth = LocalAuthentication();
|
|
final storage = getIt<SecureStorage>();
|
|
final canCheck = await localAuth.canCheckBiometrics;
|
|
|
|
if (!canCheck) {
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content:
|
|
Text(AppLocalizations.of(context).biometricsNotAvailable)),
|
|
);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (enable) {
|
|
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) {
|
|
try {
|
|
final didAuth = await localAuth.authenticate(
|
|
localizedReason: AppLocalizations.of(context).authenticateToEnable,
|
|
options: const AuthenticationOptions(
|
|
stickyAuth: true,
|
|
biometricOnly: true,
|
|
),
|
|
);
|
|
if (didAuth) {
|
|
await storage.write('biometric_enabled', true);
|
|
if (mounted) {
|
|
setState(() {
|
|
_isBiometricEnabled = true;
|
|
});
|
|
}
|
|
} else {
|
|
// Authentication failed, reload state to refresh UI
|
|
if (mounted) {
|
|
await _loadBiometricStatus();
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// Handle exceptions, reload state to ensure consistency
|
|
if (mounted) {
|
|
await _loadBiometricStatus();
|
|
}
|
|
}
|
|
} else {
|
|
// User cancelled, reload state to refresh UI
|
|
if (mounted) {
|
|
await _loadBiometricStatus();
|
|
}
|
|
}
|
|
} else {
|
|
final optOut = await showDialog<bool>(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (ctx) => AlertDialog(
|
|
title: Text(AppLocalizations.of(context).disableFingerprintLogin),
|
|
content: Text(AppLocalizations.of(context).disableFingerprintMessage),
|
|
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 (optOut == true) {
|
|
await storage.write('biometric_enabled', false);
|
|
if (mounted) {
|
|
setState(() {
|
|
_isBiometricEnabled = false;
|
|
});
|
|
}
|
|
} else {
|
|
// User cancelled, reload state to refresh UI
|
|
if (mounted) {
|
|
await _loadBiometricStatus();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final loc = AppLocalizations.of(context);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(loc.profile), // Localized "Profile"
|
|
),
|
|
body: Stack(
|
|
children: [
|
|
ListView(
|
|
children: [
|
|
ListTile(
|
|
leading: const Icon(Icons.settings),
|
|
title: Text(loc.preferences),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const PreferenceScreen()),
|
|
);
|
|
},
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.security),
|
|
title: Text(loc.securitySettings),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => SecuritySettingsScreen(
|
|
mobileNumber: widget.mobileNumber,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.currency_rupee),
|
|
title: Text(AppLocalizations.of(context).dailylimit),
|
|
onTap: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => const DailyLimitScreen()),
|
|
);
|
|
},
|
|
),
|
|
SwitchListTile(
|
|
title:
|
|
Text(AppLocalizations.of(context).enableFingerprintLogin),
|
|
value: _isBiometricEnabled,
|
|
onChanged: (bool value) {
|
|
// The state is now managed within _handleBiometricToggle
|
|
_handleBiometricToggle(value);
|
|
},
|
|
secondary: const Icon(Icons.fingerprint),
|
|
),
|
|
ListTile(
|
|
leading: const Icon(Icons.smartphone),
|
|
title: const Text("App Version"),
|
|
trailing: FutureBuilder<String>(
|
|
future: _getAppVersion(),
|
|
builder:
|
|
(BuildContext context, AsyncSnapshot<String> snapshot) {
|
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
|
// Show a loading indicator while waiting for the future to complete
|
|
return const CircularProgressIndicator();
|
|
} else if (snapshot.hasError) {
|
|
return const Text("Error");
|
|
} else {
|
|
// Display the version number once the future is complete
|
|
return Text(
|
|
snapshot.data ?? "N/A",
|
|
selectionColor: const Color(0xFFFFFFFF),
|
|
);
|
|
}
|
|
},
|
|
),
|
|
),
|
|
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(
|
|
leading: const Icon(Icons.logout),
|
|
title: Text(AppLocalizations.of(context).deregister),
|
|
onTap: () async {
|
|
final shouldLogout = await showDialog<bool>(
|
|
context: context,
|
|
builder: (_) => const LogoutDialog(),
|
|
);
|
|
|
|
if (shouldLogout == true) {
|
|
await _handleLogout(context);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
IgnorePointer(
|
|
child: Center(
|
|
child: Opacity(
|
|
opacity: 0.07, // Reduced opacity
|
|
child: ClipOval(
|
|
child: Image.asset(
|
|
'assets/images/logo.png',
|
|
width: 200, // Adjust size as needed
|
|
height: 200, // Adjust size as needed
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|