device_safety_info 1.1.0 copy "device_safety_info: ^1.1.0" to clipboard
device_safety_info: ^1.1.0 copied to clipboard

Device Safety Info Flutter Plugin used for checking JailBreak, Rooted Device, Emulator/Simulator, External storage, VPN Detector, Application Update Checker and Screen Lock.

example/lib/main.dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:device_safety_info/device_safety_info.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Device Safety Info',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
      ),
      home: const DeviceSafetyHome(),
    );
  }
}

class DeviceSafetyHome extends StatefulWidget {
  const DeviceSafetyHome({super.key});
  @override
  State<DeviceSafetyHome> createState() => _DeviceSafetyHomeState();
}

class _DeviceSafetyHomeState extends State<DeviceSafetyHome> {
  // --- Check results ---
  bool? isRootedDevice;
  bool? isScreenLock;
  bool? isRealDevice;
  bool? isExternalStorage;
  bool? isDeveloperMode;
  bool? isVPN;
  bool? isInstalledFromStore;
  bool? isHooked;
  bool? isScreenCaptured;
  bool? isDebuggerAttached;

  // --- Stream state ---
  bool _screenCaptureActive = false;
  int _screenshotCount = 0;

  bool _loading = false;

  final VPNCheck _vpnCheck = VPNCheck();
  late final Stream<VPNState> _vpnStream;

  @override
  void initState() {
    super.initState();
    _vpnStream = _vpnCheck.vpnState;
    _listenVpn();
    _listenScreenCapture();
    _listenScreenshots();
    _refreshAll();
  }

  @override
  void dispose() {
    _vpnCheck.dispose();
    super.dispose();
  }

  void _listenVpn() {
    _vpnStream.listen((state) {
      if (mounted) setState(() => isVPN = state == VPNState.connectedState);
    }, onError: (e) => debugPrint('VPN error: $e'));
  }

  void _listenScreenCapture() {
    DeviceSafetyInfo.onScreenCapturedChanged.listen((capturing) {
      if (mounted) setState(() => _screenCaptureActive = capturing);
    }, onError: (e) => debugPrint('Screen capture error: $e'));
  }

  void _listenScreenshots() {
    DeviceSafetyInfo.onScreenshotTaken.listen((_) {
      if (mounted) setState(() => _screenshotCount++);
    }, onError: (e) => debugPrint('Screenshot error: $e'));
  }

