Skip to documentation content
DocumentationFlutter SDK

SDKs / Flutter

IKYC Flutter SDK

Build a native Android or iOS verification experience while IKYC keeps provider credentials and the liveness session hand-off server-side.

How it works: Flutter asks your existing application server to start verification. Your server creates an IKYC native session. Flutter receives only the short-lived native session token and uses it to run the SDK flow.

1. Install safely

The package metadata in this repository is version 0.1.5. Confirm the exact version IKYC has approved and published for your environment before adding it. Do not assume local metadata proves a pub.dev release exists.

pubspec.yaml
dependencies:
  ikyc_flutter_sdk: <APPROVED_IKYC_FLUTTER_SDK_VERSION>
Install dependency
flutter pub get

Pin the approved version. Before any upgrade, read its release notes, run your app tests, and test release builds on real Android and iOS devices. Do not vendor, fork, or patch the SDK in a merchant application.

Compatibility updates: When the exact IKYC-approved published package version is available, upgrade iOS hosts using qoreidsdk 2.1.0 to0.1.4 for the QoreID launch crash that could occur before its UI appeared. Version 0.1.5 also improves QoreID terminal-status compatibility on Android and iOS. Neither update requires additional merchant configuration or data.

2. Create a native session on your application server

Your existing API, serverless function, or protected server environment is the application server in this guide. It authenticates the applicant, creates your verification attempt, and uses your secret IKYC project key. Flutter must not call this endpoint with a secret key.

Server-only native session request
POST https://<YOUR_IKYC_HOST>/api/v1/native/v1/sessions
x-api-key: <YOUR_SECRET_PROJECT_API_KEY>
Idempotency-Key: <NEW_STABLE_UUID>
Content-Type: application/json

{
  "externalUserId": "your-server-side-verification-attempt-id",
  "subjectRef": "a-pseudonymous-random-uuid",
  "operations": ["config", "consent", "liveness:reference", "liveness:result", "verify"],
  "ttlSeconds": 300
}
Subject reference: subjectRef is compulsory when your requested operations include liveness. Generate it on your server as a pseudonymous random identifier. It must differ from your server-side externalUserId and must not contain a name, phone number, email, BVN, NIN, or other personal data.

Save the native session details required by your server-side verification attempt, then return only the short-lived sessionToken and expiry to Flutter. Never return a project API key, QoreID credential, subject reference, external user ID, provider token, or raw provider payload.

3. Add the Flutter flow

The SDK exposes a small NativeTransport interface. Implement it with your approved HTTP stack. Pass SDK-provided headers through unchanged and do not log headers or request/response bodies.

MerchantNativeTransport
import 'dart:convert';
import 'dart:io';
import 'package:ikyc_flutter_sdk/ikyc_flutter_sdk.dart';

class MerchantNativeTransport implements NativeTransport {
  MerchantNativeTransport(this._client);
  final HttpClient _client;

  @override
  Future<NativeResponse> send(NativeRequest request) async {
    final outgoing = await _client.openUrl(request.method, request.uri);
    request.headers.forEach(outgoing.headers.set);
    if (request.body != null) outgoing.write(request.body);
    final response = await outgoing.close();
    return NativeResponse(
      status: response.statusCode,
      body: await utf8.decoder.bind(response).join(),
    );
  }

  void dispose() => _client.close(force: true);
}

Create the client and render the flow after your backend returns the short-lived native session.

Flutter verification screen
final transport = MerchantNativeTransport(HttpClient());
final client = IkycNativeClient(
  apiBaseUrl: Uri.parse('https://<YOUR_IKYC_HOST>'),
  sessionToken: nativeSession.sessionToken,
  transport: transport,
);
final controller = NativeFlowController(
  client: client,
  onSensitiveMediaCleanup: () async {
    // Clear host-owned temporary state if your app creates any.
  },
);

IkycVerificationFlow(
  controller: controller,
  onComplete: (result) {
    // Notify your application server. It obtains the authoritative outcome.
  },
  onError: (error) {
    // Show a safe message. Do not log tokens or response bodies.
  },
  onCancelled: () {
    // Return to the host application flow.
  },
)

Use an HTTPS base URL in released apps. The native flow uses only Authorization: Bearer …; it does not use browser public-key or Origin headers.

Configure the approved native mobile consent policy in Project → Flow → Native mobile consent. The SDK calls native /config and displays the exact active and effective consent text/version returned by IKYC. New integrations should omit consentDocument.

If policy is missing, inactive, malformed, or not yet effective, the flow stops before consent, liveness, and verification. There is no fallback wording. Existing integrations that pass consentDocument stay compatible, but their text and version must exactly match the active IKYC policy.

Existing controlled consent rendering
IkycVerificationFlow(
  controller: controller,
  consentDocument: NativeConsentDocument(
    text: '<EXACT_ACTIVE_IKYC_CONSENT_TEXT>',
    version: '<EXACT_ACTIVE_IKYC_CONSENT_VERSION>',
  ),
)

5. QoreID liveness and final verification

The configured applicant checks use human labels such as Identity check, NIN verification, BVN verification, Facial liveness check, and Selfie face match. Internal or provider codes are not shown in the applicant UI.

