guardix 0.2.0
guardix: ^0.2.0 copied to clipboard
A Flutter plugin for device security checks. Detects root/jailbreak, emulator/simulator, and developer mode on Android and iOS.
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:guardix/guardix.dart';
import 'package:permission_handler/permission_handler.dart';
void main() => runApp(const GuardixExampleApp());
class GuardixExampleApp extends StatelessWidget {
const GuardixExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Guardix Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFFDC2626)),
useMaterial3: true,
),
home: const SecurityCheckScreen(),
);
}
}
class SecurityCheckScreen extends StatefulWidget {
const SecurityCheckScreen({super.key});
@override
State<SecurityCheckScreen> createState() => _SecurityCheckScreenState();
}
class _SecurityCheckScreenState extends State<SecurityCheckScreen> {
DeviceSecurityStatus? _status;
bool? _isMockLocation;
bool _isLoading = true;
String? _error;
bool _locationDenied = false;
@override
void initState() {
super.initState();
_runAllChecks();
}
Future<void> _runAllChecks() async {
await _runSecurityChecks();
await _runMockLocationCheck();
}
Future<void> _runSecurityChecks() async {
setState(() {
_isLoading = true;
_error = null;
});
try {
final status = await Guardix.getSecurityStatus();
if (mounted) {
setState(() {
_status = status;
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_error = e.toString();
_isLoading = false;
});
}
}
}
Future<void> _runMockLocationCheck() async {
setState(() {
_isLoading = true;
// _locationDenied = false;
});
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.deniedForever) {
setState(() => _locationDenied = true);
}
// Actually fetch location
try {
await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
timeLimit: Duration(seconds: 5),
),
);
} catch (e) {
// Location fetch failed — still proceed with security check
// isMockLocation may return false but other checks still run
}
// Now run mock check
try {
final isMock = await Guardix.checkMockLocation(strictMode: false);
if (mounted) {
setState(() {
_isMockLocation = isMock;
_isLoading = false;
});
}
} catch (e) {
if (mounted) {
setState(() {
_error = e.toString();
_isLoading = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F4),
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
titleSpacing: 16,
title: Row(
children: [
Container(
width: 34,
height: 34,
decoration: BoxDecoration(
color: const Color(0xFFFEE2E2),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.shield_outlined,
color: Color(0xFFDC2626),
size: 18,
),
),
const SizedBox(width: 10),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Guardix',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
Text(
'Device security check',
style: TextStyle(
fontSize: 12,
color: Colors.grey,
fontWeight: FontWeight.w400,
),
),
],
),
],
),
),
body: RefreshIndicator(
onRefresh: _runAllChecks,
child: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildBanner(),
const SizedBox(height: 20),
const Text(
'SECURITY CHECKS',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Colors.grey,
letterSpacing: 1,
),
),
const SizedBox(height: 8),
_buildCheckCard(
icon: Icons.code,
label: 'Developer mode',
description: 'USB debugging & developer options',
value: _status?.isDeveloperMode,
),
const SizedBox(height: 8),
_buildCheckCard(
icon: Icons.phone_android_outlined,
label: 'Emulator / simulator',
description: 'Running on virtual device',
value: _status?.isEmulator,
),
const SizedBox(height: 8),
_buildCheckCard(
icon: Icons.lock_open_outlined,
label: 'Root / jailbreak',
description: 'System integrity',
value: _status?.isRootedOrJailbroken,
),
const SizedBox(height: 8),
_buildCheckCard(
icon: Icons.location_on_outlined,
label: 'Mock location',
description: 'GPS location integrity',
value: _isMockLocation,
),
const SizedBox(height: 16),
OutlinedButton.icon(
onPressed: _isLoading ? null : _runAllChecks,
icon: _isLoading
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.refresh, size: 18),
label: const Text('Run checks again'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
],
),
),
);
}
Widget _buildBanner() {
// Location permanently denied — special state
if (_locationDenied) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xFFFFF7ED),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFFFED7AA)),
),
child: Row(
children: [
const Icon(
Icons.location_off_outlined,
color: Color(0xFFEA580C),
size: 32,
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Location permission required',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Color(0xFFEA580C),
),
),
const SizedBox(height: 2),
const Text(
'Enable in Settings to check mock location',
style: TextStyle(fontSize: 12, color: Color(0xFFC2410C)),
),
const SizedBox(height: 8),
GestureDetector(
onTap: () => openAppSettings(),
child: const Text(
'Open Settings →',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFFEA580C),
decoration: TextDecoration.underline,
),
),
),
],
),
),
],
),
);
}
final isClean =
_status != null &&
!(_status!.isDeveloperMode ||
_status!.isEmulator ||
_status!.isRootedOrJailbroken) &&
_isMockLocation == false;
Color bg = _isLoading
? Colors.grey.shade100
: isClean
? const Color(0xFFDCFCE7)
: const Color(0xFFFEE2E2);
Color fg = _isLoading
? Colors.grey
: isClean
? const Color(0xFF15803D)
: const Color(0xFFDC2626);
IconData ic = _isLoading
? Icons.shield_outlined
: isClean
? Icons.shield
: Icons.shield_outlined;
String title = _isLoading
? 'Checking device…'
: isClean
? 'Device looks clean'
: 'Security issue detected';
String sub = _isLoading
? 'Running security checks'
: isClean
? 'No security issues detected'
: _error ?? 'One or more checks failed';
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: bg,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: fg.withValues(alpha: 0.25)),
),
child: Row(
children: [
Icon(ic, color: fg, size: 32),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: fg,
),
),
const SizedBox(height: 2),
Text(
sub,
style: TextStyle(
fontSize: 12,
color: fg.withValues(alpha: 0.7),
),
),
],
),
),
],
),
);
}
Widget _buildCheckCard({
required IconData icon,
required String label,
required String description,
required bool? value,
}) {
final isLoading = value == null;
final isWarn = value == true;
Color iconBg = isLoading
? Colors.grey.shade100
: isWarn
? const Color(0xFFFEE2E2)
: const Color(0xFFDCFCE7);
Color iconFg = isLoading
? Colors.grey
: isWarn
? const Color(0xFFDC2626)
: const Color(0xFF15803D);
String badge = isLoading
? '…'
: isWarn
? 'Detected'
: 'Clear';
Color badgeBg = isLoading
? Colors.grey.shade100
: isWarn
? const Color(0xFFFEE2E2)
: const Color(0xFFDCFCE7);
Color badgeFg = isLoading
? Colors.grey
: isWarn
? const Color(0xFFDC2626)
: const Color(0xFF15803D);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.black.withValues(alpha: 0.06)),
),
child: Row(
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: iconBg,
borderRadius: BorderRadius.circular(8),
),
child: Icon(icon, color: iconFg, size: 20),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 2),
Text(
description,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: badgeBg,
borderRadius: BorderRadius.circular(999),
),
child: Text(
badge,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: badgeFg,
),
),
),
],
),
);
}
}