stopou_blocker 0.1.0
stopou_blocker: ^0.1.0 copied to clipboard
Plugin do Stopou para bloqueio por VPN local (preparado para estratégias futuras).
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:stopou_blocker/stopou_blocker.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Stopou Blocker Demo',
theme: ThemeData(useMaterial3: true),
home: const DemoPage(),
);
}
}
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
final _log = <String>[];
bool _vpnRunning = false;
bool _keywordBlockerRunning = false;
bool _hasVpnPermission = false;
bool _hasAccessibilityPermission = false;
bool _hasNotificationPermission = false;
@override
void initState() {
super.initState();
// ✅ Assina o stream de eventos do plugin.
StopouBlocker.events.listen((e) {
setState(() {
_log.add(
"${e.ts.toIso8601String()} ${e.protocol.padRight(5)} ${e.host} ${e.appPackage ?? ''}",
);
});
});
// ✅ Verifica status inicial
_updateStatus();
}
Future<void> _updateStatus() async {
final vpnRunning = await StopouBlocker.isVpnRunning();
final keywordRunning = await StopouBlocker.isKeywordBlockerRunning();
final hasVpn = await StopouBlocker.hasVpnPermission();
final hasAccessibility = await StopouBlocker.hasAccessibilityPermission();
final hasNotification = await StopouBlocker.hasNotificationPermission();
setState(() {
_vpnRunning = vpnRunning;
_keywordBlockerRunning = keywordRunning;
_hasVpnPermission = hasVpn;
_hasAccessibilityPermission = hasAccessibility;
_hasNotificationPermission = hasNotification;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Stopou Blocker Demo'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _updateStatus,
tooltip: 'Atualizar Status',
),
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
// Status Section
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('📊 Status dos Serviços',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
Row(
children: [
Icon(
_vpnRunning ? Icons.vpn_lock : Icons.vpn_lock_outlined,
color: _vpnRunning ? Colors.green : Colors.grey,
),
const SizedBox(width: 8),
Text('VPN: ${_vpnRunning ? "Ativo" : "Inativo"}'),
],
),
const SizedBox(height: 8),
Row(
children: [
Icon(
_keywordBlockerRunning ? Icons.accessibility : Icons.accessibility_outlined,
color: _keywordBlockerRunning ? Colors.green : Colors.grey,
),
const SizedBox(width: 8),
Text('Bloqueador Keywords: ${_keywordBlockerRunning ? "Ativo" : "Inativo"}'),
],
),
],
),
),
),
const SizedBox(height: 16),
// Permissions Section
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('🔑 Permissões',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: () async {
final ok = await StopouBlocker.requestPermission();
await _updateStatus();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Permissão VPN: $ok')),
);
}
},
icon: Icon(_hasVpnPermission ? Icons.check : Icons.vpn_key),
label: Text('VPN ${_hasVpnPermission ? "✓" : "✗"}'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () async {
final ok = await StopouBlocker.requestAccessibilityPermission();
await _updateStatus();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Permissão Acessibilidade: $ok')),
);
}
},
icon: Icon(_hasAccessibilityPermission ? Icons.check : Icons.accessibility),
label: Text('Acessibilidade ${_hasAccessibilityPermission ? "✓" : "✗"}'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () async {
final ok = await StopouBlocker.requestNotificationPermission();
await _updateStatus();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Permissão Notificação: $ok')),
);
}
},
icon: Icon(_hasNotificationPermission ? Icons.check : Icons.notifications),
label: Text('Notificações ${_hasNotificationPermission ? "✓" : "✗"}'),
),
],
),
),
),
const SizedBox(height: 16),
// Controls Section
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('🎛️ Controles',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: _vpnRunning ? null : () async {
await StopouBlocker.start(
blocklist: const ['.bet.br', 'exemplo.com'],
logAttempts: true,
dnsServers: const ['1.1.1.1', '8.8.8.8'],
strategies: const [BlockStrategies.vpn],
);
await _updateStatus();
},
icon: const Icon(Icons.play_arrow),
label: const Text('Iniciar VPN'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: _keywordBlockerRunning ? null : () async {
await StopouBlocker.startKeywordBlocker([
'bet',
'casino',
'apostas',
]);
await _updateStatus();
},
icon: const Icon(Icons.block),
label: const Text('Iniciar Bloqueador Keywords'),
),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: (!_vpnRunning && !_keywordBlockerRunning) ? null : () async {
await StopouBlocker.stop();
await StopouBlocker.stopKeywordBlocker();
await _updateStatus();
},
icon: const Icon(Icons.stop),
label: const Text('Parar Tudo'),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red[100]),
),
],
),
),
),
const SizedBox(height: 16),
// Events Log
const Align(
alignment: Alignment.centerLeft,
child: Text('📝 Log de Eventos:', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
),
const SizedBox(height: 8),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
borderRadius: BorderRadius.circular(8),
),
child: _log.isEmpty
? const Center(child: Text('Nenhum evento registrado ainda...'))
: ListView.builder(
itemCount: _log.length,
itemBuilder: (_, i) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Text(
_log[_log.length - 1 - i], // Mais recentes primeiro
style: const TextStyle(fontFamily: 'monospace', fontSize: 12),
),
),
),
),
),
],
),
),
);
}
}