This Flutter plugin provides means to perform local, on-device authentication of the user.
This means referring to biometric authentication on iOS (Touch ID or lock code) and the fingerprint APIs on Android (introduced in Android 6.0).
Import the relevant file:
import 'package:local_auth/local_auth.dart';
To check whether there is local authentication available on this device or not, call canCheckBiometrics:
bool canCheckBiometrics = await localAuth.canCheckBiometrics;
Currently the following biometric types are implemented:
To get a list of enrolled biometrics, call getAvailableBiometrics:
List<BiometricType> availableBiometrics = await auth.getAvailableBiometrics(); if (Platform.isIOS) { if (availableBiometrics.contains(BiometricType.face)) { // Face ID. } else if (availableBiometrics.contains(BiometricType.fingerprint)) { // Touch ID. } }
We have default dialogs with an ‘OK’ button to show authentication error messages for the following 2 cases:
Which means, if there‘s no fingerprint on the user’s device, a dialog with instructions will pop up to let the user set up fingerprint. If the user clicks ‘OK’ button, it will return ‘false’.
Use the exported APIs to trigger local authentication with default dialogs:
The authenticate()
method uses biometric authentication, but also allows users to use pin, pattern, or passcode.
var localAuth = LocalAuthentication(); bool didAuthenticate = await localAuth.authenticate( localizedReason: 'Please authenticate to show account balance');
To authenticate using biometric authentication only, set biometricOnly
to true
.
var localAuth = LocalAuthentication(); bool didAuthenticate = await localAuth.authenticate( localizedReason: 'Please authenticate to show account balance', biometricOnly: true);
If you don't want to use the default dialogs, call this API with ‘useErrorDialogs = false’. In this case, it will throw the error message back and you need to handle them in your dart code:
bool didAuthenticate = await localAuth.authenticate( localizedReason: 'Please authenticate to show account balance', useErrorDialogs: false);
You can use our default dialog messages, or you can use your own messages by passing in IOSAuthMessages and AndroidAuthMessages:
import 'package:local_auth/auth_strings.dart'; const iosStrings = const IOSAuthMessages( cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); await localAuth.authenticate( localizedReason: 'Please authenticate to show account balance', useErrorDialogs: false, iOSAuthStrings: iosStrings);
If needed, you can manually stop authentication for android:
void _cancelAuthentication() { localAuth.stopAuthentication(); }
There are 6 types of exceptions: PasscodeNotSet, NotEnrolled, NotAvailable, OtherOperatingSystem, LockedOut and PermanentlyLockedOut. They are wrapped in LocalAuthenticationError class. You can catch the exception and handle them by different types. For example:
import 'package:flutter/services.dart'; import 'package:local_auth/error_codes.dart' as auth_error; try { bool didAuthenticate = await local_auth.authenticate( localizedReason: 'Please authenticate to show account balance'); } on PlatformException catch (e) { if (e.code == auth_error.notAvailable) { // Handle this exception here. } }
Note that this plugin works with both TouchID and FaceID. 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 TouchID.
Note that local_auth plugin requires the use of a FragmentActivity as opposed to Activity. This can be easily done by switching to use FlutterFragmentActivity
as opposed to FlutterActivity
in your manifest (or your own Activity class if you are extending the base class).
Update your MainActivity.java:
import android.os.Bundle; import io.flutter.app.FlutterFragmentActivity; import io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin; import io.flutter.plugins.localauth.LocalAuthPlugin; public class MainActivity extends FlutterFragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FlutterAndroidLifecyclePlugin.registerWith( registrarFor( "io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin")); LocalAuthPlugin.registerWith(registrarFor("io.flutter.plugins.localauth.LocalAuthPlugin")); } }
OR
Update your MainActivity.kt:
import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugins.GeneratedPluginRegistrant class MainActivity: FlutterFragmentActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine) } }
Update your project's AndroidManifest.xml
file to include the USE_FINGERPRINT
permissions:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.app"> <uses-permission android:name="android.permission.USE_FINGERPRINT"/> <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.
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.
For help getting started with Flutter, view our online documentation.
For help on editing plugin code, view the documentation.