facedetect_tadi_sdk 0.0.9 copy "facedetect_tadi_sdk: ^0.0.9" to clipboard
facedetect_tadi_sdk: ^0.0.9 copied to clipboard

Flutter plugin for face detection, liveness verification, and biometric identity verification with ML Kit and secure backend integration.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/services.dart';
import 'package:facedetect_tadi_sdk/facedetect_tadi_sdk.dart';

import 'services/keychain_service.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Face Detection SDK Demo',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const FaceDetectionDemo(),
    );
  }
}

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

  @override
  State<FaceDetectionDemo> createState() => _FaceDetectionDemoState();
}

class _FaceDetectionDemoState extends State<FaceDetectionDemo> {
  String _platformVersion = 'Unknown';
  final _sdk = FacedetectTadiSdk();
  final _keychainService = KeychainService();
  FaceDetectionResult? _result;
  bool _isLoading = false;
  String? _errorMessage;

  // Certificate configuration (iOS-compatible format with 3DES encryption)
  static const String _certificateAssetPath = 'assets/client_ios.pfx';
  static const String _certificatePassword = '1234'; // Update with actual password
  static const String _certificateAlias = 'ed7c2f742d7abad0c4d94ce9064771ec5bd527f9';
  bool _certificateInstalled = false;
  bool _isInstallingCertificate = false;

  // Configuration fields
  final _accessTokenController = TextEditingController(
    text: 'laskdlksadjlkasdlkajsdlaksd',
  );
  final _baseUrlController = TextEditingController(
    text: 'https://bank.gsi.uz.ngrok.app/',
  );
  String _selectedLanguage = 'en';
  bool _useNative = true;
  ResultFormat _resultFormat = ResultFormat.flat;
  CameraShape _selectedCameraShape = CameraShape.oval;
  String? _logoImageBase64;

  @override
  void initState() {
    super.initState();
    initPlatformState();
    _checkCertificateStatus();
  }

  Future<void> _checkCertificateStatus() async {
    final installed = await _keychainService.isCertificateInstalled();
    if (!mounted) return;
    setState(() {
      _certificateInstalled = installed;
    });
  }

