kyvshield_lite
KyvShield Lite — Flutter KYC SDK powered by WebView. Same API as the native kyvshield SDK, zero native ML dependencies.
Ideal for MVPs and cross-platform apps that want identity verification without heavy native camera/ML packages.
How it works
Kyvshield.initKyc()opens a full-screen WebView.- The WebView loads the KyvShield Web SDK from your API server.
- The entire KYC flow runs inside the WebView (camera, liveness, OCR, face match).
- The result is sent back to Flutter via a JS bridge and parsed into typed Dart objects.
Installation
dependencies:
kyvshield_lite: ^0.0.3
Full Example
All possible enum values listed. Code is copy-paste ready.
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:kyvshield_lite/kyvshield_lite.dart';
// ── Possible values ───────────────────────────────────────────────────
// CaptureStep : selfie, recto, verso
// ChallengeMode : minimal, standard, strict
// SelfieDisplayMode : standard, compact, immersive, neonHud
// DocumentDisplayMode: standard, compact, immersive, neonHud
// ChallengeAudioRepeat: once, twice, thrice
// Brightness : light, dark
// ── Fetch available documents from API ────────────────────────────────
// See https://kyvshield.innolink.sn/developer for full API docs
final res = await http.get(
Uri.parse('https://kyvshield.innolink.sn/api/v1/documents'),
headers: {'X-API-Key': 'YOUR_API_KEY'},
);
final docs = (jsonDecode(res.body)['documents'] as List)
.map((d) => KyvshieldDocument.fromJson(d))
.toList();
final selectedDoc = docs.first; // or let user pick
// ── Request camera permission ─────────────────────────────────────────
final granted = await Kyvshield.requestCameraPermission();
if (!granted) {
final denied = await Kyvshield.isCameraPermissionPermanentlyDenied();
if (denied) await Kyvshield.openSettings();
return;
}
// ── Start KYC ─────────────────────────────────────────────────────────
final result = await Kyvshield.initKyc(
context: context,
config: KyvshieldConfig(
baseUrl: 'https://kyvshield.innolink.sn',
apiKey: 'YOUR_API_KEY',
enableLog: true,
theme: KyvshieldThemeConfig(
primaryColor: Color(0xFFEF8352),
brightness: Brightness.light,
),
),
flow: KyvshieldFlowConfig(
steps: [CaptureStep.selfie, CaptureStep.recto, CaptureStep.verso],
language: 'fr',
showIntroPage: true,
showInstructionPages: true,
showResultPage: true,
showSuccessPerStep: true,
selfieDisplayMode: SelfieDisplayMode.standard,
documentDisplayMode: DocumentDisplayMode.standard,
challengeMode: ChallengeMode.minimal,
requireFaceMatch: true,
playChallengeAudio: true,
maxChallengeAudioPlay: ChallengeAudioRepeat.once,
pauseBetweenAudioPlay: Duration(seconds: 1),
stepChallengeModes: {
CaptureStep.selfie: ChallengeMode.minimal,
CaptureStep.recto: ChallengeMode.standard,
CaptureStep.verso: ChallengeMode.minimal,
},
target: selectedDoc,
kycIdentifier: 'user-12345',
),
);
// ── Start KYC ─────────────────────────────────────────────────────────
final result = await Kyvshield.initKyc(
context: context,
config: config,
flow: flow,
);
// ── Handle result ─────────────────────────────────────────────────────
print('Success: ${result.success}');
print('Status: ${result.overallStatus}'); // pass, review, reject, error
print('Session: ${result.sessionId}');
// Selfie
if (result.selfieResult != null) {
print('Live: ${result.selfieResult!.isLive}');
print('Image: ${result.selfieResult!.capturedImage?.length} bytes');
}
// Recto
if (result.rectoResult != null) {
print('Recto score: ${result.rectoResult!.score}');
print('Aligned doc: ${result.rectoResult!.alignedDocument?.length} bytes');
print('Photos: ${result.rectoResult!.extractedPhotos.length}');
print('Fields: ${result.rectoResult!.extraction?.fields.length}');
// Face match (attached to recto)
if (result.rectoResult!.faceVerification != null) {
print('Face match: ${result.rectoResult!.faceVerification!.isMatch}');
print('Similarity: ${result.rectoResult!.faceVerification!.similarityScore}');
}
}
// Verso
if (result.versoResult != null) {
print('Verso score: ${result.versoResult!.score}');
print('Fields: ${result.versoResult!.extraction?.fields.length}');
}
// Extracted data (searches recto + verso)
print('Nom: ${result.getExtractedValue("nom")}');
print('NIN: ${result.getExtractedValue("nin")}');
// Loop all fields sorted by priority
for (final field in result.rectoResult?.extraction?.sortedFields ?? []) {
print('${field.label}: ${field.stringValue}');
}
Configuration
KyvshieldConfig
KyvshieldConfig(
baseUrl: 'https://kyvshield.innolink.sn', // API server URL
apiKey: 'your-api-key', // API key from dashboard
apiVersion: 'v1', // API version (default: v1)
enableLog: true, // Debug logs (default: false)
timeoutSeconds: 60, // Request timeout
theme: KyvshieldThemeConfig(
primaryColor: Color(0xFFEF8352), // Brand color
successColor: Colors.green,
warningColor: Colors.orange,
errorColor: Colors.red,
brightness: Brightness.light, // Light or dark mode
),
)
KyvshieldFlowConfig
KyvshieldFlowConfig(
// Steps to perform
steps: [CaptureStep.selfie, CaptureStep.recto, CaptureStep.verso],
// Language ('fr', 'en', 'wo')
language: 'fr',
// UI pages
showIntroPage: true, // Welcome screen
showInstructionPages: true, // Per-step instruction pages
showResultPage: true, // Final result summary
showSuccessPerStep: true, // Success animation after each step
// Display modes
selfieDisplayMode: SelfieDisplayMode.standard, // standard, compact, immersive, neonHud
documentDisplayMode: DocumentDisplayMode.standard, // standard, compact, immersive, neonHud
// Verification options
challengeMode: ChallengeMode.normal, // easy, normal, hard
requireFaceMatch: true, // Selfie vs document face comparison
// Audio
playChallengeAudio: true,
maxChallengeAudioPlay: ChallengeAudioRepeat.once, // once, twice, thrice
pauseBetweenAudioPlay: Duration(seconds: 1),
// Document target
target: KyvshieldDocument(
docType: 'SN-CIN',
name: "Carte Nationale d'Identite",
category: 'identity_card',
country: 'SN',
countryName: 'Senegal',
),
// Optional: per-step challenge modes
stepChallengeModes: {
CaptureStep.selfie: ChallengeMode.hard,
CaptureStep.recto: ChallengeMode.normal,
},
// Optional: webhook correlation ID
kycIdentifier: 'user-12345',
)
Display Modes
| Mode | Description |
|---|---|
standard |
Classic layout with header, camera, and instructions below |
compact |
Camera fills screen, instructions overlay at bottom |
immersive |
Full-screen camera with glass-effect overlays |
neonHud |
Futuristic dark theme with glow effects and monospace font |
Result Structure
final result = await Kyvshield.initKyc(...);
// Overall result
result.success; // bool
result.overallStatus; // VerificationStatus (pass, review, reject, error)
result.sessionId; // String?
result.errorMessage; // String? (if error)
result.totalProcessingTimeMs; // int
// Selfie result
result.selfieResult?.isLive; // bool
result.selfieResult?.confidence; // double
result.selfieResult?.capturedImage; // Uint8List? (JPEG bytes)
// Document results (recto / verso)
result.rectoResult?.status; // VerificationStatus
result.rectoResult?.alignedDocument; // Uint8List? (aligned JPEG)
result.rectoResult?.extraction?.fields; // List<ExtractedField>
result.rectoResult?.extraction?.sortedFields; // Sorted by displayPriority
result.rectoResult?.extractedPhotos; // List<ExtractedPhoto>
result.rectoResult?.faceVerification?.isMatch; // bool?
result.rectoResult?.faceVerification?.similarityScore; // double?
// Convenience getters
result.getExtractedValue('nom'); // Search across recto + verso
result.mainPhoto; // First extracted face photo
result.faceMatches; // Face match boolean
result.selfieImage; // Uint8List?
result.rectoImage; // Uint8List?
result.versoImage; // Uint8List?
result.authenticityScore; // Average document score
ExtractedField
Each OCR field has rich metadata for display:
for (final field in result.rectoResult?.extraction?.sortedFields ?? []) {
print('${field.label}: ${field.stringValue}');
// field.key — generic key (e.g. "first_name")
// field.documentKey — document-specific key (e.g. "prenoms")
// field.label — localized label (e.g. "Prénoms")
// field.value — extracted value
// field.displayPriority — sort order (lower = first)
// field.icon — icon name (e.g. "user", "calendar")
}
Permission Helpers
await Kyvshield.checkCameraPermission(); // bool
await Kyvshield.requestCameraPermission(); // bool
await Kyvshield.isCameraPermissionPermanentlyDenied(); // bool
await Kyvshield.openSettings(); // Opens app settings
Platform Setup
Android
android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
android/app/build.gradle — minimum SDK 21:
minSdkVersion 21
iOS
ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Camera access is required for identity verification.</string>
API Documentation
Full documentation and developer dashboard: https://kyvshield.innolink.sn/developer
License
BSD 3-Clause License. See LICENSE.
Libraries
- kyvshield_lite
- KyvShield Lite — WebView-based KYC SDK for Flutter.