biometricPermission&Authentication

This commit is contained in:
Trina Bakshi 2025-04-29 16:30:40 +05:30
parent 3108093686
commit 8fb84abe83
7 changed files with 162 additions and 40 deletions

View File

@ -82,39 +82,4 @@ class AuthService {
throw AuthException('Error fetching user profile: ${e.toString()}');
}
}
static Future<void> 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");
}
}
}
}

View File

@ -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<NavigationScaffold> {
}
}
class BiometricPermissionScreen extends StatelessWidget {
const BiometricPermissionScreen({super.key});
@override
Widget build(BuildContext context) {
final cubit = context.read<AuthCubit>();
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'),
),
],
),
),
);
}
}

View File

@ -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<AuthState> {
emit(AuthError(e.toString()));
}
}
Future<void> 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<void> 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<void> 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());
}
}
}

View File

@ -10,6 +10,8 @@ class AuthInitial extends AuthState {}
class AuthLoading extends AuthState {}
class ShowBiometricPermission extends AuthState {}
class Authenticated extends AuthState {
final User user;

View File

@ -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<MPinScreen> {
@override
Widget build(BuildContext context) {
final cubit = context.read<AuthCubit>();
return Scaffold(
body: SafeArea(
child: Column(
@ -131,7 +131,7 @@ class MPinScreenState extends State<MPinScreen> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(onPressed: () {
AuthService.authenticateWithBiometrics(context);
cubit.authenticateBiometric();
}, child: const Text("Try another way")),
TextButton(onPressed: () {}, child: const Text("Register?")),
],

View File

@ -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

View File

@ -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: