faceping 1.0.0-preview.4 copy "faceping: ^1.0.0-preview.4" to clipboard
faceping: ^1.0.0-preview.4 copied to clipboard

Offline face verification with liveness for Flutter: enrol a face, then verify it on the device in under 0.3 s, with no network. A bridge over the native FacePing SDKs for Android and iOS.

FacePing for Flutter #

Offline face verification with liveness, for Flutter apps on Android and iOS. Enrol a face, then verify it on the device in under 0.3 s, with no network. 1:1 by default: you check that someone is the person they say they are.

This package is a thin bridge over the native FacePing SDKs (Android and iOS), so results, errors and timings are the same as in a native app.

final faceping = await FacePing.init(sandboxKey: 'fp_test_…');

// Add the face once, on the device
await faceping.enroll('me', photo);

// Verify any time, with no network
final result = await faceping.verifyLive('me', camera);
// result.outcome → VerifyOutcome.match

Preview. This is 1.0.0-preview.4. Get a free sandbox key at app.faceping.ai/signup (Developers, Sandbox keys, Create sandbox key). Sandbox faces are enrolled on the device, up to 25 per device, and deleted after 24 hours.

Requirements #

Android Android 7 (API 24) or later, on 64-bit phones (arm64-v8a) and x86_64 emulators
iOS iOS 15.1 or later, on iPhones and the simulator; Xcode 26 or later
Flutter 3.24 or later (Dart 3.5)

On a 32-bit Android phone the app still runs, and FacePing reports FacePingModelException so you can send people to your normal way in.

Set up #

flutter pub add faceping

This adds the latest preview. To pin it, put faceping: 1.0.0-preview.4 in pubspec.yaml.

iOS #

  1. Set the minimum iOS version to 15.1: in Xcode, open ios/Runner.xcworkspace and set the Runner project's Minimum Deployments to 15.1. With CocoaPods, also set platform :ios, '15.1' in ios/Podfile.

  2. Add a camera usage description to ios/Runner/Info.plist. Without it, iOS stops the app when the camera starts:

    <key>NSCameraUsageDescription</key>
    <string>We use the front camera to check it's you.</string>
    

Flutter fetches the FacePing Swift package (github.com/faceping/faceping-ios) with Swift Package Manager, which current Flutter uses by default. If your app has Swift Package Manager turned off, the podspec downloads the same release (FacePing.xcframework.zip) during pod install and checks its SHA-256 before using it.

The simulator has no front camera: test the live check on an iPhone.

Android #

The SDK (ai.faceping:faceping-android, from Maven Central) declares the CAMERA and INTERNET permissions, and FaceCamera asks for the camera the first time it appears. It needs Android 7 (API 24): current Flutter sets minSdk = flutter.minSdkVersion, which is 24 or later, so there's nothing to change. If your app sets a lower minSdk in android/app/build.gradle.kts, raise it to 24, or the build stops with a manifest merger error.

To keep your APK small, build only the two ABIs FacePing supports (64-bit phones and x86_64 emulators):

flutter build apk --release --target-platform android-arm64,android-x64

(Don't add ndk { abiFilters … } for this: it conflicts with flutter build apk --split-per-abi. App bundles for Google Play need nothing: each phone downloads only its own ABI.)

FacePing encrypts its face store (files/faceping/) with a key in the device's Android Keystore, wrapped in the shared preferences file faceping.keys.xml. The key can't be used on another device, so keep both out of backups and out of transfers to a new phone. Set android:allowBackup="false" on <application> in AndroidManifest.xml, and on Android 12 and later also add android:dataExtractionRules="@xml/data_extraction_rules" (device-to-device transfer ignores allowBackup), with android/app/src/main/res/xml/data_extraction_rules.xml:

<data-extraction-rules>
    <cloud-backup>
        <exclude domain="file" path="faceping/" />
        <exclude domain="sharedpref" path="faceping.keys.xml" />
    </cloud-backup>
    <device-transfer>
        <exclude domain="file" path="faceping/" />
        <exclude domain="sharedpref" path="faceping.keys.xml" />
    </device-transfer>
</data-extraction-rules>

On the new phone the app starts fresh: a check-in device syncs its list again.

Use it #

Set up once #

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Returns at once and loads the face models in the background.
  await FacePing.init(sandboxKey: 'fp_test_…');   // sandbox: faces enrolled on the device
  // await FacePing.init(deviceToken: 'fpd_…');   // production: faces synced from your check-in list
  runApp(const MyApp());
}

FacePing.current returns the same instance anywhere in your app. Calling init again with the same options returns the same instance; with different options it throws FacePingInvalidOperationException (call close() first).

