innova_id_verify 0.0.2
innova_id_verify: ^0.0.2 copied to clipboard
A Flutter plugin for ID verification
example/lib/main.dart
import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'dart:math';
import 'package:innova_id_verify/innova_id_verify.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(home: HomeScreen());
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFF60CFFF),
const Color.fromARGB(255, 197, 238, 255),
],
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
toolbarHeight: 100,
backgroundColor: Colors.transparent,
// centerTitle: true,
actions: [
SizedBox(height: 60, child: Image.asset('lib/assets/logo.png')),
SizedBox(
width: 15,
)
],
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 15,
),
const Text(
'InnoKYC',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 32),
),
const Text(
'Secure, real-time identity verification.',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16),
),
],
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 20,
),
CarouselSlider(
items: [
_buildCarouselItem(
'lib/assets/liveliness.jpg',
'Liveliness Detection',
'Detects liveness through blink\nand head movements.'),
_buildCarouselItem('lib/assets/ocr.jpg', 'OCR Reading',
'Extracts key details like ID\nnumber and name.'),
_buildCarouselItem(
'lib/assets/tamper.jpg',
'Tamper Detection',
'Detects alterations made\nto the ID card.'),
_buildCarouselItem(
'lib/assets/image-extraction.jpg',
'Image Extraction',
'Extracts the face photo\nfrom the ID card.'),
_buildCarouselItem(
'lib/assets/face-matching.jpg',
'Face Matching',
'Compares ID card face\nphoto with selfie.'),
],
options: CarouselOptions(
height: 200,
aspectRatio: 9 / 16,
viewportFraction: 0.85,
initialPage: 0,
enableInfiniteScroll: true,
reverse: false,
autoPlay: true,
autoPlayInterval: Duration(seconds: 3),
autoPlayAnimationDuration: Duration(seconds: 2),
autoPlayCurve: Curves.fastOutSlowIn,
enlargeCenterPage: true,
enlargeFactor: 0.2,
scrollDirection: Axis.horizontal,
)),
],
)
],
),
bottomNavigationBar: BottomAppBar(
color: Colors.transparent,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15), // Shadow color
offset: Offset(0, 4), // Shadow position (x, y)
blurRadius: 3, // Spread of the shadow
spreadRadius: 0, // How much the shadow spreads (optional)
),
],
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color.fromARGB(255, 255, 162, 62),
const Color(0xFFFDBA74),
],
),
),
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
),
onPressed: () {
try {
Navigator.of(context, rootNavigator: true)
.pushReplacement(MaterialPageRoute(
builder: (context) => const ImageDisplayScreen(),
));
} catch (e) {
print("Error navigating to ImageDisplayScreen: $e");
}
},
child: Text('Launch eKYC',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18)),
),
),
),
),
);
}
Widget _buildCarouselItem(
String imagePath, String title, String description) {
return Stack(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Image.asset(
imagePath,
fit: BoxFit.cover,
height: double.infinity,
width: double.infinity,
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black],
),
),
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 40,
),
Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 5),
Text(
description,
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
],
),
),
),
),
],
);
}
}
class ImageDisplayScreen extends StatefulWidget {
const ImageDisplayScreen({super.key});
@override
State<ImageDisplayScreen> createState() => _ImageDisplayScreenState();
}
class _ImageDisplayScreenState extends State<ImageDisplayScreen> {
Uint8List? _capturedImage;
Uint8List? _frontImage;
Uint8List? _backImage;
String? _fullName;
String? _fcn;
String? _dob;
String? _gender;
String? _nationality;
String? _dateOfExpiry;
String? _phoneNumber;
String? _region;
String? _zone;
String? _woreda;
String? _fin;
final _innovaIDVerify = InnovaIDVerify();
@override
void initState() {
launchKyc();
super.initState();
}
Future<void> launchKyc() async {
try {
print("\n=== Starting KYC Process ===");
await _innovaIDVerify.processIDVerification(
onVerificationComplete: (verificationData) {
print("\n=== Verification Data Received ===");
print("Raw verification data: $verificationData");
// Handle Front OCR Data
print("\n=== Front ID OCR Data ===");
final frontData = verificationData['ocrDataFront'];
if (frontData != null) {
setState(() {
_fullName = frontData['fullName'];
_dob = frontData['dateOfBirth'];
_fcn = frontData['fcn'];
_nationality = frontData['nationality'];
_gender = frontData['sex'];
// Add other state updates as needed
});
} else {
print("✗ No front OCR data received");
}
// Handle Back OCR Data
print("\n=== Back ID OCR Data ===");
final backData = verificationData['ocrDataBack'];
if (backData != null) {
_dateOfExpiry = backData['dateOfExpiry'];
_phoneNumber = backData['phoneNumber'];
_region = backData['regionCityAdmin'];
_zone = backData['zoneCityAdminSubCity'];
_woreda = backData['woredaCityAdminKebele'];
_fin = backData['fin'];
} else {
print("✗ No back OCR data received");
}
// Handle Selfie Image if needed
if (verificationData['selfieImage'] != null) {
final selfieImage = verificationData['selfieImage'] as Uint8List;
print("✓ Selfie Image received (${selfieImage.length} bytes)");
setState(() {
_capturedImage = selfieImage;
});
} else {
print("✗ No selfie image received");
}
if (verificationData['frontImage'] != null) {
final frontImage = verificationData['frontImage'] as Uint8List;
print("✓ Front Image received (${frontImage.length} bytes)");
setState(() {
_frontImage = frontImage;
});
} else {
print("✗ No Front image received");
}
if (verificationData['backImage'] != null) {
final backImage = verificationData['backImage'] as Uint8List;
print("✓ Front Image received (${backImage.length} bytes)");
setState(() {
_backImage = backImage;
});
} else {
print("✗ No Front image received");
}
print("\n=== Verification Process Complete ===");
},
);
print("✓ Camera launched successfully!");
} catch (e, stackTrace) {
print("\n=== Error in KYC Process ===");
print("Error: $e");
print("Stack trace:\n$stackTrace");
}
}
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFF60CFFF),
const Color.fromARGB(255, 197, 238, 255),
],
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
title: Text(
_capturedImage != null ? "eKYC Details" : '',
style: TextStyle(fontWeight: FontWeight.bold),
),
centerTitle: true,
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 15, horizontal: 20),
child: _capturedImage != null
? Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Name:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_fullName ?? "N/A",
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'FCN:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_fcn ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Gender:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_gender ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Date of Birth:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_dob ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Nationality:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_nationality ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Date of Expiry:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_dateOfExpiry ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Phone Number:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_phoneNumber ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Region:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_region ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Zone:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_zone ?? 'N/A',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'Woreda:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_woreda ?? 'NA',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 13,
),
Text(
'FIN:',
style: TextStyle(
fontSize: 15,
color: Colors.white,
),
),
Text(
_fin ?? 'N/a',
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 5,
),
Divider(
color: Colors.white,
),
SizedBox(
height: 5,
),
Text(
'ID Card:',
style: TextStyle(
fontSize: 17,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 7,
),
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (_frontImage != null)
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: AspectRatio(
aspectRatio: 4 /
3, // Set an aspect ratio if you want to maintain a square box
child: Transform.rotate(
angle: pi /
2, // Rotate the front image by -90 degrees
child: Image.memory(
_frontImage!,
fit: BoxFit
.contain, // Ensures the entire image fits without cropping
),
),
),
),
SizedBox(
height: 10,
),
if (_backImage != null)
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: AspectRatio(
aspectRatio: 4 /
3, // Set an aspect ratio if you want to maintain a square box
child: Transform.rotate(
angle: pi /
2, // Rotate the back image by -90 degrees
child: Image.memory(
_backImage!,
fit: BoxFit
.contain, // Ensures the entire image fits without cropping
),
),
),
),
],
),
SizedBox(
height: 10,
),
Divider(
color: Colors.white,
),
SizedBox(
height: 10,
),
Text(
'Selfie:',
style: TextStyle(
fontSize: 17,
color: Colors.white,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 7,
),
if (_capturedImage != null)
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: SizedBox(
width: MediaQuery.of(context).size.width / 1.8,
child: Image.memory(_capturedImage!),
),
),
],
)
: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"eKyc Failed",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 30),
),
SizedBox(
height: 40,
),
Image.asset(
'lib/assets/fail.png',
scale: 3,
)
],
)),
),
),
bottomNavigationBar: BottomAppBar(
color: Colors.transparent,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
offset: Offset(0, 4),
blurRadius: 3,
spreadRadius: 0,
),
],
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color.fromARGB(255, 255, 162, 62),
const Color(0xFFFDBA74),
],
),
),
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
),
onPressed: () {
try {
Navigator.of(context, rootNavigator: true)
.pushReplacement(MaterialPageRoute(
builder: (context) => const HomeScreen(),
));
} catch (e) {
print("Error navigating to ImageDisplayScreen: $e");
}
},
child: Text(_capturedImage != null ? 'Go Home' : 'Retry',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18)),
),
),
),
),
);
}
}