Blog

Autenticación biométrica en Flutter: login con huella y Face ID

La autenticación biométrica en Flutter es una de esas features que, bien implementada, mejora dos cosas a la vez: seguridad y UX. El usuario desbloquea con huella o Face ID en segundos y tú reduces fricción sin caer en atajos inseguros como “guardar la password” o inventarte un PIN pobre.

En este tutorial vamos a implementar autenticación biométrica en Flutter de forma completa: desde dependencias y permisos en Android/iOS, hasta un helper reutilizable y una pantalla de login que muestra el botón biométrico solo cuando tiene sentido. También vamos a cubrir lo más importante para producción: qué protege realmente la biometría, qué no, y cómo evitar errores típicos.

Autenticación biométrica en Flutter: login con huella y Face ID | 4

Qué protege de verdad la autenticación biométrica en Flutter

Antes de tocar código, la regla de oro: la biometría no “logea” contra tu backend. La autenticación biométrica en Flutter lo que hace es pedirle al sistema operativo que verifique que el usuario es quien dice ser (con hardware y APIs del dispositivo) y devolver un true/false.

Lo correcto es usar esa verificación para:

  • Desbloquear una sesión ya existente (por ejemplo, un refresh token almacenado en Keychain/Keystore)
  • Confirmar operaciones sensibles (pago, transferencia, cambio de email, exportación de datos)
  • Reautenticar al abrir la app tras X minutos en background

Lo incorrecto es:

  • Tratar la biometría como sustituto de credenciales o de una sesión validada por servidor
  • Guardar “algo equivalente a una password” en texto plano y protegerlo solo con biometría

Planteado así, la autenticación biométrica en Flutter se convierte en un “unlock” seguro y cómodo.

Cómo funciona el flujo biométrico en Flutter

El flujo típico de autenticación biométrica en Flutter es:

  • UI Flutter solicita autenticación
  • Plugin local_auth delega en el sistema operativo
  • iOS/Android invoca el sensor (Face ID, Touch ID, huella)
  • El sistema devuelve éxito o fallo
  • Tu app decide qué hacer (abrir sesión, mostrar error, pedir fallback) Secure Biometric Authentication…

Este diseño tiene una ventaja clave: tu app no gestiona biometría directamente. Ni almacenas huellas ni “ves” la cara. Eso simplifica compliance y reduce superficie de ataque.

Requisitos previos

Para implementar autenticación biométrica en Flutter:

  • Proyecto Flutter creado
  • Dispositivo real para testing (en simulador puede ser poco fiable)
  • Biometría configurada en el dispositivo (si el usuario no la ha activado, no hay magia)

Paso 1. Crear el proyecto y añadir la dependencia

Creamos el proyecto:

flutter create biometrics_poc
cd biometrics_poc

Añadimos la dependencia en pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  local_auth: ^3.0.0

Instalamos:

flutter pub get

Con esto ya podemos empezar la autenticación biométrica en Flutter.


Paso 2. Configurar permisos en Android

En Android, necesitas declarar permisos para biometría. Edita:

android/app/src/main/AndroidManifest.xml

Dentro de <manifest> añade:

<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-feature android:name="android.hardware.fingerprint" android:required="false" />
  • USE_BIOMETRIC cubre métodos modernos
  • USE_FINGERPRINT cubre compatibilidad con implementaciones más antiguas
  • uses-feature con required="false" evita que Play Store marque tu app como incompatible en dispositivos sin sensor Secure Biometric Authentication…

Este punto es básico para que la autenticación biométrica en Flutter sea “robusta” y no te rompa distribución.

Paso 3. Configurar permisos en iOS

En iOS debes incluir la razón de uso de Face ID/Touch ID. Edita:

ios/Runner/Info.plist

Añade:

<key>NSFaceIDUsageDescription</key>
<string>Autentícate para continuar.</string>

Este string es el texto que verá el usuario cuando el sistema pida permiso. Si es vago o raro, baja la confianza. Si es claro, sube la conversión del primer intento. Secure Biometric Authentication…

Con esto ya tienes la base para autenticación biométrica en Flutter en iOS.

