ekyc_plugin 1.0.4 copy "ekyc_plugin: ^1.0.4" to clipboard
ekyc_plugin: ^1.0.4 copied to clipboard

A Flutter plugin that provides native eKYC functionality for Android and iOS. Features include face liveness detection, camera flash support, and document verification with seamless integration into F [...]

eKYC Plugin #

pub package platform license

A Flutter plugin that provides native eKYC (Electronic Know Your Customer) functionality for Android and iOS platforms. This plugin enables face liveness detection, camera handling, and document verification capabilities, seamlessly integrated into Flutter applications.


Features #

  • Face Liveness Detection: Native implementation for detecting real human faces vs. photos or videos
  • Camera Flash Support: Configurable camera flash for improved image quality in low-light conditions
  • Native SDK Integration: Direct integration with platform-specific eKYC SDKs
  • Cross-platform: Unified API for both Android and iOS
  • Easy Integration: Simple widget-based API that fits naturally into Flutter apps

Installation #

Add the following dependency to your pubspec.yaml file:

dependencies:
  ekyc_plugin: ^1.0.0

Then install the package:

flutter pub get

Platform Requirements #

Android #

Component Required Version
minSdkVersion 23 (Android 6.0)
compileSdkVersion 34
Kotlin 1.9.25
Android Gradle Plugin 8.6.0
Gradle 8.10

iOS #

Component Required Version
iOS Deployment Target 13.4+
Swift 5.0+
TensorFlow Lite 2.7.0

Android Integration #

Step 1: Configure Gradle Build Settings #

1.1 Update android/build.gradle

Add the JitPack repository to your project-level build.gradle file:

allprojects {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

1.2 Update android/app/build.gradle

Add packaging options to prevent native library conflicts:

android {
    // ... other configurations

    packagingOptions {
        pickFirst 'lib/arm64-v8a/libc++_shared.so'
        pickFirst 'lib/armeabi-v7a/libc++_shared.so'
        // Uncomment if targeting x86 devices
        // pickFirst 'lib/x86/libc++_shared.so'
        // pickFirst 'lib/x86_64/libc++_shared.so'
    }
}

Why this is needed: The eKYC plugin uses native C++ libraries (TensorFlow Lite) that may conflict with other plugins using the same shared libraries. This configuration ensures the build system picks the first occurrence and prevents "duplicate files" errors during APK/AAB generation.

Step 2: Configure MainActivity #

The plugin uses Platform Views to render native camera interfaces. Your MainActivity must extend FlutterFragmentActivity instead of the default FlutterActivity.

Open android/app/src/main/kotlin/[your-package]/MainActivity.kt and update:

import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity : FlutterFragmentActivity() {
    // Your existing code (if any)
}

Why this is needed: Platform Views require FlutterFragmentActivity to properly embed native Android views within the Flutter view hierarchy.

Step 3: Configure ProGuard Rules (Production Builds) #

If you're building a release APK/AAB with code obfuscation enabled, you must preserve certain classes from being stripped or renamed.

Create or update android/app/proguard-rules.pro:

# Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

# Google Play Core (for Flutter deferred components)
-keep class com.google.android.play.core.** { *; }
-dontwarn com.google.android.play.core.**

# TensorFlow Lite
-keep class org.tensorflow.lite.** { *; }
-keep class org.tensorflow.lite.gpu.** { *; }
-dontwarn org.tensorflow.lite.**

Ensure ProGuard is enabled in android/app/build.gradle:

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

iOS Integration #

Step 1: Update iOS Deployment Target #

Ensure your iOS deployment target is set to 13.4 or higher. Open ios/Podfile and verify:

platform :ios, '13.4'

Step 2: Add Camera Permissions #

Add camera usage description to ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app requires camera access for identity verification and liveness detection.</string>

Step 3: Configure Podfile #

Update your ios/Podfile with the following configuration:

platform :ios, '13.4'

target 'Runner' do
  use_frameworks! :linkage => :static
  use_modular_headers!

  # TensorFlow Lite dependencies for face detection
  pod 'TensorFlowLiteC', '~> 2.7.0'
  pod 'TensorFlowLiteSwift', '~> 2.7.0'

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)

    target.build_configurations.each do |config|
      # Enable library distribution for XCFramework compatibility
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'

      # Exclude arm64 architecture for iOS Simulator (Intel Macs)
      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'

      # Set minimum deployment target
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.4'

      # Swift language version
      config.build_settings['SWIFT_VERSION'] = '5.0'

      # Module configuration
      config.build_settings['DEFINES_MODULE'] = 'YES'
      config.build_settings['CLANG_ENABLE_MODULES'] = 'YES'

      # Framework search paths
      config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= ['$(inherited)', '${PODS_ROOT}/**']
    end
  end
