Face Detection TADI SDK - Flutter Plugin

A Flutter plugin that integrates TADI Face Detection SDK for both iOS and Android platforms, providing face detection, liveness verification, and biometric identity verification capabilities.

Features

  • Real-time Face Detection: Using Google ML Kit (Android) and TrueDepth camera (iOS)
  • Liveness Verification: Prevents photo/video spoofing
  • Biometric Identity Verification: Integration with government identity services
  • Multi-language Support: English, Russian, and Uzbek
  • Flexible Result Formats: Flat (simple) or Nested (structured)
  • Security Features:
    • JWT signature validation
    • Nonce-based challenge-response
    • Root/Jailbreak detection
    • Mutual SSL/TLS support
  • UI Customization: Full control over colors, fonts, and layout

Platform Support

Platform Minimum Version
Android API 21 (Android 5.0)
iOS iOS 13.0+

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  facedetect_tadi_sdk: ^0.0.1

Then run:

flutter pub get

Platform-Specific Setup

Android Setup

  1. Add Maven Repository: The native Android SDK needs access to the Nexus repository. Add credentials to your gradle.properties file (project root):
nexusUsername=romvn
nexusPassword=!QAZ1qaz

Security Note: Never commit credentials to version control. Add gradle.properties to your .gitignore.

  1. Add Permissions: Add these permissions to your AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>

Optional hardware features:

<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
  1. Minimum SDK: Ensure your android/app/build.gradle has:
android {
    defaultConfig {
        minSdkVersion 21
    }
}

iOS Setup

  1. Add TGFISBIN Framework: You need to add the TGFISBIN.xcframework to your iOS project.

    Option 1: Using Swift Package Manager (if available)

    Add to your iOS project in Xcode:

    https://gitlab.tadi.uz/tadi_public/TGFISBIN.git
    

    Option 2: Manual Framework Integration

    • Place TGFISBIN.xcframework in your ios/ folder or ios/Frameworks/
    • Update the podspec to include vendored frameworks (see ios/facedetect_tadi_sdk.podspec)
  2. Add Permissions: Add these entries to your Info.plist:

<key>NSCameraUsageDescription</key>
<string>We need camera access to verify your identity and scan your documents</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access to select identity documents</string>
  1. Update Deployment Target: Ensure your iOS deployment target is at least 13.0 in Xcode.

Quick Start

Basic Usage

import 'package:facedetect_tadi_sdk/facedetect_tadi_sdk.dart';

// Create SDK instance
final sdk = FacedetectTadiSdk();

// Configure SDK
final config = SdkConfig(
  accessToken: 'YOUR_ACCESS_TOKEN',
  language: 'en',  // 'en', 'ru', or 'uz'
  useNative: true,
);

// Start face detection
final result = await sdk.startFaceDetection(config);

if (result != null) {
  print('Name: ${result.firstName} ${result.lastName}');
  print('Document: ${result.docSeria} ${result.docNumber}');
  print('PINFL: ${result.pinfl}');
  print('Birth Date: ${result.birthDate}');
  print('Score: ${result.score}');
} else {
  print('Verification cancelled or failed');
}

Advanced Configuration

final config = SdkConfig(
  accessToken: 'YOUR_ACCESS_TOKEN',
  baseUrl: 'https://your-api-url.com/',
  language: 'en',
  useNative: true,
  resultFormat: ResultFormat.flat,
  privacyPolicyUrl: 'https://yourcompany.com/privacy',

  // UI Customization
  uiConfig: UiConfig(
    logoUrl: 'https://yourcdn.com/logo.png',
    fontFamily: 'YourFont-Regular',
    inputShape: 'rounded',
    inputBorderStyle: 'solid',
    inputSize: 'medium',
    inputColor: '#FFFFFF',
    buttonColor: '#007AFF',
    linkColor: '#007AFF',
    elementColor: '#1C1C1E',
    layoutScale: 'medium',
    hideBackButton: false,
  ),
);

With Mutual SSL/TLS

For enhanced security with client certificates:

final config = SdkConfig(
  accessToken: 'YOUR_ACCESS_TOKEN',
  enableMutualSSL: true,
  clientCertificateBase64: 'BASE64_ENCODED_P12_CERTIFICATE',
  clientCertificatePassword: 'certificate_password',
);

API Reference

SdkConfig

Configuration object for the SDK:

