simple_biometric 0.1.1
simple_biometric: ^0.1.1 copied to clipboard
A Flutter plugin for easy implementation of biometric authentication (fingerprint, face ID, etc.) on Android and iOS, with support for different biometric types and error handling.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:simple_biometric/simple_biometric.dart';
import 'package:simple_biometric/enums.dart';
void main() {
runApp(const BiometricApp());
}
/// The entry point for the "Biometric Dashboard" example application.
///
/// This app demonstrates the capabilities of the `simple_biometric` plugin
/// by detecting and listing available biometric hardware on the current device.
class BiometricApp extends StatelessWidget {
const BiometricApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Biometric Dashboard',
home: DashboardPage(),
);
}
}
class DashboardPage extends StatefulWidget {
const DashboardPage({super.key});
@override
State<DashboardPage> createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
final SimpleBiometric _simpleBiometric = SimpleBiometric();
String _platformVersion = 'Unknown';
bool _isAndroidHardwareAvailable = false;
IOSBiometricType _iosType = IOSBiometricType.none;
List<AndroidBiometricType> _androidTypes = [];
String _authStatus = "Not attempted";
@override
void initState() {
super.initState();
_loadData();
}
/// Loads biometric data from the platform.
///
/// This method checks:
/// 1. Platform version.
/// 2. Android: Hardware availability and specific types (Fingerprint, Face, Iris).
/// 3. iOS: Specific biometric type (FaceID, TouchID).
Future<void> _loadData() async {
String platformVersion;
bool isAndroidHardwareAvailable = false;
IOSBiometricType iosType = IOSBiometricType.none;
List<AndroidBiometricType> androidTypes = [];
try {
platformVersion = await _simpleBiometric.getPlatformVersion() ?? 'Unknown';
} catch (e) {
platformVersion = "Error: $e";
}
if (Platform.isAndroid) {
try {
isAndroidHardwareAvailable = await _simpleBiometric.isAndroidBiometricHardwareAvailable();
androidTypes = await _simpleBiometric.getAndroidBiometricTypes();
} catch (_) {}
} else if (Platform.isIOS) {
try {
iosType = await _simpleBiometric.getIOSBiometricType();
} catch (_) {}
}
if (mounted) {
setState(() {
_platformVersion = platformVersion;
_isAndroidHardwareAvailable = isAndroidHardwareAvailable;
_iosType = iosType;
_androidTypes = androidTypes;
});
}
}
/// Triggers the native biometric prompt for testing purposes.
///
/// [testName] is used to customize the dialog title and feedback messages.
Future<void> _testAuth(String testName) async {
setState(() {
_authStatus = "Testing $testName...";
});
try {
// API call to show the biometric prompt
final result = await _simpleBiometric.showBiometricPrompt(
title: 'Testing $testName',
description: 'Verify your identity to test $testName',
cancelText: 'Cancel',
);
setState(() {
_authStatus = "$testName Result: ${result.toString().split('.').last}";
if (result == BiometricStatusResult.authSuccess) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Success: $testName Verified"), backgroundColor: Colors.green));
}
});
} catch (e) {
setState(() {
_authStatus = "Error: $e";
});
}
}
@override
Widget build(BuildContext context) {
final isAndroid = Platform.isAndroid;
final isIOS = Platform.isIOS;
return Scaffold(
appBar: AppBar(title: const Text('Biometric Capabilities')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildInfoTile("Platform Detected", _platformVersion, Icons.perm_device_information),
if (_authStatus != "Not attempted")
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Container(
padding: const EdgeInsets.all(8),
color: Colors.grey.shade200,
child: Text(_authStatus, textAlign: TextAlign.center, style: const TextStyle(fontWeight: FontWeight.bold)),
),
),
const SizedBox(height: 10),
_buildSectionHeader("Library Support Matrix"),
const Text("Tap an active item to test.", style: TextStyle(color: Colors.grey, fontSize: 12)),
const SizedBox(height: 10),
// Android Capabilities
_buildCapabilityTile(
"Android Hardware Presence",
"BiometricManager.canAuthenticate()",
Icons.settings_system_daydream,
isActive: isAndroid && _isAndroidHardwareAvailable,
platformActive: isAndroid,
onTap: () => _testAuth("Android Hardware"),
),
_buildCapabilityTile(
"Android Fingerprint",
"PackageManager.FEATURE_FINGERPRINT",
Icons.fingerprint,
isActive: isAndroid && _androidTypes.contains(AndroidBiometricType.fingerprint),
platformActive: isAndroid,
onTap: () => _testAuth("Fingerprint"),
),
_buildCapabilityTile(
"Android Face",
"PackageManager.FEATURE_FACE",
Icons.face,
isActive: isAndroid && _androidTypes.contains(AndroidBiometricType.face),
platformActive: isAndroid,
onTap: () => _testAuth("Face"),
),
_buildCapabilityTile(
"Android Iris",
"PackageManager.FEATURE_IRIS",
Icons.visibility,
isActive: isAndroid && _androidTypes.contains(AndroidBiometricType.iris),
platformActive: isAndroid,
onTap: () => _testAuth("Iris"),
),
const Divider(),
// iOS Capabilities
_buildCapabilityTile(
"iOS Touch ID",
"LAContext.biometryType == .touchID",
Icons.fingerprint,
isActive: isIOS && _iosType == IOSBiometricType.touchId,
platformActive: isIOS,
onTap: () => _testAuth("Touch ID"),
),
_buildCapabilityTile(
"iOS Face ID",
"LAContext.biometryType == .faceID",
Icons.face,
isActive: isIOS && _iosType == IOSBiometricType.faceId,
platformActive: isIOS,
onTap: () => _testAuth("Face ID"),
),
],
),
);
}
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.only(top: 8, bottom: 4),
child: Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.blueGrey)),
);
}
Widget _buildInfoTile(String title, String value, IconData icon) {
return Card(
child: ListTile(
leading: Icon(icon, color: Colors.blue),
title: Text(title),
subtitle: Text(value),
),
);
}
Widget _buildCapabilityTile(String title, String subtitle, IconData icon, {required bool isActive, required bool platformActive, VoidCallback? onTap}) {
// If platform matches, show regular disabled/enabled colors.
// If platform doesn't match (e.g. Android tile on iOS), show it very dimmed to indicate irrelevance.
final Color iconColor = isActive ? Colors.green : (platformActive ? Colors.grey : Colors.grey.shade300);
final Color textColor = isActive ? Colors.black : (platformActive ? Colors.grey : Colors.grey.shade300);
final IconData statusIcon = isActive ? Icons.play_circle_fill : (platformActive ? Icons.cancel : Icons.block);
return ListTile(
leading: Icon(icon, color: iconColor),
title: Text(title, style: TextStyle(color: textColor, fontWeight: isActive ? FontWeight.bold : FontWeight.normal)),
subtitle: Text(subtitle, style: TextStyle(fontSize: 12, color: textColor.withValues(alpha: 0.7))),
trailing: isActive
? Icon(statusIcon, color: Colors.blue, size: 30)
: Icon(statusIcon, color: iconColor, size: 20),
onTap: isActive ? onTap : null,
tileColor: isActive ? Colors.blue.withValues(alpha: 0.05) : null,
);
}
}