Skip to main content

Flutter SDK Integration Guide

Documented versions. Flutter plugin 1.0.2 / iOS SDK 1.0.11 / Android SDK 1.0.11. This page follows the WIDTH integration guide dated 11 September 2026. Confirm the selected SDK build and its target environment before deployment.

Overview

The WIDTH Identity SDK ships as a drop-in Flutter plugin for the identity verification flow (KYC and KYB). You add one dependency and call one entry point; the native SDK presents the full capture / liveness / review UI full-screen and returns a single result. The plugin wraps the native iOS and Android SDKs — iOS and Android only (no web or desktop).

Plugin 1.0.2 — pins iOS SDK 1.0.11 and Android SDK 1.0.11. Upgrading the plugin upgrades the native SDKs together; do not override the native dependencies manually.

Requirements

Requirement Minimum
Flutter 3.10+
iOS 16.0+ deployment target, Xcode 16+, CocoaPods
Android minSdk 26 (Android 8.0), compileSdk 36, core library desugaring enabled (see Android below)
Credentials must match the build environment The native SDKs are single-environment — each published build talks to one WIDTH environment, baked in at build time. The versions pinned by this release are production builds, so the domain / accessToken WIDTH issues you must be production credentials too. A token from another environment never gets past the SDK's security init, surfacing as W-RISK-102.

Installation

The plugin repository and the private Specs repo its iOS side pulls from both require your GitHub account to have been granted access first — until then flutter pub get and pod install report them as not found.

The plugin is distributed through WIDTH's private Git repository. Add it to your pubspec.yaml:

dependencies:
  flutter_width_identity_sdk_plugin:
    git:
      url: https://github.com/width-com/WidthIdentity-Flutter.git
      ref: 1.0.2

Then flutter pub get.

iOS

The native WidthIdentity pod resolves from WIDTH's private Specs repo. Declare both sources at the top of ios/Podfile and set the platform:

source 'https://github.com/width-com/Specs.git'
source 'https://cdn.cocoapods.org/'   # default trunk, for any public pods

platform :ios, '16.0'

Then cd ios && pod install.

Android

Set minSdk 26 in android/app/build.gradle. The plugin registers the WIDTH Maven repository, the vendor capture repositories (Veriff, Regula), and the native dependency automatically.

Two build settings are required by the native SDK's bundled AWS liveness engine — both on your app, neither involves your source code:

// android/app/build.gradle.kts
android {
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
    }
}

dependencies {
    coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}

The engine's Amplify AARs declare "requires core library desugaring" in their metadata, and AGP enforces that on the app module regardless of your minSdk; without it the build fails outright.

# android/gradle.properties — AGP 9 only
android.uniquePackageNames=false

On AGP 9 the manifest merger validates library namespace uniqueness, and two artifacts the liveness engine pulls in transitively (com.google.ai.edge.litert:litert-support and litert-support-api) declare the same namespace org.tensorflow.lite.support — an upstream packaging bug. Without the flag the build fails with Validation failed, exiting.

If your project enforces RepositoriesMode.FAIL_ON_PROJECT_REPOS, declare the repositories in android/settings.gradle instead:

dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://maven.width.com/repository/sdk-releases/") }
        google()
        mavenCentral()
        maven {
            url = uri("https://cdn.veriff.me/android/")
            content { includeGroup("com.veriff") }
        }
        maven {
            url = uri("https://maven-master.regulaforensics.com/RegulaDocumentReader/")
            content { includeGroupByRegex("com\\.regula.*") }
        }
    }
}

Permissions

iOS — add to ios/Runner/Info.plist. Camera, microphone and speech recognition are required; the photo library key is optional:

<!-- Required: document capture and liveness. -->
<key>NSCameraUsageDescription</key>
<string>Used to capture identity documents and complete the liveness check.</string>
<!-- Required: the document verification provider records background audio
     (anti-fraud) — the app crashes at runtime without this key. -->
<key>NSMicrophoneUsageDescription</key>
<string>Used by the document verification provider during video capture.</string>
<!-- Required by App Store ingestion; never requested at runtime (a linked but
     unreachable API inside the AWS liveness component). -->
