ekyc_plugin 1.0.4
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 #
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 flashtrue: 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 cameracolorImage(String?): Base64-encoded color-processed image used for liveness verificationcolor(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:
-
MainActivity not extending FlutterFragmentActivity
- Ensure
MainActivityextendsFlutterFragmentActivityinstead ofFlutterActivity
- Ensure
-
Missing camera permissions
- Verify that camera permissions are granted at runtime
- Check
AndroidManifest.xmlincludes<uses-permission android:name="android.permission.CAMERA" />
-
Incorrect minSdkVersion
- Ensure
minSdkVersionis set to 23 or higher
- Ensure
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:
-
TensorFlow Lite not compatible with simulator architecture
- Test on a physical device instead
- Ensure
EXCLUDED_ARCHS[sdk=iphonesimulator*]is set correctly in Podfile
-
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:
- Verify the widget is properly mounted in the widget tree
- Check for errors in Flutter console/logs
- Ensure native platform setup is complete
- 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: truein 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 #
- Flutter Plugin Development Guide
- Platform Views Documentation
- Android Camera API Best Practices
- iOS AVFoundation Framework
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.