end

Step 4: Install Pods #

cd ios
pod install
cd ..

Usage #

Basic Implementation #

Import the plugin and use the LivenessView widget:

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

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

  @override
  State<FaceVerificationScreen> createState() => _FaceVerificationScreenState();
}

class _FaceVerificationScreenState extends State<FaceVerificationScreen> {
  void _handleLivenessResult(ExtractedLiveness result) {
    // The result contains base64-encoded images
    print('Original Image: ${result.originalImage?.substring(0, 50)}...');
    print('Color Image: ${result.colorImage?.substring(0, 50)}...');
    print('SDK Color: ${result.color}');

    // Process the images (e.g., send to your backend API)
    _sendToBackend(result);
  }

  Future<void> _sendToBackend(ExtractedLiveness data) async {
    // Example: Send to your verification API
    // The images are already in base64 format, ready to be sent
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: LivenessView(
        isFlash: true,  // Enable camera flash
        onCallback: _handleLivenessResult,
      ),
    );
  }
}

Advanced Example #

For a complete implementation with API integration, see the example below:

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

class AdvancedFaceVerification extends StatefulWidget {
  const AdvancedFaceVerification({super.key, required this.userId});

  final String userId;

  @override
  State<AdvancedFaceVerification> createState() => _AdvancedFaceVerificationState();
}

class _AdvancedFaceVerificationState extends State<AdvancedFaceVerification> {
  String? _originalImageBase64;
  String? _colorImageBase64;
  String? _sdkColor;

  Uint8List? _originalImageBytes;
  Uint8List? _colorImageBytes;

  bool _isProcessing = false;

  Uint8List? _decodeBase64Image(String? base64String) {
    if (base64String == null) return null;

    try {
      // Remove data URI prefix if present
      String cleanBase64 = base64String;
      if (cleanBase64.startsWith('data:')) {
        final commaIndex = cleanBase64.indexOf(',');
        if (commaIndex != -1) {
          cleanBase64 = cleanBase64.substring(commaIndex + 1);
        }
      }

      return base64Decode(cleanBase64);
    } catch (e) {
      print('Error decoding base64: $e');
      return null;
    }
  }

  void _handleLivenessCallback(ExtractedLiveness data) {
    setState(() {
      _isProcessing = true;

      // Store base64 strings (for API submission)
      _originalImageBase64 = data.originalImage;
      _colorImageBase64 = data.colorImage;
      _sdkColor = data.color;

      // Convert to bytes for UI display
      _originalImageBytes = _decodeBase64Image(data.originalImage);
      _colorImageBytes = _decodeBase64Image(data.colorImage);

      _isProcessing = false;
    });

    // Show results dialog
    _showResultsDialog();
  }

  void _showResultsDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Liveness Detection Result'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            if (_originalImageBytes != null)
              Image.memory(_originalImageBytes!, height: 200),
            const SizedBox(height: 16),
            Text('SDK Color: $_sdkColor'),
            Text('Original image size: ${_originalImageBytes?.length ?? 0} bytes'),
            Text('Color image size: ${_colorImageBytes?.length ?? 0} bytes'),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
          ElevatedButton(
            onPressed: () {
              // Send to your backend
              _submitToBackend();
            },
            child: const Text('Verify'),
          ),
        ],
      ),
    );
  }

  Future<void> _submitToBackend() async {
    // Implement your API call here using _originalImageBase64 and _colorImageBase64
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Face Verification'),
      ),
      body: LivenessView(
        isFlash: true,
        onCallback: _handleLivenessCallback,
      ),
    );
  }
}

API Reference #

LivenessView

A widget that displays the native camera interface for liveness detection.

Properties:

  • isFlash (bool, required): Enable or disable camera flash
    • true: Flash enabled (recommended for better image quality)
    • false: Flash disabled
  • onCallback (Function(ExtractedLiveness), required): Callback function invoked when liveness detection completes successfully

Example:

LivenessView(
  isFlash: true,
  onCallback: (ExtractedLiveness result) {
    // Handle the result
  },
)

ExtractedLiveness

Data model containing the results of liveness detection.

Properties:

  • originalImage (String?): Base64-encoded original face image captured by the camera
  • colorImage (String?): Base64-encoded color-processed image used for liveness verification
  • color (String?): Color information detected by the SDK (used internally for anti-spoofing)

