device_safety_info 1.0.3 copy "device_safety_info: ^1.0.3" to clipboard
device_safety_info: ^1.0.3 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/foundation.dart';
import 'package:flutter/material.dart';
import 'package:device_safety_info/device_safety_info.dart';
import 'package:device_safety_info/new_version_check.dart';
import 'package:device_safety_info/vpn_check.dart';
import 'package:device_safety_info/vpn_state.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> {
  bool? isRootedDevice;
  bool? isScreenLock;
  bool? isRealDevice;
  bool? isExternalStorage;
  bool? isDeveloperMode;
  bool? isVPN;
  bool? isInstalledFromStore;

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

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

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

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

  Future<void> _refreshAll() async {
    if (!mounted) return;
    setState(() {
      _loading = true;
    });

    try {
      final futures = <Future<Object>>[
        DeviceSafetyInfo.isRootedDevice,
        DeviceSafetyInfo.isScreenLock,
        DeviceSafetyInfo.isRealDevice,
      ];

      if (Platform.isAndroid) {
        futures.addAll([
          DeviceSafetyInfo.isExternalStorage,
          DeviceSafetyInfo.isDeveloperMode,
        ]);
      }

      futures.add(DeviceSafetyInfo.isInstalledFromStore);

      final results = await Future.wait(futures);

      int idx = 0;
      setState(() {
        isRootedDevice = _boolFrom(results[idx++]);
        isScreenLock = _boolFrom(results[idx++]);
        isRealDevice = _boolFrom(results[idx++]);

        if (Platform.isAndroid) {
          isExternalStorage = _boolFrom(results[idx++]);
          isDeveloperMode = _boolFrom(results[idx++]);
        } else {
          isExternalStorage = null;
          isDeveloperMode = null;
        }

        isInstalledFromStore = _boolFrom(results[idx++]);
      });
    } catch (e) {
      if (kDebugMode) debugPrint('Error fetching device info: $e');
      if (mounted) {
        setState(() {
          isRootedDevice ??= false;
          isScreenLock ??= false;
          isRealDevice ??= true;
          isExternalStorage ??= (Platform.isAndroid ? false : null);
          isDeveloperMode ??= (Platform.isAndroid ? false : null);
          isInstalledFromStore ??= false;
        });
      }
    } finally {
      if (mounted) {
        setState(() {
          _loading = false;
        });
      }
    }
  }

  bool _boolFrom(Object? o) {
    if (o is bool) return o;
    if (o is int) return o != 0;
    if (o is String) return o.toLowerCase() == 'true';
    return false;
  }

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

  Widget _statusTile({
    required String title,
    required bool? value,
    String? subtitle,
    IconData? leadingIcon,
  }) {
    final bool unknown = value == null;
    final bool positive = value == true;

    final Color bgColor;
    final IconData displayIcon;
    final String label;
    if (unknown) {
      bgColor = Theme.of(context).colorScheme.surfaceContainerHighest;
      displayIcon = Icons.help_outline;
      label = 'Unknown';
    } else if (positive) {
      bgColor = Colors.green.shade50;
      displayIcon = leadingIcon ?? Icons.check_circle;
      label = 'Yes';
    } else {
      bgColor = Colors.red.shade50;
      displayIcon = leadingIcon ?? Icons.cancel;
      label = 'No';
    }

    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: unknown ? Colors.orange : (positive ? Colors.green : Colors.red)),
        ),
        title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
        subtitle: subtitle != null ? Text(subtitle) : null,
        trailing: AnimatedSwitcher(
          duration: const Duration(milliseconds: 300),
          transitionBuilder: (child, anim) => ScaleTransition(scale: anim, child: child),
          child: unknown
              ? Text('—', key: const ValueKey('unknown'))
              : Chip(
                  key: ValueKey(label),
                  side: BorderSide.none,
                  label: Text(label),
                  backgroundColor: positive ? Colors.green.shade100 : Colors.red.shade100,
                ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Device Safety Info'),
        actions: [
          IconButton(
            tooltip: 'Refresh',
            onPressed: _loading ? null : _refreshAll,
            icon: _loading ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.refresh),
          ),
          IconButton(
            tooltip: 'Version Check',
            onPressed: _checkAppVersion,
            icon: const Icon(Icons.system_update),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _refreshAll,
        icon: const Icon(Icons.search),
        label: const Text('Re-check'),
      ),
      body: SafeArea(
        child: RefreshIndicator(
          onRefresh: _refreshAll,
          child: ListView(
            padding: const EdgeInsets.symmetric(vertical: 12),
            children: [
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16.0),
                child: Row(
                  children: [
                    Expanded(
                      child: Text('Summary', style: theme.textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold)),
                    ),
                    const SizedBox(width: 8),
                    FilledButton.icon(
                      onPressed: _refreshAll,
                      icon: const Icon(Icons.refresh),
                      label: const Text('Refresh'),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 12),
              _statusTile(
                title: 'Device is rooted / jailbroken',
                value: isRootedDevice,
                leadingIcon: Icons.security,
                subtitle: 'Root/jailbreak increases risk — block if required.',
              ),
              _statusTile(
                title: 'Screen lock enabled',
                value: isScreenLock,
                leadingIcon: Icons.lock,
                subtitle: 'Secure lockscreen is recommended.',
              ),
              _statusTile(
                title: 'Real device (not emulator)',
                value: isRealDevice,
                leadingIcon: Icons.phone_android,
                subtitle: 'Emulators are often insecure / for testing only.',
              ),
              if (Platform.isAndroid) ...[
                _statusTile(
                  title: 'External storage available',
                  value: isExternalStorage,
                  leadingIcon: Icons.sd_storage,
                  subtitle: 'External storage access can be risky depending on app usage.',
                ),
                _statusTile(
                  title: 'Developer mode enabled',
                  value: isDeveloperMode,
                  leadingIcon: Icons.developer_mode,
                  subtitle: 'Developer options may expose debugging surfaces.',
                ),
              ],
              _statusTile(
                title: 'VPN connected',
                value: isVPN,
                leadingIcon: Icons.vpn_lock,
                subtitle: 'A VPN is active (useful for network security or suspicious traffic).',
              ),
              _statusTile(
                title: 'Installed from store',
                value: isInstalledFromStore,
                leadingIcon: Icons.storefront,
                subtitle: 'Install source: store vs sideload.',
              ),
              const SizedBox(height: 59),
            ],
          ),
        ),
      ),
    );
  }
}
30
likes
140
points
1.98k
downloads

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

Documentation

API reference

License

MIT (license)

Dependencies

connectivity_plus, flutter, http, package_info_plus

More

Packages that depend on device_safety_info

Packages that implement device_safety_info