small fix, don't worry about it

This commit is contained in:
Tomas Dvorak
2026-04-10 12:05:40 +02:00
parent 7b7ed0083f
commit 5ab2773f98
55 changed files with 3240 additions and 483 deletions
@@ -0,0 +1,347 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/widgets/app_scaffold.dart';
import '../../../data/services/biometric_service.dart';
import '../../auth/application/auth_controller.dart';
import 'package:local_auth/local_auth.dart' as local_auth;
class BiometricSettingsScreen extends ConsumerStatefulWidget {
const BiometricSettingsScreen({super.key});
@override
ConsumerState<BiometricSettingsScreen> createState() => _BiometricSettingsScreenState();
}
class _BiometricSettingsScreenState extends ConsumerState<BiometricSettingsScreen> {
final BiometricService _biometricService = BiometricService();
bool _isLoading = false;
BiometricAvailability? _availability;
bool _isEnabled = false;
String _statusMessage = '';
List<local_auth.BiometricType> _availableBiometrics = [];
local_auth.BiometricType? _primaryBiometricType;
@override
void initState() {
super.initState();
_loadBiometricStatus();
}
Future<void> _loadBiometricStatus() async {
setState(() => _isLoading = true);
try {
final availability = await _biometricService.checkAvailability();
final isEnabled = await _biometricService.isBiometricEnabled();
final statusMessage = await _biometricService.getBiometricStatusMessage();
final availableBiometrics = await _biometricService.getAvailableBiometrics();
final primaryBiometricType = await _biometricService.getPrimaryBiometricType();
setState(() {
_availability = availability;
_isEnabled = isEnabled;
_statusMessage = statusMessage;
_availableBiometrics = availableBiometrics;
_primaryBiometricType = primaryBiometricType;
_isLoading = false;
});
} catch (e) {
setState(() => _isLoading = false);
}
}
Future<void> _toggleBiometric() async {
if (_availability != BiometricAvailability.available) {
return;
}
setState(() => _isLoading = true);
try {
final authController = ref.read(authControllerProvider.notifier);
if (_isEnabled) {
// Disable biometric
final success = await authController.disableBiometric();
if (success) {
setState(() => _isEnabled = false);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Biometric login disabled')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Failed to disable biometric login')),
);
}
} else {
// Enable biometric
final success = await authController.enableBiometric();
if (success) {
setState(() => _isEnabled = true);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Biometric login enabled successfully')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Failed to enable biometric login')),
);
}
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: ${e.toString()}')),
);
} finally {
setState(() => _isLoading = false);
}
}
Future<void> _testBiometric() async {
if (!_isEnabled) return;
setState(() => _isLoading = true);
try {
final authController = ref.read(authControllerProvider.notifier);
final success = await authController.signInWithBiometric();
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Biometric login successful')),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Biometric login failed')),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: ${e.toString()}')),
);
} finally {
setState(() => _isLoading = false);
}
}
String _getBiometricTypeEmoji(local_auth.BiometricType type) {
switch (type) {
case local_auth.BiometricType.fingerprint:
return '👆';
case local_auth.BiometricType.face:
return '👤';
case local_auth.BiometricType.iris:
return '👁️';
default:
return '🔒';
}
}
String _getBiometricTypeName(local_auth.BiometricType type) {
switch (type) {
case local_auth.BiometricType.fingerprint:
return 'Fingerprint';
case local_auth.BiometricType.face:
return 'Face ID';
case local_auth.BiometricType.iris:
return 'Iris Scanner';
default:
return 'Biometric';
}
}
Color _getStatusColor() {
switch (_availability) {
case BiometricAvailability.available:
return _isEnabled ? Colors.green : Colors.orange;
case BiometricAvailability.notAvailable:
return Colors.grey;
case BiometricAvailability.notEnrolled:
return Colors.orange;
case BiometricAvailability.lockedOut:
return Colors.red;
case BiometricAvailability.permanentlyUnavailable:
return Colors.red;
case null:
return Colors.grey;
}
}
@override
Widget build(BuildContext context) {
return AppScaffold(
title: 'Biometric Login',
body: _isLoading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.all(16),
children: [
// Status Card
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
_primaryBiometricType != null
? Icons.fingerprint
: Icons.lock,
size: 32,
color: _getStatusColor(),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Biometric Status',
style: Theme.of(context).textTheme.titleMedium,
),
Text(
_statusMessage,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: _getStatusColor(),
),
),
],
),
),
],
),
const SizedBox(height: 16),
if (_availability == BiometricAvailability.available)
SwitchListTile(
title: const Text('Enable Biometric Login'),
subtitle: const Text('Use fingerprint or face ID for quick access'),
value: _isEnabled,
onChanged: (value) => _toggleBiometric(),
secondary: Icon(
_isEnabled ? Icons.lock_open : Icons.lock,
color: _getStatusColor(),
),
),
],
),
),
),
const SizedBox(height: 16),
// Available Biometrics
if (_availableBiometrics.isNotEmpty)
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Available Biometrics',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
..._availableBiometrics.map((type) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(
_getBiometricTypeEmoji(type),
style: const TextStyle(fontSize: 24),
),
const SizedBox(width: 12),
Text(
_getBiometricTypeName(type),
style: Theme.of(context).textTheme.bodyLarge,
),
const Spacer(),
if (type == _primaryBiometricType)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
borderRadius: BorderRadius.circular(12),
),
child: Text(
'Primary',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
],
),
)),
],
),
),
),
const SizedBox(height: 16),
// Test Biometric (if enabled)
if (_isEnabled)
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Test Biometric Login',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 8),
Text(
'Test your biometric authentication to make sure it\'s working properly.',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: _testBiometric,
icon: const Icon(Icons.fingerprint),
label: const Text('Test Biometric Login'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
],
),
),
),
const SizedBox(height: 16),
// Information Card
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'About Biometric Login',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 12),
Text(
'• Biometric login allows you to sign in quickly using your fingerprint or face ID.\n'
'• Your biometric data is stored securely on your device and never sent to our servers.\n'
'• You can disable biometric login at any time in these settings.\n'
'• If you change your password, you may need to re-enable biometric login.',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
),
],
),
);
}
}