mm_device_security 0.0.1 copy "mm_device_security: ^0.0.1" to clipboard
mm_device_security: ^0.0.1 copied to clipboard

Detecta root, jailbreak, emulador/simulador y modo desarrollador en Android e iOS.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mm_device_security/mm_device_security.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'MM Device Security',
      theme: ThemeData(
        useMaterial3: true,
        brightness: Brightness.light,
        scaffoldBackgroundColor: const Color(0xFFF5F7FA),
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2563EB),
        ),
      ),
      home: const SecurityPage(),
    );
  }
}

class SecurityPage extends StatefulWidget {
  const SecurityPage({super.key});

  @override
  State<SecurityPage> createState() => _SecurityPageState();
}

class _SecurityPageState extends State<SecurityPage> {
  final _plugin = MmDeviceSecurity();

  DeviceSecurityResult? _resultado;
  String? _error;
  bool _cargando = false;

  @override
  void initState() {
    super.initState();
    _consultar();
  }

  Future<void> _consultar() async {
    setState(() {
      _cargando = true;
      _error = null;
    });

    try {
      final resultado = await _plugin.getSecuritySummary();

      debugPrint('Resultado: $resultado');

      if (!mounted) return;

      setState(() {
        _resultado = resultado;
      });
    } on MissingPluginException catch (error) {
      if (!mounted) return;

      setState(() {
        _resultado = const DeviceSecurityResult();
        _error = 'Canal no registrado: $error';
      });
    } on PlatformException catch (error) {
      if (!mounted) return;

      setState(() {
        _error = 'Error nativo: $error';
      });
    } catch (error) {
      if (!mounted) return;

      setState(() {
        _error = 'Error inesperado: $error';
      });
    } finally {
      if (mounted) {
        setState(() {
          _cargando = false;
        });
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final resultado = _resultado;

    final bloqueado = resultado?.isBlocked ?? false;
    final seguro = resultado?.isSecure ?? false;

    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'MM Device Security',
          style: TextStyle(fontWeight: FontWeight.w700),
        ),
        centerTitle: false,
      ),
      body: RefreshIndicator(
        onRefresh: _consultar,
        child: ListView(
          physics: const AlwaysScrollableScrollPhysics(),
          padding: const EdgeInsets.fromLTRB(20, 16, 20, 32),
          children: [
            _SecurityHeader(
              resultado: resultado,
              seguro: seguro,
              bloqueado: bloqueado,
            ),

            const SizedBox(height: 24),

            if (_error != null)
              _ErrorCard(
                message: _error!,
                onRetry: _consultar,
              ),

            if (resultado != null) ...[
              const _SectionTitle(
                title: 'Diagnóstico',
                subtitle: 'Estado de seguridad del dispositivo',
              ),

              const SizedBox(height: 12),

              _SecurityCard(
                icon: Icons.security_rounded,
                title: 'Root / Jailbreak',
                description:
                    'Comprueba si el dispositivo presenta privilegios elevados.',
                value: resultado.isRooted,
              ),

              _SecurityCard(
                icon: Icons.devices_rounded,
                title: 'Emulador / Simulador',
                description:
                    'Detecta si la aplicación está ejecutándose en un entorno virtual.',
                value: resultado.isEmulator,
              ),

              _SecurityCard(
                icon: Icons.developer_mode_rounded,
                title: 'Modo desarrollador',
                description:
                    'Comprueba si el modo desarrollador está habilitado.',
                value: resultado.isDeveloperMode,
              ),

              _SecurityCard(
                icon: Icons.science_rounded,
                title: 'Firebase Test Lab',
                description:
                    'Detecta ejecución dentro de Firebase Test Lab.',
                value: resultado.isTestLab,
              ),

              _SecurityCard(
                icon: Icons.apple_rounded,
                title: 'TestFlight',
                description:
                    'Indica si la aplicación está instalada mediante TestFlight.',
                value: resultado.isTestFlight,
              ),

              const SizedBox(height: 20),

              const _SectionTitle(
                title: 'Resultado',
                subtitle: 'Evaluación general del dispositivo',
              ),

              const SizedBox(height: 12),

              _ResultCard(
                blocked: bloqueado,
                secure: seguro,
              ),
            ] else if (_cargando)
              const Padding(
                padding: EdgeInsets.symmetric(vertical: 80),
                child: Center(
                  child: CircularProgressIndicator(),
                ),
              ),

            const SizedBox(height: 24),

            FilledButton.icon(
              onPressed: _cargando ? null : _consultar,
              icon: _cargando
                  ? const SizedBox(
                      width: 18,
                      height: 18,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                      ),
                    )
                  : const Icon(Icons.refresh_rounded),
              label: Text(
                _cargando ? 'Consultando...' : 'Volver a consultar',
              ),
            ),

          ],
        ),
      ),
    );
  }
}

class _SecurityHeader extends StatelessWidget {
  const _SecurityHeader({
    required this.resultado,
    required this.seguro,
    required this.bloqueado,
  });

  final DeviceSecurityResult? resultado;
  final bool seguro;
  final bool bloqueado;

