Flutter Implementation
Flutter Installation & Setup
Project Requirements
Before Flutter SDK integration, ensure your environment meets these requirements:
- Flutter: 3.0.0+
- Dart: 3.0.0+
- Android: API level 21+ (Android 5.0+)
- iOS: 11.0+
- Xcode: 12.0+ (for iOS builds)
Installation
Add the Kora Liveness Flutter SDK to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
kora_identity_flutter_sdk: ^1.0.0Then run:
flutter pub getPlatform SetupAndroid Configuration
Add camera permissions to your android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<application
android:usesCleartextTraffic="true">
</application>
</manifest>iOS Configuration
Add camera usage descriptions to your ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>This app needs access to camera for identity verification and liveness checks</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to microphone for enhanced verification experience</string>Ensure your iOS deployment target is set to 11.0 or higher in ios/Runner.xcodeproj/project.pbxproj:
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
Basic Import and Setup:
import 'package:kora_identity_flutter_sdk/kora_identity_flutter_sdk.dart';Flutter Integration Workflow
This section outlines the steps to integrate with our Flutter SDK.
Step 1: SDK Import and Setup
Import the necessary components from the SDK:
import 'package:flutter/material.dart';
import 'package:kora_identity_flutter_sdk/kora_identity_flutter_sdk.dart';
class VerificationScreen extends StatefulWidget {
@override
_VerificationScreenState createState() => _VerificationScreenState();
}
class _VerificationScreenState extends State<VerificationScreen> {
final KoraLivenessService _livenessService = koraLivenessService;
bool _isLoading = false;
LivenessResult? _result;
}Step 2: Collect Verification Details
Prepare the configuration object with user details and verification settings:
final verificationConfig = KoraLivenessOptionsExtended(
user: LivenessUser(
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
),
branding: LivenessBranding(
color: '#2376F3',
logo: 'https://your-domain.com/logo.png',
name: 'Your Company',
),
presentation: 'modal',
allowAudio: false,
debugMode: true,
sandboxEnvironment: true,
);Step 3: Start Verification Flow
Option A: Auto-Determined Type (Recommended)
Let the backend determine the best verification type:
Future<void> _startAutoVerification() async {
setState(() => _isLoading = true);
try {
// Initialize the service
await _livenessService.initialize(
publicKey: 'your_public_key_here',
livenessOptions: KoraLivenessOptions(
debugMode: true,
sandboxEnvironment: true,
),
);
// Start verification with auto-detection
final component = await _livenessService.checkLiveness(
KoraLivenessOptionsExtended(
// No type specified - backend auto-determines based on risk
user: LivenessUser(
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
),
branding: LivenessBranding(
color: '#2376F3',
logo: 'https://your-domain.com/logo.png',
name: 'Your Company',
),
presentation: 'modal',
allowAudio: false,
onSuccess: (result) {
setState(() {
_result = result;
_isLoading = false;
});
print('Verification successful: ${result.data}');
// Handle success - user verified as live human
},
onFailure: (result) {
setState(() {
_result = result;
_isLoading = false;
});
print('Verification failed: ${result.error}');
// Handle failure - verification did not pass
},
onClose: () {
setState(() => _isLoading = false);
print('User closed verification');
// Handle user cancellation
},
onStart: () {
print('Verification started');
},
),
);
// Navigate to verification screen or display component
if (component != null && mounted) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Scaffold(
body: component as Widget,
),
),
);
}
} catch (error) {
setState(() => _isLoading = false);
print('Failed to start verification: $error');
}
}Option B: Specific Verification Types
For specific verification types:
// Passive Liveness (Frictionless, good for low-risk flows)
Future<void> _startPassiveVerification() async {
await _livenessService.initialize(
publicKey: 'your_public_key_here',
livenessOptions: KoraLivenessOptions(debugMode: true, sandboxEnvironment: true),
);
final component = await _livenessService.checkLiveness(
KoraLivenessOptionsExtended(
type: 'passive',
user: LivenessUser(firstName: 'John', lastName: 'Doe', email: '[email protected]'),
// ...callbacks
),
);
}
// Active Liveness (Interactive, good for high-assurance flows)
Future<void> _startActiveVerification() async {
await _livenessService.initialize(
publicKey: 'your_public_key_here',
livenessOptions: KoraLivenessOptions(debugMode: true, sandboxEnvironment: true),
);
final component = await _livenessService.checkLiveness(
KoraLivenessOptionsExtended(
type: 'active',
user: LivenessUser(firstName: 'John', lastName: 'Doe', email: '[email protected]'),
tasks: [
{
'id': 'complete_the_circle',
'difficulty': 'medium',
'timeout': 30000,
},
{
'id': 'blink',
'difficulty': 'easy',
'maxBlinks': 3,
'timeout': 15000,
}
],
allowAudio: true,
// ...callbacks
),
);
}Step 4: Handle the Verification Component
Render the verification component in your Flutter application:
class VerificationApp extends StatefulWidget {
@override
_VerificationAppState createState() => _VerificationAppState();
}
class _VerificationAppState extends State<VerificationApp> {
Widget? _livenessComponent;
LivenessResult? _result;
bool _isLoading = false;
@override
Widget build(BuildContext context) {
if (_livenessComponent != null) {
return Scaffold(
body: _livenessComponent!,
);
}
return Scaffold(
appBar: AppBar(title: Text('Identity Verification')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_isLoading)
CircularProgressIndicator()
else
ElevatedButton(
onPressed: _handleVerification,
child: Text('Start Verification'),
),
if (_result != null)
Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
_result!.success ? 'Verification Successful!' : 'Verification Failed',
style: TextStyle(
color: _result!.success ? Colors.green : Colors.red,
fontSize: 16,
),
),
),
],
),
),
);
}
Future<void> _handleVerification() async {
setState(() => _isLoading = true);
try {
await koraLivenessService.initialize(
publicKey: 'your_public_key',
livenessOptions: KoraLivenessOptions(
debugMode: true,
sandboxEnvironment: true,
),
);
final component = await koraLivenessService.checkLiveness(
KoraLivenessOptionsExtended(
user: LivenessUser(
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
),
onSuccess: (result) {
setState(() {
_result = result;
_livenessComponent = null;
_isLoading = false;
});
},
onFailure: (result) {
setState(() {
_result = result;
_livenessComponent = null;
_isLoading = false;
});
},
onClose: () {
setState(() {
_livenessComponent = null;
_isLoading = false;
});
},
),
);
if (component != null && mounted) {
setState(() {
_livenessComponent = component as Widget;
_isLoading = false;
});
}
} catch (error) {
setState(() => _isLoading = false);
print('Verification error: $error');
}
}
}Alternative: Widget-Based Integration
For direct widget embedding in your Flutter app:
class DirectWidgetVerification extends StatefulWidget {
@override
_DirectWidgetVerificationState createState() => _DirectWidgetVerificationState();
}
class _DirectWidgetVerificationState extends State<DirectWidgetVerification> {
bool _showVerification = false;
LivenessResult? _result;
@override
Widget build(BuildContext context) {
if (_showVerification) {
return LivenessCheck(
props: LivenessCheckProps(
publicKey: 'your_public_key_here',
type: 'passive', // or 'active', or null for auto-detection
config: KoraLivenessConfig(
debugMode: true,
sandboxEnvironment: true,
user: LivenessUser(
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
),
branding: LivenessBranding(
color: '#2376F3',
logo: 'assets/images/logo.png',
name: 'Your Company',
),
presentation: 'page',
allowAudio: true,
),
onSuccess: (result) {
setState(() {
_result = result;
_showVerification = false;
});
},
onFailure: (result) {
setState(() {
_result = result;
_showVerification = false;
});
},
onClose: () {
setState(() {
_showVerification = false;
});
},
loadingComponent: CircularProgressIndicator(),
errorComponent: Text('Failed to load verification'),
),
);
}
return Scaffold(
appBar: AppBar(title: Text('Identity Verification')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_showVerification = true;
});
},
child: Text('Start Verification'),
),
if (_result != null)
Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
_result!.success ? 'Success!' : 'Failed',
style: TextStyle(
color: _result!.success ? Colors.green : Colors.red,
),
),
),
],
),
),
);
}
}Flutter Configuration Classes
// Main configuration
class KoraLivenessConfig {
final String? publicKey;
final bool? debugMode;
final bool? sandboxEnvironment;
final LivenessUser? user;
final LivenessBranding? branding;
final String? presentation;
final bool? allowAudio;
final List<Map<String, dynamic>>? tasks;
final void Function(LivenessResult)? onSuccess;
final void Function(LivenessResult)? onFailure;
final void Function()? onClose;
final void Function()? onStart;
}
// User information
class LivenessUser {
final String firstName;
final String lastName;
final String? email;
final String? phone;
}
// Branding options
class LivenessBranding {
final String? color;
final String? logo;
final String? name;
final String? logoAlt;
final bool? hideLogoOnMobile;
final bool? showPoweredBy;
final String? poweredByText;
final String? poweredByLogo;
}
// Result object
class LivenessResult {
final bool success;
final dynamic data;
final String? error;
final Map<String, dynamic>? metadata;
}Advanced Configuration for Active Liveness
Custom Task Configuration:
final customTasks = [
{
'id': 'yes_or_no',
'difficulty': 'medium',
'questions': [
{
'question': 'Are you ready to proceed?',
'answer': true,
'errorMessage': 'Please nod your head to the right for yes'
}
]
},
{
'id': 'motions',
'difficulty': 'hard',
'maxNods': 3,
'maxBlinks': 2,
'timeout': 45000
}
];Flutter-Specific Notes
- Uses flutter_inappwebview for webview implementation ensuring consistent behavior across iOS and Android
- Built-in permission handling for camera access
- Supports both widget-based and service-based integration patterns
- Comprehensive error handling and state management
- Full type safety with Dart null safety support
- Hot reload compatible for rapid development
Updated 3 days ago