  Future<void> _refreshAll() async {
    if (!mounted) return;
    setState(() => _loading = true);
    try {
      final results = await Future.wait([
        DeviceSafetyInfo.isRootedDevice,
        DeviceSafetyInfo.isScreenLock,
        DeviceSafetyInfo.isRealDevice,
        DeviceSafetyInfo.isInstalledFromStore,
        DeviceSafetyInfo.isHooked,
        DeviceSafetyInfo.isScreenCaptured,
        DeviceSafetyInfo.isDebuggerAttached,
        if (Platform.isAndroid) DeviceSafetyInfo.isExternalStorage,
        if (Platform.isAndroid) DeviceSafetyInfo.isDeveloperMode,
      ]);

      if (!mounted) return;
      setState(() {
        isRootedDevice = results[0];
        isScreenLock = results[1];
        isRealDevice = results[2];
        isInstalledFromStore = results[3];
        isHooked = results[4];
        isScreenCaptured = results[5];
        isDebuggerAttached = results[6];
        if (Platform.isAndroid) {
          isExternalStorage = results[7];
          isDeveloperMode = results[8];
        } else {
          isExternalStorage = null;
          isDeveloperMode = null;
        }
      });
    } catch (e) {
      debugPrint('Error refreshing: $e');
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  Future<void> _checkAppVersion() async {
    final checker = NewVersionChecker(iOSId: '', androidId: '');
    try {
      final status = await checker.getVersionStatus();
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text(status != null && status.canUpdate
            ? 'New version available: ${status.storeVersion}'
            : 'App is up to date'),
      ));
    } catch (e) {
      debugPrint('Version check error: $e');
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Version check failed')),
        );
      }
    }
  }

  Future<void> _confirm(String message, VoidCallback onConfirm) {
    return showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (_) => AlertDialog(
        title: const Text('Are you sure?'),
        content: Text(message),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Cancel')),
          TextButton(
            onPressed: () {
              Navigator.pop(context);
              onConfirm();
            },
            child: const Text('Confirm'),
          ),
        ],
      ),
    );
  }

  Widget _tile({
    required String title,
    required bool? value,
    String? subtitle,
    IconData? icon,
  }) {
    final unknown = value == null;
    final positive = value == true;
    final color =
        unknown ? Colors.orange : (positive ? Colors.green : Colors.red);
    final bgColor = unknown
        ? Theme.of(context).colorScheme.surfaceContainerHighest
        : (positive ? Colors.green.shade50 : Colors.red.shade50);
    final displayIcon = unknown
        ? Icons.help_outline
        : (positive ? (icon ?? Icons.check_circle) : (icon ?? Icons.cancel));

    return Card(
      elevation: 2,
      margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ListTile(
        leading: CircleAvatar(
          radius: 20,
          backgroundColor: bgColor,
          child: Icon(displayIcon, color: color),
        ),
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
        subtitle: subtitle != null ? Text(subtitle) : null,
        trailing: unknown
            ? const Text('—')
            : Chip(
                side: BorderSide.none,
                label: Text(positive ? 'Yes' : 'No'),
                backgroundColor:
                    positive ? Colors.green.shade100 : Colors.red.shade100,
              ),
      ),
    );
  }

  Widget _streamTile({
    required String title,
    required String value,
    required IconData icon,
    required Color color,
    String? subtitle,
  }) {
    return Card(
      elevation: 2,
      margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: ListTile(
        leading: CircleAvatar(
          radius: 20,
          backgroundColor: color.withValues(alpha: 0.1),
          child: Icon(icon, color: color),
        ),
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
        subtitle: subtitle != null ? Text(subtitle) : null,
        trailing: Chip(
          side: BorderSide.none,
          label: Text(value),
          backgroundColor: color.withValues(alpha: 0.15),
        ),
      ),
    );
  }

  Widget _sectionHeader(String label) => Padding(
        padding: const EdgeInsets.fromLTRB(16, 20, 16, 8),
        child: Text(
          label,
          style: Theme.of(context)
              .textTheme
              .titleMedium
              ?.copyWith(fontWeight: FontWeight.bold),
        ),
      );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Device Safety Info'),
        actions: [
          IconButton(
            tooltip: 'Version Check',
            onPressed: _checkAppVersion,
            icon: const Icon(Icons.system_update),
          ),
          IconButton(
            tooltip: 'Refresh',
            onPressed: _loading ? null : _refreshAll,
            icon: _loading
                ? const SizedBox(
                    width: 20,
                    height: 20,
                    child: CircularProgressIndicator(strokeWidth: 2))
                : const Icon(Icons.refresh),
          ),
        ],
      ),
      body: SafeArea(
        child: RefreshIndicator(
          onRefresh: _refreshAll,
          child: ListView(
            padding: const EdgeInsets.only(bottom: 100),
            children: [
              // --- Detection checks ---
              _sectionHeader('Detection'),
              _tile(
                title: 'Device is rooted / jailbroken',
                value: isRootedDevice,
                icon: Icons.security,
                subtitle:
                    'Root/jailbreak detected via native C + platform checks.',
              ),
              _tile(
                title: 'Real device (not emulator)',
                value: isRealDevice,
                icon: Icons.phone_android,
                subtitle: 'Emulators are often used for tampering.',
              ),
              _tile(
                title: 'Screen lock enabled',
                value: isScreenLock,
                icon: Icons.lock,
                subtitle: 'Secure lockscreen is recommended.',
              ),
              _tile(
                title: 'Installed from store',
                value: isInstalledFromStore,
                icon: Icons.storefront,
                subtitle: 'Sideloaded apps skip store security checks.',
              ),
              _tile(
                title: 'Hooking framework detected',
                value: isHooked,
                icon: Icons.bug_report,
                subtitle:
                    'Frida / Xposed / Substrate detected via FFI + platform.',
              ),
              _tile(
                title: 'Debugger attached',
                value: isDebuggerAttached,
                icon: Icons.adb,
                subtitle:
                    'TracerPid (Android) / sysctl P_TRACED (iOS) + platform check.',
              ),
              _tile(
                title: 'Screen is being captured',
                value: isScreenCaptured,
                icon: Icons.cast,
                subtitle: 'Screen recording or mirroring active.',
              ),
              if (Platform.isAndroid) ...[
                _tile(
                  title: 'Developer mode enabled',
                  value: isDeveloperMode,
                  icon: Icons.developer_mode,
                  subtitle:
                      'Android-only. Developer options expose debug surfaces.',
                ),
                _tile(
                  title: 'App on external storage',
                  value: isExternalStorage,
                  icon: Icons.sd_storage,
                  subtitle:
                      'Android-only. External storage can be tampered with.',
                ),
              ],

              // --- Live streams ---
              _sectionHeader('Live Streams'),
              _streamTile(
                title: 'VPN status',
                value: isVPN == true ? 'Connected' : 'Disconnected',
                icon: Icons.vpn_lock,
                color: isVPN == true ? Colors.orange : Colors.green,
                subtitle: 'Updates instantly on network change.',
              ),
              _streamTile(
                title: 'Screen recording stream',
                value: _screenCaptureActive ? 'Active' : 'Inactive',
                icon: Icons.screen_search_desktop,
                color: _screenCaptureActive ? Colors.red : Colors.green,
                subtitle: 'Real-time recording/mirroring detection.',
              ),
              _streamTile(
                title: 'Screenshots taken',
                value: '$_screenshotCount',
                icon: Icons.screenshot_monitor,
                color: _screenshotCount > 0 ? Colors.orange : Colors.blueGrey,
                subtitle: Platform.isAndroid
                    ? 'Android 34+: no permission. 24–33: needs READ_MEDIA_IMAGES.'
                    : 'iOS: no permission needed.',
              ),

              // --- Actions ---
              _sectionHeader('Actions'),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 12),
                child: Wrap(
                  spacing: 8,
                  runSpacing: 8,
                  children: [
                    ElevatedButton.icon(
                      onPressed: () =>
                          DeviceSafetyInfo.blockScreenshots(block: true),
                      icon: const Icon(Icons.screen_lock_portrait),
                      label: const Text('Block Screenshots'),
                    ),
                    ElevatedButton.icon(
                      onPressed: () =>
                          DeviceSafetyInfo.blockScreenshots(block: false),
                      icon: const Icon(Icons.screenshot),
                      label: const Text('Allow Screenshots'),
                    ),
                    ElevatedButton.icon(
                      onPressed: () => DeviceSafetyInfo.setRecentsOverlay(
                          argbColor: 0xFF1A1A2E),
                      icon: const Icon(Icons.blur_on),
                      label: const Text('Enable Recents Overlay'),
                    ),
                    ElevatedButton.icon(
                      onPressed: () => DeviceSafetyInfo.clearRecentsOverlay(),
                      icon: const Icon(Icons.blur_off),
                      label: const Text('Clear Recents Overlay'),
                    ),
                    if (Platform.isAndroid) ...[
                      ElevatedButton.icon(
                        onPressed: () => DeviceSafetyInfo.hideMenu(hide: true),
                        icon: const Icon(Icons.visibility_off),
                        label: const Text('Hide in Recents'),
                      ),
                      ElevatedButton.icon(
                        onPressed: () => DeviceSafetyInfo.hideMenu(hide: false),
                        icon: const Icon(Icons.visibility),
                        label: const Text('Show in Recents'),
                      ),
                    ],
                    ElevatedButton.icon(
                      onPressed: () => _confirm(
                        'This will exit the app if a hooking framework is detected.',
                        () => DeviceSafetyInfo.checkHooked(
                            exitProcessIfTrue: true),
                      ),
                      icon: const Icon(Icons.exit_to_app),
                      label: const Text('Check Hooked & Exit'),
                      style: ElevatedButton.styleFrom(
                          foregroundColor: Colors.orange.shade800),
                    ),
                    ElevatedButton.icon(
                      onPressed: () => _confirm(
                        'This will attempt to uninstall the app if hooking is detected.',
                        () =>
                            DeviceSafetyInfo.checkHooked(uninstallIfTrue: true),
                      ),
                      icon: const Icon(Icons.delete_forever),
                      label: const Text('Check Hooked & Uninstall'),
                      style: ElevatedButton.styleFrom(
                          foregroundColor: Colors.red.shade800),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _loading ? null : _refreshAll,
        icon: const Icon(Icons.search),
        label: const Text('Re-check'),
      ),
    );
  }
}
33
likes
140
points
1.89k
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Device Safety Info Flutter Plugin used for checking JailBreak, Rooted Device, Emulator/Simulator, External storage, VPN Detector, Application Update Checker and Screen Lock.

Repository (GitHub)
View/report issues

Topics

#security #rooted #jailbreak #safety #anti-fraud

License

MIT (license)

Dependencies

connectivity_plus, ffi, flutter, http, package_info_plus

More

Packages that depend on device_safety_info

Packages that implement device_safety_info