Harsh Mittal
Back to blog
2026-02-13ยท4 min read

๐Ÿ” Biometric Login in Flutter (Android & iOS) โ€” Complete Production Guide (2026)

SecurityFlutterAlso on Medium

Biometric Login in Flutter (Android & iOS) โ€” Complete Production Guide (2026)

In 2026, users expect secure and frictionless authentication. Password-only login is outdated. Modern apps use biometric authentication like fingerprint, Face ID, and device passcode for seamless security.

If you're building a Flutter app for fintech, health-tech, or ecommerce, biometric login is no longer optional โ€” it's expected.

This guide walks through understanding biometric authentication, Android and iOS setup, a production-ready Flutter implementation, a secure architecture approach, error handling and edge cases, and best practices for real apps.

Let's build it properly โ€” not just copy-paste code.

What Is Biometric Authentication?

Biometric authentication verifies users using fingerprint, face recognition (Face ID), or iris (rare in mobile).

Instead of storing biometric data in your app, the OS handles verification securely, and your app only receives a success/failure response.

Flutter uses the local_auth package for this.

Step 1: Add Required Package

Add this to pubspec.yaml:

dependencies:
  local_auth: ^2.1.6

Then run flutter pub get.

Android Configuration

1. Minimum SDK. Open android/app/build.gradle and ensure:

defaultConfig {
    minSdkVersion 23
}

2. Add permissions. Open android/app/src/main/AndroidManifest.xml and add inside <manifest>:

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

3. Device requirements. The device must have a lock screen enabled, and biometrics must be enrolled.

iOS Configuration

Open ios/Runner/Info.plist and add:

<key>NSFaceIDUsageDescription</key>
<string>We use Face ID to secure your account.</string>

Without this, Face ID will crash your app.

How Does local_auth Work Under the Hood?

local_auth checks device support, checks enrolled biometrics, shows the system biometric dialog, and returns the authentication result. Your app never accesses fingerprint data.

Production-Ready Architecture Approach

Instead of writing authentication inside UI, create:

lib/
โ”œโ”€โ”€ services/
โ”‚   โ””โ”€โ”€ biometric_service.dart
โ”œโ”€โ”€ screens/
โ”‚   โ””โ”€โ”€ login_screen.dart

Clean architecture always wins.

Step 2: Create Biometric Service

biometric_service.dart:

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

class BiometricService {
  final LocalAuthentication _auth = LocalAuthentication();

  /// Check if device supports biometrics
  Future<bool> isDeviceSupported() async {
    return await _auth.isDeviceSupported();
  }

  /// Check if biometrics are available & enrolled
  Future<bool> canCheckBiometrics() async {
    return await _auth.canCheckBiometrics;
  }

  /// Get available biometric types
  Future<List<BiometricType>> getAvailableBiometrics() async {
    return await _auth.getAvailableBiometrics();
  }

  /// Authenticate user
  Future<bool> authenticate() async {
    try {
      return await _auth.authenticate(
        localizedReason: 'Authenticate to login securely',
        options: const AuthenticationOptions(
          biometricOnly: true,
          stickyAuth: true,
          useErrorDialogs: true,
        ),
      );
    } on PlatformException catch (e) {
      print("Biometric Error: ${e.code}");
      return false;
    }
  }
}

Step 3: Create Login Screen

login_screen.dart:

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

class LoginScreen extends StatefulWidget {
  @override
  State<LoginScreen> createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final BiometricService _biometricService = BiometricService();
  bool _isAuthenticated = false;

  Future<void> _loginWithBiometrics() async {
    final isSupported = await _biometricService.isDeviceSupported();
    final canCheck = await _biometricService.canCheckBiometrics();

    if (!isSupported || !canCheck) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text("Biometric not available")),
      );
      return;
    }

    final authenticated = await _biometricService.authenticate();
    if (authenticated) {
      setState(() => _isAuthenticated = true);
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text("Authentication failed")),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Secure Login")),
      body: Center(
        child: _isAuthenticated
            ? const Text("Welcome Back!", style: TextStyle(fontSize: 22))
            : ElevatedButton(
                onPressed: _loginWithBiometrics,
                child: const Text("Login with Biometrics"),
              ),
      ),
    );
  }
}

Handling Edge Cases (Important for Interviews & Production)

1. No biometrics enrolled. If the user hasn't added a fingerprint:

if (availableBiometrics.isEmpty) {
  // Show dialog to guide user to settings
}

2. Locked out (too many attempts).

if (e.code == 'lockedOut') {
  // Show retry after timeout message
}

3. Provide a fallback option. Always allow PIN, password, or OTP. Never force biometrics only.

Secure Flow (Real-World Best Practice)

Production apps follow this pattern: first login via email/password, enable the biometric toggle, save the auth token securely (flutter_secure_storage), and on the next launch trigger biometrics โ€” if successful, use the stored token.

Never store the password locally.

Testing Biometric Login

On an emulator: Android Studio Emulator โ†’ Extended Controls โ†’ Fingerprint โ†’ Simulate touch.

On a real device, always test on Android (Samsung + Pixel), an iPhone with Face ID, and an iPhone with Touch ID.

UX Best Practices

Show a clear reason for authentication. Don't trigger biometrics automatically on every screen. Respect user consent. Allow a disable option. Don't overuse biometric prompts.

Common Mistakes Developers Make

Not checking device support. Not handling errors. Forcing biometrics without a fallback. Storing sensitive data insecurely. Triggering biometrics during app resume incorrectly.

When Should You Use Biometric Login?

Use it for fintech apps, banking apps, health apps, ecommerce saved cards, messaging apps, and enterprise apps.

Avoid using it for simple content apps or guest browsing apps.

Conclusion

Biometric authentication in Flutter is simple to implement โ€” but production-ready implementation requires proper device checks, clean architecture, secure storage, error handling, and fallback mechanisms.

When done correctly, it improves security, user experience, and app trustworthiness.

If you're preparing for Flutter interviews, this topic is frequently asked at mid and senior level.

Get new posts by email

No spam, no schedule โ€” just an email when a new post goes up.