Live Photo Capture

Features

A Flutter plugin to use for Face Detection to detect faces in an image, identify key facial features, and get the contours of detected faces. Multiple liveness challenge types (blinking, smiling, head turns, nodding).A real-time facial verification feature using Google ML Kit for liveliness detection. It ensures user interaction through smiling, blinking, and head movements. Features include face detection, dynamic feedback, a countdown timer, and customizable UIβ€”ideal for secure authentication and anti-spoofisng verification. πŸš€

πŸ”„ Random challenge sequence generation for enhanced security
🎯 Face centering guidance with visual feedback
πŸ” Anti-spoofing measures (screen glare detection, motion correlation)
🎨 Fully customizable UI with theming support
🌈 Animated progress indicators, status displays, and overlays
πŸ“± Simple integration with Flutter apps
πŸ“Έ Optional image capture capability

Installation

  1. Add the latest version of package to your pubspec.yaml (and rundart pub get):
dependencies:
 livephotocapture: ^1.0.3
  1. Import the package and use it in your Flutter App.
 import 'package:livephotocapture/livephotocapture.dart';

Make sure to add camera permission to your app: ios Minimum iOS Deployment Target: 15.5 Add the following to your Info.plist:

<key>NSCameraUsageDescription</key>
 <string>your usage description here</string>
 <key>NSMicrophoneUsageDescription</key>
 <string>your usage description here</string>
Ios Setting

Android Add the following to your AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

Usage

import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:livephotocapture/livephotocapture.dart';

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

 @override
 Widget build(BuildContext context) {
   return const _FaceDetector();
 }
}

class _FaceDetector extends StatefulWidget {
 const _FaceDetector();

 @override
 State<_FaceDetector> createState() => __FaceDetectorState();
}

class __FaceDetectorState extends State<_FaceDetector> {
 final List<Rulesets> _completedRuleset = [];
 XFile? capturedImage;
 bool _hasCapturedImage = false;

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     backgroundColor: Colors.white,
     body: SafeArea(
       child: FaceDetectorScreen(
         onSuccessValidation: (validated) {
         },
         onValidationDone: (controller) {
           _captureIfSmiled(controller);

           return Column(
             mainAxisAlignment: MainAxisAlignment.center,
             children: [
               const Text(
                 "Liveness Detection Complete",
                 style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
               ),
               const SizedBox(height: 20),
               if (capturedImage != null)
                 Column(
                   children: [
                     Image.file(
                       File(capturedImage!.path),
                       width: 200,
                       height: 200,
                       fit: BoxFit.cover,
                     ),
                     const SizedBox(height: 10),
                     Text(
                       "Image saved at:\n${capturedImage!.path}",
                       textAlign: TextAlign.center,
                       style: const TextStyle(fontSize: 12),
                     ),
                   ],
                 )
               else
                 const Text("No image captured yet."),
             ],
           );
         },
         child:
             ({
               required int countdown,
               required Rulesets state,
               required bool hasFace,
             }) => Column(
               mainAxisAlignment: MainAxisAlignment.center,
               children: [
                 // const Spacer(),
                 SizedBox(height: 60),
                 // Face Detection Status
                 AnimatedSize(
                   duration: const Duration(milliseconds: 200),
                   child: Text(
                     hasFace ? "βœ… Face detected" : "❌ Face not detected",
                     style: TextStyle(
                       fontSize: 16,
                       color: hasFace ? Colors.green : Colors.red,
                       fontWeight: FontWeight.w600,
                     ),
                   ),
                 ),
                 const SizedBox(height: 20),

                 // Hint Text
                 Text(
                   getHintText(state),
                   style: const TextStyle(
                     fontSize: 20,
                     fontWeight: FontWeight.bold,
                   ),
                 ),

                 const SizedBox(height: 30),
               ],
             ),
         onRulesetCompleted: (ruleset) {
           if (!_completedRuleset.contains(ruleset)) {
             setState(() {
               _completedRuleset.add(ruleset);
             });
           }
         },
       ),
     ),
   );
 }

 void _captureIfSmiled(dynamic controller) async {
   try {
     if (_hasCapturedImage) return;
     if (controller != null && controller.value.isInitialized) {
       final XFile image = await controller.takePicture();
       log('Captured image at: ${image.path}');
       setState(() {
         capturedImage = image;
         _hasCapturedImage = true;
       });
     } else {
       log("Camera controller not initialized");
     }
   } catch (e) {
     log("Error capturing image: $e");
   }
 }
}

String getHintText(Rulesets state) {
 switch (state) {
   case Rulesets.smiling:
     return '😊 Please Smile';
   case Rulesets.blink:
     return 'πŸ˜‰ Please Blink';
   case Rulesets.tiltUp:
     return 'πŸ‘† Look Up';
   case Rulesets.tiltDown:
     return 'πŸ‘‡ Look Down';
   case Rulesets.toLeft:
     return 'πŸ‘ˆ Look Left';
   case Rulesets.toRight:
     return 'πŸ‘‰ Look Right';
   case Rulesets.normal:
     return 'πŸ§β€β™‚οΈ Center Your Face';
 }
}

Available Challenge Types


Rulesets.smiling - Verify that the user can smile
Rulesets.blink - Verify that the user can blink
Rulesets.tiltUp - Verify that the user can turn their head up
Rulesets.tiltDown - Verify that the user can turn their head down
Rulesets.toLeft - Verify that the user can turn their head left
Rulesets.toRight - Verify that the user can turn their head right
Rulesets.normal - Verify that the user can keep neutral

Libraries

livephotocapture