<key>NSSpeechRecognitionUsageDescription</key>
<string>This app does not request speech recognition.</string>
<!-- Optional: only if your app also lets users pick a document image from the
     photo library. -->
<key>NSPhotoLibraryUsageDescription</key>
<string>Used to select identity document images.</string>

Android — the native SDK's manifest merges its permissions automatically; you declare nothing. The runtime (dangerous) permissions are CAMERA and RECORD_AUDIO (the latter bundled by the capture / liveness engine), requested by the SDK inside its own Activity. See the Android Permissions section for the full list, and reflect every permission in your Google Play Data Safety disclosure.

Quick Start

Import the plugin, build the SDK with a fluent chain, and await sdk.launch(). The future resolves when the native flow closes.

import 'package:flutter_width_identity_sdk_plugin/flutter_width_identity_sdk_plugin.dart';

final sdk = WIDTHMobileSDK.init(sdkToken)
    .withCredentials(domain: domain) // tenant routing domain (appId/apiKey are inside the token)
    .withApplicant(customerId: 'C_abc123', email: 'user@example.com')
    .withEventType(WIDTHEventType.kyc)               // optional; omit → user picks KYC/KYB in-SDK
    .withForcedDayNightMode(WIDTHDayNightMode.night) // optional; omit → follows system
    .withLanguage(WIDTHLanguage.zh)                  // optional; omit → English (never the device locale)
    .withHandlers(
      onStatusChanged: (newStatus, prevStatus) { /* terminal status change */ },
      verificationHandler: (isApproved) { /* final approve / reject only */ },
    )
    .build();

final result = await sdk.launch(); // resolves when the native flow closes

switch (result.status) {
  case WIDTHStatus.approved:        // final decision: approved
  case WIDTHStatus.finallyRejected: // final decision: rejected
  case WIDTHStatus.pending:         // under manual review
  case WIDTHStatus.incomplete:      // mid-flow exit / waiting; resumable
  case WIDTHStatus.cancelled:       // discarded; not resumable
  case WIDTHStatus.failed:          // see result.exception
}

Event logging (optional)

Pass an onEvent handler to receive SDK lifecycle events (INFO / WARN / ERROR) in your own pipeline (Crashlytics, DataDog, etc.); PII is redacted before the callback fires.

.withHandlers(
  onEvent: (event) {
    // event.level / event.tag / event.message
  },
)

UI language (optional)

withLanguage(WIDTHLanguage.zh) switches the flow to Simplified Chinese — the SDK's own copy plus the backend's {en, zh} dynamic content. Omitting it keeps English: the native SDKs never follow the device locale, so existing integrations are unaffected.

The bundled third-party capture screens have their own limits: the document-scan provider shows Chinese only when the device is online and the tenant integration enables it (English otherwise), and the document reader supports Simplified Chinese only.

Handling Results

launch() always resolves; it never throws for SDK-level failures.

result.status When
approved Server approved
finallyRejected Server rejected
pending Submission is under manual review out-of-band
incomplete User exited mid-flow or decision still pending; resumable with the same customerId
cancelled User explicitly discarded the verification; not resumable
failed Internal error; see result.exception

When .failed, result.exception (WIDTHException) carries a type — one of initialization, network, api, permission, workflow, unknown — with the original code (e.g. W-LIV-499) and message preserved. result.executionId identifies the run for support.

API Reference

Symbol Purpose
WIDTHMobileSDK.init(accessToken) Entry point, returns the builder
WIDTHMobileSDKBuilder withCredentials / withApplicant / withEventType / withForcedDayNightMode / withLanguage / withHandlers / build()
WIDTHMobileSDK.launch() Presents the flow; resolves with the result
WIDTHMobileSDKResult status / success / exception / executionId
WIDTHStatus / WIDTHException / WIDTHExceptionType Status / error
WIDTHSDKEvent / WIDTHLogLevel Event log
WIDTHEventType Optional KYC/KYB flow pre-selection
WIDTHDayNightMode Forced theme
WIDTHLanguage Optional UI language (en default / zh)
Upgrading: bump the ref tag in pubspec.yaml and run flutter pub get. Each plugin release pins the native SDK versions it was verified against — do not override the native dependencies manually.