Flutter SDK Integration Guide
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).
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) |
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.2Then 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)
|
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.