diff --git a/lib/api/services/auth_service.dart b/lib/api/services/auth_service.dart index 417c0f3..5169306 100644 --- a/lib/api/services/auth_service.dart +++ b/lib/api/services/auth_service.dart @@ -82,39 +82,4 @@ class AuthService { throw AuthException('Error fetching user profile: ${e.toString()}'); } } - - static Future authenticateWithBiometrics(BuildContext context) async { - final LocalAuthentication localAuth = LocalAuthentication(); - try { - bool isBiometricAvailable = await localAuth.canCheckBiometrics; - bool isAuthenticated = false; - - if (isBiometricAvailable) { - isAuthenticated = await localAuth.authenticate( - localizedReason: 'Touch the fingerprint sensor', - options: const AuthenticationOptions( - biometricOnly: true, - stickyAuth: true, - ), - ); - } - - if (isAuthenticated) { - // Navigate to Dashboard - Navigator.push( - context, - MaterialPageRoute(builder: (context) => const NavigationScaffold()), - ); - } else { - // Show error/snack bar - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Authentication failed")), - ); - } - } catch (e) { - if (kDebugMode) { - print("Biometric error: $e"); - } - } - } } \ No newline at end of file diff --git a/lib/app.dart b/lib/app.dart index ac567a4..bc628df 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -2,8 +2,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kmobile/features/auth/screens/customer_info_screen.dart'; +import 'package:kmobile/security/secure_storage.dart'; +import 'api/services/auth_service.dart'; import 'config/themes.dart'; import 'config/routes.dart'; +import 'data/repositories/auth_repository.dart'; import 'di/injection.dart'; import 'features/auth/controllers/auth_cubit.dart'; import 'features/auth/controllers/auth_state.dart'; @@ -56,6 +59,10 @@ class KMobile extends StatelessWidget { return const _SplashScreen(); } + if(state is ShowBiometricPermission){ + return const BiometricPermissionScreen(); + } + if (state is Authenticated) { return const NavigationScaffold(); } @@ -168,4 +175,33 @@ class _NavigationScaffoldState extends State { } } +class BiometricPermissionScreen extends StatelessWidget { + const BiometricPermissionScreen({super.key}); + + @override + Widget build(BuildContext context) { + final cubit = context.read(); + + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Would you like to enable biometric authentication?'), + ElevatedButton( + onPressed: () => cubit.handleBiometricChoice(true), + child: const Text('Yes'), + ), + TextButton( + onPressed: () => cubit.handleBiometricChoice(false), + child: const Text('No, thanks'), + ), + ], + ), + ), + ); + } +} + + diff --git a/lib/features/auth/controllers/auth_cubit.dart b/lib/features/auth/controllers/auth_cubit.dart index 4130a1e..8f5f394 100644 --- a/lib/features/auth/controllers/auth_cubit.dart +++ b/lib/features/auth/controllers/auth_cubit.dart @@ -1,4 +1,10 @@ import 'package:bloc/bloc.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import '../../../app.dart'; import '../../../data/repositories/auth_repository.dart'; import 'auth_state.dart'; @@ -47,4 +53,60 @@ class AuthCubit extends Cubit { emit(AuthError(e.toString())); } } + + Future checkFirstLaunch() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + final isFirstLaunch = prefs.getBool('isFirstLaunch') ?? true; + + if (isFirstLaunch) { + emit(ShowBiometricPermission()); + } else { + // Continue to authentication logic (e.g., check token) + emit(AuthLoading()); // or Unauthenticated/Authenticated + } + } + + Future handleBiometricChoice(bool enabled) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setBool('biometric_opt_in', enabled); + await prefs.setBool('isFirstLaunch', false); + + // Then continue to auth logic or home + if (enabled) { + authenticateBiometric(); // implement biometric logic + } else { + emit(Unauthenticated()); + } + } + + Future authenticateBiometric() async { + final LocalAuthentication auth = LocalAuthentication(); + + try { + final isAvailable = await auth.canCheckBiometrics; + final isDeviceSupported = await auth.isDeviceSupported(); + + if (isAvailable && isDeviceSupported) { + final authenticated = await auth.authenticate( + localizedReason: 'Touch the fingerprint sensor', + options: const AuthenticationOptions( + biometricOnly: true, + stickyAuth: true, + ), + ); + + if (authenticated) { + // Continue to normal auth logic (e.g., auto login) + emit(AuthLoading()); + await checkAuthStatus(); // Your existing method to verify token/session + } else { + emit(Unauthenticated()); + } + } else { + emit(Unauthenticated()); + } + } catch (e) { + emit(Unauthenticated()); + } + } } diff --git a/lib/features/auth/controllers/auth_state.dart b/lib/features/auth/controllers/auth_state.dart index eebc0fc..cdc4139 100644 --- a/lib/features/auth/controllers/auth_state.dart +++ b/lib/features/auth/controllers/auth_state.dart @@ -10,6 +10,8 @@ class AuthInitial extends AuthState {} class AuthLoading extends AuthState {} +class ShowBiometricPermission extends AuthState {} + class Authenticated extends AuthState { final User user; diff --git a/lib/features/auth/screens/mpin_screen.dart b/lib/features/auth/screens/mpin_screen.dart index 38dd703..e681787 100644 --- a/lib/features/auth/screens/mpin_screen.dart +++ b/lib/features/auth/screens/mpin_screen.dart @@ -1,11 +1,9 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:kmobile/features/auth/controllers/auth_cubit.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; import '../../../app.dart'; -import '../../dashboard/screens/dashboard_screen.dart'; -import '../../../api/services/auth_service.dart'; -import 'package:material_symbols_icons/get.dart'; class MPinScreen extends StatefulWidget { const MPinScreen({super.key}); @@ -112,6 +110,8 @@ class MPinScreenState extends State { @override Widget build(BuildContext context) { + final cubit = context.read(); + return Scaffold( body: SafeArea( child: Column( @@ -131,7 +131,7 @@ class MPinScreenState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton(onPressed: () { - AuthService.authenticateWithBiometrics(context); + cubit.authenticateBiometric(); }, child: const Text("Try another way")), TextButton(onPressed: () {}, child: const Text("Register?")), ], diff --git a/pubspec.lock b/pubspec.lock index 0418b7d..3289244 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -472,6 +472,62 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.4" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 549d9a5..42c94dd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: flutter_svg: ^2.1.0 local_auth: ^2.3.0 material_symbols_icons: ^4.2815.0 + shared_preferences: ^2.5.3 dev_dependencies: flutter_test: