• About TrustVision
  • Android SDK
  • iOS SDK
  • React Native SDK
  • Web SDK
  • API Client Libraries
  • eKYC Platform
  • Integration Case Studies
  • TS eKYC/FA App
TrustVision API Documentation

Flutter 4.x.x

Environment

Flutter

Flutter SDK ">=2.12.0 <3.0.0"

iOS

iOS 11 and above

Xcode "15.2"

Android

  • Gradle Version 7.2.2
  • Tested with Gradle Plugin for Android Studio - version Android Studio Electric Eel | 2022.1.1 Patch 2
  • minSdkVersion 21
  • targetSdkVersion 33

Installation

Common

1. Add Trust Vision Flutter lib

Add to pubspec.yaml file under dependencies group:

  trust_vision_plugin:
    git:
     # Replace the below with your specific info
      url: to_be_replaced
      ref: to_be_replaced

2. Precompile

$ flutter pub get

iOS

1. Add to podfile

# Add below lines to the end of podfile
post_install do |installer|
    installer.pods_project.targets.each do |target|
        // you can add more modules which have the error "Undefined symbol" into the list
        if ['CocoaLumberjack', 'TensorFlowLiteC', 'TensorFlowLiteSwift', 'PromiseKit', 'CryptoSwift'].include? "#{target}"
            target.build_configurations.each do |config|
                config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
            end
        end
    end
end

2. Setup Xcode

Set Build Libraries for Distribution in Build Settings to No

alt text

3. Precompile

$ pod install

Android

Add to root-level build.gradle file (host app):

groovy
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

allprojects {
    repositories {
        google()
        mavenCentral()

        /*
        Replace the below with your path to `trust_vision_plugin` Android repo which downloaded by `flutter pub get`.
        */
        maven { url "$flutterRoot/.pub-cache/git/replace_by_git_repo_name-replace_by_commit_hash/android/repo" }
        /*
        For example:
        maven { url "$flutterRoot/.pub-cache/git/tv_flutter_sdk_xyz-1a5eb61ccb44ffafa4d3557f4d9d8087bc1a4666/android/repo" }
        */
    }
}

Add to app/build.gradle:

groovy
aaptOptions {
    noCompress "tflite"
    noCompress "lite"
}

Usage

dart
import 'package:trust_vision_plugin/enums.dart';
import 'package:trust_vision_plugin/trust_vision_plugin.dart';

1. Initialize SDK

Before using any TV SDK functions, it needs to be successfully initialized once.

Init UIOnly mode

dart
  void initUiOnlyModeSDK() async {
    try {
      var initResult = await TrustVisionPlugin.instance.initialize(
        jsonConfigurationByServer: jsonConfigurationByServer,
        languageCode: 'vi',
      );

    } catch (exception) {
      print("Handle exception: $exception");
    }
  }

where:

  • jsonConfigurationByServer: String. It's the setting specialized for each client from TS server. It's the response json string get by API https://ekyc.trustingsocial.com/api-reference/customer-api/#get-client-settings. When it's null or unmatched with the expected type then the default setting in the SDK will be used.
  • languageCode: String the code of the language that will show and sound to user. E.g Vietnamese (vi), English (en).

Init Full mode

dart
   void initFullModeSDK() async {
    try {
      var initResult = await TrustVisionPlugin.instance.initialize(
        accessKeyId: "your_key",
        accessKeySecret: 'your_key',
        endpoint: 'your_endpoint',
        xLenderRequestId: "your_id",
        languageCode: 'vi',
      );
    } catch (exception) {
      print("Handle exception: $exception");
    }
  }

2. ID card capturing

2.1. Configuration

Change the following parameters to suit your needs.

dart

