Yusuf Ahmed Software engineer, technical writer, vibes.

Implementing face recognition and authentication in Flutter

6 min read 1692

Implementing Facial Recognition And Authentication In Flutter

In this article, we’ll be describing how you can implement biometric authentication using facial recognition and Touch ID in Flutter applications.

To this end, we’ll be using a plugin called local_auth, which is developed by the Flutter team. This plugin provides the means to perform local, on-device authentication of users. With the plugin, we will implement local authentication in our Flutter applications, using both facial recognition and fingerprint scanning.

In this tutorial, we will cover:

  • What biometric authentication is
  • The local_auth plugin and how its used
  • Implementing biometric authentication in a Flutter app
  • Setting app permissions

Contents

What is biometric authentication?

Biometric authentication is a type of multifactor authentication (MFA) that uses data derived from a device user’s biological traits, such as facial characteristics, voice recognition, and fingerprints, to facilitate access and better secure personal information and sensitive assets.

One advantage of adopting biometric authentication in applications is that the verification of the user is fully conducted locally on a physical device, removing the possibility of sensitive information being transmitted and potentially exposed via third-party servers.

Aside from effectively verifying users’ identities, it also serves as an additional layer of security over traditional sign-in methods like password credentials.

Prerequisites

If you wish to follow along with this tutorial, I recommend you have the following set up:

  • Any IDE that has the Flutter SDK installed (i.e., Android Studio, VSCode)
  • A basic understanding of Dart and Flutter

This tutorial was verified with Flutter v2.5.1 and Android Studio v3.5.

So, with all that out the way, let’s get started.

Project setup

To get started with our tutorial, let’s create a new Flutter project. Do this by running the following command on your terminal:

We made a custom demo for .
No really. Click here to check it out.

$ flutter create local_auth_example

Next, we need to add the local_auth plugin as a dependency to our project. Run the following commands in your terminal to get and install the local_auth plugin in your project.

$ flutter pub add local_auth
$ flutter pub get

Now that we have installed the required dependency, let’s get on with building the UI of our application.

Building the UI

Our application will consist of two screens: the LoginScreen, where we will authenticate the users, and the PrivateScreen, which the users will be able to view after a successful authentication.

Demonstration of Login And Private Screens For Authentication

Let’s get started by building the LoginScreen.

Login screen

As shown in the code snippet below, the LoginScreen comprises of an Icon, a Text, and a Button in which we will implement our biometric functionality.

//...

