Mobile

Flutter

Okyc.start pushes a full-screen verification route (Flutter 3.24+, Android and iOS) and returns a typed result.

View as Markdown

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.

Terminal
flutter pub add okyc_flutter

Requirements: 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:

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.

docs_snippets.dart
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',
    );
docs_snippets.dart
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:

docs_snippets.dart
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

OptionTypeDefaultDescription
sessionUrlstringrequiredThe url returned by POST /v1/sessions. Never embed a secret API key in an app.
localestringnullFlow language (en, hi); must be one of the workflow's languages. Sent as ?locale=.
themelight | dark | system"system"Colour scheme of the flow. Sent as ?theme=.
allowedHostsstring[][]Extra hosts the WebView may load (self-hosting).
blockScreenshotsbooleantrueAndroid: FLAG_SECURE. iOS: cover the flow in the app switcher and while the screen is recorded.
autoCloseDelayduration_ms2000 msHow 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

docs_snippets.dart
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');
  }
}
docs_snippets.dart
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:

CodeMeaning
invalid_configMissing or malformed session URL or configuration.
load_failedThe flow page failed to load (HTTP error or blank page).
network_errorNo connection or DNS/TLS failure while loading.
navigation_blockedRepeated navigation to non-allowlisted hosts.
permission_deniedThe camera permission was permanently denied and the flow cannot continue.
flow_errorThe 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 OkycFlutter channel and are dropped unless the WebView is showing the session's own origin (not DigiLocker or allowedHosts), 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 with navigation_blocked.
  • Camera access only while the session origin is shown (and, on Android, after the OS permission).
  • On finish: the flow's sessionStorage and localStorage, 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_SECURE on Android; on iOS a privacy cover in the app switcher and while recording.

Troubleshooting

SymptomFix
Local http URL refusedPlain http is accepted only for localhost, 127.0.0.1, 10.0.2.2 and ::1.
iOS build fails on the deployment targetSet platform :ios, '15.0' in the Podfile (or the Runner target).
clearCache affects other WebViewsOn 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.

On this page