void _captureIdCard() async {
    try {

      TVCardType cardType = TVCardType(
        id: "vn.national_id",
        name: "CMND cũ / CMND mới / CCCD / Hộ chiếu",
        hasBackSide: true,
        orientation: TVCardOrientation.HORIZONTAL,
      );

      CardSide cardSide = CardSide.front; // CardSide.back

      Map<String, dynamic> idCardConfig = {
        "cardTypes": [cardType.toMap()],
        "cardSide": cardSide.name,
        "isEnableSound": true,
        "isEnableSanityCheck": true,
        "isReadBothSide": false,
        "skipConfirmScreen": true,
        "isEnableDetectingIdCardTampering": true,
        "isEnableScanNfc": false,
        "isEnableScanQr": false,
      };

      // Start ID card capturing
      TVDetectionResult? result = await TrustVisionPlugin.instance.captureIdCard(idCardConfig);

      print("_captureIdCard result: $result");

    } catch (exception) {
      print("Handle exception: $exception");
    }
  }

Where:

  • cardType: TVCardType
  • cardSide: CardSide (back, front)
  • isEnableSound: bool. enable/disable guiding sound
  • isEnableSanityCheck: bool. Id card sanity checking should be enabled or not
  • isReadBothSide: bool. whether or not read both sides of the selected id card at one flow
  • skipConfirmScreen: bool. Whether or not TV SDK should skip its own confirmation screen.
  • isEnableDetectingIdCardTampering: bool. enable/disable ID card tampering detection
  • isEnableScanNfc: bool. enable/disable scan NFC flow
  • isEnableScanQr: bool. enable/disable scan QRCode flow

2.2. Start ID card capturing

dart
TVDetectionResult? result = await TrustVisionPlugin.instance.captureIdCard(idCardConfig);

2.2. Handle id result

2.2.1 Get front image

dart
var idFrontImage = result?.idFrontImage;
 // TODO call your api to upload image

2.2.2 Get back image

dart
var idBackImage = result?.idBackImage;
 // TODO call your api to upload image

2.2.3 Get qr image

dart
 var frontIdQr = result?.frontIdQr;
  // TODO call your api to upload image

3. Selfie capturing

3.1. Configuration

Change the following parameters to suit your needs.

dart
 void _captureSelfie() async {
    try {

      LivenessMode livenessMode = LivenessMode.flash;
      SelfieCameraMode selfieCameraMode = SelfieCameraMode.back; // or SelfieCameraMode.front, SelfieCameraMode.back

      final selfieConfig = {
        "cameraOption": selfieCameraMode.name,
        "livenessMode": livenessMode.name,
        "isEnableSound": true,
        "isEnableSanityCheck": false,
        "isEnableVerifyLiveness": false,
        "skipConfirmScreen": true
      };

      // Start Selfie capturing
       TVDetectionResult? result = await TrustVisionPlugin.instance.captureSelfie(
        selfieConfig,
        onNewFrameBatch: (TvFrameBatch frameBatch) {
          // Handle frame batch
          // Refer: https://ekyc.trustingsocial.com/sdks/Android-SDK#303-handle-onnewframebatch-callback
        },
      );

      print("_captureSelfie result: $result");
    } catch (exception) {
      print("Handle exception: $exception");
    }
  }

Where:

  • onNewFrameBatch: Function(TvFrameBatch)?. This callback function can be called multiple times during the capturing process to return frames.
  • cameraOption: SelfieCameraMode. Camera side
  • isEnableSound: bool. enable/disable guiding sound
  • isEnableSanityCheck: bool. Selfie sanity checking should be enabled or not
  • skipConfirmScreen: bool. Whether or not TV SDK should skip its own confirmation screen.
  • livenessMode: LivenessMode. Liveness mode
dart
enum LivenessMode {
  active,
  passive,
  none,
  flash,
  flash_edge,
  flash_advanced,
  flash_8,
  flash_16,
  flash_32
}

3.2. Start Selfie capturing

dart
TVDetectionResult? result = await TrustVisionPlugin.instance.captureSelfie(selfieConfig);

3.3. Handle selfie result

Please don't remove any item from result.selfieImages list

3.3.1. Get frontal images
dart
    var selfieImages = result?.selfieImages;
    var frontalImages = <TVImageClass?>[];
    selfieImages?.forEach((item) {
      if(item.frontalImage?.rawImageBase64 != null) {
        // Get frontal image
        frontalImages.add(item.frontalImage);
      }
    });
    // TODO call your api to upload image
3.3.2. Get final frontal image (frontal step)
dart
    var selfieImages = result?.selfieImages;
    var finalFrontalImage = selfieImages?.firstWhere((item) => item.gestureType?.toLowerCase() == 'frontal').frontalImage;