Imagen de cabecera de aplicaciones innovadoras creadas con Flutter

Paso 4. Crear un helper reutilizable

Para que la autenticación biométrica en Flutter no quede pegada a una pantalla, conviene encapsular la lógica en un helper.

Crea: lib/helpers/biometric_helper.dart

import 'package:local_auth/local_auth.dart';

class BiometricHelper {
  final LocalAuthentication _localAuth = LocalAuthentication();

  Future<bool> isDeviceCapable() async {
    try {
      return await _localAuth.isDeviceSupported();
    } catch (_) {
      return false;
    }
  }

  Future<bool> isBiometricEnabled() async {
    try {
      final available = await _localAuth.getAvailableBiometrics();
      return available.isNotEmpty;
    } catch (_) {
      return false;
    }
  }

  Future<String?> getBiometricTypeLabel() async {
    try {
      final available = await _localAuth.getAvailableBiometrics();
      if (available.isEmpty) return null;

      if (available.contains(BiometricType.face)) {
        return 'Face ID';
      }
      if (available.contains(BiometricType.fingerprint)) {
        return 'Touch ID';
      }
      return 'Biometría';
    } catch (_) {
      return null;
    }
  }

  Future<bool> authenticate({required String reason}) async {
    try {
      return await _localAuth.authenticate(
        localizedReason: reason,
      );
    } catch (_) {
      return false;
    }
  }
}

Este helper cubre lo esencial para autenticación biométrica en Flutter:

  • Capacidad del dispositivo
  • Biometría configurada
  • Etiqueta para UI
  • Trigger de autenticación Secure Biometric Authentication…

Paso 5. Integrar la autenticación biométrica en la UI de login

Ahora montamos una pantalla de login que:

  • Comprueba estado biométrico al cargar
  • Muestra botón biométrico solo si procede
  • Ejecuta autenticación y actúa en consecuencia

Ejemplo de LoginPage minimalista:

import 'package:flutter/material.dart';
import 'helpers/biometric_helper.dart';

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

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  final _biometricHelper = BiometricHelper();

  bool _isDeviceCapable = false;
  bool _isBiometricEnabled = false;
  String? _biometricTypeLabel;

  bool _loading = false;
  String? _message;

  bool get _canUseBiometric => _isDeviceCapable && _isBiometricEnabled;

  @override
  void initState() {
    super.initState();
    _checkBiometricStatus();
  }

  Future<void> _checkBiometricStatus() async {
    final capable = await _biometricHelper.isDeviceCapable();
    final enabled = await _biometricHelper.isBiometricEnabled();
    final label = await _biometricHelper.getBiometricTypeLabel();

    if (!mounted) return;
    setState(() {
      _isDeviceCapable = capable;
      _isBiometricEnabled = enabled;
      _biometricTypeLabel = label;
    });
  }

  Future<void> _handleBiometricLogin() async {
    setState(() {
      _loading = true;
      _message = null;
    });

    final ok = await _biometricHelper.authenticate(
      reason: 'Autentícate para iniciar sesión',
    );

    if (!mounted) return;
    setState(() {
      _loading = false;
      _message = ok ? 'Autenticación correcta' : 'No se ha podido autenticar';
    });

    if (ok) {
      // Aquí normalmente desbloqueas un token local y navegas al home.
      // Navigator.of(context).pushReplacement(...);
    }
  }

  @override
  Widget build(BuildContext context) {
    final biometricText = _biometricTypeLabel != null
        ? 'Entrar con $_biometricTypeLabel'
        : 'Entrar con biometría';

    return Scaffold(
      appBar: AppBar(title: const Text('Login')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            const SizedBox(height: 16),

            // Login clásico (placeholder)
            TextField(
              decoration: const InputDecoration(labelText: 'Email'),
              keyboardType: TextInputType.emailAddress,
            ),
            const SizedBox(height: 12),
            TextField(
              decoration: const InputDecoration(labelText: 'Password'),
              obscureText: true,
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: _loading ? null : () {},
              child: const Text('Entrar'),
            ),

            const SizedBox(height: 24),

            if (_canUseBiometric)
              OutlinedButton(
                onPressed: _loading ? null : _handleBiometricLogin,
                child: _loading
                    ? const SizedBox(
                        height: 18,
                        width: 18,
                        child: CircularProgressIndicator(strokeWidth: 2),
                      )
                    : Text(biometricText),
              )
            else
              Text(
                _isDeviceCapable
                    ? 'Biometría no configurada en el dispositivo'
                    : 'Dispositivo sin soporte biométrico',
              ),

            if (_message != null) ...[
              const SizedBox(height: 12),
              Text(_message!),
            ],
          ],
        ),
      ),
    );
  }
}

