This tutorial adds device fingerprinting to a Flutter app. fingerly_flutter is a plugin over the native iOS and Android SDKs: collection happens in native code, and Dart gets typed results. The server returns a visitor ID and a suspect score, and your backend decides what to do.
Before you start
- Flutter 3.10 or newer with Dart 3, and iOS 13 and Android 5.0 (API 21) or newer.
- A public key for the development environment, and a secret key for your server.
- A decision about consent: the SDK collects nothing until
consentisConsentState.granted.
Add the SDK and verify the result
Step 1: Create your keys
In the Fingerly dashboard, open Integration > SDK keys and create a public key in the development environment, and a secret key in the same environment for your server. Development keys run the same detection as production and cost nothing. A production key must also name your app, an Android package or an Apple app ID, as in API keys and environments.
Step 2: Add the plugin
flutter pub add fingerly_flutterOn iOS, the plugin fetches the native framework on the first build and checks it against a pinned checksum, through Swift Package Manager or CocoaPods, whichever your app uses. On Android, it adds Fingerly's Maven repository to the build itself.
Step 3: Load the client and identify at sign-in
import 'package:fingerly_flutter/fingerly_flutter.dart'; // consent: granted only once you have consent (or another lawful basis) final fingerly = await Fingerly.load(apiKey: 'fly_pk_development_…', 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); }suspectScoreandsuspectLevelarenullwhen absent, andvisitorIsNew,visitorConfidence,identifiableandduplicateare nullable too. Withsubmit: false, onlyreportis filled in.Step 4: Verify on your server
A phone is a device you do not control, so the decision is never made in the app. Send the
requestIdto your backend with the action, read the stored event with your secret key, and check it belongs to this action and is recent.import { FingerlyAPIError, load } from '@fingerly/node' const fingerly = load({ secretKey: process.env.FINGERLY_SECRET_KEY! }) export async function afterPasswordCheck(requestId: unknown) { if (typeof requestId !== 'string' || requestId === '') return 'second-factor' // missing evidence try { const event = await fingerly.events.get(requestId) if (event.tag !== 'sign-in') return 'second-factor' if (Date.now() - Date.parse(event.occurred_at) > 2 * 60 * 1000) return 'second-factor' return event.suspect_level === 'high' ? 'second-factor' : 'allow' } catch (error) { if (error instanceof FingerlyAPIError && error.code === 'event_not_found') return 'second-factor' throw error // still pending: answer with a retry, never a refusal } }Also check the request ID has not been used before, and answer a pending event with a retry rather than a refusal. Server-side verification explains each check, and the use-case recipes include a helper that does all of them.
Step 5: Handle 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 */ } }Until consent is
granted,identifyandcollectthrowFingerlyConsentException. If identification fails for any reason, let the user continue and send no request ID.Step 6: Run it on both platforms
Run the app on an iOS and an Android target, sign in, then open Identification > Events in the dashboard to see the visitor, score and signals for each.
Consent and erasure
Load with the default ConsentState.pending and call setConsent from your consent tool, or pass granted once you have consent or another lawful basis. The consent state is shared by the whole app, so pass it on every load.
await Fingerly.deleteStoredIdentity();
This deletes the install record on the device, for erasure requests and account deletion. See account deletion and erasure.
Common questions
Does the plugin fingerprint in Dart?
No. Collection happens in the native iOS and Android SDKs, and Dart receives typed results. The same visitor ID rules apply as in a native app.
Which platforms does the plugin support?
iOS 13 and newer and Android 5.0 (API 21) and newer, with Flutter 3.10 or newer. Never use the browser SDK inside an app's web view: it collects nothing there and the answer has identifiable: false.
What is the identity on a Flutter device?
A random install ID the native SDK keeps, never a hardware identifier. It lives in the Keychain on iOS and in a file excluded from backups on Android.
How do I read the native SDK version?
Call Fingerly.nativeVersion(), which returns a Future<String>.
Can I decide in Dart whether to allow the action?
You can adapt the interface, for example to add a step when suspectLevel is high. The decision itself belongs on your backend, from the stored event, because an app can be modified.