3.3.3. Get gesture images
dart
    var selfieImages = result?.selfieImages;
    var gestureImages = <TVImageClass?>[];
    selfieImages?.forEach((item) {
      if(item.gestureImage?.rawImageBase64 != null) {
        // Get gesture image
        gestureImages.add(item.gestureImage);
      }
    });
     // TODO call your api to upload image
3.3.4. Flash images

If you are using liveness modes such as flash, flash_edge, flash_advanced, flash_8, flash_16, or flash_32, the images should be handled as follows:

  • frontalImages[0]: Image captured during the close step.
  • frontalImages[1]: Image captured during the far step.

4. NFC reader

4.1 Add NFC Capability

alt text

4.2 Set config Info.plist for iOS

<key>NFCReaderUsageDescription</key>
<string>This app would like to use NFC to scan CCCD chip</string>

<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
    <array>
        <string>A0000002471001</string>
        <string>A0000002472001</string>
        <string>00000000000000</string>
    </array>

4.3. Configuration

Change the following parameters to suit your needs.

dart
void _readNfc() async {
  try {
    final nfcConfig = {
    nfcCode: "12 digits of CCCD number",
    nfcSod: sod,
    cardIssueDate: issueDate,
    nfcCacheFields: nfcCacheFields,
    isRequestReadImageNfc: true,
    isRequestCloneDetectionNfc: true,
    isRequestIntegrityCheckNfc: true,
    nfcMaxRetries: 5,
    isEnableCheckNfcData: true,
    isEnableVerifyNfc: true,
    dateOfBirth:  "dd/MM/yyyy",
    dateOfExpiry : "dd/MM/yyyy"
    };

    // Start NFC reader
    TVDetectionResult? result = await TrustVisionPlugin.instance.readNfc(nfcConfig);
    print("_readNfc result: $result");

    } catch (exception) {
      print("Handle exception: $exception");
    }
  }

Where:

  • nfcCode: String 12 digits of CCCD number
  • nfcSod: String (optional) is the hash of SOD
  • cardIssueDate: String (optional) is the issue date of ID card (dd/MM/yyyy)
  • nfcCacheFields: List<String> (optional) is the list of fields that was cached from previous scanning
  • isRequestReadImageNfc: Bool. Read image in the chip when scan nfc or not
  • isRequestCloneDetectionNfc: Bool. Check clone of the chip when scanning nfc or not
  • isRequestIntegrityCheckNfc: Bool. Check integrity of the chip when scanning nfc or not
  • nfcMaxRetries: Int. The maximum number of times the SDK retries an NFC scanning before giving up
  • isEnableCheckNfcData: Bool. Enable check NFC data or not. If it's true then the SDK will call the API to get sod and cached fields of the NFC data.
  • isEnableVerifyNfc: Bool. Enable verify NFC or not. If it's true then the SDK will call the API to verify the NFC data.
  • dateOfBirth: String? (required for iOS) is the date of birth (dd/MM/yyyy)
  • dateOfExpiry: String? (required for iOS) is the expired of ID card (dd/MM/yyyy). For cards with unlimited expiration, please input: 31/12/9999

4.4. Start NFC reader

dart
  TVDetectionResult? result = await TrustVisionPlugin.instance.readNfc(nfcConfig);

4.5. Check if the device supports NFC.

dart
bool? isSupported =  await TrustVisionPlugin.instance.checkNfcSupport();

5. Scan QR code

5.1. Configuration

Change the following parameters to suit your needs.

dart
  void _scanQrCode () async  {
    try {

      var selectedCardType = TVCardType(
        id: "vn.national_id",
        name: "CMND cũ / CMND mới / CCCD / Hộ chiếu",
        hasBackSide: true,
        orientation: TVCardOrientation.HORIZONTAL,
        frontQr: TVQrSdk(
          exist: true,
          type: TVQRType.qrCode,
          widthHeightRatio: 1,
        )
      );

      final config = {
        "cardType": selectedCardType.toMap(),
        "cardSide": CardSide.front.name,
        "skipConfirmScreen": true,
        "isEnableUploadFrames" : false,
        "isEnableUploadImages" : false
      };

      TVDetectionResult? result = await TrustVisionPlugin.instance.scanQrCode(config);
    } catch (exception) {
      showSnackbar("Error: ${exception.toString()}");
    }
  }

