innova_id_verify 0.1.0 copy "innova_id_verify: ^0.1.0" to clipboard
innova_id_verify: ^0.1.0 copied to clipboard

A Flutter plugin for ID verification

example/lib/main.dart

import 'dart:convert';

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:innova_id_verify/innova_id_verify.dart';
import 'package:http/http.dart' as http;

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
  ]).then((_) {
    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(0xFFD1F1FF),
            Colors.white,
          ],
        ),
      ),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        appBar: AppBar(
          toolbarHeight: 100,
          backgroundColor: Colors.transparent,
          // centerTitle: true,
          actions: [
            SizedBox(height: 50, child: Image.asset('lib/assets/logo.png')),
            SizedBox(
              width: 15,
            )
          ],
          title: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              SizedBox(
                height: 15,
              ),
              const Text(
                'InnoTrust',
                style: TextStyle(
                    color: Colors.black,
                    fontWeight: FontWeight.bold,
                    fontSize: 32),
              ),
              const Text(
                'Seamless eKYC: Verify Instantly, Securely!',
                style: TextStyle(
                    color: Colors.black,
                    fontWeight: FontWeight.bold,
                    fontSize: 14),
              ),
            ],
          ),
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                CarouselSlider(
                    items: [
                      _buildCarouselItem('lib/assets/liveliness.jpg'),
                      _buildCarouselItem('lib/assets/ocr.png'),
                      _buildCarouselItem('lib/assets/face-matching.png'),
                    ],
                    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(0xFF221BC7),
                  const Color(0xFF0800C9),
                ],
              ),
            ),
            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) {
    return ClipRRect(
      borderRadius: BorderRadius.circular(5),
      child: Image.asset(
        imagePath,
        fit: BoxFit.fitWidth,
        height: double.infinity,
        width: double.infinity,
      ),
    );
  }
}

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;
  String _status = "OCR- FAILED , FACE MATCHING- FAILED";
  final _innovaIDVerify = InnovaIDVerify();
  int? _mathing_score;
  bool _isLoading = false;
  String? _error;
  String? _currentReferenceNumber;
  bool _isInitialLaunch = true;

  @override
  void initState() {
    super.initState();
    if (_isInitialLaunch) {
      _isInitialLaunch = false;
      launchKyc();
    }
  }

  Future<String> generateReferenceNumber({String prefix = 'INNOVAVERIFY'}) async {
    final dateTimeNow = DateTime.now();
    final dateTimeString =
        '${dateTimeNow.year}${dateTimeNow.month.toString().padLeft(2, '0')}${dateTimeNow.day.toString().padLeft(2, '0')}'
        '${dateTimeNow.hour.toString().padLeft(2, '0')}${dateTimeNow.minute.toString().padLeft(2, '0')}${dateTimeNow.second.toString().padLeft(2, '0')}${dateTimeNow.millisecond.toString().padLeft(3, '0')}';
    return '$prefix$dateTimeString';
  }

  Future<void> launchKyc() async {
    if (_isLoading) return;

    setState(() {
      _isLoading = true;
      _error = null;
    });

    try {
      _currentReferenceNumber = await generateReferenceNumber();
      print("\n=== Starting KYC Process ===");
      print("Generated Reference Number: $_currentReferenceNumber");
      
      await _innovaIDVerify.processIDVerification(
        referenceNum: _currentReferenceNumber!,
        onVerificationComplete: (verificationData) async {
          print("Verification complete, starting API polling...");
          await startApiPolling(_currentReferenceNumber!);
        },
        onActivityClosed: () {
          if (mounted) {
            setState(() {
              _isLoading = false;
              _error = "KYC process was cancelled";
            });
          }
        },
      );
    } catch (e, stackTrace) {
      print("Error in launchKyc: $e");
      print("Stack trace:\n$stackTrace");
      if (mounted) {
        setState(() {
          _error = "Error during KYC process: ${e.toString()}";
          _isLoading = false;
        });
      }
    }
  }

  Future<void> startApiPolling(String referenceNum) async {
    int maxAttempts = 10;
    int currentAttempt = 0;
    const Duration pollInterval = Duration(seconds: 3);
    bool isDataProcessed = false;

    while (currentAttempt < maxAttempts && !isDataProcessed) {
      try {
        print("Polling attempt ${currentAttempt + 1} of $maxAttempts");
        final response = await makeApiRequest(referenceNum);
        
        if (response['success'] == true && response['data'] != null) {
          await processVerificationData(referenceNum);
          isDataProcessed = true;
          print("Data successfully processed on attempt ${currentAttempt + 1}");
          break;
        } else {
          print("Polling attempt ${currentAttempt + 1}: Data not ready yet");
          print("Response: $response");
        }
      } catch (e) {
        print("Error during polling attempt ${currentAttempt + 1}: $e");
      }

      if (!isDataProcessed && currentAttempt < maxAttempts - 1) {
        print("Waiting ${pollInterval.inSeconds} seconds before next attempt...");
        await Future.delayed(pollInterval);
      }
      
      currentAttempt++;
    }

    if (mounted) {
      setState(() {
        _isLoading = false;
        if (!isDataProcessed) {
          _error = "Failed to retrieve data after $maxAttempts attempts";
        }
      });
    }
  }

  Future<void> processVerificationData(String referenceNum) async {
    try {
      print("Processing verification data for reference: $referenceNum");
      final response = await makeApiRequest(referenceNum);
      print("API Response received, processing data...");

      if (response['success'] == true && response['data'] != null) {
        final data = response['data'];
        
        // Extract OCR data
        final frontOcrData = data['Front_OCR_Data']?['id_analysis'];
        final frontDetails = frontOcrData?['front'] ?? {};
        final backOcrData = data['Back_OCR_Data']?['id_analysis'];
        final backDetails = backOcrData?['back'] ?? {};

        if (mounted) {
          setState(() {
            // Process images
            if (data['Front_ID_Image'] != null) {
              try {
                _frontImage = base64Decode(data['Front_ID_Image']);
              } catch (e) {
                print("Error decoding front image: $e");
                _frontImage = null;
              }
            }

            if (data['Back_ID_Image'] != null) {
              try {
                _backImage = base64Decode(data['Back_ID_Image']);
              } catch (e) {
                print("Error decoding back image: $e");
                _backImage = null;
              }
            }

            if (data['Selfie_Image'] != null) {
              try {
                _capturedImage = base64Decode(data['Selfie_Image']);
              } catch (e) {
                print("Error decoding selfie image: $e");
                _capturedImage = null;
              }
            }

            // Process front ID details
            _fullName = frontDetails['Full_name'] ?? 'N/A';
            _fcn = frontDetails['FCN'] ?? 'N/A';
            _dob = frontDetails['Date_of_birth'] ?? 'N/A';
            _gender = frontDetails['Sex'] ?? 'N/A';
            _nationality = frontDetails['Nationality'] ?? 'N/A';

            // Process back ID details
            _dateOfExpiry = backDetails['Date_of_Expiry'] ?? 'N/A';
            _phoneNumber = backDetails['Phone_Number'] ?? 'N/A';
            _region = backDetails['Region_City_Admin'] ?? 'N/A';
            _zone = backDetails['Zone_City_Admin_Sub_City'] ?? 'N/A';
            _woreda = backDetails['Woreda_City_Admin_Kebele'] ?? 'N/A';
            _fin = backDetails['FIN'] ?? 'N/A';

            // Process additional data
            _mathing_score = data['Score'] ?? 0;
            _status = data['STATUS'] ?? 'Unknown Status';
          });
        }
        print("Verification data processing completed successfully");
      } else {
        throw Exception("Invalid response data structure");
      }
    } catch (e) {
      print("Error processing verification data: $e");
      if (mounted) {
        setState(() {
          _error = "Error processing verification data: ${e.toString()}";
        });
      }
      rethrow;
    }
  }

  Future<Map<String, dynamic>> makeApiRequest(String referenceNum) async {
    final url = Uri.parse(
        'https://innotrust.innovitegra.online/api/enroll/$referenceNum');
    final headers = {'Content-Type': 'application/json'};

    try {
      final response = await http.get(url, headers: headers);
      if (response.statusCode == 200) {
        return jsonDecode(response.body);
      } else {
        throw Exception('Failed to load data: ${response.statusCode}');
      }
    } catch (e) {
      throw Exception('Failed to make API request: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [
            const Color(0xFFD1F1FF),
            Colors.white,
          ],
        ),
      ),
      child: Scaffold(
        backgroundColor: Colors.transparent,
        body: _isLoading
            ? Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(
                      valueColor:
                          AlwaysStoppedAnimation<Color>(Color(0xFF221BC7)),
                    ),
                    SizedBox(height: 20),
                    Text(
                      'Processing KYC...',
                      style: TextStyle(
                        color: Color(0xFF221BC7),
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ],
                ),
              )
            : _error != null
                ? Center(
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        SizedBox(
                          width: 100,
                          height: 100,
                          child: Image.asset("lib/assets/fail.png")),
                          SizedBox(height: 20,),
                        Text('$_error',style: TextStyle(color: Colors.red,fontSize: 18,fontWeight: FontWeight.bold),),
                      ],
                    ),
                  )
                : SingleChildScrollView(
                    physics: BouncingScrollPhysics(),
                    child: Padding(
                        padding: const EdgeInsets.symmetric(
                            vertical: 30, horizontal: 20),
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.start,
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            SizedBox(
                              height: 40,
                            ),
                            Text(
                              'Status',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 10,
                            ),
                            Text(
                              _status,
                              style: TextStyle(
                                  fontSize: 18,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 10,
                            ),
                            Divider(
                              color: Colors.white,
                            ),
                            SizedBox(
                              height: 15,
                            ),
                            Text(
                              'Name:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _fullName ?? "N/A",
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'FCN:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _fcn ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Gender:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _gender ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Date of Birth:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _dob ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Nationality:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _nationality ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Date of Expiry:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _dateOfExpiry ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Phone Number:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _phoneNumber ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Region:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _region ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Zone:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _zone ?? 'N/A',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Woreda:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _woreda ?? 'NA',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'FIN:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _fin ?? 'N/a',
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 13,
                            ),
                            Text(
                              'Face Matching Score:',
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.black,
                              ),
                            ),
                            Text(
                              _mathing_score.toString(),
                              style: TextStyle(
                                  fontSize: 20,
                                  color: Colors.black,
                                  fontWeight: FontWeight.bold),
                            ),
                            SizedBox(
                              height: 15,
                            ),
                            if (_frontImage != null && _backImage != null)
                              Divider(
                                color: Colors.white,
                              ),
                            SizedBox(
                              height: 5,
                            ),
                            if (_frontImage != null && _backImage != null)
                              Text(
                                'ID Card:',
                                style: TextStyle(
                                    fontSize: 17,
                                    color: Colors.black,
                                    fontWeight: FontWeight.bold),
                              ),
                            if (_frontImage != null && _backImage != null)
                              SizedBox(
                                height: 7,
                              ),
                            Column(
                              mainAxisSize: MainAxisSize.min,
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                if (_frontImage != null)
                                  Card(
                                    child: ClipRRect(
                                      borderRadius: BorderRadius.circular(10),
                                      child: Image.memory(
                                        _frontImage!,
                                        fit: BoxFit
                                            .contain, // Ensures the entire image fits without cropping
                                      ),
                                    ),
                                  ),
                                if (_backImage != null)
                                  Card(
                                    child: ClipRRect(
                                      borderRadius: BorderRadius.circular(10),
                                      child: Image.memory(
                                        _backImage!,
                                        fit: BoxFit
                                            .contain,
                                      ),
                                    ),
                                  ),
                              ],
                            ),
                            if (_capturedImage != null && _capturedImage != "")
                              SizedBox(
                                height: 10,
                              ),
                            if (_capturedImage != null && _capturedImage != "")
                              Divider(
                                color: Colors.white,
                              ),
                            if (_capturedImage != null && _capturedImage != "")
                              SizedBox(
                                height: 10,
                              ),
                            if (_capturedImage != null && _capturedImage != "")
                              Text(
                                'Selfie:',
                                style: TextStyle(
                                    fontSize: 17,
                                    color: Colors.black,
                                    fontWeight: FontWeight.bold),
                              ),
                            SizedBox(
                              height: 7,
                            ),
                            if (_capturedImage != null && _capturedImage != "")
                              ClipRRect(
                                borderRadius: BorderRadius.circular(10),
                                child: SizedBox(
                                  width:
                                      MediaQuery.of(context).size.width / 1.8,
                                  child: Image.memory(_capturedImage!),
                                ),
                              ),
                          ],
                        )),
                  ),
        bottomNavigationBar: _isLoading
            ? null
            : 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(0xFF221BC7),
                        const Color(0xFF0800C9),
                      ],
                    ),
                  ),
                  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)),
                  ),
                ),
              ),
      ),
    );
  }
}
0
likes
140
points
12
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for ID verification

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

flutter, http, plugin_platform_interface

More

Packages that depend on innova_id_verify

Packages that implement innova_id_verify