FlutterFlow has no built-in biometric widget, but you can add Touch ID and Face ID support using the local_auth Flutter package inside a Custom Action. Add the package to pubspec.yaml, create a Custom Action that checks available biometrics then calls authenticate(), and wire it to your login flow in the Action Flow editor after Firebase Auth completes.
Adding Native Biometric Authentication to FlutterFlow Apps
Biometric authentication — fingerprint (Touch ID) and face recognition (Face ID on iOS, face unlock on Android) — adds a fast, secure second factor for your app's login flow. FlutterFlow does not include a built-in biometric widget, but its Custom Code panel lets you call any Flutter package, including the official local_auth package maintained by the Flutter team. The implementation pattern is: after a user completes Firebase Auth email/password login, check whether the user has biometrics enabled (stored as a field on their Firestore user document), then call your biometric Custom Action. If authentication succeeds, navigate to the home screen; if it fails, show an error or sign the user out. This guide walks through the full setup from adding the package to wiring the Action Flow.
Prerequisites
- FlutterFlow project with Firebase Authentication already configured
- Users Firestore collection with a biometricEnabled boolean field on user documents
- Basic familiarity with FlutterFlow Action Flows and Custom Actions
- An iOS physical device or Android device for testing (biometrics do not work in simulators)
- FlutterFlow Pro plan for code export (required to modify iOS entitlements and Info.plist)
Step-by-step guide
Add local_auth to pubspec.yaml
Add local_auth to pubspec.yaml
Open the Custom Code panel (</> icon in the left sidebar). Click the 'pubspec.yaml' tab at the top of the panel. Under the 'dependencies' section, add a new line: 'local_auth: ^2.2.0'. After typing the dependency, tap the 'Get Packages' button and wait for the success message. This installs the local_auth package into your FlutterFlow project's build environment. The local_auth package is maintained by the Flutter team and supports fingerprint, Face ID, and iris authentication on both iOS and Android. Do not proceed to write Custom Actions until Get Packages completes successfully.
Expected result: The pubspec.yaml tab shows local_auth listed under dependencies and Get Packages completes with a green success indicator.
Create the checkBiometrics Custom Action
Create the checkBiometrics Custom Action
In the Custom Code panel, open the 'Custom Actions' tab and tap '+' to create a new action. Name it 'checkBiometrics'. This action will check whether the device supports biometrics and which types are available — it does not authenticate yet. Add no parameters. Set the return type to 'Boolean'. In the code editor, write the logic that instantiates LocalAuthentication, calls canCheckBiometrics, and calls getAvailableBiometrics() to get the list. Return true only if biometrics are both supported and enrolled. Tap Compile and confirm no errors. This step is critical — calling authenticate() on a device with no enrolled biometrics throws an error, so you must always check first.
1import 'package:local_auth/local_auth.dart';23Future<bool> checkBiometrics() async {4 final LocalAuthentication auth = LocalAuthentication();5 try {6 final bool canCheck = await auth.canCheckBiometrics;7 if (!canCheck) return false;8 final List<BiometricType> available =9 await auth.getAvailableBiometrics();10 return available.isNotEmpty;11 } catch (e) {12 debugPrint('checkBiometrics error: $e');13 return false;14 }15}Expected result: checkBiometrics action compiles successfully with Boolean return type.
Create the authenticateWithBiometrics Custom Action
Create the authenticateWithBiometrics Custom Action
Still in the Custom Actions tab, tap '+' to create a second action named 'authenticateWithBiometrics'. This action performs the actual biometric prompt. Add one String parameter named 'reason' — this is the message shown to the user in the system biometric dialog (e.g., 'Verify your identity to continue'). Set the return type to Boolean. Write the Dart body that calls auth.authenticate() with the localizedReason, stickyAuth set to true (keeps the prompt alive if the user switches apps briefly), and biometricOnly set to true (disables PIN/password fallback if you want pure biometric-only). Return the result directly. Tap Compile.
1import 'package:local_auth/local_auth.dart';2import 'package:local_auth/error_codes.dart' as auth_error;3import 'package:flutter/services.dart';45Future<bool> authenticateWithBiometrics(String reason) async {6 final LocalAuthentication auth = LocalAuthentication();7 try {8 return await auth.authenticate(9 localizedReason: reason,10 options: const AuthenticationOptions(11 stickyAuth: true,12 biometricOnly: false, // false = allow PIN fallback13 useErrorDialogs: true,14 ),15 );16 } on PlatformException catch (e) {17 if (e.code == auth_error.notEnrolled) {18 debugPrint('No biometrics enrolled on this device.');19 } else if (e.code == auth_error.lockedOut) {20 debugPrint('Biometrics locked out — too many failed attempts.');21 } else {22 debugPrint('authenticateWithBiometrics error: ${e.code} ${e.message}');23 }24 return false;25 }26}Expected result: authenticateWithBiometrics action compiles successfully with a String parameter and Boolean return type.
Configure platform permissions
Configure platform permissions
Biometric APIs require platform-level permissions that must be declared in the iOS Info.plist and AndroidManifest.xml. In FlutterFlow, go to Settings (gear icon) → App Details → Permissions. Enable the 'Face ID Usage' permission for iOS — this adds the NSFaceIDUsageDescription key to Info.plist automatically. For Android, the local_auth package's own AndroidManifest additions are usually handled automatically when you add the package. If you are using Code Export (Pro plan), verify that android/app/src/main/AndroidManifest.xml contains the USE_BIOMETRIC and USE_FINGERPRINT permissions, and that ios/Runner/Info.plist contains NSFaceIDUsageDescription with a user-visible string.
Expected result: Face ID permission is listed under iOS permissions in FlutterFlow Settings. Android permissions are confirmed in the manifest.
Wire the biometric flow in the Login Action Flow
Wire the biometric flow in the Login Action Flow
Navigate to your Login page and find the Login button. Open its Action Flow editor. You should already have a 'Log In' action (Firebase Auth) as the first step. After the Log In action, add a conditional: tap '+' → 'Conditional Action' → set the condition to check the current user's 'biometricEnabled' Firestore field. In the TRUE branch, add a 'Custom Action' step and choose 'checkBiometrics'. After checkBiometrics, add another conditional checking its output (true/false). In the TRUE branch of that, add 'Custom Action' → 'authenticateWithBiometrics', set the reason parameter to 'Verify your identity to sign in'. Add a final conditional on the auth result — TRUE branch navigates to the home screen, FALSE branch shows a Snackbar error and signs the user out via the Sign Out action.
Expected result: The Login button's Action Flow shows: Log In → check biometricEnabled → checkBiometrics → authenticateWithBiometrics → navigate home or sign out.
Test on a physical device via Run Mode
Test on a physical device via Run Mode
Biometric authentication does not work in the FlutterFlow canvas preview or in iOS Simulator / Android emulators without special configuration. Tap 'Run' in the FlutterFlow toolbar to build and run the app on a connected physical device. On iOS, connect your iPhone via USB and select it as the run target. On Android, enable Developer Mode and USB Debugging in phone settings then connect via USB. Go through the login flow: sign in with a test account that has biometricEnabled set to true in Firestore. The system biometric dialog should appear. Test both success (valid fingerprint/face) and failure (cancel or wrong finger) paths to verify the Action Flow branches work correctly.
Expected result: On a physical device, the system biometric dialog appears after Firebase login for users with biometricEnabled: true, and the app navigates to the home screen on successful authentication.
Complete working example
1// ─── Custom Action: checkBiometrics ─────────────────────────────────────────2// Returns true if the device has biometrics available and enrolled.3// Call this BEFORE authenticateWithBiometrics.4// pubspec.yaml: local_auth: ^2.2.056import 'package:local_auth/local_auth.dart';78Future<bool> checkBiometrics() async {9 final LocalAuthentication auth = LocalAuthentication();10 try {11 final bool canCheck = await auth.canCheckBiometrics;12 if (!canCheck) {13 debugPrint('checkBiometrics: device does not support biometrics.');14 return false;15 }16 final List<BiometricType> available =17 await auth.getAvailableBiometrics();18 debugPrint('checkBiometrics: available types = $available');19 return available.isNotEmpty;20 } catch (e) {21 debugPrint('checkBiometrics error: $e');22 return false;23 }24}2526// ─── Custom Action: authenticateWithBiometrics ───────────────────────────────27// Parameters: reason (String, required)28// Returns true on successful authentication, false on failure or cancellation.2930import 'package:local_auth/local_auth.dart';31import 'package:local_auth/error_codes.dart' as auth_error;32import 'package:flutter/services.dart';3334Future<bool> authenticateWithBiometrics(String reason) async {35 final LocalAuthentication auth = LocalAuthentication();36 try {37 final bool result = await auth.authenticate(38 localizedReason: reason,39 options: const AuthenticationOptions(40 stickyAuth: true,41 biometricOnly: false,42 useErrorDialogs: true,43 ),44 );45 debugPrint('authenticateWithBiometrics: result = $result');46 return result;47 } on PlatformException catch (e) {48 if (e.code == auth_error.notEnrolled) {49 debugPrint('No biometrics enrolled — falling back.');50 } else if (e.code == auth_error.lockedOut) {51 debugPrint('Biometrics temporarily locked out.');52 } else if (e.code == auth_error.permanentlyLockedOut) {53 debugPrint('Biometrics permanently locked — user must use PIN.');54 } else {55 debugPrint('Biometric error: ${e.code} / ${e.message}');56 }57 return false;58 } catch (e) {59 debugPrint('Unexpected biometric error: $e');60 return false;61 }62}6364// ─── pubspec.yaml (Custom Code → pubspec.yaml tab) ───────────────────────────65// dependencies:66// local_auth: ^2.2.067//68// iOS: Settings → App Details → Permissions → enable Face ID Usage69// Android: permissions handled automatically by the local_auth packageCommon mistakes when adding Biometric Authentication in FlutterFlow
Why it's a problem: Calling authenticateWithBiometrics() without first checking canCheckBiometrics and getAvailableBiometrics()
How to avoid: Always run the checkBiometrics Custom Action first and check its return value. Only call authenticateWithBiometrics if checkBiometrics returns true. Add this as a conditional step in your Action Flow before the authentication action.
Why it's a problem: Testing biometric authentication in the FlutterFlow canvas preview or iOS Simulator
How to avoid: Always test biometric features on a physical iOS or Android device using FlutterFlow's Run Mode. Connect the device via USB and select it as the run target.
Why it's a problem: Forgetting to add the NSFaceIDUsageDescription permission for iOS
How to avoid: In FlutterFlow Settings → App Details → Permissions, enable 'Face ID Usage' and enter a descriptive reason string. For FlutterFlow Pro with code export, verify the key exists in ios/Runner/Info.plist.
Why it's a problem: Setting biometricOnly: true without providing a PIN fallback path in the Action Flow
How to avoid: Either set biometricOnly: false to allow system PIN fallback, or add an explicit 'Sign in with Password' alternative path in your app's UI so users are never locked out.
Best practices
- Store biometric preference as a Firestore field on the user document rather than in local app state — this allows the preference to sync across user's devices and persist across app reinstalls.
- Always offer an alternative login method (email/password or magic link) alongside biometrics — biometric hardware can fail, be locked out, or be unavailable on older devices.
- Wrap biometric calls in try/catch and handle specific PlatformException error codes (notEnrolled, lockedOut, permanentlyLockedOut) with user-friendly messages rather than silent failures.
- Use stickyAuth: true in AuthenticationOptions to keep the biometric dialog visible if the user briefly switches apps — without this, the dialog dismisses and the login fails.
- Never use biometrics as the sole authentication factor for high-value actions like payment authorization — combine with a server-side session check to ensure the biometric succeeded on a legitimate session.
- Test the full failure path: cancel the biometric dialog, provide wrong fingerprint, and verify the app signs the user out and shows a helpful error rather than silently hanging.
- Log biometric authentication events (success/failure, error codes) to Firestore for security auditing, but never log biometric data itself — only the outcome.
- For apps targeting enterprise users, check getAvailableBiometrics() and display the appropriate icon (fingerprint or face) in your UI so users know which sensor to use.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a FlutterFlow app with Firebase Authentication and want to add biometric authentication as a second factor. After the user logs in with email/password, I want to check if they have biometrics enabled (stored as a boolean field in their Firestore user document), then prompt for fingerprint or Face ID. Please write two Dart functions for FlutterFlow Custom Actions: one that checks biometric availability (checkBiometrics, returns bool) and one that runs the authentication prompt (authenticateWithBiometrics, takes a String reason, returns bool). Use the local_auth ^2.2.0 package.
Write a FlutterFlow Custom Action for biometric authentication using local_auth ^2.2.0. The action is called 'authenticateWithBiometrics', takes one String parameter named 'reason', and returns Boolean. It should handle the notEnrolled and lockedOut PlatformException error codes with debugPrint messages and return false on any error. Write only the function body — I will paste it into the FlutterFlow Custom Code editor.
Frequently asked questions
Does FlutterFlow have a built-in biometric authentication widget?
No. FlutterFlow does not include a native biometric widget in its widget palette. You implement biometric authentication using the local_auth Flutter package inside a Custom Action, which you write in the Custom Code panel. The Custom Action integrates with your visual Action Flow just like any built-in action.
Does biometric authentication work on iOS Simulator?
Partially. iOS Simulator can simulate Touch ID via Hardware → Touch ID → Matching Touch, but Face ID simulation is unreliable. The local_auth package may behave differently in the simulator than on a real device. Always perform final testing on a physical iPhone or iPad to confirm the full biometric flow works correctly.
What biometric types does local_auth support?
local_auth supports fingerprint (Touch ID on iOS, fingerprint scanner on Android), face recognition (Face ID on iOS, face unlock on Android), and iris scanning (on supported Samsung devices). The getAvailableBiometrics() method returns a list of BiometricType values: fingerprint, face, and iris. You can check this list to customize your UI.
Can I require biometrics on every app launch or only at login?
Yes. You can add the biometric Custom Action to the Page Load action of any page, not just the login screen. A common pattern is to add it to the home screen's 'On Page Load' action and check when the user last authenticated (stored in app state). If the session is older than a threshold (e.g., 15 minutes), prompt for biometrics again.
What happens if the user's device does not support biometrics?
Your checkBiometrics Custom Action returns false, and your Action Flow conditional skips the biometric prompt, taking the user directly to the home screen. This is the correct behavior — biometrics should enhance security for users who have it, not block users who do not.
Do I need FlutterFlow Pro to use biometric authentication?
You can write and compile the Custom Action on any FlutterFlow plan. However, testing on iOS requires the Run Mode feature and proper Info.plist configuration (NSFaceIDUsageDescription), which may require Pro plan access or Code Export for manual iOS configuration. Android testing via Run Mode works on Free plans.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation