This Flutter plugin provides means to perform local, on-device authentication of the user.
On supported devices, this includes authentication with biometrics such as fingerprint or facial recognition.
Android | iOS | Windows | |
---|---|---|---|
Support | SDK 16+* | 9.0+ | Windows 10+ |
To check whether there is local authentication available on this device or not, call canCheckBiometrics
(if you need biometrics support) and/or isDeviceSupported()
(if you just need some device-level authentication):
import 'package:local_auth/local_auth.dart'; // ··· final LocalAuthentication auth = LocalAuthentication(); // ··· final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics; final bool canAuthenticate = canAuthenticateWithBiometrics || await auth.isDeviceSupported();
Currently the following biometric types are implemented:
canCheckBiometrics
only indicates whether hardware support is available, not whether the device has any biometrics enrolled. To get a list of enrolled biometrics, call getAvailableBiometrics()
.
The types are device-specific and platform-specific, and other types may be added in the future, so when possible you should not rely on specific biometric types and only check that some biometric is enrolled:
final List<BiometricType> availableBiometrics = await auth.getAvailableBiometrics(); if (availableBiometrics.isNotEmpty) { // Some biometrics are enrolled. } if (availableBiometrics.contains(BiometricType.strong) || availableBiometrics.contains(BiometricType.face)) { // Specific types of biometrics are available. // Use checks like this with caution! }
The authenticate()
method uses biometric authentication when possible, but also allows fallback to pin, pattern, or passcode.
try { final bool didAuthenticate = await auth.authenticate( localizedReason: 'Please authenticate to show account balance'); // ··· } on PlatformException { // ... }
To require biometric authentication, pass AuthenticationOptions
with biometricOnly
set to true
.
final bool didAuthenticate = await auth.authenticate( localizedReason: 'Please authenticate to show account balance', options: const AuthenticationOptions(biometricOnly: true));
Note: biometricOnly
is not supported on Windows since the Windows implementation‘s underlying API (Windows Hello) doesn’t support selecting the authentication method.
The plugin provides default dialogs for the following cases:
If a user does not have the necessary authentication enrolled when authenticate
is called, they will be given the option to enroll at that point, or cancel authentication.
If you don't want to use the default dialogs, set the useErrorDialogs
option to false
to have authenticate
immediately return an error in those cases.
import 'package:local_auth/error_codes.dart' as auth_error; // ··· try { final bool didAuthenticate = await auth.authenticate( localizedReason: 'Please authenticate to show account balance', options: const AuthenticationOptions(useErrorDialogs: false)); // ··· } on PlatformException catch (e) { if (e.code == auth_error.notAvailable) { // Add handling of no hardware here. } else if (e.code == auth_error.notEnrolled) { // ... } else { // ... } }
If you want to customize the messages in the dialogs, you can pass AuthMessages
for each platform you support. These are platform-specific, so you will need to import the platform-specific implementation packages. For instance, to customize Android and iOS:
import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_ios/local_auth_ios.dart'; // ··· final bool didAuthenticate = await auth.authenticate( localizedReason: 'Please authenticate to show account balance', authMessages: const <AuthMessages>[ AndroidAuthMessages( signInTitle: 'Oops! Biometric authentication required!', cancelButton: 'No thanks', ), IOSAuthMessages( cancelButton: 'No thanks', ), ]);
See the platform-specific classes for details about what can be customized on each platform.
authenticate
throws PlatformException
s in many error cases. See error_codes.dart
for known error codes that you may want to have specific handling for. For example:
import 'package:flutter/services.dart'; import 'package:local_auth/error_codes.dart' as auth_error; import 'package:local_auth/local_auth.dart'; // ··· final LocalAuthentication auth = LocalAuthentication(); // ··· try { final bool didAuthenticate = await auth.authenticate( localizedReason: 'Please authenticate to show account balance', options: const AuthenticationOptions(useErrorDialogs: false)); // ··· } on PlatformException catch (e) { if (e.code == auth_error.notEnrolled) { // Add handling of no hardware here. } else if (e.code == auth_error.lockedOut || e.code == auth_error.permanentlyLockedOut) { // ... } else { // ... } }
Note that this plugin works with both Touch ID and Face ID. However, to use the latter, you need to also add:
<key>NSFaceIDUsageDescription</key> <string>Why is my app authenticating using face id?</string>
to your Info.plist file. Failure to do so results in a dialog that tells the user your app has not been updated to use Face ID.
* The plugin will build and run on SDK 16+, but isDeviceSupported()
will always return false before SDK 23 (Android 6.0).
Note that local_auth
requires the use of a FragmentActivity
instead of an Activity
. To update your application:
If you are using FlutterActivity
directly, change it to FlutterFragmentActivity
in your AndroidManifest.xml
.
If you are using a custom activity, update your MainActivity.java
:
import io.flutter.embedding.android.FlutterFragmentActivity; public class MainActivity extends FlutterFragmentActivity { // ... }
or MainActivity.kt:
import io.flutter.embedding.android.FlutterFragmentActivity class MainActivity: FlutterFragmentActivity() { // ... }
to inherit from FlutterFragmentActivity
.
Update your project's AndroidManifest.xml
file to include the USE_BIOMETRIC
permissions:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app"> <uses-permission android:name="android.permission.USE_BIOMETRIC"/> <manifest>
On Android, you can check only for existence of fingerprint hardware prior to API 29 (Android Q). Therefore, if you would like to support other biometrics types (such as face scanning) and you want to support SDKs lower than Q, do not call getAvailableBiometrics
. Simply call authenticate
with biometricOnly: true
. This will return an error if there was no hardware available.
Your LaunchTheme
's parent must be a valid Theme.AppCompat
theme to prevent crashes on Android 8 and below. For example, use Theme.AppCompat.DayNight
to enable light/dark modes for the biometric dialog. To do that go to android/app/src/main/res/values/styles.xml
and look for the style with name LaunchTheme
. Then change the parent for that style as follows:
... <resources> <style name="LaunchTheme" parent="Theme.AppCompat.DayNight"> ... </style> ... </resources> ...
If you don't have a styles.xml
file for your Android project you can set up the Android theme directly in android/app/src/main/AndroidManifest.xml
:
... <application ... <activity ... android:theme="@style/Theme.AppCompat.DayNight" ... > </activity> </application> ...
You can set the stickyAuth
option on the plugin to true so that plugin does not return failure if the app is put to background by the system. This might happen if the user receives a phone call before they get a chance to authenticate. With stickyAuth
set to false, this would result in plugin returning failure result to the Dart app. If set to true, the plugin will retry authenticating when the app resumes.