Parameter Type Required Description
accessToken String API authentication token
baseUrl String Backend service URL (default: https://face.mbabm.uz/)
language String UI language: 'en', 'ru', or 'uz' (default: 'uz')
useNative bool Use native face detection (default: true)
resultFormat ResultFormat Result format: flat or nested (default: flat)
privacyPolicyUrl String Custom privacy policy URL
jwtSecretKey String HMAC secret key for JWT validation
jwtPublicKey String RSA public key for JWT validation
clientCertificateBase64 String P12 certificate (Base64) for mTLS
clientCertificatePassword String Password for P12 certificate
enableMutualSSL bool Enable mutual SSL authentication
uiConfig UiConfig UI customization options

FaceDetectionResult

Result object containing verification data:

class FaceDetectionResult {
  // Identity
  String? id;
  String? pin;
  String? pinfl;
  String? transactionId;

  // Names
  String? firstName;
  String? lastName;
  String? patronym;
  String? name;

  // Document Information
  String? docSeria;
  String? docNumber;
  String? dateIssue;
  String? dateExpiry;

  // Demographics
  String? gender;
  String? genderName;
  String? birthDate;
  String? birthPlace;
  String? citizenship;
  String? citizenshipName;

  // Verification
  double? score;  // Confidence score (0.0 - 1.0)

  // Media (Base64 encoded)
  String? photo;
  String? signature;
  String? capture;

  // Additional Documents
  List<DocumentInfo>? documents;
  List<ForeignDocument>? foreignDocuments;
}

UiConfig

UI customization options:

UiConfig(
  logoUrl: 'https://example.com/logo.png',
  fontFamily: 'CustomFont-Regular',
  inputShape: 'rounded',  // 'rounded' or 'standard'
  inputBorderStyle: 'solid',  // 'solid', 'dashed', 'dotted'
  inputSize: 'medium',  // 'small', 'medium', 'large'
  inputColor: '#FFFFFF',
  buttonColor: '#007AFF',
  linkColor: '#007AFF',
  elementColor: '#1C1C1E',
  layoutScale: 'medium',  // 'small', 'medium', 'large'
  hideBackButton: false,
  hideDateOfBirthClear: false,
)

Result Formats

Flat Format (Default)

Returns a simple, flat structure with all fields at the top level:

final config = SdkConfig(
  accessToken: 'TOKEN',
  resultFormat: ResultFormat.flat,
);

final result = await sdk.startFaceDetection(config);
print(result?.firstName);  // Direct access
print(result?.docNumber);

Nested Format

Returns a hierarchical, structured format (useful for complex integrations):

final config = SdkConfig(
  accessToken: 'TOKEN',
  resultFormat: ResultFormat.nested,
);

// Note: Nested format support is available on the native side
// but requires additional mapping in Flutter layer

Handling Results

Display Photo

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

Widget buildPhoto(FaceDetectionResult result) {
  if (result.photo != null && result.photo!.isNotEmpty) {
    return Image.memory(
      base64Decode(result.photo!),
      height: 200,
      fit: BoxFit.contain,
    );
  }
  return Icon(Icons.person, size: 200);
}

Validate Score

void handleResult(FaceDetectionResult result) {
  if (result.score != null) {
    if (result.score! >= 0.9) {
      print('✅ High confidence verification');
    } else if (result.score! >= 0.7) {
      print('⚠️ Medium confidence - manual review recommended');
    } else {
      print('❌ Low confidence - verification failed');
    }
  }
}

Error Handling

try {
  final result = await sdk.startFaceDetection(config);

  if (result == null) {
    // User cancelled or verification failed
    print('Verification cancelled');
  } else {
    // Success
    print('Verification successful');
  }
} on PlatformException catch (e) {
  // Handle platform-specific errors
  print('Error: ${e.code} - ${e.message}');

  switch (e.code) {
    case 'NO_ACTIVITY':
      print('Activity not available');
      break;
    case 'ALREADY_RUNNING':
      print('Face detection already in progress');
      break;
    case 'SDK_ERROR':
      print('SDK initialization error');
      break;
    default:
      print('Unknown error');
  }
}

Example App

See the example directory for a complete demo app that shows:

  • Configuration options
  • Starting face detection
  • Handling results
  • Displaying photos
  • Error handling

Run the example:

cd example
flutter run

Troubleshooting

Android Issues

Build fails with "Could not find uz.tadi:facedetectsdk:1.0.4"

  • Ensure gradle.properties has the correct Nexus credentials
  • Check your internet connection

Face detection doesn't start

  • Verify camera permissions are granted
  • Check that the device is not rooted
  • Ensure access token is valid

iOS Issues

Build fails with "Module 'TGFISBIN' not found"

  • Ensure TGFISBIN.xcframework is properly added to your project
  • Run pod install in the ios folder
  • Clean build folder in Xcode (Cmd+Shift+K)

Completion returns nil

  • Check if running on a real device (not simulator)
  • Verify device is not jailbroken
  • Ensure camera permissions are granted

Platform-Native Documentation

For detailed information about the native SDKs:

Security Considerations

  • Never hardcode access tokens in your app
  • Store tokens securely using flutter_secure_storage or similar
  • Use mutual SSL/TLS in production
  • Validate JWT responses for additional security
  • Never commit certificates or credentials to version control

License

See LICENSE file for details.

Support

For issues and questions:

  • Create an issue in the repository
  • Contact TADI support team