TrueID SDK for Flutter

A Flutter plugin for identity verification via Ghana Card (NIA). Captures a selfie with ML Kit face detection, submits it alongside a Ghana Card PIN to TrueID, and returns the verification result.

Features

  • Hosted document verification — document capture + selfie with liveness via TrueID's hosted flow in a Chrome Custom Tab, one Dart call, same UI/UX as the TrueID web widget
  • End-to-end verification — PIN entry, selfie capture, and NIA verification in one call
  • NFC chip reading — reads the ICAO 9303 chip on Ghana Card / ePassport-style documents (BAC/PACE, DG1/DG2/DG7/DG11) for stronger-than-OCR accuracy
  • Standalone selfie capture — Use just the camera + face detection
  • ML Kit face detection with real-time alignment guidance
  • Simple async Dart API

Platform Support

Platform Supported
Android Yes
iOS No (planned)

Installation

dependencies:
  trueid_sdk: ^1.0.0

Then run:

flutter pub get

Android Setup

Add the TrueID Maven repository to your android/settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://app.trueid.info/sdk/android") }
    }
}

On-prem institutions: replace app.trueid.info with your TrueID server origin.

JitPack (maven { url = uri("https://jitpack.io") }, com.github.elimichaells:trueid-selfie-sdk) is still supported as a legacy fallback if you can't reach the self-hosted repo.

Ensure your minSdkVersion is at least 24 in android/app/build.gradle:

android {
    defaultConfig {
        minSdkVersion 24
    }
}

Quick Start

1. Initialize

import 'package:trueid_sdk/trueid_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await TrueIdSdk.initialize(apiKey: 'your-api-key');
  runApp(MyApp());
}

Full document verification — document capture, selfie with liveness, review — using TrueID's hosted flow in a Chrome Custom Tab. No camera UI to build; same UI/UX as the TrueID web widget.

Future<void> verifyDocument() async {
  final result = await TrueIdSdk.launchHostedVerification(
    config: HostedVerificationConfig(
      mode: 'standard',           // or 'pin_selfie', 'identity_lookup'
      documentType: 'auto',        // optional preselect
      referenceId: 'your-ref-123', // optional correlation id
    ),
  );

  switch (result.status) {
    case 'CANCELLED':
      print('User cancelled');
      break;
    default:
      if (result.isSuccess) {
        // Send result.scanRecordId to your backend, then fetch the full
        // record with your secret key: GET /api/v1/scan-records/{id}
      } else {
        print('Failed: ${result.status} ${result.errorMessage}');
      }
  }
}

For production-grade key hygiene, create the session from your backend (POST /api/widget-sessions with your API key) and hand the app only the session url + token:

TrueIdSdk.launchHostedVerification(
  config: HostedVerificationConfig(sessionUrl: url, sessionToken: token),
);

3. Native NIA Verification

Future<void> verifyIdentity() async {
  try {
    final result = await TrueIdSdk.verify(
      config: VerificationConfig(
        forceNia: false,
        enforceFaceComparison: true,
        transactionType: 'onboarding',
      ),
    );

    if (result == null) {
      print('User cancelled');
      return;
    }

    if (result.isSuccess) {
      print('Verified: ${result.fullName}');
      print('Document: ${result.documentNumber}');
      print('DOB: ${result.dateOfBirth}');
    } else {
      print('Failed: ${result.errorMessage}');
    }
  } on TrueIdException catch (e) {
    print('Error: ${e.code} - ${e.message}');
  }
}

4. Standalone Selfie Capture

No API key required for just the camera:

Future<void> takeSelfie() async {
  final result = await TrueIdSdk.captureSelfie(
    config: SelfieCaptureConfig(
      captureMode: CaptureMode.auto,
      resultFormat: ResultFormat.base64,
    ),
  );

  if (result != null) {
    print('Got selfie: ${result.base64?.length} chars');
  }
}

5. NFC Chip Read

Reads the ICAO 9303 chip on Ghana Card / ePassport-style documents. The three BAC key fields normally come from a prior MRZ camera scan. There is no browser/widget equivalent — Web NFC cannot perform the ISO 7816 APDU exchanges an ICAO 9303 chip requires, so this is native-only.