  @override
  Widget build(BuildContext context) {
    final color = bloqueado
        ? const Color(0xFFDC2626)
        : seguro
            ? const Color(0xFF16A34A)
            : const Color(0xFF64748B);

    final icon = bloqueado
        ? Icons.warning_rounded
        : seguro
            ? Icons.verified_user_rounded
            : Icons.security_rounded;

    final title = bloqueado
        ? 'Dispositivo no seguro'
        : seguro
            ? 'Dispositivo seguro'
            : 'Sin diagnóstico';

    final subtitle = bloqueado
        ? 'Se detectó una condición que puede comprometer la seguridad.'
        : seguro
            ? 'No se detectaron condiciones de riesgo.'
            : 'Ejecuta el diagnóstico para comprobar el dispositivo.';

    return Container(
      padding: const EdgeInsets.all(22),
      decoration: BoxDecoration(
        color: color,
        borderRadius: BorderRadius.circular(24),
        boxShadow: [
          BoxShadow(
            color: color.withValues(alpha: 0.20),
            blurRadius: 20,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Row(
        children: [
          Container(
            width: 56,
            height: 56,
            decoration: BoxDecoration(
              color: Colors.white.withValues(alpha: 0.18),
              shape: BoxShape.circle,
            ),
            child: Icon(
              icon,
              color: Colors.white,
              size: 30,
            ),
          ),
          const SizedBox(width: 16),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 20,
                    fontWeight: FontWeight.w800,
                  ),
                ),
                const SizedBox(height: 6),
                Text(
                  subtitle,
                  style: TextStyle(
                    color: Colors.white.withValues(alpha: 0.88),
                    fontSize: 13,
                    height: 1.4,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _SectionTitle extends StatelessWidget {
  const _SectionTitle({
    required this.title,
    required this.subtitle,
  });

  final String title;
  final String subtitle;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: const TextStyle(
            fontSize: 18,
            fontWeight: FontWeight.w800,
            color: Color(0xFF172033),
          ),
        ),
        const SizedBox(height: 4),
        Text(
          subtitle,
          style: const TextStyle(
            fontSize: 13,
            color: Color(0xFF697386),
          ),
        ),
      ],
    );
  }
}

class _SecurityCard extends StatelessWidget {
  const _SecurityCard({
    required this.icon,
    required this.title,
    required this.description,
    required this.value,
  });

  final IconData icon;
  final String title;
  final String description;
  final bool value;

  @override
  Widget build(BuildContext context) {
    final statusColor = value
        ? const Color(0xFFDC2626)
        : const Color(0xFF16A34A);

    return Container(
      margin: const EdgeInsets.only(bottom: 10),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(18),
        border: Border.all(
          color: const Color(0xFFE5E7EB),
        ),
      ),
      child: Row(
        children: [
          Container(
            width: 44,
            height: 44,
            decoration: BoxDecoration(
              color: const Color(0xFFF1F5F9),
              borderRadius: BorderRadius.circular(14),
            ),
            child: Icon(
              icon,
              color: statusColor,
            ),
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: const TextStyle(
                    fontWeight: FontWeight.w700,
                    fontSize: 15,
                    color: Color(0xFF172033),
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  description,
                  style: const TextStyle(
                    fontSize: 12,
                    height: 1.35,
                    color: Color(0xFF697386),
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(width: 12),
          _StatusBadge(value: value),
        ],
      ),
    );
  }
}

class _StatusBadge extends StatelessWidget {
  const _StatusBadge({
    required this.value,
  });

  final bool value;

  @override
  Widget build(BuildContext context) {
    final color = value
        ? const Color(0xFFDC2626)
        : const Color(0xFF16A34A);

    return Container(
      padding: const EdgeInsets.symmetric(
        horizontal: 10,
        vertical: 6,
      ),
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.10),
        borderRadius: BorderRadius.circular(20),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(
            value
                ? Icons.warning_rounded
                : Icons.check_circle_rounded,
            size: 15,
            color: color,
          ),
          const SizedBox(width: 5),
          Text(
            value ? 'SÍ' : 'NO',
            style: TextStyle(
              fontSize: 11,
              fontWeight: FontWeight.w800,
              color: color,
            ),
          ),
        ],
      ),
    );
  }
}

class _ResultCard extends StatelessWidget {
  const _ResultCard({
    required this.blocked,
    required this.secure,
  });

  final bool blocked;
  final bool secure;

  @override
  Widget build(BuildContext context) {
    final color = blocked
        ? const Color(0xFFDC2626)
        : const Color(0xFF16A34A);

    return Container(
      padding: const EdgeInsets.all(18),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(20),
        border: Border.all(
          color: color.withValues(alpha: 0.25),
        ),
      ),
      child: Row(
        children: [
          Icon(
            blocked
                ? Icons.block_rounded
                : Icons.check_circle_rounded,
            color: color,
            size: 30,
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  blocked ? 'Acceso restringido' : 'Acceso permitido',
                  style: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.w800,
                    color: color,
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  blocked
                      ? 'El dispositivo cumple una condición de bloqueo.'
                      : 'El dispositivo cumple las condiciones de seguridad.',
                  style: const TextStyle(
                    fontSize: 12,
                    color: Color(0xFF697386),
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _ErrorCard extends StatelessWidget {
  const _ErrorCard({
    required this.message,
    required this.onRetry,
  });

  final String message;
  final VoidCallback onRetry;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(bottom: 20),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: const Color(0xFFFFF7ED),
        borderRadius: BorderRadius.circular(16),
        border: Border.all(
          color: const Color(0xFFFED7AA),
        ),
      ),
      child: Row(
        children: [
          const Icon(
            Icons.error_outline_rounded,
            color: Color(0xFFEA580C),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Text(
              message,
              style: const TextStyle(
                fontSize: 12,
                color: Color(0xFF9A3412),
              ),
            ),
          ),
          IconButton(
            onPressed: onRetry,
            icon: const Icon(Icons.refresh_rounded),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
73
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

Detecta root, jailbreak, emulador/simulador y modo desarrollador en Android e iOS.

Repository (GitHub)
View/report issues

Topics

#security #root #jailbreak #emulator #flutter-plugin

License

BSD-3-Clause (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on mm_device_security

Packages that implement mm_device_security