safety_net_flutter 1.0.0
safety_net_flutter: ^1.0.0 copied to clipboard
Flutter plugin wrapping SafetyNet (iOS) and SafetyNetAndroid for report-only root/jailbreak/tamper/debugger detection on both platforms.
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(), 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 #
Add the dependency to your app's pubspec.yaml:
dependencies:
safety_net_flutter:
git:
url: https://github.com/DipakPanchasara/SafetyNetFlutter.git
tag: 1.0.0 # once tagged; otherwise omit to track the default branch
Then:
flutter pub get
iOS dependency resolution #
SafetyNet is not yet published to CocoaPods trunk, so the two supported iOS integration
paths differ slightly:
-
If your app uses Swift Package Manager for iOS plugins (Flutter's modern default,
ios/Flutter/Generated.xcconfigreferencingPackage.swift): nothing further to do. This plugin's ownPackage.swiftalready declares the SafetyNet git dependency (https://github.com/DipakPanchasara/SafetyNet.git, from2.1.0), and SPM resolves it transitively. -
If your app uses CocoaPods for iOS plugins (
ios/Podfile): you must add these two lines to your ownPodfileso CocoaPods can resolveSafetyNetand its ownSafetyNetObjCdependency (a podspec'ss.dependencycan only point at a name registered in a spec repo, not an arbitrary git URL — only aPodfile'spodline can do that):pod 'SafetyNet', :git => 'https://github.com/DipakPanchasara/SafetyNet.git', :tag => '2.1.0' pod 'SafetyNetObjC', :git => 'https://github.com/DipakPanchasara/SafetyNet.git', :tag => '2.1.0'Then run
pod installfromios/. (Seeexample/ios/Podfilein this repo for a working reference.)
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());
}
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(),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.