Skip to main content

ekyc_vn_flutter_sdk 1.0.0 copy "ekyc_vn_flutter_sdk: ^1.0.0" to clipboard
ekyc_vn_flutter_sdk: ^1.0.0 copied to clipboard

On-device eKYC capture for Flutter: guided Vietnamese ID card and passport capture with card liveness, and a face liveness flow (look straight, left, right). Ready-made screens for Android and iOS.

ekyc_vn_flutter_sdk #

Ready-made eKYC capture screens for Flutter apps, running entirely on the device:

  • CardValidationView: guides the user to capture the front and back of a Vietnamese ID card (CCCD/CMND) or a passport. A card liveness model checks every frame and rejects screen photos, fingers over the card and glare.
  • FaceValidationView: a face liveness flow. The user looks straight, then left, then right. Face detection and a liveness model run on every frame, and the SDK returns a straight-on face photo.

Both widgets are full screens: drop one into a route and handle the callback. The SDK sends no images or user data anywhere; you upload the returned images to your own eKYC backend.

The only network request comes from google_fonts, which downloads the Roboto font from fonts.gstatic.com the first time a screen opens (without network, text falls back to the system font). If your app must not reach Google servers, bundle the Roboto .ttf files in your app's assets and set GoogleFonts.config.allowRuntimeFetching = false in main().

Requirements #

Minimum
Flutter 3.38.1 (Dart 3.10). Tested with 3.47.7
Android minSdk as set by your Flutter version (flutter.minSdkVersion, 24 on Flutter 3.47; the SDK itself runs on API 21+), compileSdk 36, Java 17
Android build tools Whatever your Flutter version requires. Flutter 3.47 needs Gradle 8.14+, Android Gradle Plugin 8.11.1+, Kotlin 2.2.20+
iOS 12.0, a real device (the simulator has no camera)

1. Install #

Add the SDK and the camera override to your app's pubspec.yaml:

dependencies:
  ekyc_vn_flutter_sdk: ^1.0.0

# Required. The SDK is tuned for Techainer's build of the camera plugin,
# which picks the capture resolution differently on Android.
dependency_overrides:
  camera:
    git:
      url: https://github.com/Techainer/flutter-camera-package.git
      ref: lucas_handle_resolution_prest
      path: packages/camera/camera

Then run flutter pub get.

Without the override the app still builds against the official camera 0.10.x, but card images on some Android phones come out at a lower resolution than the SDK was tested with.

2. Platform setup #

Android #

In android/app/build.gradle (or build.gradle.kts):

android {
    compileSdk 36
    defaultConfig {
        minSdkVersion flutter.minSdkVersion
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '17'
    }
}

Add these two lines to android/gradle.properties. Projects created with a recent Flutter use Android Gradle Plugin 9 and Kotlin 2, which turn two warnings from tflite_flutter into build errors; on older projects the lines are harmless:

# tflite_flutter declares Java 11 while Kotlin 2 defaults to 17.
kotlin.jvm.target.validation.mode=warning
# litert-support and litert-support-api (pulled in by tflite_flutter) share a namespace.
android.uniquePackageNames=false

The camera permission is declared by the camera plugin; you do not need to add it to AndroidManifest.xml.

iOS #

  1. New Flutter projects have no ios/Podfile until the first iOS build; Flutter creates it then. If your Podfile sets a platform, make it at least 12.0:

    platform :ios, '12.0'
    
  2. Add the camera description to ios/Runner/Info.plist. iOS terminates the app on the first camera access if this key is missing:

    <key>NSCameraUsageDescription</key>
    <string>The camera is used to capture your ID card and face for identity verification.</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>The microphone is not used.</string>
    

    The SDK never records audio, but the camera plugin links the audio APIs, so App Store review expects the microphone key to be present.

  3. Run cd ios && pod install.

3. Capture an ID card #

import 'package:ekyc_vn_flutter_sdk/ekyc_flutter_sdk.dart';
import 'package:flutter/material.dart';

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

  @override
  State<CardFrontScreen> createState() => _CardFrontScreenState();
}

class _CardFrontScreenState extends State<CardFrontScreen> {
  bool _loading = false;

  Future<void> _onCard(
    bool isValid,
    String rawImagePath,
    String cardImagePath,
    String message,
    Future<void> Function(dynamic command) camera,
  ) async {
    setState(() => _loading = false);
    if (!mounted) return;

    if (isValid) {
      // cardImagePath: the card, cropped and straightened. Upload this one.
      // rawImagePath: the full camera frame, useful for audits.
      await camera(CameraStatus.DISPOSE);
      if (mounted) Navigator.of(context).pop(cardImagePath);
    } else {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
      await camera(CameraStatus.RESUME_PREVIEW); // let the user try again
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        CardValidationView(
          cardValidationType: CardValidationType.CCCD, // or PASSPORT
          currentStep: CardValidationStep.CARDFRONT,   // or CARDBACK
          callback: _onCard,
          loadingCallBack: (show) => setState(() => _loading = show),
        ),
        if (_loading) const Center(child: CircularProgressIndicator()),
      ],
    );
  }
}

