root_jailbreak_check

On-device root (Android) and jailbreak (iOS) detection for Flutter.

Status: standalone, general-purpose package — not tied to any specific app. Not yet published to pub.flutter-io.cn; until then, depend on it directly via git in pubspec.yaml:

dependencies:
  root_jailbreak_check:
    git:
      url: https://github.com/yess-wee/root_jailbreak_check.git
  • No bundled native binaries — detection is plain Kotlin (Android) and Swift (iOS), so there's nothing to go out of alignment with Android's 16 KB page-size requirement, and no prebuilt .so/framework to trust.
  • No network calls, no third-party SDK, no telemetry. Everything runs locally on the device; nothing about the check or its result ever leaves the app.
  • Fails open. If detection itself errors out, the result is false — a bug in a heuristic should never lock out a legitimate user.

What it checks

Android — signing build tags (test-keys), presence of common su binaries, known root-management packages (Magisk, SuperSU, KingRoot, etc.), whether su is executable on PATH, and whether /system is writable.

iOS — known jailbreak-tool paths (Cydia, Sileo, MobileSubstrate, etc.), whether the app can write outside its sandbox, and the DYLD_INSERT_LIBRARIES environment variable (used by dynamic-injection tools like Frida).

These are standard, well-documented heuristics — like any on-device check, they raise the bar rather than guarantee detection against a sufficiently determined attacker.

Usage

import 'package:root_jailbreak_check/root_jailbreak_check.dart';

final compromised = await RootJailbreakCheck.isJailbroken();
if (compromised) {
  // show a block/warning screen, restrict sensitive actions, etc.
}

Testing on a clean device

Root/jailbreak detection is naturally hard to positive-test without an actual rooted/jailbroken device. During development you can force a positive result at the call site with a debug-only flag in your own app, e.g.:

final compromised = kReleaseMode
    ? await RootJailbreakCheck.isJailbroken()
    : (const bool.fromEnvironment('FORCE_COMPROMISED') ||
        await RootJailbreakCheck.isJailbroken());

then run with --dart-define=FORCE_COMPROMISED=true.