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 7.0)
iOS iOS 13.0+

Installation

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

dependencies:
  facedetect_tadi_sdk: ^0.0.10

Then run:

flutter pub get

Platform-Specific Setup

Android Setup

  1. Native SDK: The native Android SDK is bundled with this plugin via a local Maven repository. No external repository configuration or credentials are required.

    Note: The plugin includes the TADI Face Detection SDK (v1.0.8) in the android/maven-repo directory, so you don't need to configure any external Maven repositories.

  2. Update Minimum SDK: Ensure your android/app/build.gradle has:

android {
    defaultConfig {
        minSdkVersion 24
    }
}
  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"/>

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',
  bankId: 'YOUR_BANK_ID',  // Optional: Bank identifier

  // 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
bankId String ❌ Bank identifier for tracking purposes
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