flutter_biometric_crypto 0.1.4 copy "flutter_biometric_crypto: ^0.1.4" to clipboard
flutter_biometric_crypto: ^0.1.4 copied to clipboard

A Flutter package for biometric-protected encryption of small secrets using Android Keystore and iOS Keychain/Secure Enclave.

example/lib/main.dart

import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_biometric_crypto/flutter_biometric_crypto.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Biometric Crypto',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blueAccent),
        useMaterial3: true,
        appBarTheme: const AppBarTheme(
          centerTitle: true,
          elevation: 2,
        ),
        cardTheme: CardTheme(
          elevation: 2,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(12),
          ),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
          ),
        ),
      ),
      home: const BiometricCryptoPage(),
    );
  }
}

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

  @override
  State<BiometricCryptoPage> createState() => _BiometricCryptoPageState();
}

class _BiometricCryptoPageState extends State<BiometricCryptoPage> {
  String _status = 'Ready';
  String? _encryptedDataHex;
  String? _decryptedData;
  bool _isBiometricAvailable = false;

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

  Future<void> _checkBiometricAvailability() async {
    try {
      final available = await FlutterBiometricCrypto.isBiometricAvailable();
      setState(() {
        _isBiometricAvailable = available;
        _status = available
            ? 'Biometric authentication is available'
            : 'Biometric authentication is not available';
      });
    } catch (e) {
      setState(() {
        _status = 'Error checking biometric availability: $e';
      });
    }
  }

  Future<void> _initKey() async {
    setState(() {
      _status = 'Initializing key...';
    });

    try {
      await FlutterBiometricCrypto.initKey();
      setState(() {
        _status = 'Key initialized successfully';
      });
    } catch (e) {
      setState(() {
        _status = 'Error initializing key: $e';
      });
    }
  }

  Future<void> _encryptData() async {
    setState(() {
      _status = 'Encrypting data...';
      _encryptedDataHex = null;
      _decryptedData = null;
    });

    try {
      // Sample data to encrypt
      const sampleText = 'Hello, Flutter Biometric Crypto!';
      final data = Uint8List.fromList(sampleText.codeUnits);

      final encrypted = await FlutterBiometricCrypto.encrypt(data);
      final hexString = encrypted
          .map((byte) => byte.toRadixString(16).padLeft(2, '0'))
          .join(' ');

      setState(() {
        _encryptedDataHex = hexString;
        _status = 'Data encrypted successfully';
      });
    } on DataTooLargeException catch (e) {
      setState(() {
        _status = 'Error: Data too large - $e';
      });
    } on KeyNotFoundException catch (e) {
      setState(() {
        _status = 'Error: Key not found - $e. Please initialize key first.';
      });
    } catch (e) {
      setState(() {
        _status = 'Error encrypting data: $e';
      });
    }
  }

  Future<void> _decryptData() async {
    if (_encryptedDataHex == null) {
      setState(() {
        _status = 'Please encrypt data first';
      });
      return;
    }

    setState(() {
      _status = 'Decrypting data (biometric authentication required)...';
      _decryptedData = null;
    });

    try {
      // Convert hex string back to bytes
      final hexParts = _encryptedDataHex!.split(' ');
      final encrypted = Uint8List.fromList(
        hexParts.map((hex) => int.parse(hex, radix: 16)).toList(),
      );

      final decrypted = await FlutterBiometricCrypto.decrypt(encrypted);
      final decryptedText = String.fromCharCodes(decrypted);

      setState(() {
        _decryptedData = decryptedText;
        _status = 'Data decrypted successfully';
      });
    } on BiometricNotAvailableException catch (e) {
      setState(() {
        _status = 'Error: Biometric not available - $e';
      });
    } on BiometricAuthenticationFailedException catch (e) {
      setState(() {
        _status = 'Error: Biometric authentication failed - $e';
      });
    } on KeyNotFoundException catch (e) {
      setState(() {
        _status = 'Error: Key not found - $e';
      });
    } catch (e) {
      setState(() {
        _status = 'Error decrypting data: $e';
      });
    }
  }

  Future<void> _deleteKey() async {
    setState(() {
      _status = 'Deleting key...';
    });

    try {
      await FlutterBiometricCrypto.deleteKey();
      setState(() {
        _status = 'Key deleted successfully';
        _resetState();
      });
    } catch (e) {
      setState(() {
        _status = 'Error deleting key: $e';
      });
    }
  }

