import "package:flutter/material.dart"; import "../biometric.dart"; import "../main.dart"; import "../theme.dart"; /// Shown when the app is authenticated but locked (biometric login is enabled /// and the app was just launched or resumed). The session token is still valid; /// a successful biometric check simply reveals the app. "Use password" drops the /// session and returns to the login screen. class LockScreen extends StatefulWidget { const LockScreen({super.key}); @override State createState() => _LockScreenState(); } class _LockScreenState extends State { bool _busy = false; String? _error; @override void initState() { super.initState(); // Prompt once as soon as the lock screen appears. WidgetsBinding.instance.addPostFrameCallback((_) => _unlock()); } Future _unlock() async { if (_busy) return; setState(() { _busy = true; _error = null; }); try { final creds = await biometricAuth.unlock(reason: "Unlock DriverVault"); if (creds == null) { // Cancelled — leave locked; the buttons let them retry or use password. if (mounted) setState(() => _busy = false); return; } // Token from a previous session is still held by ApiClient; just reveal. authService.unlock(); } on BiometricException catch (e) { if (mounted) setState(() => _error = e.message); } catch (e) { if (mounted) setState(() => _error = e.toString()); } finally { if (mounted) setState(() => _busy = false); } } @override Widget build(BuildContext context) { final u = authService.user; final name = u == null ? null : (u.name.isNotEmpty ? u.name : u.email); return Scaffold( body: Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 64, height: 64, decoration: BoxDecoration( color: DriverVault.brand700, borderRadius: BorderRadius.circular(16), ), child: const Icon(Icons.lock_outline, color: Colors.white, size: 32), ), const SizedBox(height: 16), const Text("DriverVault is locked", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700, letterSpacing: -0.4)), if (name != null) Padding( padding: const EdgeInsets.only(top: 4), child: Text(name, style: TextStyle(color: DriverVault.muted(context))), ), const SizedBox(height: 24), if (_error != null) Padding( padding: const EdgeInsets.only(bottom: 12), child: Text(_error!, textAlign: TextAlign.center, style: const TextStyle(color: DriverVault.danger)), ), SizedBox( width: 260, child: FilledButton.icon( onPressed: _busy ? null : _unlock, icon: const Icon(Icons.fingerprint), label: Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Text(_busy ? "Unlocking…" : "Unlock"), ), ), ), const SizedBox(height: 8), TextButton( onPressed: _busy ? null : () => authService.logout(), child: const Text("Use password instead"), ), ], ), ), ), ); } }