How the screen behaves:

  1. The camera opens with a card frame. The text under the frame tells the user what is wrong (no card, wrong side, glare) and changes as they move.
  2. When the card is valid, the capture button becomes active and the user taps it.
  3. The SDK pauses the camera, calls loadingCallBack(true), checks the captured frame again, then calls your callback once.
  4. The camera stays paused after the callback. Call camera(CameraStatus.RESUME_PREVIEW) to capture again, or camera(CameraStatus.DISPOSE) before leaving the screen. The SDK never calls loadingCallBack(false); hide your loader in the callback.

Card callback parameters:

Parameter Type Meaning
isValid bool true if the captured card passed the liveness check
rawImagePath String JPEG of the full camera frame, in the app's temporary directory. Empty if capture failed
cardImagePath String JPEG of the card only, cropped. Empty if capture failed
message String Vietnamese text to show the user, for example "Mặt trước CCCD hợp lệ" or "Ảnh bị loá"
camera Future<void> Function(dynamic) Controls the paused camera: CameraStatus.RESUME_PREVIEW, PAUSE_PREVIEW, DISPOSE, INIT

4. Face liveness #

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

  @override
  Widget build(BuildContext context) {
    return FaceValidationView(
      multiLangStep: const {
        'CHECKING_STRAIGHT': 'Nhìn thẳng',
        'CHECKING_LEFT': 'Quay sang trái',
        'CHECKING_RIGHT': 'Quay sang phải',
        'DONE': 'Hoàn thành',
      },
      faceValidationCallback: (isValid, faceStraight, videoPath, errorCode, message, camera) async {
        if (errorCode == 'CAMERA_ERROR') {
          // The camera could not start: permission denied or camera busy.
          Navigator.of(context).pop();
          return;
        }
        // The callback also fires on every analysed frame while the user is moving.
        // The flow is finished only when a face photo comes back.
        if (isValid && faceStraight.isNotEmpty) {
          Navigator.of(context).pop(faceStraight);
        }
      },
    );
  }
}

How the screen behaves:

  1. The front camera opens with an oval. The user puts their face inside it and follows the text: straight, left, right. Each step has a time limit; if it runs out, the flow restarts by itself.
  2. While the user moves, the callback fires for each analysed frame, with errorCode set to the detector's status. You can ignore these calls or use them for your own hints.
  3. When all three steps pass, the SDK stops the camera and calls the callback with isValid = true and faceStraight set to the path of the straight-on face photo.

Face callback parameters:

Parameter Type Meaning
isValid bool Per frame: whether this frame passed. Final call: true
faceStraight String Path of the straight-on face JPEG. Only set on the final call; empty otherwise
videoPath String Always empty in this version; video recording is turned off
errorCode String Detector status for the frame, or CAMERA_ERROR if the camera failed to start
message String Text for the user (Vietnamese or English depending on the status)
camera Future<void> Function(dynamic) Same camera control as the card screen

multiLangStep maps step keys to the instruction shown on screen. Pass all four keys: CHECKING_STRAIGHT, CHECKING_LEFT, CHECKING_RIGHT, DONE. The SDK has no built-in text for this line; a missing key shows the CHECKING_STRAIGHT text instead, or nothing if that is missing too.

5. Images returned #

All images are JPEG files in the app's temporary directory (getTemporaryDirectory()). The OS may delete them at any time, so upload them or copy them somewhere permanent right away.

6. A typical eKYC flow #

CardValidationView(CARDFRONT)  ->  front image
CardValidationView(CARDBACK)   ->  back image
FaceValidationView             ->  face image
          |
          v
your backend: OCR, face match, final decision

Example #

The example/ folder has a working app with one screen per flow (card front, card back, passport, face). Run it on a real device:

cd example
flutter run

Support #

Techainer, https://techainer.com

0
likes
90
points
69
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

On-device eKYC capture for Flutter: guided Vietnamese ID card and passport capture with card liveness, and a face liveness flow (look straight, left, right). Ready-made screens for Android and iOS.

Repository (GitHub)

Topics

#ekyc #liveness #face-detection #identity-verification #camera

License

unknown (license)

Dependencies

camera, device_info_plus, ffi, flex_color_scheme, flutter, flutter_svg, google_fonts, image, image_picker, path_provider, provider, scidart, sliding_up_panel, tflite_flutter

More

Packages that depend on ekyc_vn_flutter_sdk

Packages that implement ekyc_vn_flutter_sdk