Flutter
Okyc.start pushes a full-screen verification route (Flutter 3.24+, Android and iOS) and returns a typed result.
Version0.1.2
Install
Add okyc_flutter from pub.dev. It is built on webview_flutter 4.x,
plus a small native plugin for screenshots, the Android camera permission and the Android file chooser.
flutter pub add okyc_flutterRequirements: Flutter ≥ 3.24, Dart ≥ 3.5, Android minSdk 24, iOS 15.
Permissions
iOS: set the minimum iOS version to 15.0 and add camera and photo-library strings to Info.plist:
<key>NSCameraUsageDescription</key>
<string>Take a selfie and photograph your documents to verify your identity.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Some verification steps may record a short video.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Choose a photo of your document to verify your identity.</string>Android: nothing to add. The plugin's manifest declares INTERNET, CAMERA, the <queries> for camera and
document pickers, and a FileProvider scoped to cache/okyc/.
Start the flow
Create the session on your backend and pass its url to the app.
OkycConfig makeConfig(String sessionUrl) => OkycConfig(
sessionUrl: sessionUrl, // the `url` returned by POST /v1/sessions
locale: 'en', // optional; defaults to the session's language
theme: OkycTheme.system, // light | dark | system
allowedHosts: const [], // extra hosts, only when self-hosting
blockScreenshots: true,
autoCloseDelay: const Duration(seconds: 2), // result screen stays visible after `complete`
// Flutter only:
confirmExit: true, // ask "Exit verification?" on ✕ / back
title: 'Verify your identity',
);class VerifyButton extends StatelessWidget {
const VerifyButton({super.key});
Future<void> _start(BuildContext context) async {
final sessionUrl = await fetchSessionUrlFromYourBackend();
if (!context.mounted) return;
final result = await Okyc.start(context, makeConfig(sessionUrl), onEvent: onEvent);
handleResult(result);
}
@override
Widget build(BuildContext context) =>
FilledButton(onPressed: () => _start(context), child: const Text('Verify identity'));
}Okyc.start pushes a full-screen route with an ✕ button; ✕ and Android back ask "Exit verification?". To place
the flow in your own layout, embed OkycVerification(config:, onResult:) — your app then owns navigation and
the exit confirmation:
class VerifyScreen extends StatelessWidget {
const VerifyScreen({super.key, required this.sessionUrl});
final String sessionUrl;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Verification')),
body: OkycVerification(
config: makeConfig(sessionUrl),
onEvent: onEvent,
onResult: (result) {
Navigator.of(context).pop();
handleResult(result);
},
),
);
}Options
| Option | Type | Default | Description |
|---|---|---|---|
sessionUrl | string | required | The url returned by POST /v1/sessions. Never embed a secret API key in an app. |
locale | string | null | Flow language (en, hi); must be one of the workflow's languages. Sent as ?locale=. |
theme | light | dark | system | "system" | Colour scheme of the flow. Sent as ?theme=. |
allowedHosts | string[] | [] | Extra hosts the WebView may load (self-hosting). |
blockScreenshots | boolean | true | Android: FLAG_SECURE. iOS: cover the flow in the app switcher and while the screen is recorded. |
autoCloseDelay | duration_ms | 2000 ms | How long the result screen stays visible after complete before the SDK closes and returns completed. 0 or null = return the result immediately (no result screen). |
Flutter also has: confirmExit, title (see the SDK README).
Results and events
void handleResult(OkycResult result) {
switch (result) {
case OkycCompleted(:final sessionId, :final status):
// UX only: confirm the decision on your backend (webhook or GET /v1/sessions/:id).
print('finished $sessionId $status');
case OkycCancelled():
print('user closed the flow');
case OkycFailed(:final code, :final message):
if (code == OkycFailureCode.networkError) print('offline?');
print('verification failed: $code $message');
}
}void onEvent(OkycEvent event) {
// Payloads carry ids and statuses only — never personal data.
track('okyc_${event.type.name}', {'sessionId': event.sessionId, ...event.payload});
}OkycResult is OkycCompleted(sessionId, status), OkycCancelled(sessionId) or OkycFailed(code, message).
The route closes about 2 seconds after complete, so the applicant sees the result screen.
Failure codes in Flutter:
| Code | Meaning |
|---|---|
invalid_config | Missing or malformed session URL or configuration. |
load_failed | The flow page failed to load (HTTP error or blank page). |
network_error | No connection or DNS/TLS failure while loading. |
navigation_blocked | Repeated navigation to non-allowlisted hosts. |
permission_denied | The camera permission was permanently denied and the flow cannot continue. |
flow_error | The flow reported an unrecoverable error. |
Theming and locale
theme (light, dark, system) forces the flow's colour scheme. locale is sent as ?locale= (the token
stays in the fragment); also pass locale when you create the session so the flow starts in that language.
title sets the app bar title. The SDK's own screens (app bar, loading bar, load-error screen, exit dialog) follow
theme too, in the flow's neutral colours rather than your app's theme, because the flow itself uses your brand
colour.
Security defaults
- Bridge messages arrive on the
OkycFlutterchannel and are dropped unless the WebView is showing the session's own origin (not DigiLocker orallowedHosts), and when larger than 10 KB, not{source:'okyc', v:1}, of an unknown type, for another session, or nested. - The WebView visits only the session origin, DigiLocker and
allowedHosts; other https links on the top-level page open in the system browser; links inside frames, plain http and every other scheme are refused. More than 5 refused navigations in 3 s ends withnavigation_blocked. - Camera access only while the session origin is shown (and, on Android, after the OS permission).
- On finish: the flow's
sessionStorageandlocalStorage, the WebView cache and (Android) the camera photos are cleared, and screenshot blocking is turned off. Android WebView file access is off. Debugging only in debug builds. - The WebView shares cookies with your app, so the SDK leaves them. If your app has no other web views, call
WebViewCookieManager().clearCookies()after the result to also sign out of DigiLocker. blockScreenshots:FLAG_SECUREon Android; on iOS a privacy cover in the app switcher and while recording.
Troubleshooting
| Symptom | Fix |
|---|---|
| Local http URL refused | Plain http is accepted only for localhost, 127.0.0.1, 10.0.2.2 and ::1. |
| iOS build fails on the deployment target | Set platform :ios, '15.0' in the Podfile (or the Runner target). |
clearCache affects other WebViews | On Android the WebView cache is app-wide. |
Going live
- Create sessions with a
sk_live_…key on your backend; nothing changes in the app. - Decide on the webhook, not on
OkycCompleted.
React Native
A hardened react-native-webview wrapper (RN 0.74+) with a component, a modal and a promise API.
Organizations and projects
People belong to an organization; keys, workflows, sessions and settings belong to a project.
© 2026 Aiistic Technologies Private Limited. OKYC is a product of Aiistic Technologies Private Limited. Privacy notice