  void _resetState() {
    setState(() {
      _encryptedDataHex = null;
      _decryptedData = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Flutter Biometric Crypto'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            tooltip: 'Check Availability',
            onPressed: _checkBiometricAvailability,
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            _buildStatusCard(context),
            const SizedBox(height: 16),
            Text(
              'Key Management',
              style: Theme.of(context).textTheme.titleMedium?.copyWith(
                    color: Theme.of(context).colorScheme.primary,
                    fontWeight: FontWeight.bold,
                  ),
            ),
            const SizedBox(height: 8),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _initKey,
                    icon: const Icon(Icons.vpn_key),
                    label: const Text('Init Key'),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _deleteKey,
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red.shade50,
                      foregroundColor: Colors.red,
                    ),
                    icon: const Icon(Icons.delete_forever),
                    label: const Text('Delete Key'),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24),
            Text(
              'Operations',
              style: Theme.of(context).textTheme.titleMedium?.copyWith(
                    color: Theme.of(context).colorScheme.primary,
                    fontWeight: FontWeight.bold,
                  ),
            ),
            const SizedBox(height: 8),
            ElevatedButton.icon(
              onPressed: _encryptData,
              icon: const Icon(Icons.lock),
              label: const Text('Encrypt Sample Data'),
            ),
            const SizedBox(height: 8),
            ElevatedButton.icon(
              onPressed: _decryptData,
              icon: const Icon(Icons.fingerprint),
              label: const Text('Decrypt Data (Biometric Required)'),
            ),
            const SizedBox(height: 8),
            OutlinedButton.icon(
              onPressed: _resetState,
              icon: const Icon(Icons.restart_alt),
              label: const Text('Reset UI'),
            ),
            const SizedBox(height: 24),
            if (_encryptedDataHex != null) _buildEncryptedDataCard(context),
            if (_decryptedData != null) _buildDecryptedDataCard(context),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusCard(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(
                  Icons.info_outline,
                  color: Theme.of(context).colorScheme.primary,
                ),
                const SizedBox(width: 8),
                Text(
                  'Status',
                  style: Theme.of(context).textTheme.titleLarge,
                ),
              ],
            ),
            const SizedBox(height: 12),
            Text(
              _status,
              style: Theme.of(context).textTheme.bodyMedium,
            ),
            const Divider(height: 24),
            Row(
              children: [
                Icon(
                  _isBiometricAvailable ? Icons.check_circle : Icons.cancel,
                  color: _isBiometricAvailable ? Colors.green : Colors.red,
                  size: 20,
                ),
                const SizedBox(width: 8),
                Text(
                  _isBiometricAvailable
                      ? 'Biometric Available'
                      : 'Biometric Not Available',
                  style: TextStyle(
                    fontWeight: FontWeight.w500,
                    color: _isBiometricAvailable ? Colors.green : Colors.red,
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildEncryptedDataCard(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 16.0),
      child: Card(
        color: Colors.grey.shade50,
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  const Icon(Icons.lock_outline, size: 20),
                  const SizedBox(width: 8),
                  Text(
                    'Encrypted Data (Hex)',
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                ],
              ),
              const SizedBox(height: 8),
              SelectableText(
                _encryptedDataHex!,
                style: const TextStyle(
                  fontFamily: 'monospace',
                  fontSize: 12,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildDecryptedDataCard(BuildContext context) {
    return Card(
      color: Colors.green.shade50,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                const Icon(Icons.lock_open, size: 20, color: Colors.green),
                const SizedBox(width: 8),
                Text(
                  'Decrypted Data',
                  style: Theme.of(context).textTheme.titleMedium?.copyWith(
                        color: Colors.green.shade900,
                      ),
                ),
              ],
            ),
            const SizedBox(height: 8),
            SelectableText(
              _decryptedData!,
              style: TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.bold,
                color: Colors.green.shade900,
              ),
            ),
          ],
        ),
      ),
    );
  }
}
2
likes
150
points
85
downloads

Publisher

verified publishergodfreylebo.dev

Weekly Downloads

A Flutter package for biometric-protected encryption of small secrets using Android Keystore and iOS Keychain/Secure Enclave.

Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on flutter_biometric_crypto

Packages that implement flutter_biometric_crypto