stopou_blocker 0.1.5
stopou_blocker: ^0.1.5 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;
});
}
void _showAccessibilityInstructions() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('⚠️ Permissão de Acessibilidade'),
content: const SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Se o Android mostrar "permissão restrita", siga estes passos:',
style: TextStyle(fontWeight: FontWeight.bold),
),
SizedBox(height: 12),
Text('1. Nas configurações de Acessibilidade, encontre "Stopou"'),
SizedBox(height: 8),
Text('2. Toque em "Stopou" para abrir as configurações'),
SizedBox(height: 8),
Text('3. Se aparecer aviso de "permissão restrita", toque em "Aprenda a conceder acesso"'),
SizedBox(height: 8),
Text('4. Ative o toggle para "Usar Stopou"'),
SizedBox(height: 8),
Text('5. Confirme tocando em "OK" quando perguntado'),
SizedBox(height: 12),
Text(
'ℹ️ O Stopou é seguro: apenas detecta palavras específicas e exibe alertas. Não coleta dados pessoais.',
style: TextStyle(fontStyle: FontStyle.italic),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Entendi'),
),
ElevatedButton(
onPressed: () async {
Navigator.of(context).pop();
await StopouBlocker.openAccessibilitySettings();
},
child: const Text('Abrir Configurações'),
),
],
),
);
}
@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) {
if (!ok) {
_showAccessibilityInstructions();
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Permissão Acessibilidade: ✓')),
);
}
}
},
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),
),
),
),
),
),
],
),
),
);
}
}