Note: All base64 strings may include data URI prefix (e.g., data:image/jpeg;base64,). You may need to strip this prefix before sending to your backend.


Troubleshooting #

Android Issues #

Issue: Build fails with "Duplicate files" error

> A failure occurred while executing com.android.build.gradle.internal.tasks.MergeNativeLibsTask
  > 2 files found with path 'lib/arm64-v8a/libc++_shared.so'

Solution: Add packaging options to android/app/build.gradle:

android {
    packagingOptions {
        pickFirst 'lib/arm64-v8a/libc++_shared.so'
        pickFirst 'lib/armeabi-v7a/libc++_shared.so'
    }
}

Issue: Black screen or crash when opening camera

Possible causes and solutions:

  1. MainActivity not extending FlutterFragmentActivity

    • Ensure MainActivity extends FlutterFragmentActivity instead of FlutterActivity
  2. Missing camera permissions

    • Verify that camera permissions are granted at runtime
    • Check AndroidManifest.xml includes <uses-permission android:name="android.permission.CAMERA" />
  3. Incorrect minSdkVersion

    • Ensure minSdkVersion is set to 23 or higher

Issue: ProGuard strips required classes in release build

Solution: Ensure proguard-rules.pro is properly configured with the rules provided in Step 3 of Android integration.

iOS Issues #

Issue: Pod installation fails

Solution:

cd ios
rm -rf Pods Podfile.lock
pod cache clean --all
pod install --repo-update
cd ..

Issue: App crashes on iOS Simulator

Possible causes:

  1. TensorFlow Lite not compatible with simulator architecture

    • Test on a physical device instead
    • Ensure EXCLUDED_ARCHS[sdk=iphonesimulator*] is set correctly in Podfile
  2. Missing camera on simulator

    • Camera-based features require physical device testing

Issue: Camera permission not requested

Solution: Verify Info.plist contains NSCameraUsageDescription with a descriptive message.

Cross-platform Issues #

Issue: Callback not triggered

Checklist:

  1. Verify the widget is properly mounted in the widget tree
  2. Check for errors in Flutter console/logs
  3. Ensure native platform setup is complete
  4. Test with a simplified callback function to isolate the issue

Issue: Base64 decoding fails

Solution: The base64 strings may include data URI prefix. Strip it before decoding:

String cleanBase64(String base64String) {
  if (base64String.startsWith('data:')) {
    final commaIndex = base64String.indexOf(',');
    if (commaIndex != -1) {
      return base64String.substring(commaIndex + 1);
    }
  }
  return base64String;
}

Best Practices #

Performance #

  • Use isFlash: true in low-light environments for better image quality and liveness detection accuracy
  • Decode base64 images only when needed for display purposes; keep original base64 for API transmission
  • Handle large images efficiently: Consider image compression before sending to backend

User Experience #

  • Provide clear instructions to users before showing the liveness detection screen
  • Handle errors gracefully: Show user-friendly messages if liveness detection fails
  • Add loading indicators during image processing and API calls

Security #

  • Always verify liveness on the backend: Never trust client-side validation alone
  • Use HTTPS for API transmission when sending face images
  • Implement timeout mechanisms for the verification process
  • Follow GDPR/privacy regulations when storing or processing biometric data

Project Structure #

ekyc_plugin/
├── android/              # Android native implementation
├── ios/                  # iOS native implementation
├── lib/
│   ├── ekyc_plugin.dart # Main plugin entry point
│   └── src/
│       └── screens/
│           ├── liveness_view.dart  # LivenessView widget
│           └── version.dart
├── example/              # Example application
└── plugins/
    └── bridge_native/    # Native bridge communication

Additional Resources #


License #

This project is licensed under the MIT License. See the LICENSE file for details.


Contributing #

Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.

Maintainer: SON (Lucas)

For bug reports and feature requests, please create an issue on the project repository.


Note: This plugin uses native SDKs for liveness detection and face recognition. Ensure you comply with all applicable laws and regulations regarding biometric data collection and processing in your jurisdiction.

0
likes
130
points
304
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin that provides native eKYC functionality for Android and iOS. Features include face liveness detection, camera flash support, and document verification with seamless integration into Flutter apps.

Repository (GitHub)
View/report issues

Topics

#ekyc #kyc #face-detection #liveness-detection #biometric

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

bridge_native, flutter, plugin_platform_interface

More

Packages that depend on ekyc_plugin

Packages that implement ekyc_plugin