Skip to content

Instantly share code, notes, and snippets.

@CoderNamedHendrick
Last active April 11, 2024 12:06
Show Gist options
  • Save CoderNamedHendrick/27e9272cf9a0c6f8cdede8ad4dbfaedb to your computer and use it in GitHub Desktop.
Save CoderNamedHendrick/27e9272cf9a0c6f8cdede8ad4dbfaedb to your computer and use it in GitHub Desktop.
Clean Biometrics implementation
import 'package:flutter/services.dart';
import 'package:local_auth/error_codes.dart' as auth_error;
import 'package:local_auth/local_auth.dart' as local_auth;
class BiometricsService implements BiometricsInterface {
const BiometricsService();
static final _auth = local_auth.LocalAuthentication();
@override
Future<EitherExceptionOr<bool>> authenticateBiometric() async {
try {
final bool didAuthenticate = await _auth.authenticate(
localizedReason: 'Please authenticate to enter #App Name',
options: const local_auth.AuthenticationOptions(
stickyAuth: true,
),
);
return Right(didAuthenticate);
} on PlatformException catch (e) {
if (e.code == auth_error.notAvailable ||
e.code == auth_error.biometricOnlyNotSupported) {
return const Left(
ClientException(exceptionMessage: 'No Biometrics Available'));
}
if (e.code == auth_error.notEnrolled) {
return const Left(ClientException(
exceptionMessage:
'No Auth Available, please create biometric authentication then try again.'));
}
if (e.code == auth_error.passcodeNotSet) {
return const Left(ClientException(
exceptionMessage:
'Can\'t use biometrics, no passcode/pin set for device'));
}
if (e.code == auth_error.lockedOut) {
return const Left(ClientException(
exceptionMessage:
'Biometric auth locked due to too many attempts! Try again later'));
}
if (e.code == auth_error.permanentlyLockedOut) {
return const Left(ClientException(
exceptionMessage:
'Biometric auth locked dut to too many attempts! Use passcode/pin to unlock'));
}
return const Left(
ClientException(exceptionMessage: 'Something went wrong'),
);
}
}
@override
Future<BiometricType> getAvailableBiometricType() async {
final availableBiometrics = await _auth.getAvailableBiometrics();
if (availableBiometrics.contains(local_auth.BiometricType.strong)) {
return _getBiometricTypeFromAvailable(availableBiometrics);
}
return _getBiometricTypeFromAvailable(availableBiometrics);
}
BiometricType _getBiometricTypeFromAvailable(
List<local_auth.BiometricType> availableBiometrics,
) {
if (Platform.isIOS) {
if (availableBiometrics.contains(local_auth.BiometricType.face)) {
return BiometricType.faceId;
}
return BiometricType.fingerprint;
}
return BiometricType.fingerprint;
}
@override
Future<bool> isBiometricAvailable() async {
final canCheckBiometrics = await _auth.canCheckBiometrics;
final availableBiometrics = await _auth.getAvailableBiometrics();
return canCheckBiometrics && availableBiometrics.isNotEmpty;
}
}
enum BiometricType { fingerprint, faceId }
abstract interface class BiometricsInterface {
Future<bool> isBiometricAvailable();
Future<BiometricType> getAvailableBiometricType();
Future<EitherExceptionOr<bool>> authenticateBiometric();
}
final biometricsServiceProvider =
Provider.autoDispose<BiometricsInterface>((ref) {
return const BiometricsService();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment