facedetect_tadi_sdk 0.0.7
facedetect_tadi_sdk: ^0.0.7 copied to clipboard
Flutter plugin for face detection, liveness verification, and biometric identity verification with ML Kit and secure backend integration.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:facedetect_tadi_sdk/facedetect_tadi_sdk.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Face Detection SDK Demo',
theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
home: const FaceDetectionDemo(),
);
}
}
class FaceDetectionDemo extends StatefulWidget {
const FaceDetectionDemo({super.key});
@override
State<FaceDetectionDemo> createState() => _FaceDetectionDemoState();
}
class _FaceDetectionDemoState extends State<FaceDetectionDemo> {
String _platformVersion = 'Unknown';
final _sdk = FacedetectTadiSdk();
FaceDetectionResult? _result;
bool _isLoading = false;
String? _errorMessage;
// Configuration fields
final _accessTokenController = TextEditingController(
text: 'YOUR_ACCESS_TOKEN',
);
final _baseUrlController = TextEditingController(
text: 'https://face.mbabm.uz/',
);
String _selectedLanguage = 'en';
bool _useNative = true;
ResultFormat _resultFormat = ResultFormat.flat;
CameraShape _selectedCameraShape = CameraShape.oval;
String? _logoImageBase64;
@override
void initState() {
super.initState();
initPlatformState();
}
@override
void dispose() {
_accessTokenController.dispose();
_baseUrlController.dispose();
super.dispose();
}
Future<void> initPlatformState() async {
String platformVersion;
try {
platformVersion =
await _sdk.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
Future<void> _loadLogoFromAssets() async {
try {
// Load Hamkorbank logo from assets (PNG format for native SDK compatibility)
final ByteData data = await rootBundle.load('hamkor_bank.png');
final bytes = data.buffer.asUint8List();
final base64String = base64Encode(bytes);
print(
'Loaded logo: ${bytes.length} bytes, base64 length: ${base64String.length}',
);
setState(() {
_logoImageBase64 = base64String;
});
} catch (e) {
print('Failed to load logo: $e');
setState(() {
_errorMessage = 'Failed to load logo: $e';
});
}
}
Future<void> _startFaceDetection() async {
if (_accessTokenController.text.isEmpty) {
setState(() {
_errorMessage = 'Please enter an access token';
});
return;
}
setState(() {
_isLoading = true;
_errorMessage = null;
_result = null;
});
try {
final config = SdkConfig(
accessToken: _accessTokenController.text,
baseUrl: _baseUrlController.text.isNotEmpty
? _baseUrlController.text
: null,
language: _selectedLanguage,
useNative: _useNative,
resultFormat: _resultFormat,
cameraShape: _selectedCameraShape,
logoImageBase64: _logoImageBase64,
);
final result = await _sdk.startFaceDetection(config);
if (!mounted) return;
setState(() {
_result = result;
_isLoading = false;
if (result == null) {
_errorMessage = 'Verification cancelled or failed';
}
});
} on PlatformException catch (e) {
if (!mounted) return;
setState(() {
_errorMessage = 'Error: ${e.message}';
_isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Face Detection SDK Demo'),
centerTitle: true,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Platform Info
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Platform Information',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text('Running on: $_platformVersion'),
],
),
),
),
const SizedBox(height: 16),
// Configuration Section
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Configuration',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
TextField(
controller: _accessTokenController,
decoration: const InputDecoration(
labelText: 'Access Token',
border: OutlineInputBorder(),
hintText: 'Enter your access token',
),
),
const SizedBox(height: 16),
TextField(
controller: _baseUrlController,
decoration: const InputDecoration(
labelText: 'Base URL',
border: OutlineInputBorder(),
hintText: 'https://face.mbabm.uz/',
),
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
initialValue: _selectedLanguage,
decoration: const InputDecoration(
labelText: 'Language',
border: OutlineInputBorder(),
),
items: const [
DropdownMenuItem(value: 'en', child: Text('English')),
DropdownMenuItem(value: 'ru', child: Text('Russian')),
DropdownMenuItem(value: 'uz', child: Text('Uzbek')),
],
onChanged: (value) {
setState(() {
_selectedLanguage = value!;
});
},
),
const SizedBox(height: 16),
SwitchListTile(
title: const Text('Use Native Face Detection'),
subtitle: const Text('Use device native face detection'),
value: _useNative,
onChanged: (value) {
setState(() {
_useNative = value;
});
},
),
DropdownButtonFormField<ResultFormat>(
initialValue: _resultFormat,
decoration: const InputDecoration(
labelText: 'Result Format',
border: OutlineInputBorder(),
),
items: const [
DropdownMenuItem(
value: ResultFormat.flat,
child: Text('Flat (Simple)'),
),
DropdownMenuItem(
value: ResultFormat.nested,
child: Text('Nested (Structured)'),
),
],
onChanged: (value) {
setState(() {
_resultFormat = value!;
});
},
),
const SizedBox(height: 16),
DropdownButtonFormField<CameraShape>(
initialValue: _selectedCameraShape,
decoration: const InputDecoration(
labelText: 'Camera Shape',
border: OutlineInputBorder(),
),
items: const [
DropdownMenuItem(
value: CameraShape.oval,
child: Text('Oval'),
),
DropdownMenuItem(
value: CameraShape.circle,
child: Text('Circle'),
),
],
onChanged: (value) {
setState(() {
_selectedCameraShape = value!;
});
},
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _loadLogoFromAssets,
icon: const Icon(Icons.image),
label: Text(
_logoImageBase64 != null
? 'Hamkorbank Logo Loaded ✓'
: 'Load Hamkorbank Logo (Optional)',
),
style: ElevatedButton.styleFrom(
backgroundColor: _logoImageBase64 != null
? Colors.green
: null,
),
),
if (_logoImageBase64 != null)
TextButton.icon(
onPressed: () {
setState(() {
_logoImageBase64 = null;
});
},
icon: const Icon(Icons.clear),
label: const Text('Clear Logo'),
),
],
),
),
),
const SizedBox(height: 16),
// Start Button
ElevatedButton.icon(
onPressed: _isLoading ? null : _startFaceDetection,
icon: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.face),
label: Text(
_isLoading ? 'Processing...' : 'Start Face Detection',
style: const TextStyle(fontSize: 16),
),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.all(16),
),
),
// Error Message
if (_errorMessage != null) ...[
const SizedBox(height: 16),
Card(
color: Colors.red.shade50,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Icon(Icons.error, color: Colors.red),
const SizedBox(width: 8),
Expanded(
child: Text(
_errorMessage!,
style: const TextStyle(color: Colors.red),
),
),
],
),
),
),
],
// Result Display
if (_result != null) ...[
const SizedBox(height: 16),
Card(
color: Colors.green.shade50,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.check_circle, color: Colors.green),
const SizedBox(width: 8),
const Text(
'Verification Successful',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.green,
),
),
],
),
const Divider(height: 24),
_buildResultRow('Name', _result!.firstName ?? 'N/A'),
_buildResultRow('Last Name', _result!.lastName ?? 'N/A'),
_buildResultRow('Patronym', _result!.patronym ?? 'N/A'),
_buildResultRow(
'Birth Date',
_result!.birthDate ?? 'N/A',
),
_buildResultRow('Gender', _result!.genderName ?? 'N/A'),
_buildResultRow(
'Document',
'${_result!.docSeria ?? ''} ${_result!.docNumber ?? ''}',
),
_buildResultRow('PINFL', _result!.pinfl ?? 'N/A'),
_buildResultRow(
'Issue Date',
_result!.dateIssue ?? 'N/A',
),
_buildResultRow(
'Expiry Date',
_result!.dateExpiry ?? 'N/A',
),
if (_result!.score != null)
_buildResultRow(
'Confidence Score',
'${(_result!.score! * 100).toStringAsFixed(1)}%',
),
// Display photo if available
if (_result!.photo != null &&
_result!.photo!.isNotEmpty) ...[
const SizedBox(height: 16),
const Text(
'Photo:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.memory(
base64Decode(_result!.photo!),
height: 200,
fit: BoxFit.contain,
),
),
],
],
),
),
),
],
],
),
),
);
}
Widget _buildResultRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.w500),
),
),
Expanded(
child: Text(value, style: const TextStyle(color: Colors.black87)),
),
],
),
);
}
}