FLUTTER ECOSYSTEM

bagussubagja/flutter-liveness-detection-randomized-plugin

A Flutter plugin for liveness detection with randomized challenge response method

flutter-liveness-detection-randomized-plugin project cover
Stars
31
Forks
39
Last push (UTC)
Jul 30, 2026
Project status
Active
Bagus Subagja GitHub avatar
GITHUB User

Bagus Subagja ↗

Mobile Engineer

Bandung, Indonesia.
LanguagesDartRubyKotlinSwiftObjective-C

Technical topics

Packages published by this repository

Dependencies used

Dependency list 13 items

Original README

English project snapshot. Visit GitHub for the latest content.

Expand / collapse project README

Flutter Liveness Detection Randomized Plugin

A Flutter plugin for liveness detection with randomized challenge response method with an interaction mechanism between the user and the system in the form of a movement challenge that indicates life is detected on the face. This plugin helps implement secure biometric authentication by detecting real human presence through dynamic facial verification challenges.

pub package

Codacy Badge

Author

Crafted with love by Bagus Subagja ❤️

Feel free to fork and modify this package to suit your needs - that's much more enjoyable and respectful 😊

Preview 🪟

Slide 16_9 - 1

https://github.com/user-attachments/assets/f7266dc9-c4a2-4fba-8684-0ead2f678180

Update 1.2.1

  • ⬆️ Kotlin updated to 2.1.0 for better performance and support 16KB google page size policy
  • ⬆️ compileSdk & targetSdk bumped to 36 (Android 16)
  • ☕ Java & Kotlin JVM target upgraded to VERSION_17
  • 📱 minSdk raised to 24 (Android 7.0)
  • 🗑️ Removed useLegacyPackaging — no longer needed with modern AGP
  • 🔍 google_mlkit_face_detection updated to 0.13.2
  • 🐦 Flutter minimum version bumped to 3.38.7

Update 1.1.0

  • ⏱️ Added automatic cooldown feature after 3 failed verification attempts
  • 🔒 10-minute waiting period with persistent countdown (survives app restarts)
  • 🎯 Countdown only decreases when app is active (pauses when app is backgrounded)
  • 🔄 API Refactor: All parameters consolidated into LivenessDetectionConfig
  • 🎯 Simplified API - only requires context and config parameters
  • 🛠️ Fixed customizedLabel logic for proper skip challenge behavior
  • ✅ Added validation: customizedLabel must not be null when useCustomizedLabel is true

Update 1.0.6

Slide 16_9 - 9 Face stretching already fixed on this version

Features ✨

  • 📱 Real-time face detection
  • 🎲 Randomized challenge sequence generation
  • 💫 Cross-platform support (iOS & Android)
  • 🎨 Light and dark mode support
  • ✅ High accuracy liveness verification
  • 🚀 Simple integration API
  • 🎭 Customizable liveness challenge labels
  • ⏳ Flexible security verification duration
  • 🎲 Adjustable number of liveness challenges
  • 🛠️ Adjustable image quality result
  • ⏱️ Automatic cooldown after failed attempts

Getting Started 🌟

Add this to your package's pubspec.yaml file:

dependencies:
  flutter_liveness_detection_randomized_plugin: ^1.2.1

Usage 🚀

final String? response = await FlutterLivenessDetectionRandomizedPlugin.instance.livenessDetection(
  context: context,
  config: LivenessDetectionConfig(
    // Camera & Image Settings
    cameraResolution: ResolutionPreset.medium, // Camera resolution
    imageQuality: 100, // Image quality (0-100)
    isEnableMaxBrightness: true, // Auto brightness adjustment
    
    // Detection Settings
    durationLivenessVerify: 60, // Detection timeout in seconds
    showDurationUiText: false, // Show countdown timer
    startWithInfoScreen: true, // Show tutorial screen
    
    // UI Settings
    isDarkMode: false, // Dark/light theme
    showCurrentStep: true, // Show step counter
    isEnableSnackBar: true, // Show result notifications
    shuffleListWithSmileLast: true, // Randomize challenges with smile last
    
    // Customization
    useCustomizedLabel: false, // Enable custom labels
    customizedLabel: LivenessDetectionLabelModel(
      blink: '', // Empty string = skip challenge
      lookDown: '', // Skip this challenge
      lookLeft: null, // null = use default "Look LEFT"
      lookRight: 'Turn Right', // Custom label
      lookUp: 'Look Up Please', // Custom label
      smile: null, // null = use default "Smile"
    ),
    
    // Security Features
    enableCooldownOnFailure: true, // Enable cooldown after failures
    maxFailedAttempts: 3, // Failed attempts before cooldown
    cooldownMinutes: 10, // Cooldown duration
  ),
);

Configuration Parameters 📋

Camera & Image Settings
  • cameraResolution: Camera quality (ResolutionPreset.low/medium/high)
  • imageQuality: Output image quality 0-100 (default: 100)
  • isEnableMaxBrightness: Auto brightness adjustment (default: true)