A key that doesn't start with fp_test_ throws FacePingSetupException from init. A sandbox key the server doesn't accept, or a first run with no connection, is reported by the first call that needs the network, for example sync(). A device token is different: a revoked or mistyped token, or one whose list was deleted, never throws. sync() returns SyncStatus.revoked and the device wipes its copy.

The camera #

final camera = FacePingCameraController();

SizedBox(
  height: 420,
  child: FaceCamera(
    controller: camera,
    onCameraFailed: (message) => showMessage(message), // permission refused, or no front camera
  ),
)

FaceCamera shows the native front camera with an oval face guide and the liveness prompts ("Turn your head left", "Look back at the camera"). It runs the camera itself, so you don't need another camera package.

Enrol and verify #

final faceping = FacePing.current;

// Sandbox: add a face on this device (in production, faces are added on your server, with consent).
final photo = await camera.capture();
final enrolled = await faceping.enroll('me', photo);
if (!enrolled.isEnrolled) showMessage('${enrolled.outcome}'); // for example EnrollOutcome.faceTooSmall

// A live check: match, a head turn in a random direction, match again. About 10 seconds at most.
final result = await faceping.verifyLive('me', camera);
if (result.isMatch) {
  showMessage('Match in ${result.elapsed.inMilliseconds} ms');
}

You can also use a photo you already have: FaceImage.fromBytes(jpegBytes) or FaceImage.fromFile(path). faceping.verify(id, photo) checks one photo with passive liveness only.

Method Notes
enroll(id, photo) Sandbox only. Replaces any face already stored under id.
verify(id, photo) One photo, passive liveness only.
verifyLive(id, camera) The live check on a FaceCamera. Stop it early with camera.cancel().
forget(id) Sandbox: deletes that face from the device now. Returns false if there was none.
forgetAll() Deletes every face on the device now.
enrolledIds() Who can be verified on this device.
sync() Production: downloads the latest faces for the device's check-in list. Sandbox: activates the key.
synced A stream of every sync, automatic or not.
close() Stops automatic sync and frees the engine.

Results #

VerifyResult has outcome, similarity, livenessScore, elapsed (a Duration), isSandbox and isMatch.

VerifyOutcome Meaning
match A live face that matches the enrolled one.
noMatch A live face, but not the enrolled person.
notEnrolled Nothing is enrolled under this id.
noFace No usable face was seen.
livenessFailed The face looked like a photo or a screen.
challengeFailed The person didn't turn their head as asked in time.
expired The faces on this device have expired and were deleted.
leaseExpired Production: the device hasn't synced within its lease (24 hours by default).

EnrollOutcome is one of enrolled, noFace, multipleFaces, faceTooSmall, notLive or limitReached (the sandbox already holds 25 faces on this device).

Errors #

A face check never throws because someone looked away: that's an outcome. Everything FacePing throws is a FacePingException with a message ready to show:

Exception When
FacePingSetupException The key or token is missing or malformed (neither set, both set, or a sandbox key that doesn't start with fp_test_), or the sandbox key couldn't be activated (the first run needs a connection, or the server refused the key). A revoked or mistyped device token, or a deleted list, never throws: sync() returns SyncStatus.revoked.
FacePingModelException The face models couldn't load on this device. Send people to your normal way in.
FacePingInvalidOperationException Not allowed in this mode or state, for example enrolling in production or a closed FacePing.
FacePingInvalidArgumentException A bad argument: an empty id, or a photo that isn't a readable image.
FacePingStorageException The device's secure storage failed.
FacePingCameraException The camera isn't on screen, couldn't start or didn't deliver a picture.
FacePingCanceledException A live check was stopped with camera.cancel().

Testing #

package:faceping/faceping_platform_interface.dart exports FacePingPlatform. Set FacePingPlatform.instance to a fake in your unit tests to run your app's logic without a device.

Example #

The example app is FaceCheck, the same app as the native starters: enrol your face, then verify it with a live check, with or without a network.

cd example
flutter run --dart-define=FACEPING_SANDBOX_KEY=fp_test_yourkey

Licence #

The FacePing SDK licence: see LICENSE. You need a FacePing account to use it. The native SDKs include third-party components under their own licences (YuNet, SFace, MiniFASNet, ONNX Runtime and others): see the THIRD-PARTY-NOTICES.txt that ships inside them.

0
likes
140
points
0
downloads

Documentation

Documentation
API reference

Publisher

verified publisherfaceping.ai

Weekly Downloads

Offline face verification with liveness for Flutter: enrol a face, then verify it on the device in under 0.3 s, with no network. A bridge over the native FacePing SDKs for Android and iOS.

Homepage

Topics

#face-verification #liveness #biometrics #offline #camera

License

unknown (license)

Dependencies

flutter, meta, plugin_platform_interface

More

Packages that depend on faceping

Packages that implement faceping