# Flutter

> Identify devices in Flutter apps through the native iOS and Android SDKs, with a typed Dart API and the same verdicts on both platforms.

Last updated: 2026-09-17

`fingerly_flutter` is a plugin over the native [iOS](https://docs.fingerly.io/docs/sdks/ios) and [Android](https://docs.fingerly.io/docs/sdks/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

```bash Terminal
flutter pub add fingerly_flutter
```

```yaml pubspec.yaml
dependencies:
  fingerly_flutter: ^0.1.0
```

The iOS side supports both CocoaPods and Swift Package Manager.

## Identify a device

```dart lib/fingerly.dart
import 'package:fingerly_flutter/fingerly.dart';

final fingerly = await Fingerly.load(apiKey: 'fly_pk_us_production_…');

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](https://docs.fingerly.io/docs/server-side-verification).

## API

| Member | Returns | Notes |
| --- | --- | --- |
| `Fingerly.load({required String apiKey, String endpoint = '', ConsentState consent = ConsentState.granted})` | `Future<Fingerly>` | An empty endpoint uses the region in the key. The consent state is shared by the whole app, so pass it on every `load`. |
| `setConsent(ConsentState state)` | `Future<void>` | Changes the [consent state](https://docs.fingerly.io/docs/privacy-and-consent#consent). `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.nativeVersion()` | `Future<String>` | The native SDK version. |

## 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. |

```dart verdicts.dart
if (result.verdicts.jailbreak.value &&
    result.verdicts.jailbreak.confidence == Confidence.high) {
  // add friction, and let your server make the final decision
}
```

## Errors

```dart errors.dart
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 */ }
}
```

> **Tip:** If you see a missing plugin error after installing, run `flutter clean` and rebuild the app rather than hot reloading.