Este patrón es el “mínimo viable” para autenticación biométrica en Flutter sin ensuciar la UI con lógica de plataforma.

Autenticación biométrica en Flutter: login con huella y Face ID | 5

Paso 6. Conectar la pantalla en main.dart

En main.dart:

import 'package:flutter/material.dart';
import 'login_page.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: LoginPage(),
    );
  }
}

Ya tienes autenticación biométrica en Flutter funcionando end-to-end.

Paso 7. Testing de la autenticación biométrica en Flutter

Para testear bien:

  • Android: dispositivo físico con huella o face unlock activado. Deberías ver el prompt del sistema al pulsar el botón biométrico
  • iOS: dispositivo físico con Face ID o Touch ID configurado. Al pulsar, debería saltar el diálogo del sistema
  • Simuladores: pueden fallar o comportarse distinto; para una validación seria, prioriza dispositivos reales Secure Biometric Authentication…

Si en un dispositivo real no aparece el botón biométrico, revisa:

  • El usuario no tiene biometría configurada
  • Falta un permiso en AndroidManifest
  • Falta NSFaceIDUsageDescription en iOS

Buenas prácticas de seguridad para producción

Si vas a llevar autenticación biométrica en Flutter a producción, estas son las decisiones que separan un “demo” de un producto serio.

No guardes secretos en storage normal

La biometría debe desbloquear secretos guardados en almacenamiento seguro (Keychain/Keystore). La autenticación biométrica en Flutter solo verifica identidad; el secreto lo guardas en una capa segura.

Ofrece fallback sin drama

Si la biometría falla o el usuario cancela, la app debe seguir funcionando con email/password o con el método principal que definas. La autenticación biométrica en Flutter es convenience, no bloqueo.

Textos claros en prompts

El localizedReason tiene que explicar el “para qué” de forma directa. Prompts confusos bajan la tasa de éxito del primer intento.

Usa biometría para operaciones sensibles

Además del login, la autenticación biométrica en Flutter encaja muy bien para confirmar acciones de alto riesgo: mostrar datos privados, exportar, borrar cuenta, cambiar método de pago.

Autenticación biométrica en Flutter: login con huella y Face ID | 6

Errores típicos y cómo evitarlos

  • Mostrar el botón siempre
    No lo hagas. La autenticación biométrica en Flutter debe mostrarse solo si el dispositivo es capaz y el usuario la ha configurado.
  • Confundir “capaz” con “habilitado”
    Un móvil puede soportar biometría pero el usuario no la ha activado. Por eso separamos isDeviceCapable e isBiometricEnabled.
  • No contemplar cancelación
    Cancelar no es “error”; es un resultado esperado. Trátalo como tal.
  • No testear en hardware real
    Para validación real de autenticación biométrica en Flutter, el dispositivo físico manda.

Conclusión

Con este enfoque tienes una implementación sólida de autenticación biométrica en Flutter: permisos correctos en Android e iOS, helper reutilizable, UI que enseña el botón cuando toca y un flujo listo para crecer hacia un login real (desbloqueo de token, refresh de sesión y operaciones sensibles).

Si quieres llevar la autenticación biométrica en Flutter al siguiente nivel, el siguiente paso lógico es conectar este flujo con tu capa de sesión: guardar tokens en almacenamiento seguro y usar biometría como “unlock” al abrir la app o antes de acciones críticas.

Compartir en:

From offline to online.

Comparte tus ideas con nosotros