  Future<void> _installCertificate() async {
    setState(() {
      _isInstallingCertificate = true;
      _errorMessage = null;
    });

    try {
      final alias = await _keychainService.importCertificateFromAssets(
        assetPath: _certificateAssetPath,
        password: _certificatePassword,
        alias: _certificateAlias,
      );

      if (!mounted) return;

      if (alias != null) {
        setState(() {
          _certificateInstalled = true;
          _isInstallingCertificate = false;
        });
      } else {
        setState(() {
          _errorMessage = 'Failed to install certificate';
          _isInstallingCertificate = false;
        });
      }
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _errorMessage = 'Certificate installation error: $e';
        _isInstallingCertificate = false;
      });
    }
  }

  Future<void> _removeCertificate() async {
    await _keychainService.removeCertificate();
    if (!mounted) return;
    setState(() {
      _certificateInstalled = false;
    });
  }

  @override
  void dispose() {
    _accessTokenController.dispose();
    _baseUrlController.dispose();
    super.dispose();
  }

  Future<void> initPlatformState() async {
    String platformVersion;
    try {
      platformVersion =
          await _sdk.getPlatformVersion() ?? 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  Future<void> _loadLogoFromAssets() async {
    try {
      // Load Hamkorbank logo from assets (PNG format for native SDK compatibility)
      final ByteData data = await rootBundle.load('hamkor_bank.png');
      final bytes = data.buffer.asUint8List();
      final base64String = base64Encode(bytes);

      setState(() {
        _logoImageBase64 = base64String;
      });
    } catch (e) {
      debugPrint('Failed to load logo: $e');
      setState(() {
        _errorMessage = 'Failed to load logo: $e';
      });
    }
  }

  Future<void> _startFaceDetection() async {
    if (_accessTokenController.text.isEmpty) {
      setState(() {
        _errorMessage = 'Please enter an access token';
      });
      return;
    }

    setState(() {
      _isLoading = true;
      _errorMessage = null;
      _result = null;
    });

    try {
      // For Android, retrieve the certificate data from secure storage
      // For iOS, use the keychain alias approach
      String? certBase64;
      String? certPassword;
      String? certAlias;

      if (_certificateInstalled) {
        if (Platform.isAndroid) {
          // Android: pass certificate data directly to the SDK
          certBase64 = await _keychainService.getCertificateBase64();
          certPassword = _certificatePassword;
        } else if (Platform.isIOS) {
          // iOS: use keychain alias
          certAlias = _certificateAlias;
        }
      }

      final config = SdkConfig(
        accessToken: _accessTokenController.text,
        baseUrl: _baseUrlController.text.isNotEmpty
            ? _baseUrlController.text
            : null,
        language: _selectedLanguage,
        useNative: _useNative,
        resultFormat: _resultFormat,
        cameraShape: _selectedCameraShape,
        logoImageBase64: _logoImageBase64,
        enableMutualSSL: _certificateInstalled,
        clientCertificateBase64: certBase64,
        clientCertificatePassword: certPassword,
        clientCertificateAlias: certAlias,
      );

      final result = await _sdk.startFaceDetection(config);

      if (!mounted) return;

      setState(() {
        _result = result;
        _isLoading = false;
        if (result == null) {
          _errorMessage = 'Verification cancelled or failed';
        }
      });
    } on PlatformException catch (e) {
      if (!mounted) return;

      setState(() {
        _errorMessage = 'Error: ${e.message}';
        _isLoading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Face Detection SDK Demo'),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Platform Info
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Platform Information',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 8),
                    Text('Running on: $_platformVersion'),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Certificate Management Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Client Certificate (mTLS)',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 8),
                    Row(
                      children: [
                        Icon(
                          _certificateInstalled
                              ? Icons.check_circle
                              : Icons.warning,
                          color: _certificateInstalled
                              ? Colors.green
                              : Colors.orange,
                        ),
                        const SizedBox(width: 8),
                        Expanded(
                          child: Text(
                            _certificateInstalled
                                ? 'Certificate installed in Keychain'
                                : 'Certificate not installed',
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 12),
                    if (!_certificateInstalled)
                      ElevatedButton.icon(
                        onPressed:
                            _isInstallingCertificate ? null : _installCertificate,
                        icon: _isInstallingCertificate
                            ? const SizedBox(
                                width: 16,
                                height: 16,
                                child: CircularProgressIndicator(strokeWidth: 2),
                              )
                            : const Icon(Icons.security),
                        label: Text(
                          _isInstallingCertificate
                              ? 'Installing...'
                              : 'Install Certificate',
                        ),
                      )
                    else
                      TextButton.icon(
                        onPressed: _removeCertificate,
                        icon: const Icon(Icons.delete_outline, color: Colors.red),
                        label: const Text(
                          'Remove Certificate',
                          style: TextStyle(color: Colors.red),
                        ),
                      ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Configuration Section
            Card(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      'Configuration',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _accessTokenController,
                      decoration: const InputDecoration(
                        labelText: 'Access Token',
                        border: OutlineInputBorder(),
                        hintText: 'Enter your access token',
                      ),
                    ),
                    const SizedBox(height: 16),
                    TextField(
                      controller: _baseUrlController,
                      decoration: const InputDecoration(
                        labelText: 'Base URL',
                        border: OutlineInputBorder(),
                        hintText: 'https://bank.gsi.uz.ngrok.app/',
                      ),
                    ),
                    const SizedBox(height: 16),
                    DropdownButtonFormField<String>(
                      initialValue: _selectedLanguage,
                      decoration: const InputDecoration(
                        labelText: 'Language',
                        border: OutlineInputBorder(),
                      ),
                      items: const [
                        DropdownMenuItem(value: 'en', child: Text('English')),
                        DropdownMenuItem(value: 'ru', child: Text('Russian')),
                        DropdownMenuItem(value: 'uz', child: Text('Uzbek')),
                      ],
                      onChanged: (value) {
                        setState(() {
                          _selectedLanguage = value!;
                        });
                      },
                    ),
                    const SizedBox(height: 16),
                    SwitchListTile(
                      title: const Text('Use Native Face Detection'),
                      subtitle: const Text('Use device native face detection'),
                      value: _useNative,
                      onChanged: (value) {
                        setState(() {
                          _useNative = value;
                        });
                      },
                    ),
                    DropdownButtonFormField<ResultFormat>(
                      initialValue: _resultFormat,
                      decoration: const InputDecoration(
                        labelText: 'Result Format',
                        border: OutlineInputBorder(),
                      ),
                      items: const [
                        DropdownMenuItem(
                          value: ResultFormat.flat,
                          child: Text('Flat (Simple)'),
                        ),
                        DropdownMenuItem(
                          value: ResultFormat.nested,
                          child: Text('Nested (Structured)'),
                        ),
                      ],
                      onChanged: (value) {
                        setState(() {
                          _resultFormat = value!;
                        });
                      },
                    ),
                    const SizedBox(height: 16),
                    DropdownButtonFormField<CameraShape>(
                      initialValue: _selectedCameraShape,
                      decoration: const InputDecoration(
                        labelText: 'Camera Shape',
                        border: OutlineInputBorder(),
                      ),
                      items: const [
                        DropdownMenuItem(
                          value: CameraShape.oval,
                          child: Text('Oval'),
                        ),
                        DropdownMenuItem(
                          value: CameraShape.circle,
                          child: Text('Circle'),
                        ),
                      ],
                      onChanged: (value) {
                        setState(() {
                          _selectedCameraShape = value!;
                        });
                      },
                    ),
                    const SizedBox(height: 16),
                    ElevatedButton.icon(
                      onPressed: _loadLogoFromAssets,
                      icon: const Icon(Icons.image),
                      label: Text(
                        _logoImageBase64 != null
                            ? 'Hamkorbank Logo Loaded ✓'
                            : 'Load Hamkorbank Logo (Optional)',
                      ),
                      style: ElevatedButton.styleFrom(
                        backgroundColor: _logoImageBase64 != null
                            ? Colors.green
                            : null,
                      ),
                    ),
                    if (_logoImageBase64 != null)
                      TextButton.icon(
                        onPressed: () {
                          setState(() {
                            _logoImageBase64 = null;
                          });
                        },
                        icon: const Icon(Icons.clear),
                        label: const Text('Clear Logo'),
                      ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 16),

            // Start Button
            ElevatedButton.icon(
              onPressed: _isLoading ? null : _startFaceDetection,
              icon: _isLoading
                  ? const SizedBox(
                      width: 20,
                      height: 20,
                      child: CircularProgressIndicator(strokeWidth: 2),
                    )
                  : const Icon(Icons.face),
              label: Text(
                _isLoading ? 'Processing...' : 'Start Face Detection',
                style: const TextStyle(fontSize: 16),
              ),
              style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.all(16),
              ),
            ),

            // Error Message
            if (_errorMessage != null) ...[
              const SizedBox(height: 16),
              Card(
                color: Colors.red.shade50,
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Row(
                    children: [
                      const Icon(Icons.error, color: Colors.red),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          _errorMessage!,
                          style: const TextStyle(color: Colors.red),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],

            // Result Display
            if (_result != null) ...[
              const SizedBox(height: 16),
              Card(
                color: Colors.green.shade50,
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          const Icon(Icons.check_circle, color: Colors.green),
                          const SizedBox(width: 8),
                          const Text(
                            'Verification Successful',
                            style: TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                              color: Colors.green,
                            ),
                          ),
                        ],
                      ),
                      const Divider(height: 24),
                      _buildResultRow('Name', _result!.firstName ?? 'N/A'),
                      _buildResultRow('Last Name', _result!.lastName ?? 'N/A'),
                      _buildResultRow('Patronym', _result!.patronym ?? 'N/A'),
                      _buildResultRow(
                        'Birth Date',
                        _result!.birthDate ?? 'N/A',
                      ),
                      _buildResultRow('Gender', _result!.genderName ?? 'N/A'),
                      _buildResultRow(
                        'Document',
                        '${_result!.docSeria ?? ''} ${_result!.docNumber ?? ''}',
                      ),
                      _buildResultRow('PINFL', _result!.pinfl ?? 'N/A'),
                      _buildResultRow(
                        'Issue Date',
                        _result!.dateIssue ?? 'N/A',
                      ),
                      _buildResultRow(
                        'Expiry Date',
                        _result!.dateExpiry ?? 'N/A',
                      ),
                      if (_result!.score != null)
                        _buildResultRow(
                          'Confidence Score',
                          '${(_result!.score! * 100).toStringAsFixed(1)}%',
                        ),

                      // Display photo if available
                      if (_result!.photo != null &&
                          _result!.photo!.isNotEmpty) ...[
                        const SizedBox(height: 16),
                        const Text(
                          'Photo:',
                          style: TextStyle(fontWeight: FontWeight.bold),
                        ),
                        const SizedBox(height: 8),
                        ClipRRect(
                          borderRadius: BorderRadius.circular(8),
                          child: Image.memory(
                            base64Decode(_result!.photo!),
                            height: 200,
                            fit: BoxFit.contain,
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),
            ],
          ],
        ),
      ),
    );
  }

  Widget _buildResultRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 120,
            child: Text(
              '$label:',
              style: const TextStyle(fontWeight: FontWeight.w500),
            ),
          ),
          Expanded(
            child: Text(value, style: const TextStyle(color: Colors.black87)),
          ),
        ],
      ),
    );
  }
}
0
likes
145
points
290
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter plugin for face detection, liveness verification, and biometric identity verification with ML Kit and secure backend integration.

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on facedetect_tadi_sdk

Packages that implement facedetect_tadi_sdk