At liveness, IKYC provides a private sdkSessionToken to the adapter for the immediate QoreID launch and required empty/default provider data. The adapter does not forward a customer reference, subject reference, external user ID, or IKYC liveness reference to QoreID. The provider token never appears in public callbacks, errors, logs, or request bodies.

Important: A capture-complete screen from QoreID alone is not an IKYC dashboard record. The SDK must submit liveness evidence and complete the final native /verify step. That final step creates the verification record.

Android host setup

Use Android min SDK 23, compile/target SDK 36, Java 17, Android Gradle Plugin 8.13.2, and Kotlin 2.0.0. Add only camera permission for liveness:

android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

Add the QoreID Maven releases repository and JitPack:

Android repositories
allprojects {
    repositories {
        google()
        mavenCentral()
        maven(url = "https://repo.qoreid.com/repository/maven-releases/")
        maven(url = "https://jitpack.io")
    }
}

Apply the QoreID Kotlin plugin workaround before its subproject is evaluated, then initialize the plugin in the host activity:

Gradle workaround
gradle.beforeProject {
    if (name == "qoreidsdk") {
        pluginManager.apply("org.jetbrains.kotlin.android")
    }
}
MainActivity.kt
import android.os.Bundle
import com.qoreid.qoreidsdk.QoreidsdkPlugin
import io.flutter.embedding.android.FlutterActivity

class MainActivity : FlutterActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        QoreidsdkPlugin.initialize(this)
    }
}

The current QoreID package declares a dynamic Android native dependency. Pin the reviewed artifact in your app build using the syntax that matches your Gradle file:

Groovy Gradle pin
configurations.configureEach {
    resolutionStrategy.force 'com.qoreid:qoreid-sdk:2.0.1'
}
Kotlin Gradle pin
configurations.configureEach {
    resolutionStrategy.force("com.qoreid:qoreid-sdk:2.0.1")
}
Release ProGuard / R8 rule
-keep class com.qoreid.sdk.** { *; }

Build and install a release APK on a real device before distribution. Do not enable cleartext traffic, use debug signing for release, or patch provider code to work around a build issue.

iOS host setup

Use iOS 13.0+, Flutter 3.41+, and Xcode 15+. Keep framework linkage in the Podfile, retain a UIScene host with a UINavigationController root, and add only NSCameraUsageDescription for liveness.

ios/Podfile
platform :ios, '13.0'

target 'Runner' do
  use_frameworks!
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
ios/Runner/Info.plist
<key>NSCameraUsageDescription</key>
<string>Camera access is required for identity liveness verification.</string>

QoreID’s iOS SDK is declared through the Flutter package’s Swift Package definition. Enable Flutter Swift Package Manager integration from the consuming app directory:

Enable iOS package resolution
flutter config --enable-swift-package-manager
flutter clean
flutter pub get
flutter run -d <IOS_DEVICE_ID>

If Xcode cannot find QoreIDSDK, verify the Flutter/Xcode versions, repeat those commands, and open ios/Runner.xcworkspace to let Xcode resolve packages. Do not add a manual QoreID pod or copy frameworks into the app.

Testing and controlled rollout

Use DeterministicMockLivenessAdapter only for local demos and automated tests. It does not open a camera or contact a provider. For a controlled provider test, use the approved environment, active native consent, approved test data, and a real Android/iOS test device.

Expected native sequence
config → consent → liveness reference → liveness result → verify

For support, capture only the operation name, HTTP status, IKYC error code, URI path, and safe platform exception type. Never log or share headers, bearer tokens, API keys, request/response bodies, provider callbacks, references, or applicant data.

Troubleshooting

SymptomLikely causeSafe action
Configuration will not loadExpired session, wrong base URL, or transport failureMint a new session and check operation, status, error code, and URI path only.
Consent unavailablePolicy is absent, inactive, malformed, future-dated, or explicit text/version differsActivate the approved policy; omit consentDocument or make it exactly match.
QoreID will not openCamera permission or host setup is incompleteCheck native permission/setup and test a release build on a real device.
Capture completes but no dashboard recordFinal native verify step did not completeCheck the safe verify operation metadata; do not use provider UI as the final status.
Android works only in debugR8 rule, native artifact, or release build differenceKeep the dependency pin and ProGuard rule; test the release APK.
iOS cannot resolve QoreIDSDKSwift Package Manager has not resolved the dependencyEnable Flutter SPM, clean, run pub get, then open Runner.xcworkspace.

Never expose bearer tokens, API keys, headers, full bodies, raw provider payloads, customer references, subject references, external user IDs, or personal data in a support request.

Security checklist and consent migration

Do

  • Keep secret keys and references server-side.
  • Use HTTPS in released apps.
  • Let the SDK load active consent by default.
  • Complete final verification before trusting the result.

Do not

  • Mint sessions from Flutter.
  • Expose secrets, tokens, or raw provider data.
  • Send subjectRef or externalUserId from Flutter.
  • Blindly retry IDEMPOTENCY_EFFECT_UNRESOLVED.

Existing apps that pass consentDocument continue to work when its text and version exactly match the active policy. New integrations should use IkycVerificationFlow(controller: controller) and let IKYC provide the approved policy.