Future<void> readChip(String documentNumber, String dob, String doe) async {
  if (!await TrueIdSdk.isNfcEnabled()) {
    print('Turn on NFC to continue');
    return;
  }

  try {
    final chip = await TrueIdSdk.readNfcChip(
      config: NfcReadConfig(
        documentNumber: documentNumber,
        dateOfBirth: dob,   // yyMMdd
        dateOfExpiry: doe,  // yyMMdd
      ),
    );

    if (chip == null) {
      print('User cancelled');
      return;
    }

    print('Chip read: ${chip.firstName} ${chip.lastName}');
  } on TrueIdException catch (e) {
    // e.code: NFC_NOT_SUPPORTED, NFC_DISABLED, NFC_TIMEOUT, NFC_READ_FAILED
    print('NFC error: ${e.code} - ${e.message}');
  }
}

API Reference

TrueIdSdk

Method Description
initialize({apiKey, environment, customBaseUrl}) Initialize with your API key. Call once before verify() or launchHostedVerification().
launchHostedVerification({config}) Launch the hosted document verification flow. Returns HostedVerificationResult (check .status for "CANCELLED").
verify({config}) Launch full native verification flow. Returns VerificationResult?.
captureSelfie({config}) Launch standalone selfie capture. Returns SelfieCaptureResult?.
isNfcSupported() Whether this device has NFC hardware at all.
isNfcEnabled() Whether NFC hardware is present and switched on.
readNfcChip({config}) Read an ICAO 9303 chip over NFC. Returns NfcReadResult? (null if cancelled).

HostedVerificationConfig

Parameter Type Default Description
sessionUrl String? null Hosted verification URL from a backend-created session
sessionToken String? null Session token matching sessionUrl; used to watch for completion
mode String 'standard' Flow when the SDK creates the session: 'standard', 'pin_selfie' or 'identity_lookup'
documentType String? null Preselected document type (e.g. 'ghana_card', 'auto')
theme String? null 'light', 'dark' or 'auto'
referenceId String? null Your own correlation id, echoed back on the scan record
completionGraceMillis int 4000 How long to keep watching for a result after the browser tab closes

HostedVerificationResult

Field Type Description
isSuccess bool Whether the flow completed successfully
status String COMPLETED, EXPIRED, CANCELLED or FAILED
scanRecordId String? Record ID to retrieve full results server-side
sessionToken String? The widget session token
errorMessage String? Error description (if failed)

VerificationConfig

Parameter Type Default Description
forceNia bool false Force NIA lookup even if local match exists
enforceFaceComparison bool true Require face match on local lookups
transactionType String? null Optional label for your records
captureConfig SelfieCaptureConfig default Selfie camera settings

VerificationResult

Field Type Description
verified bool Whether identity was verified
isSuccess bool verified && errorMessage == null
fullName String? Full name from Ghana Card
documentNumber String? Ghana Card number
nationality String? Nationality
dateOfBirth String? Date of birth
gender String? Gender
expiryDate String? Card expiry date
phoneNumber String? Phone number on record
email String? Email on record
selfieUrl String? URL of captured selfie
niaPhotoUrl String? URL of NIA photo on file
errorMessage String? Error description (if failed)
errorCode String? Error code (if failed)

NfcReadConfig

Parameter Type Default Description
documentNumber String required BAC key field, from a prior MRZ scan
dateOfBirth String required BAC key field, yyMMdd
dateOfExpiry String required BAC key field, yyMMdd
title String 'Scan your document chip' Screen title
instructions String default copy Screen body text
timeoutMs int 20000 How long to wait for a chip before reporting NFC_TIMEOUT

NfcReadResult

Field Type Description
firstName / lastName String From the chip's MRZ (DG1)
gender String From DG1
issuingState / nationality String From DG1
documentNumber / documentCode String From DG1
dateOfBirth / dateOfExpiry String From DG1
personalNumber String From DG1, falling back to DG11
photoBase64 String? Face image (DG2), PNG bytes base64-encoded
signatureBase64 String? Signature image (DG7), when present on the chip

Environments

// Production (default)
TrueIdSdk.initialize(apiKey: 'key');

// Staging
TrueIdSdk.initialize(
  apiKey: 'key',
  environment: TrueIdEnvironment.staging,
);

// Custom
TrueIdSdk.initialize(
  apiKey: 'key',
  environment: TrueIdEnvironment.custom,
  customBaseUrl: 'https://your-server.com',
);

Permissions

The SDK handles these automatically:

  • CAMERA — For selfie capture
  • INTERNET — For API calls
  • NFC — For chip reads (readNfcChip()); no-op on devices without NFC hardware

You must handle the runtime camera permission in your app before calling verify() or captureSelfie().

Getting an API Key

Sign up at app.trueid.info to get your API key.

License

MIT License. See LICENSE for details.

Libraries

trueid_sdk