Detects rooted, jailbroken, tampered and otherwise untrusted devices from Flutter.
Uses RootBeer + Magisk and Frida checks for Android root detection, and IOSSecuritySuite (~> 1.9.10) for iOS jailbreak detection.
| Minimum | |
|---|---|
| Flutter | 3.44.0 |
| Dart SDK | 3.12.0 |
| Android | minSdk 21, compileSdk 34, Java/Kotlin 17 |
| iOS | 11.0 |
The iOS side ships as a Swift Package (Swift Package Manager). A CocoaPods podspec is still included, so both dependency managers work.
Add the dependency to your pubspec.yaml:
dependencies:
jailbreak_root_detection: "^1.2.3"Or:
flutter pub add jailbreak_root_detectionTo let the plugin probe for jailbreak-related URL schemes, add them to ios/Runner/Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>undecimus</string>
<string>sileo</string>
<string>zbra</string>
<string>filza</string>
<string>activator</string>
<string>cydia</string>
</array>Without this entry iOS blocks the canOpenURL checks and jailbreak detection is less accurate.
No extra configuration required.
import 'package:jailbreak_root_detection/jailbreak_root_detection.dart';
final detector = JailbreakRootDetection.instance;
// One combined verdict — the usual entry point.
final isNotTrust = await detector.isNotTrust;
if (isNotTrust) {
// Block, warn, or degrade functionality.
}| Member | Returns | Android | iOS | Description |
|---|---|---|---|---|
isNotTrust |
Future<bool> |
✅ | ✅ | Combined verdict: jailbroken/rooted, not a real device, or (Android) installed on external storage. Returns true if any check throws. |
isJailBroken |
Future<bool> |
✅ | ✅ | Rooted (RootBeer, Frida, Magisk) on Android; jailbroken on iOS. |
isRealDevice |
Future<bool> |
✅ | ✅ | false on an emulator or simulator. |
isDebugged |
Future<bool> |
✅ | ✅ | A debugger is attached to the process. |
isDevMode |
Future<bool> |
✅ | — | Developer options are enabled. |
isOnExternalStorage |
Future<bool> |
✅ | — | The app is installed on external storage. |
isTampered(String bundleId) |
Future<bool> |
— | ✅ | The app bundle/signature has been modified. |
checkForIssues |
Future<List<JailbreakIssue>> |
✅ | ✅ | All detected issues in one call. |
Android-only members throw a MissingPluginException on iOS and vice versa, so guard them with
Platform.isAndroid / Platform.isIOS:
if (Platform.isAndroid) {
final isOnExternalStorage = await detector.isOnExternalStorage;
final isDevMode = await detector.isDevMode;
}
if (Platform.isIOS) {
const bundleId = 'com.w3conext.jailbreakRootDetectionExample';
final isTampered = await detector.isTampered(bundleId);
}checkForIssues runs every check the platform supports and returns only the problems it found —
an empty list means the device looks clean.
final issues = await detector.checkForIssues;
for (final issue in issues) {
print('issue: $issue');
}JailbreakIssue |
Android | iOS | Meaning |
|---|---|---|---|
jailbreak |
✅ | ✅ | Device is rooted or jailbroken. |
notRealDevice |
✅ | ✅ | Running on an emulator or simulator. |
debugged |
✅ | ✅ | A debugger is attached. |
fridaFound |
✅ | ✅ | The Frida instrumentation framework was detected. |
devMode |
✅ | — | Developer options are enabled. |
onExternalStorage |
✅ | — | App is installed on external storage. |
proxied |
— | ✅ | An HTTP proxy is configured. |
reverseEngineered |
— | ✅ | Reverse-engineering tooling was detected. |
cydiaFound |
— | ✅ | Cydia was detected. |
tampered |
— | — | Reported only by isTampered(bundleId), not by checkForIssues. |
unknown |
✅ | ✅ | An issue this Dart version doesn't recognise. Android currently reports Magisk detection here. |
Handle unknown as untrusted rather than ignoring it.
This package must be tested on a physical device.
Running on an emulator or simulator may cause false positives — for example, detection may incorrectly report that the device is jailbroken or rooted.
See example/lib/main.dart for a complete app that runs every check
and prints the results.