fingerly_flutter is a plugin over the native iOS and Android SDKs. Collection happens in native code; Dart gets typed results.
Requirements
- Flutter 3.10 or newer, Dart 3.
- iOS 13 and Android 5.0 (API 21) or newer.
Install
flutter pub add fingerly_flutter
The iOS side supports both CocoaPods and Swift Package Manager.
Identify a device
import 'package:fingerly_flutter/fingerly.dart';
// consent: granted only once you have consent (or another lawful basis)
final fingerly = await Fingerly.load(apiKey: 'fly_pk_production_…', consent: ConsentState.granted);
Future<void> signIn(String email, String password) async {
final result = await fingerly.identify(tag: 'sign-in');
await api.signIn(email, password, requestId: result.requestId);
}
Send requestId to your backend with the action, and decide there after reading the stored event with a secret key. See server-side verification.
API
| Member | Returns | Notes |
|---|---|---|
Fingerly.load({required String apiKey, String endpoint = '', ConsentState consent = ConsentState.pending}) | Future<Fingerly> | An empty endpoint uses https://api.fingerly.io. Nothing is collected until consent is granted. The consent state is shared by the whole app, so pass it on every load. |
setConsent(ConsentState state) | Future<void> | Changes the consent state. consent reads it. Until it is granted, identify and collect throw FingerlyConsentException. |
identify({String? tag, bool submit = true}) | Future<IdentifyResult> | suspectScore is int? and null when nothing was scored. |
collect() | Future<SignalReport> | Collects without submitting. |
Fingerly.deleteStoredIdentity() | Future<void> | Deletes the install record on the device. See below. |
Fingerly.nativeVersion() | Future<String> | The native SDK version. |
Deleting the stored identity
await Fingerly.deleteStoredIdentity();
Deletes the install record the native SDK keeps on the device, including the install ID the server recognises it by, for erasure requests and account deletion. It needs no load and no consent, sends nothing, and completes once no record remains, including when there was none. It throws a FingerlyException when the delete fails. The next identification made with consent starts a new install ID; to stop identifying as well, set consent to ConsentState.denied first. On iOS the record survives the app being deleted, so this is the only way to remove it. See account deletion and erasure.
Verdicts
| Verdict | Platform | What it means |
|---|---|---|
instrumentation | Both | An instrumentation toolkit is attached to the app. |
mitm | Both | Something is intercepting the app's encrypted traffic. |
automation | Both | A debugger or a test runner is driving the app. |
tampering | Both | The app's code has been hooked or modified. |
farm | Both | The device looks mass-provisioned or freshly reset. |
jailbreak, simulator | iOS | Jailbroken device; iOS Simulator. |
root, emulator, appCloner | Android | Rooted device; emulator; cloning framework. |
if (result.verdicts.jailbreak.value &&
result.verdicts.jailbreak.confidence == Confidence.high) {
// add friction, and let your server make the final decision
}
Errors
try {
await fingerly.identify();
} on FingerlyException catch (error) {
if (error.status == 401) { /* the key is wrong */ }
if (error.status == 402) { /* the organization is not accepting traffic */ }
}