Where:

  • cardType: TVCardType. Type of card
  • frontQr : TVQrSdk . Qr setting
  • skipConfirmScreen: bool. Whether or not TV SDK should skip its own confirmation screen.
  • isEnableUploadFrames: Bool. Enable upload video frames or not. If it's false then the SDK won't call the API to upload the frames and the APIs that need the video frames will be skipped or called with empty frames data.
  • isEnableUploadImages: Bool. Enable upload images or not. If it's false then the SDK won't call the API to upload the images and the APIs that need the image will be skipped.
TVQrSdk
  • exist: Bool. Has QR code or not.
  • type TVQRType : Type of code: qrCode , barCode
  • widthHeightRatio Float : Card ratio

5.2. Start scan QR code

dart
  TVDetectionResult? result = await TrustVisionPlugin.instance.scanQrCode(config);

5.3. Upload QR images

If the APIs is called by the SDK, please skip this step.

if result.frontIdQr.isRequired is true then result.frontIdQr.images array should be non-empty. Otherwise, clients should be warned to re-scan QR code.

QR images will be uploaded with this api: https://ekyc.trustingsocial.com/api-reference/customer-api/#upload-image

var frontQrImage = result?.frontIdQr?.images[i];

*The same logic will be applied to result.backIdQr

6. Result Handling:

6.1. Result Object:

  • result: .TVDetectionResult:

    • cardType: TVCardType. Card type
    • actionMode: TVActionMode. Action Mode
    • cardInfoResult: TVCardInfoResult. Card information result
    • livenessResult: TVLivenessResult. Liveness result
    • idSanityResult: TVSanityResult. Id card sanity result
    • selfieSanityResult: TVSanityResult. Selfie sanity result
    • idTamperingResult: TVSanityResult. ID tampering result
    • selfieImages: [SelfieImage]. List of selfie image objects
    • idFrontImage: TVImageClass. Id front image object
    • idBackImage: TVImageClass. Id back image object
    • frontIdQr: TVCardQr. Card's QR Code image.
    • nfcInfoResult: TVNfcInfoResult. Card NFC information
    • selfieVideos: [Uint8List]. Liveness video data
    • livenessFrameBatchIds: [String]. Liveness valid frame batch id
  • SelfieImage:

    • gestureImage: String. UP | DOWN | LEFT | RIGHT | FRONTAL
    • frontalImage: TVImageClass. Frontal image object
    • gestureImage: TVImageClass. Gesture image object
  • TVImageClass:

    • rawImageBase64: String. Base64 string of image data
    • encryptedImageHexString: String. Encrypted base64 string of image data
    • imageId: String. Image id
    • metadata:Map<String, dynamic>. Metadata of the image
  • TVCardInfoResult:

    • infos: [Info] array of Info
    • requestId: String. Id of request
  • Info:

    • field: String. Name of field example name, address...
    • value: String. The value of each field
  • TVLivenessResult:

    • score: float. The score from 0 to 1
    • isLive: bool. Selfie image is live or not
    • requestId: String. Id of request
  • TVSanityResult:

    • isGood: bool. Sanity is good or not
    • score: float. The score from 0 to 1
    • requestId: String. Id of request
    • error: String. Error message
    • details: [Detail]. List of result detail
  • Detail:

    • name: String. Name of field
    • score: String. The score from 0 to 1
    • verdict: String. Verdict of field
  • TVCardQr:

    • images: [TVImageClass]. List of image
    • isRequired: bool.

    if isRequired is true then images array should be non-empty. Otherwise, clients should be warned to re-capture id card photos.

  • TVNfcInfoResult:

    • requestId: String. Id of request
    • com: String. Com data
    • sod: String. Sod data
    • dg1: String. Dg1 data
    • dg2: String. Dg2 data
    • dg13: String. Dg13 data
    • dg14: String. Dg14 data
    • dg15: String. Dg15 data
    • infos: [Info]. array of Info
    • verificationResult: TVNfcVerificationStatus. Verification result data
  • TVNfcVerificationResult:

    • verdict: String. Result's verdict
    • cloneStatus: TVNfcVerificationStatus. Clone status
    • integrityStatus: TVNfcVerificationStatus. Integrity status
    • bcaStatus: TVNfcVerificationStatus. BCA status (BCA is Bộ Công An)
  • TVNfcVerificationStatus:

    • verdict: TVNfcVerificationStatusVerdict | UNKNOWN,NOT_PRESENT,NOT_CHECKED,ALERT,GOOD,ERROR. Result's verdict
    • error: TVDetectionError. Error detail
  • TvFrameBatch:

    • id: String?. Frame's id
    • frames: [TvFrameClass]. Batch of framed to be pushed
    • metadata: Map<String, dynamic>. Batch metadata to push
    • validVideoIds: [String]. For debugging purpose only
  • TvFrameClass:

    • index: int?. Index of this frame in the list of recorded frames
    • base64: String?. The data of the frame as a base64 String
    • label: String. Label of this frame
  • TVDetectionError:

    • errorCode: int. Error code
    • detailErrorCode: String. Detail for error code
    • errorDescription: String. Error description

