facecore 0.6.0
facecore: ^0.6.0 copied to clipboard
Self-contained Flutter plugin for camera-based face liveness, depth, and anti-spoof checks before capturing a photo on Android and iOS.
facecore #
A self-contained Flutter plugin that runs a camera-based face liveness flow natively on Android and iOS, then returns a captured JPEG to Dart. The platform side handles camera permission, preview, face detection, motion and depth anti-spoof checks, and image capture — your Dart code just calls one method.
Features #
- Native liveness UI on Android (CameraX + ML Kit Face Detection) and iOS (AVFoundation + Vision).
- Configurable challenge actions: blink, turn left, turn right.
- Motion standard-deviation check to reject static photos / replays.
- Optional depth-based anti-spoof check.
- Returns the saved JPEG path plus telemetry (dimensions, motion, depth).
Platform support #
| Platform | Minimum version |
|---|---|
| Android | API 21 |
| iOS | 13.0 |
Installation #
dependencies:
facecore: ^0.1.0
Android #
Camera permission is declared by the plugin. No additional setup required.
iOS #
Add a usage description to your app's ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to verify your identity.</string>
Usage #
import 'package:facecore/facecore.dart';
final facecore = Facecore();
final result = await facecore.captureWithLiveness(
config: const LivenessConfig(
actions: [
LivenessAction.blink,
LivenessAction.turnLeft,
LivenessAction.turnRight,
],
requireDepth: true,
title: 'Verify your identity',
subtitle: 'Follow the prompts to confirm you are present.',
),
);
if (result == null) {
// User cancelled.
return;
}
print('Saved to: ${result.imagePath}');
print('Size: ${result.width}x${result.height}');
print('Motion stddev: ${result.motionStdDev}');
print('Depth: ${result.depthMethod.name} score=${result.depthScore}');
captureWithLiveness returns null when the user cancels and throws a
FaceCaptureException if the native flow fails (e.g. permission denied,
camera unavailable, anti-spoof rejection).
See example/ for a runnable demo.
Configuration #
LivenessConfig exposes the knobs that drive the native flow:
| Field | Default | Purpose |
|---|---|---|
actions |
[blink, turnLeft, turnRight] |
Ordered list of liveness challenges to run. |
requireDepth |
false |
Opt-in: verify the face is 3D; rejects photos/flat screens (below). |
minFaceFraction |
0.18 |
Minimum face height as a fraction of the frame height. |
maxFaceFraction |
0.85 |
Maximum face height (too close). |
minMotionStdDev |
1.5 |
Minimum motion variance to accept the capture. |
minDepthRange |
10.0 |
Minimum nose relief in mm (TrueDepth check). |
minParallaxScore |
0.02 |
Minimum parallax score (motion-based depth check). |
holdFrames |
6 |
Frames the user must hold still before capture. |
jpegQuality |
90 |
JPEG quality (1–100) for the saved capture. |
title |
null |
Optional title shown on the native screen. |
subtitle |
null |
Optional subtitle shown on the native screen. |
Depth check #
The depth check is opt-in. When requireDepth is true, the plugin picks the strongest method the
device supports and reports it in FaceCaptureResult.depthMethod, with the
measurement in depthScore:
| Device | Method | How it works | Rejects |
|---|---|---|---|
| iPhone/iPad with Face ID | trueDepth |
Reads the TrueDepth depth map; the nose must protrude ≥ minDepthRange mm in front of both the cheeks and the forehead/chin. |
Photos (flat or bent), screens showing photos or videos. |
| Android, iOS without Face ID | parallax |
During head turns, fits a planar homography to ~130 facial landmarks. A flat surface fits exactly; a real face cannot. Score is net of detector noise. | Photos and still images on screens, even when tilted/moved. |
The parallax method cannot detect a screen replaying a video of a real
person, because that video contains genuine 3D motion. Defaults were derived
from simulation; log depthScore on your target devices to tune thresholds.
License #
MIT — see LICENSE.