personal_validation_system 1.0.0 copy "personal_validation_system: ^1.0.0" to clipboard
personal_validation_system: ^1.0.0 copied to clipboard

A Flutter plugin for identity verification and validation. Provides a complete solution for validating personal identification documents, performing face matching, and verifying personal information.

example/lib/main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:personal_validation_system/personal_validation_system.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:image_picker/image_picker.dart';
import 'package:dotted_border/dotted_border.dart';
import 'verification_results_page.dart';
import 'models/validation_data.dart';
import 'services/validation_api_service.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      title: 'Identity Verification',
      theme: CupertinoThemeData(
        primaryColor: CupertinoColors.activeBlue,
        brightness: Brightness.light,
        textTheme: CupertinoTextThemeData(
          textStyle: GoogleFonts.poppins(
            color: CupertinoColors.black,
            fontSize: 16,
          ),
        ),
        scaffoldBackgroundColor: CupertinoColors.white,
      ),
      home: const IdentityVerificationScreen(),
    );
  }
}

class IdentityVerificationScreen extends StatefulWidget {
  const IdentityVerificationScreen({super.key});

  @override
  State<IdentityVerificationScreen> createState() =>
      _IdentityVerificationScreenState();
}

class _IdentityVerificationScreenState extends State<IdentityVerificationScreen>
    implements GestureArenaMember {
  final _personalValidationSystemPlugin = PersonalValidationSystem();
  final _fullNameController = TextEditingController();
  final _phoneNumberController = TextEditingController();
  final _idNumberController = TextEditingController();
  final _imagePicker = ImagePicker();
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  File? _selfieImage;
  File? _frontIDImage;
  File? _backIDImage;

  @override
  void initState() {
    super.initState();
    // Initialize the plugin
    _initPlugin();
  }

  Future<void> _initPlugin() async {
    try {
      await _personalValidationSystemPlugin.getPlatformVersion();
    } on PlatformException {
      // Handle initialization error
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // Set a higher touch slop for better performance on MIUI devices
    GestureBinding.instance.pointerRouter.addGlobalRoute((PointerEvent event) {
      if (event is PointerDownEvent) {
        GestureBinding.instance.gestureArena.add(event.pointer, this);
      }
    });
  }

  @override
  void dispose() {
    _fullNameController.dispose();
    _phoneNumberController.dispose();
    _idNumberController.dispose();
    if (_selfieImage != null && _selfieImage!.existsSync())
      _selfieImage!.deleteSync();
    if (_frontIDImage != null && _frontIDImage!.existsSync())
      _frontIDImage!.deleteSync();
    if (_backIDImage != null && _backIDImage!.existsSync())
      _backIDImage!.deleteSync();
    GestureBinding.instance.pointerRouter.removeGlobalRoute(
      (PointerEvent event) {},
    );
    super.dispose();
  }

  Future<void> _takeSelfie() async {
    try {
      final XFile? image = await _imagePicker.pickImage(
        source: ImageSource.camera,
        preferredCameraDevice: CameraDevice.front,
        imageQuality: 80,
      );

      if (image != null) {
        setState(() {
          _selfieImage = File(image.path);
        });
      }
    } catch (e) {
      print('Error taking selfie: $e');
    }
  }

  Future<void> _takeFrontID() async {
    try {
      final XFile? image = await _imagePicker.pickImage(
        source: ImageSource.camera,
        preferredCameraDevice: CameraDevice.rear,
        imageQuality: 80,
      );

      if (image != null) {
        setState(() {
          _frontIDImage = File(image.path);
        });
      }
    } catch (e) {
      print('Error taking front ID photo: $e');
    }
  }

  Future<void> _takeBackID() async {
    try {
      final XFile? image = await _imagePicker.pickImage(
        source: ImageSource.camera,
        preferredCameraDevice: CameraDevice.rear,
        imageQuality: 80,
      );

      if (image != null) {
        setState(() {
          _backIDImage = File(image.path);
        });
      }
    } catch (e) {
      print('Error taking back ID photo: $e');
    }
  }

  Future<void> _submitVerification() async {
    // if (_formKey.currentState == null || !_formKey.currentState!.validate()) {
    //   return;
    // }

    // Show loading dialog and store reference
    final loadingDialog = showCupertinoDialog(
      context: context,
      barrierDismissible: false,
      builder:
          (context) => CupertinoAlertDialog(
            title: Text(
              'Processing',
              style: GoogleFonts.poppins(fontWeight: FontWeight.w600),
            ),
            content: Padding(
              padding: const EdgeInsets.symmetric(vertical: 20.0),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  SizedBox(height: 16),
                  CupertinoActivityIndicator(),
                  SizedBox(height: 16),
                  Text('Submitting your verification data...'),
                ],
              ),
            ),
          ),
    );

    try {
      // Create validation data object
      final validationData = ValidationData(
        fullName:
            _fullNameController.text.isEmpty
                ? 'Georg Mathu'
                : _fullNameController.text,
        phoneNumber:
            _phoneNumberController.text.isEmpty
                ? '+254798325826'
                : _phoneNumberController.text,
        idNumber:
            _idNumberController.text.isEmpty
                ? '35788580'
                : _idNumberController.text,
      );

      // Call the API service in a separate isolate if possible
      final response = await ValidationApiService().submitValidation(
        data: validationData,
        selfieImage: _selfieImage,
        frontIDImage: _frontIDImage,
        backIDImage: _backIDImage,
      );

      // Dismiss loading dialog
      Navigator.of(context).pop();
      await loadingDialog;

      if (response['status'] == 'success') {
        // Handle success
        if (mounted) {
          // Navigate to results page with the API response
          if (!mounted) return;
          Navigator.push(
            context,
            CupertinoPageRoute(
              builder:
                  (context) => VerificationResultsPage(
                    name: response['data']['name'] ?? validationData.fullName,
                    idNumber:
                        response['data']['id_number'] ??
                        validationData.idNumber,
                    phoneNumber:
                        response['data']['phone_number'] ??
                        validationData.phoneNumber,
                    verificationTime: DateTime.now(),
                    apiResponse: {
                      'data': {
                        'is_verified': response['is_verified'] ?? false,
                        'id_validation': {
                          'valid':
                              response['validation_details']?['id_validation']?['is_valid'] ??
                              false,
                          'message':
                              response['validation_details']?['id_validation']?['message'] ??
                              'ID validation not performed',
                        },
                        'ocr_extraction': {
                          'valid':
                              response['validation_details']?['ocr_extraction']?['name_found'] ??
                              false &&
                                  response['validation_details']?['ocr_extraction']?['id_number_found'] ??
                              false,
                          'message': 'Name and ID extracted',
                        },
                        'face_detection': {
                          'valid':
                              response['validation_details']?['face_detection']?['faces_match'] ??
                              false,
                          'message':
                              response['validation_details']?['face_detection']?['faces_match'] ==
                                      true
                                  ? 'Faces match'
                                  : 'Faces do not match',
                        },
                        'phone_validation': {
                          'valid':
                              response['validation_details']?['phone_validation']?['is_valid'] ??
                              false,
                          'message':
                              response['validation_details']?['phone_validation']?['message'] ??
                              'Phone validation not performed',
                        },
                      },
                    },
                  ),
            ),
          );
        }
      } else {
        // Handle error
        if (mounted) {
          showCupertinoDialog(
            context: context,
            builder:
                (BuildContext context) => CupertinoAlertDialog(
                  title: const Text('Error'),
                  content: Text(response['message'] ?? 'Verification failed'),
                  actions: [
                    CupertinoDialogAction(
                      child: const Text('OK'),
                      onPressed: () => Navigator.of(context).pop(),
                    ),
                  ],
                ),
          );
        }
      }
    } catch (e) {
      // Ensure dialog is dismissed even on error
      Navigator.of(context).pop();
      await loadingDialog;
      print('Error submitting verification: $e');
    } finally {
      // Clear image references to free memory
      if (!mounted) return;
      setState(() {
        _selfieImage = null;
        _frontIDImage = null;
        _backIDImage = null;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text(
          'Identity Verification',
          style: GoogleFonts.poppins(fontWeight: FontWeight.w600, fontSize: 18),
        ),
        leading: GestureDetector(
          onTap: () => Navigator.of(context).pop(),
          child: const Icon(CupertinoIcons.back),
        ),
      ),
      child: SafeArea(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Please provide your information',
                  style: GoogleFonts.poppins(
                    fontSize: 16,
                    color: CupertinoColors.systemGrey,
                  ),
                ),
                const SizedBox(height: 12),
                // Progress indicator
                Stack(
                  children: [
                    Container(
                      height: 4,
                      decoration: BoxDecoration(
                        color: CupertinoColors.systemGrey5,
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                    Container(
                      height: 4,
                      width: MediaQuery.of(context).size.width * 0.7,
                      decoration: BoxDecoration(
                        color: CupertinoColors.activeBlue,
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 4),
                Align(
                  alignment: Alignment.centerRight,
                  child: Text(
                    'Step 1 of 1',
                    style: GoogleFonts.poppins(
                      fontSize: 14,
                      color: CupertinoColors.systemGrey,
                    ),
                  ),
                ),
                const SizedBox(height: 24),

                // Full Name Field
                Text(
                  'Full Name',
                  style: GoogleFonts.poppins(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                const SizedBox(height: 8),
                CupertinoTextField(
                  controller: _fullNameController,
                  placeholder: 'Enter your full name',
                  padding: const EdgeInsets.symmetric(
                    horizontal: 16,
                    vertical: 14,
                  ),
                  decoration: BoxDecoration(
                    border: Border.all(color: Color(0xffF1F1F1)),
                    borderRadius: BorderRadius.circular(8),
                  ),
                  style: GoogleFonts.poppins(),
                ),
                const SizedBox(height: 20),

                // Phone Number Field
                Text(
                  'Phone Number',
                  style: GoogleFonts.poppins(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                const SizedBox(height: 8),
                CupertinoTextField(
                  controller: _phoneNumberController,
                  placeholder: '+1 (XXX) XXX-XXXX',
                  keyboardType: TextInputType.phone,
                  padding: const EdgeInsets.symmetric(
                    horizontal: 16,
                    vertical: 14,
                  ),
                  decoration: BoxDecoration(
                    border: Border.all(color: Color(0xffF1F1F1)),
                    borderRadius: BorderRadius.circular(8),
                  ),
                  style: GoogleFonts.poppins(),
                ),
                const SizedBox(height: 20),

                // ID Number Field
                Text(
                  'ID Number',
                  style: GoogleFonts.poppins(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                const SizedBox(height: 8),
                CupertinoTextField(
                  controller: _idNumberController,
                  placeholder: 'Enter your ID number',
                  padding: const EdgeInsets.symmetric(
                    horizontal: 16,
                    vertical: 14,
                  ),
                  decoration: BoxDecoration(
                    border: Border.all(color: Color(0xffF1F1F1)),
                    borderRadius: BorderRadius.circular(8),
                  ),
                  style: GoogleFonts.poppins(),
                ),
                const SizedBox(height: 20),

                // Take a Selfie
                Text(
                  'Take a Selfie',
                  style: GoogleFonts.poppins(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                const SizedBox(height: 12),
                Center(
                  child: GestureDetector(
                    onTap: _takeSelfie,
                    child:
                        _selfieImage != null
                            ? Container(
                              width: 100,
                              height: 100,
                              decoration: BoxDecoration(
                                shape: BoxShape.circle,
                                image: DecorationImage(
                                  image: FileImage(_selfieImage!),
                                  fit: BoxFit.cover,
                                ),
                              ),
                            )
                            : DottedBorder(
                              borderType: BorderType.Circle,
                              color: CupertinoColors.activeBlue,
                              strokeWidth: 1.5,
                              dashPattern: const [6, 3],
                              child: Container(
                                width: 90,
                                height: 90,
                                decoration: const BoxDecoration(
                                  color: CupertinoColors.systemGrey6,
                                  shape: BoxShape.circle,
                                ),
                                child: const Icon(
                                  CupertinoIcons.camera,
                                  color: CupertinoColors.activeBlue,
                                  size: 32,
                                ),
                              ),
                            ),
                  ),
                ),
                const SizedBox(height: 8),
                Center(
                  child: Text(
                    'Face should be clear and well-lit',
                    style: GoogleFonts.poppins(
                      fontSize: 14,
                      color: CupertinoColors.systemGrey,
                    ),
                  ),
                ),
                const SizedBox(height: 20),

                // ID Document Images
                Text(
                  'ID Document Images',
                  style: GoogleFonts.poppins(
                    fontSize: 12,
                    fontWeight: FontWeight.w500,
                  ),
                ),
                const SizedBox(height: 12),
                Row(
                  children: [
                    Expanded(
                      child: GestureDetector(
                        onTap: _takeFrontID,
                        child:
                            _frontIDImage != null
                                ? Container(
                                  height: 120,
                                  width: double.infinity,
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(12),
                                    image: DecorationImage(
                                      image: FileImage(_frontIDImage!),
                                      fit: BoxFit.cover,
                                    ),
                                  ),
                                  child: Align(
                                    alignment: Alignment.bottomCenter,
                                    child: Container(
                                      width: double.infinity,
                                      color: CupertinoColors.black.withOpacity(
                                        0.5,
                                      ),
                                      padding: const EdgeInsets.symmetric(
                                        vertical: 4,
                                      ),
                                      child: Text(
                                        'Front of ID',
                                        textAlign: TextAlign.center,
                                        style: GoogleFonts.poppins(
                                          color: CupertinoColors.white,
                                          fontSize: 12,
                                        ),
                                      ),
                                    ),
                                  ),
                                )
                                : DottedBorder(
                                  borderType: BorderType.RRect,
                                  radius: const Radius.circular(12),
                                  color: CupertinoColors.activeBlue,
                                  strokeWidth: 1.5,
                                  dashPattern: const [6, 3],
                                  child: Container(
                                    height: 120,
                                    width: double.infinity,
                                    decoration: BoxDecoration(
                                      color: CupertinoColors.systemGrey6,
                                      borderRadius: BorderRadius.circular(12),
                                    ),
                                    child: Column(
                                      mainAxisAlignment:
                                          MainAxisAlignment.center,
                                      children: [
                                        const Icon(
                                          CupertinoIcons.creditcard,
                                          color: CupertinoColors.activeBlue,
                                          size: 32,
                                        ),
                                        const SizedBox(height: 8),
                                        Text(
                                          'Front of ID',
                                          style: GoogleFonts.poppins(
                                            color: CupertinoColors.activeBlue,
                                          ),
                                        ),
                                      ],
                                    ),
                                  ),
                                ),
                      ),
                    ),
                    const SizedBox(width: 16),
                    Expanded(
                      child: GestureDetector(
                        onTap: _takeBackID,
                        child:
                            _backIDImage != null
                                ? Container(
                                  height: 120,
                                  width: double.infinity,
                                  decoration: BoxDecoration(
                                    borderRadius: BorderRadius.circular(12),
                                    image: DecorationImage(
                                      image: FileImage(_backIDImage!),
                                      fit: BoxFit.cover,
                                    ),
                                  ),
                                  child: Align(
                                    alignment: Alignment.bottomCenter,
                                    child: Container(
                                      width: double.infinity,
                                      color: CupertinoColors.black.withOpacity(
                                        0.5,
                                      ),
                                      padding: const EdgeInsets.symmetric(
                                        vertical: 4,
                                      ),
                                      child: Text(
                                        'Back of ID',
                                        textAlign: TextAlign.center,
                                        style: GoogleFonts.poppins(
                                          color: CupertinoColors.white,
                                          fontSize: 12,
                                        ),
                                      ),
                                    ),
                                  ),
                                )
                                : DottedBorder(
                                  borderType: BorderType.RRect,
                                  radius: const Radius.circular(12),
                                  color: CupertinoColors.activeBlue,
                                  strokeWidth: 1.5,
                                  dashPattern: const [6, 3],
                                  child: Container(
                                    height: 120,
                                    width: double.infinity,
                                    decoration: BoxDecoration(
                                      color: CupertinoColors.systemGrey6,
                                      borderRadius: BorderRadius.circular(12),
                                    ),
                                    child: Column(
                                      mainAxisAlignment:
                                          MainAxisAlignment.center,
                                      children: [
                                        const Icon(
                                          CupertinoIcons.creditcard,
                                          color: CupertinoColors.activeBlue,
                                          size: 32,
                                        ),
                                        const SizedBox(height: 8),
                                        Text(
                                          'Back of ID',
                                          style: GoogleFonts.poppins(
                                            color: CupertinoColors.activeBlue,
                                          ),
                                        ),
                                      ],
                                    ),
                                  ),
                                ),
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 8),
                Text(
                  'Clear, unobstructed photos required',
                  style: GoogleFonts.poppins(
                    fontSize: 12,
                    color: CupertinoColors.systemGrey,
                  ),
                ),
                const SizedBox(height: 32),

                // Submit Button
                SizedBox(
                  width: double.infinity,
                  height: 50,
                  child: CupertinoButton(
                    onPressed: _submitVerification,
                    color: CupertinoColors.activeBlue,
                    borderRadius: BorderRadius.circular(8),
                    padding: EdgeInsets.zero,
                    child: Text(
                      'Submit Verification',
                      style: GoogleFonts.poppins(
                        fontSize: 12,
                        fontWeight: FontWeight.w500,
                        color: CupertinoColors.white,
                      ),
                    ),
                  ),
                ),
                const SizedBox(height: 20),
              ],
            ),
          ),
        ),
      ),
    );
  }

  @override
  void acceptGesture(int pointer) {}

  @override
  void rejectGesture(int pointer) {}
}
0
likes
150
points
3
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for identity verification and validation. Provides a complete solution for validating personal identification documents, performing face matching, and verifying personal information.

Documentation

API reference

License

MIT (license)

Dependencies

flutter, path, plugin_platform_interface

More

Packages that depend on personal_validation_system

Packages that implement personal_validation_system