Cyber 🔥 Week

1/ 100

promotion ends on December 2nd

Mega Bundle 🔥🔥🔥

Get developer licenses for all our products at 95% off discount.

$6,689

$499

  • 15+ fully functional mobile apps
  • 300+ screens
  • 450+ features
  • 70,000+ lines of code
  • fully customizable codebases
  • 3000+ paid customers
  • 100,000+ developers using our products
  • free 1 year updates
BUY NOW

As the Flutter community grows, it is also creating a variety of libraries to support the native functionality. When a user’s fingerprints, facial characteristics, or voice are used to authenticate their identification, this is referred to as biometric authentication. Let’s see how you can implement Local Authentication for Biometrics in Flutter.

Almost every phone on the market today has some kind of biometric authentication. We no longer need to type in a password since we can just press our fingerprints to verify our identity. Fingerprint Authentication in Flutter Apps is the topic of this essay.

Import local_auth Package

First off, We include local_auth package into our pubspec.yaml file so we can use it in our project

dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.5
local_auth: ^2.1.2
local_auth_android: ^1.0.14
local_auth_ios: ^1.0.10
local_auth_windows: ^1.0.4

How to Use local_auth

This Flutter plugin enables us to authenticate users locally, on the device, using this feature.

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

final LocalAuthentication auth = LocalAuthentication();
final bool canAuthenticateWithBiometrics = await auth.canCheckBiometrics;
final bool canAuthenticate =
canAuthenticateWithBiometrics || await auth.isDeviceSupported();

Don’t forget to import the package into your file like this:

import 'package:local_auth/local_auth.dart';

Currently the following biometric types are implemented:

  • BiometricType.face
  • BiometricType.fingerprint
  • BiometricType.weak
  • BiometricType.strong

Enrolled Biometrics

 canCheckBiometrics 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!
}

Options

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));

NotebiometricOnly is not supported on Windows since the Windows implementation’s underlying API (Windows Hello) doesn’t support selecting the authentication method.

Biometrics Permission Dialogs

The plugin provides default dialogs for the following cases:

  1. Passcode/PIN/Pattern Not Set: The user has not yet configured a passcode on iOS or PIN/pattern on Android.
  2. Biometrics Not Enrolled: The user has not enrolled any biometrics on the device.

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 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',
),
]);

Handling Local Auth Exceptions in Flutter

authenticate throws PlatformExceptions 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:local_auth/error_codes.dart' as auth_error;

// ···
void main() async{
runApp(const MyApp());

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 {
// ...
}
}

iOS Integration for Face ID / Touch ID in Flutter

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 ios/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.

Android Fingerprint Integration in Flutter

* The plugin will build and run on SDK 16+, but  isDeviceSupported() always return false before SDK 23 (Android 6.0).

Activity Changes 

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

Local Auth Permissions

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>

Compatibility

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.

Sticky Auth

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.

A Complete Code Example

Let’s look at our complete example of implementing the local_auth package 

Add the following code into the main.dart file.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
final LocalAuthentication auth = LocalAuthentication();

String _authorized = 'Not Authorized';
bool _isAuthenticating = false;

Future<void> _authenticate() async {
bool authenticated = false;
try {
setState(() {
_isAuthenticating = true;
_authorized = 'Authenticating';
});
authenticated = await auth.authenticate(
localizedReason: 'Let OS determine authentication method',
options: const AuthenticationOptions(
useErrorDialogs: true,
stickyAuth: true,
),
);
setState(() {
_isAuthenticating = false;
});
} on PlatformException catch (e) {
debugPrint('$e');
setState(() {
_isAuthenticating = false;
_authorized = "Error - ${e.message}";
});
return;
}
if (!mounted) return;

setState(
() => _authorized = authenticated ? 'Authorized' : 'Not Authorized');
}

void _cancelAuthentication() async {
await auth.stopAuthentication();
setState(() => _isAuthenticating = false);
}

@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Biometric Authentication'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Current State: $_authorized\n'),
(_isAuthenticating)
? ElevatedButton(
onPressed: _cancelAuthentication,
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text("Cancel Authentication"),
Icon(Icons.cancel),
],
),
)
: Column(
children: [
ElevatedButton(
onPressed: _authenticate,
child: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text('Authenticate'),
Icon(Icons.fingerprint),
],
),
),
],
),
],
),
),
),
);
}
}

Now let’s launch the program and see what it does now that the implementation is complete.

  

Android and iOS screens

Without a doubt, local auth has offered the simplest method of establishing biometric authentication, and we hope that Flutter will continue to introduce additional packages of this kind to make the lives of programmers easier.


Leave a Reply

Your email address will not be published. Required fields are marked *

Shopping Cart