Detection Settings
  • durationLivenessVerify: Detection timeout in seconds (default: 45)
  • showDurationUiText: Show countdown timer (default: false)
  • startWithInfoScreen: Show tutorial before detection (default: false)
UI Settings
  • isDarkMode: Dark theme mode (default: true)
  • showCurrentStep: Show current step number (default: false)
  • isEnableSnackBar: Show success/failure notifications (default: true)
  • shuffleListWithSmileLast: Randomize challenges with smile at end (default: true)
Customization
  • useCustomizedLabel: Enable custom challenge labels (default: false)
  • customizedLabel: Custom labels for each challenge type
Security Features
  • enableCooldownOnFailure: Enable cooldown after failed attempts (default: true)
  • maxFailedAttempts: Number of failures before cooldown (default: 3)
  • cooldownMinutes: Cooldown duration in minutes (default: 10)

Cooldown Feature

The plugin includes an automatic cooldown mechanism to prevent brute force attempts:

  • Configurable number of failed attempts before cooldown
  • Configurable cooldown duration
  • Countdown timer only decreases when app is active
  • Cooldown state persists through app restarts
  • Users see a countdown screen during cooldown period

Customized Steps Label

You can customize challenge labels or skip certain challenges:

  • Use empty string '' to skip a challenge
  • Use null to keep default label
  • Provide custom string for personalized labels
  • When useCustomizedLabel: true, customizedLabel must not be null

Complete Example 💡

import 'package:flutter_liveness_detection_randomized_plugin/index.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              final result = await FlutterLivenessDetectionRandomizedPlugin.instance.livenessDetection(
                context: context,
                config: LivenessDetectionConfig(
                  startWithInfoScreen: true,
                  isDarkMode: false,
                  showCurrentStep: true,
                  isEnableSnackBar: true,
                ),
              );
              
              if (result != null) {
                // Liveness detection successful
                print('Face captured: $result');
              } else {
                // Detection failed or cancelled
                print('Detection failed');
              }
            },
            child: Text('Start Liveness Detection'),
          ),
        ),
      ),
    );
  }
}

Platform Setup

Android

Add camera permission to your android/app/src/main/AndroidManifest.xml:

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

Minimum SDK version: 24

iOS

Add camera usage description to ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Camera access is required for liveness detection</string>

Testing Scenarios 🧪

The example app includes 8 comprehensive liveness scenarios to test all features:

Scenario 1: Default Configuration
LivenessDetectionConfig(
  shuffleListWithSmileLast: true,
  startWithInfoScreen: true,
  // Standard settings
)
Scenario 2: Random Shuffle
LivenessDetectionConfig(
  shuffleListWithSmileLast: false,
  durationLivenessVerify: 30,
  startWithInfoScreen: false,
)
Scenario 3: Dark Mode + High Resolution
LivenessDetectionConfig(
  isDarkMode: true,
  cameraResolution: ResolutionPreset.high,
  durationLivenessVerify: 60,
)
Scenario 4: Custom Indonesian Labels
LivenessDetectionConfig(
  useCustomizedLabel: true,
  customizedLabel: LivenessDetectionLabelModel(
    blink: 'Kedip 2-3 Kali',
    lookUp: 'Lihat ke Atas',
    smile: 'Tersenyum Lebar',
  ),
)
Scenario 5: Skip Steps (Minimal Challenges)
LivenessDetectionConfig(
  useCustomizedLabel: true,
  customizedLabel: LivenessDetectionLabelModel(
    blink: 'Blink Eyes',
    lookDown: '', // Skip
    lookLeft: '', // Skip  
    lookRight: '', // Skip
    lookUp: 'Look Up Please',
    smile: 'Smile Wide',
  ),
)
Scenario 6: Timer + Cooldown Features
LivenessDetectionConfig(
  showDurationUiText: true,
  enableCooldownOnFailure: true,
  maxFailedAttempts: 2,
  cooldownMinutes: 5,
)
Scenario 7: Minimal Features
LivenessDetectionConfig(
  isEnableMaxBrightness: false,
  isEnableSnackBar: false,
  showCurrentStep: false,
)
Scenario 8: All Features Enabled
LivenessDetectionConfig(
  isDarkMode: true,
  cameraResolution: ResolutionPreset.high,
  showDurationUiText: true,
  enableCooldownOnFailure: true,
  useCustomizedLabel: true,
  customizedLabel: LivenessDetectionLabelModel(
    blink: '👁️ Kedipkan Mata',
    smile: '😊 Senyum Manis',
  ),
)

Migration Guide 🔄

From v1.0.x to v1.1.0+

All parameters are now consolidated into the LivenessDetectionConfig object:

Before:

await plugin.livenessDetection(
  context: context,
  config: LivenessDetectionConfig(...),
  isEnableSnackBar: true,
  shuffleListWithSmileLast: true,
  showCurrentStep: true,
  isDarkMode: false,
);

After:

await plugin.livenessDetection(
  context: context,
  config: LivenessDetectionConfig(
    isEnableSnackBar: true,
    shuffleListWithSmileLast: true,
    showCurrentStep: true,
    isDarkMode: false,
    // ... other parameters
  ),
);