safety_net_flutter
A Flutter plugin wrapping SafetyNet (iOS) and SafetyNetAndroid (Android) behind one Dart API: root/jailbreak/tamper/debugger detection for Flutter apps.
Like both native libraries it wraps, this plugin never auto-reacts. It only reports what it finds — your app decides what to do with the result (block a screen, log it, step up authentication, etc.).
What each platform actually runs
| Platform | Underlying check |
|---|---|
| iOS | SafetyNet.shared.check() — the full scored pipeline: jailbreak signals, debugger/tracing, code-signature integrity, proxy/VPN. See the SafetyNet README for the full signal list and scoring thresholds. |
| Android | SafetyNetAndroid.checkRoot() + SafetyNetAndroid.checkDebugger() + SafetyNetAndroid.checkProxy(), combined into one result. See the SafetyNetAndroid README for the full check list. |
The Dart-facing result is intentionally a minimal, unified shape rather than exposing each
platform's full native richness (e.g. iOS's numeric ThreatLevel isn't surfaced) — apps that
need finer-grained platform-specific data should call the native SDKs directly on that platform.
class SafetyNetCheckResult {
final bool isCompromised;
final List<String> reasons; // raw check names that fired, e.g. "jailbreakDetected"
}
Installation
Published on pub.dev. Add it to your app's
pubspec.yaml:
dependencies:
safety_net_flutter: ^1.2.0
Then:
flutter pub get
For local development against an unreleased version of this plugin instead, use a git dependency pinned to a branch/commit (this repo's git tags lag behind pub.dev releases, so don't pin to a tag expecting it to match the latest published version):
dependencies:
safety_net_flutter:
git:
url: https://github.com/DipakPanchasara/SafetyNetFlutter.git
iOS dependency resolution
SafetyNet and SafetyNetObjC are both published on the CocoaPods trunk, so no extra
Podfile setup is needed regardless of which iOS plugin resolution your app uses:
- Swift Package Manager (Flutter's modern default,
ios/Flutter/Generated.xcconfigreferencingPackage.swift): this plugin's ownPackage.swiftalready declares the SafetyNet git dependency (https://github.com/DipakPanchasara/SafetyNet.git, from2.1.0), and SPM resolves it transitively. - CocoaPods (
ios/Podfile): this plugin's own podspec already declaress.dependency 'SafetyNet', which CocoaPods now resolves straight from the trunk — just runpod installfromios/, no extrapodlines needed in your ownPodfile.
No other iOS setup is required — see "Do I need to touch AppDelegate?" below.
Android dependency resolution
The Android side depends on SafetyNetAndroid via Maven Central
(io.github.dipakpanchasara:safetynet-android). As long as your app's repository list
includes mavenCentral() — true by default in virtually every Flutter/Gradle project — no
further setup is required.
Usage
import 'package:safety_net_flutter/safety_net_flutter.dart';
Future<void> checkDeviceSecurity() async {
final result = await SafetyNetFlutter.check();
if (result.isCompromised) {
// Your call: log it, show a warning, require step-up auth, etc.
// SafetyNetFlutter itself never blocks UI or logs the user out.
print('Device flagged: ${result.reasons}');
}
}
A common pattern is running the check once at app launch, before runApp():
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final result = await SafetyNetFlutter.check();
if (result.isCompromised) {
// record/report as appropriate for your app
}
runApp(const MyApp());
}
Play Integrity API (Android-only)
import 'package:safety_net_flutter/safety_net_flutter.dart';
// Call once (e.g. at app launch), well before you'll need a token.
// Per Google's guidance this has non-trivial latency — typically under
// 10s, budget up to a minute.
await SafetyNetFlutter.prepareIntegrityCheck(yourCloudProjectNumber);
// Later, when you need a token for a specific action:
final token = await SafetyNetFlutter.requestIntegrityToken(requestHash);
// Send `token` to your server — it is opaque and is NOT itself a verdict.
// Your server must decode it against Google
// (POST https://playintegrity.googleapis.com/v1/{PACKAGE_NAME}:decodeIntegrityToken)
// and enforce the verdict there. Never trust a client-side read of this token.
Bridges SafetyNetAndroid's IntegrityTokenProvider — see the
SafetyNetAndroid README
for cloudProjectNumber setup and the full server-side verification contract.
Both functions reject on iOS — Play Integrity is a Google Play Services API
with no iOS equivalent. This is a normal PlatformException (code
UNSUPPORTED_PLATFORM), not a crash — handle it like any other Dart error, or
gate the call yourself since you already know it's Android-only:
import 'dart:io' show Platform;
if (Platform.isAndroid) {
await SafetyNetFlutter.prepareIntegrityCheck(yourCloudProjectNumber);
}
Do I need to touch AppDelegate?
No manual AppDelegate edit is needed to register this plugin. Flutter's
GeneratedPluginRegistrant (generated automatically at build time) registers every plugin
listed in your pubspec.yaml, including this one — that's true whether your AppDelegate is
Swift or Objective-C, and regardless of whether you use SPM or CocoaPods for the iOS side.
Two things worth knowing that are independent of Flutter's plugin registration:
- SafetyNet's anti-ptrace protection (
AntiDebugBridge.m's constructor) runs automatically the moment the SafetyNet dynamic library is loaded into the process — this happens at process launch, before Dart or Flutter even start, and needs no wiring on your part. - If you specifically want the check result available as early as possible in the app
lifecycle (e.g. before the first frame renders), call
SafetyNetFlutter.check()at the top of Dart'smain()as shown above — there's no AppDelegate-level equivalent needed for this, since the check itself is a Dart-initiated platform channel call.
Example app
See example/ for a minimal app that runs the check on launch and displays
isCompromised and reasons.
cd example
flutter run
Repository layout
lib/— Dart-facing API (SafetyNetFlutter.check(),prepareIntegrityCheck()/requestIntegrityToken(),SafetyNetCheckResult, platform interface, method channel implementation).ios/— iOS plugin implementation (Swift), wrappingSafetyNet.android/— Android plugin implementation (Kotlin), wrappingSafetyNetAndroid.example/— demo app exercising the plugin on both platforms.