class LoginScreen extends StatelessWidget {
  const LoginScreen({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Scaffold(
      backgroundColor: Colors.grey.shade300,
      appBar: AppBar(title: const Text('Local Authentication Example')),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Icon(Icons.lock, size: size.width * 0.3),
              const SizedBox(height: 20),
              const Text(
                  'Tap on the button to authenticate with the device\'s local authentication system.',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 21,
                    color: Colors.black,
                  )),
              const SizedBox(height: 30),
              SizedBox(
                width: size.width,
                child: TextButton(
                  onPressed: () {
                   //implement biometric auth here
                  },
                  style: TextButton.styleFrom(
                    padding: const EdgeInsets.all(20),
                    backgroundColor: Colors.blue,
                    shadowColor: const Color(0xFF323247),
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      Text(
                        'LOGIN WITH BIOMETRICS',
                        style: TextStyle(
                          fontSize: 15,
                          color: Colors.white,
                          fontWeight: FontWeight.w600,
                          wordSpacing: 1.2,
                        ),
                      ),
                    ],
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Private screen

The PrivateScreen also consists of an Icon, a Text, and a Button for handling a user logging out, as shown in the code snippet below.

//...

class PrivateScreen extends StatelessWidget {
  const PrivateScreen({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey.shade300,
      appBar: AppBar(title: const Text('Private Page')),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Icon(Icons.verified_user,
                  size: 100, color: Colors.blueGrey),
              const SizedBox(height: 20),
              const Text(
                  'You now have access to this page. Tap on logout to go back.',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 21,
                    color: Colors.blueGrey,
                  )),
              const SizedBox(height: 20),
              SizedBox(
                width: MediaQuery.of(context).size.width,
                child: TextButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  style: TextButton.styleFrom(
                    padding: const EdgeInsets.all(20),
                    backgroundColor: Colors.blue,
                    shadowColor: const Color(0xFF323247),
                  ),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: const [
                      Text(
                        'LOGOUT',
                        style: TextStyle(
                          fontSize: 15,
                          color: Colors.white,
                          fontWeight: FontWeight.w600,
                          wordSpacing: 1.2,
                        ),
                      ),
                    ],
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

local_auth plugin overview

Before we move on to the implementation of biometric authentication in our app, let’s take a look at the primary features provided by the local_auth plugin, which include the following:

  • Check for device compatibility
  • Listed biometric types that are supported, available for view
  • User authentication using biometrics or PIN

Check for device compatibility

We can check whether the device hardware supports biometric authentication. To do this, we’ll use the isDeviceSupported method provided by the local_auth plugin.

//initialize the Local Authentication plugin 
LocalAuthentication _localAuthentication = LocalAuthentication();
//check if device supports biometrics authentication.
bool isBiometricSupported = await _localAuthentication.isDeviceSupported();

The isDeviceSupported async method returns a bool, indicating whether the user’s device supports biometric authentication. In cases where biometric support is not available on the user’s device, you should consider enabling alternative methods of authentication, such as a PIN.

Get a list of supported biometric types

The getAvailableBiometrics method provided by the local_auth plugin can be used to retrieve a list of biometric types supported by the user’s device.

List<BiometricType> biometricTypes =
      await _localAuthentication.getAvailableBiometrics();

The following biometric types are currently supported by the plugin:

  • BiometricType.face
  • BiometricType.fingerprint
  • BiometricType.iris

Authenticate users using biometrics or PIN

To authenticate users using biometrics or PIN, we use the authenticate method provided by the plugin.

await _localAuthentication.authenticate(
     localizedReason: 'To continue, you must complete the biometrics',
);

The authenticate method has some optional parameters which are used to change some specific settings, of which the following are examples:

Future<bool> authenticate({
  required String localizedReason,
  bool useErrorDialogs,
  bool stickyAuth,
  bool biometricOnly ,
})

String localizedReason

This is the message to be shown to the user while prompting them for authentication.

(Note: The localizedReason message is highlighted in red.)

Authentication Required Image, Use PIN Shown

bool biometricOnly

When set to true, non-biometric local authentication methods such as PIN and passcode are disabled.

Authentication Required With Use PIN Button Present

(Note: When the option is set to false, the image shows that the “USE PIN” button is present.)Authentication Required With Use PIN Button Removed

(Note: when the option is set to false, the image shows that the “USE PIN” button is removed.)

bool useErrorDialogs

When this parameter is set to true, the plugin checks to see whether a user fingerprint record exists on the device. If no fingerprint is registered, the plugin will attempt to direct the user to the settings to create one.

stickyAuth

Under normal circumstances, the authentication process is terminated when the app is minimized or moved to the background. If stickyAuth is set to true, the authentication process is resumed when the app is brought back into focus.

Implementing biometric authentication in a Flutter app

In our implementation, we will first check to see if the device supports biometric authentication, then limit the user to only using biometrics to authenticate; after successful authentication, we will grant the user access to the next screen.

To start with, create a new file named auth.dart and paste the following code inside it:

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

class AuthService {
  static Future<bool> authenticateUser() async {
    //initialize Local Authentication plugin.
    final LocalAuthentication _localAuthentication = LocalAuthentication();
    //status of authentication.
    bool isAuthenticated = false;
    //check if device supports biometrics authentication.
    bool isBiometricSupported = await _localAuthentication.isDeviceSupported();
    //check if user has enabled biometrics.
    //check  
    bool canCheckBiometrics = await _localAuthentication.canCheckBiometrics;

  //if device supports biometrics and user has enabled biometrics, then authenticate.
    if (isBiometricSupported && canCheckBiometrics) {
      try {
        isAuthenticated = await _localAuthentication.authenticate(
            localizedReason: 'Scan your fingerprint to authenticate',
            biometricOnly: true,
            useErrorDialogs: true,
            stickyAuth: true);
      } on PlatformException catch (e) {
        print(e);
      }
    }
    return isAuthenticated;
  }
}

In the code above, we created a class AuthService and defined a static method, authenticateUser. This method handles all the biometric authentication logic and returns a bool indicating whether the biometric authentication has been successful or not.

Now, in the LoginScreen, we need to call the authenticateUser method in the onPressed property of our TextButton widget, as shown in the code snippet below.

TextButton(
  onPressed: () async {
      bool isAuthenticated = await AuthService.authenticateUser();
      if (isAuthenticated) {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => const PrivateScreen()),
            );
      } else {
          ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(
                content: Text('Authentication failed.'),
                ),
          );
        }
  //...
  },

From the code snippet above — if the authentication is successful — we will navigate the user to the PrivateScreen; otherwise, we display a Snackbar with an error message.

Setting app permissions

For Android

To include the USE_FINGERPRINT permission, add the following line of code to your AndroidManifest.xml file, which is located in the directory android/app/src/main:

<uses-permission android:name="android.permission.USE_FINGERPRINT"/>

Then, update the MainActivity.kt file to use FlutterFragmentActivity instead of FlutterActivity:

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

Alternatively, if you use Java instead of Kotlin, update your MainActivity.java file with the following code:

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

For iOS

For FaceID to work on iOS, add the following line to the Info.plist file. This defines the message that tells the user why the app is seeking permission to authenticate with Face ID.

<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>

Demonstration Of Face Recognition In Action

Conclusion

In this tutorial, we’ve learned what biometric authentication is and how we can implement biometric authentication in Flutter apps using the local_auth plugin.

The complete source code of this project is available on GitHub.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Yusuf Ahmed Software engineer, technical writer, vibes.

One Reply to “Implementing face recognition and authentication in Flutter”

  1. Hi Yusuf, the tutorial is really a great contribution to the flutter development community.
    I want to ask one important question which is, “Can this project handle more than one user, or in simple words can the local auth lib save more than one or more user’s biometric data. Can we use this code in which multiple users can authenticate via this code?” or this code is for a single person.. If multiple users can be having the ability to register themselves from this app can you guide me? please reply me here or on my mail.

Leave a Reply