5.2. Sanity result error handling

5.2.1. Id sanity
dart
 String? error = result?.idSanityResult?.error;
if (error != null) {
    switch (result.idSanityResult.error) {
        case 'image_too_blur':
            // Image is too blurry
            break;
        case 'image_too_dark':
            // Image is too dark
            break;
        case 'image_too_bright':
            // Image is too bright
            break;
        case 'image_has_hole':
            // Image has holes
            break;
        case 'image_has_cut':
            // Images has been cut
            break;
        case 'image_has_hole_and_cut':
            // Images has holes and has been cut
            break;
    }
}
5.2.2. Selfie sanity
dart
 String? error = result?.selfieSanityResult?.error;
if (error != null) {
    switch (selfieSanityResult["error"]) {
        case 'image_too_blur':
            // Image is too blurry
            break;
        case 'image_too_dark':
            // Image is too dark
            break;
        case 'image_too_bright':
            // Image is too bright
            break;
        case 'not_white_background':
            // The background is not white enough
            break;
        case 'not_qualified':
            // The face is not qualified, could be occluded, covered or having something unusal
            break;
        case 'image_has_multiple_faces':
            // Image has multiple faces
            break;
        case 'image_has_no_faces':
            // Image does not have any face
            break;
        case 'right':
            // Face is looking to the right
            break;
        case 'left':
            // Face is looking to the left
            break;
        case 'open_eye,closed_eye':
            // Right eye is closed
            break;
        case 'closed_eye,open_eye':
            // Left eye is closed
            break;
        case 'open_eye,sunglasses':
            // Sunglass covers the right eye
            break;
        case 'sunglasses,open_eye':
            // Sunglass covers the left eye
            break;
        case 'closed_eye,closed_eye':
            // Both eyes are closed
            break;
        case 'closed_eye,sunglasses':
            // Left eye is closed, sunglass covers the right eye
            break;
        case 'sunglasses,closed_eye':
            // Sunglass covers the right eye, right eye is closed
            break;
        case 'sunglasses,sunglasses':
            // Sunglasses cover both eyes
            break;
    }
}

7. Error handling

dart
 switch (e.code) {
            // connection errors
            case 'timeout_error'
            break;
                // Network timeout. Poor internet connection.
            case 'network_error':
                // Network error. No internet connection
            break;
            // Id capturing
            case 'incorrect_card_type':
                // the input image is not same type with selected card
                break;
            case 'nocard_or_multicard_image':
                // the input image is no card or multicard detected
                break;

            // Selfie capturing
            case 'image_has_no_faces':
                // face not detected in selfie image
                break;
            case 'image_has_multipe_faces':
                // multiple faces are detected in selfie image
                break;

            // Common errors
            case 'access_denied_exception':
                break;
            case 'invalid_parameter_exception':
                break;
            case 'rate_limit_exception':
                break;
            case 'internal_server_error':
                break;

            case: 'sdk_canceled':
              break;
        }