brux88_beacon 0.1.6 copy "brux88_beacon: ^0.1.6" to clipboard
brux88_beacon: ^0.1.6 copied to clipboard

A Flutter plugin for handling BLE beacons with background detection and reliable monitoring.

example/lib/main.dart

// example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:brux88_beacon/brux88_beacon.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Brux88 Beacon Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: BeaconControlPage(),
    );
  }
}

class BeaconControlPage extends StatefulWidget {
  @override
  _BeaconControlPageState createState() => _BeaconControlPageState();
}

class _BeaconControlPageState extends State<BeaconControlPage> {
  final BeaconManager _beaconManager = BeaconManager();

  bool _isInitialized = false;
  bool _isForegroundMonitoringRunning = false;
  bool _isBackgroundServiceRunning = false;
  bool _isBackgroundServiceEnabled = false;
  List<Beacon> _detectedBeacons = [];
  MonitoringState _monitoringState = MonitoringState.unknown;
  List<String> _logs = [];
  bool _autoRestartEnabled = false;

  @override
  void initState() {
    super.initState();
    _initializeBeaconManager();
    _setupListeners();
  }

  Future<void> _initializeBeaconManager() async {
    try {
      final initialized = await _beaconManager.initialize();
      setState(() {
        _isInitialized = initialized;
      });

      if (initialized) {
        await _beaconManager.disableAllAutoRestart();
        // Controlla lo stato attuale
        final autoRestartStatus = await _beaconManager.isAutoRestartEnabled();

        setState(() {
          _isInitialized = initialized;
          _autoRestartEnabled = autoRestartStatus;
        });

        await _updateStatus();
        _showSnackBar(
            'Beacon Manager inizializzato con successo', Colors.green);
      } else {
        _showSnackBar(
            'Errore nell\'inizializzazione del Beacon Manager', Colors.red);
      }
    } catch (e) {
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  void _setupListeners() {
    // Listener per i beacon rilevati
    _beaconManager.beacons.listen((beacons) {
      setState(() {
        _detectedBeacons = beacons;
      });
    });

    // Listener per lo stato di monitoraggio
    _beaconManager.monitoringState.listen((state) {
      setState(() {
        _monitoringState = state;
      });
    });
  }

  Future<void> _toggleAutoRestart() async {
    final newValue = !_autoRestartEnabled;
    final success = await _beaconManager.setAutoRestartEnabled(newValue);

    if (success) {
      setState(() {
        _autoRestartEnabled = newValue;
      });

      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content:
              Text('Auto-restart ${newValue ? "abilitato" : "disabilitato"}'),
        ),
      );
    }
  }

  Future<void> _updateStatus() async {
    try {
      final isForegroundRunning =
          await _beaconManager.isForegroundMonitoringRunning();
      final isBackgroundRunning =
          await _beaconManager.isBackgroundServiceRunning();
      final isBackgroundEnabled =
          await _beaconManager.isBackgroundServiceEnabled();

      setState(() {
        _isForegroundMonitoringRunning = isForegroundRunning;
        _isBackgroundServiceRunning = isBackgroundRunning;
        _isBackgroundServiceEnabled = isBackgroundEnabled;
      });

      // Log dello stato per debug
      print('Status Update:');
      print('  Foreground Monitoring: $isForegroundRunning');
      print('  Background Service Running: $isBackgroundRunning');
      print('  Background Service Enabled: $isBackgroundEnabled');
    } catch (e) {
      _showSnackBar('Errore nell\'aggiornamento dello stato: $e', Colors.red);
    }
  }

  // ===== CONTROLLI FOREGROUND =====

  Future<void> _startForegroundMonitoring() async {
    try {
      // Controlla i permessi prima
      final permissionsMap = await _beaconManager.checkPermissions();
      final locationGranted = permissionsMap['location'] ?? false;

      if (!locationGranted) {
        await _beaconManager.requestPermissions();
      }

      final success = await _beaconManager.startForegroundMonitoringOnly();
      //_setupListeners();
      if (success) {
        _showSnackBar('Monitoraggio foreground avviato', Colors.green);
        await _updateStatus();
      } else {
        _showSnackBar(
            'Errore nell\'avvio del monitoraggio foreground', Colors.red);
      }
    } catch (e) {
      print('Error starting foreground monitoring: $e');
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  Future<void> _stopForegroundMonitoring() async {
    try {
      final success = await _beaconManager.stopForegroundMonitoringOnly();

      if (success) {
        _showSnackBar('Monitoraggio foreground arrestato', Colors.orange);
        await _updateStatus();
      } else {
        _showSnackBar(
            'Errore nell\'arresto del monitoraggio foreground', Colors.red);
      }
    } catch (e) {
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  // ===== CONTROLLI BACKGROUND =====

  Future<void> _startBackgroundService() async {
    try {
      // Opzionalmente, abilita il watchdog solo quando l'utente avvia il servizio
      // await _beaconManager.setWatchdogEnabled(true);
      // Prima controlla i permessi attuali
      final permissionsMap = await _beaconManager.checkPermissions();
      print('Current permissions: $permissionsMap');

      // Verifica se i permessi essenziali sono concessi
      final locationGranted = permissionsMap['location'] ?? false;
      final bluetoothScanGranted = permissionsMap['bluetoothScan'] ?? false;
      final bluetoothConnectGranted =
          permissionsMap['bluetoothConnect'] ?? false;

      if (!locationGranted ||
          !bluetoothScanGranted ||
          !bluetoothConnectGranted) {
        _showSnackBar(
            'Permessi mancanti. Concedi i permessi nelle impostazioni dell\'app.',
            Colors.orange);

        // Prova comunque a richiedere i permessi
        try {
          final permissionsGranted = await _beaconManager.requestPermissions();
          if (!permissionsGranted) {
            _showSnackBar(
                'Permessi non concessi. Impossibile avviare il servizio.',
                Colors.red);
            return;
          }
        } catch (e) {
          print('Error requesting permissions: $e');
          _showSnackBar('Errore nella richiesta dei permessi: $e', Colors.red);
          return;
        }
      }

      // Verifica se il Bluetooth è abilitato
      final bluetoothEnabled = await _beaconManager.isBluetoothEnabled();
      if (!bluetoothEnabled) {
        _showSnackBar(
            'Bluetooth non abilitato. Abilita il Bluetooth per continuare.',
            Colors.red);
        return;
      }

      // Verifica se la localizzazione è abilitata
      final locationEnabled = await _beaconManager.isLocationEnabled();
      if (!locationEnabled) {
        _showSnackBar(
            'Localizzazione non abilitata. Abilita la localizzazione per continuare.',
            Colors.red);
        return;
      }

      // Verifica se la batteria è ottimizzata
      final isBatteryOptimized =
          await _beaconManager.isBatteryOptimizationIgnored();
      if (!isBatteryOptimized) {
        _showSnackBar(
            'Raccomandato: disabilita l\'ottimizzazione batteria per prestazioni migliori',
            Colors.orange);
        await _beaconManager.requestIgnoreBatteryOptimization();
      }

      // Avvia il servizio background
      final success = await _beaconManager.startBackgroundService();

      if (success) {
        _showSnackBar('Servizio background avviato', Colors.green);
        await _updateStatus();
      } else {
        _showSnackBar('Errore nell\'avvio del servizio background', Colors.red);
      }
    } catch (e) {
      print('Error starting background service: $e');
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  Future<void> _stopBackgroundService() async {
    try {
      await _beaconManager.setWatchdogEnabled(false);

      final success = await _beaconManager.stopBackgroundService();

      if (success) {
        _showSnackBar('Servizio background arrestato', Colors.orange);
        await _updateStatus();
      } else {
        _showSnackBar(
            'Errore nell\'arresto del servizio background', Colors.red);
      }
    } catch (e) {
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  Future<void> _restartBackgroundService() async {
    try {
      final success = await _beaconManager.restartBackgroundService();

      if (success) {
        _showSnackBar('Servizio background riavviato', Colors.blue);
        await _updateStatus();
      } else {
        _showSnackBar('Errore nel riavvio del servizio background', Colors.red);
      }
    } catch (e) {
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  // ===== CONTROLLI COMPLETI =====

  Future<void> _startCompleteMonitoring() async {
    try {
      await _beaconManager.requestPermissions();

      final success = await _beaconManager.startCompleteMonitoring();

      if (success) {
        _showSnackBar('Monitoraggio completo avviato (foreground + background)',
            Colors.blue);
        await _updateStatus();
      } else {
        _showSnackBar(
            'Errore nell\'avvio del monitoraggio completo', Colors.red);
      }
    } catch (e) {
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  Future<void> _stopCompleteMonitoring() async {
    try {
      final success = await _beaconManager.stopCompleteMonitoring();

      if (success) {
        _showSnackBar('Monitoraggio completo arrestato', Colors.orange);
        await _updateStatus();
      } else {
        _showSnackBar(
            'Errore nell\'arresto del monitoraggio completo', Colors.red);
      }
    } catch (e) {
      _showSnackBar('Errore: $e', Colors.red);
    }
  }

  // ===== UTILITY E DIAGNOSTICA =====

  Future<void> _checkSystemRequirements() async {
    try {
      // Controlla i requisiti di sistema usando il nuovo metodo dettagliato
      final bluetoothEnabled = await _beaconManager.isBluetoothEnabled();
      final locationEnabled = await _beaconManager.isLocationEnabled();
      final detailedPermissions = await _beaconManager.getDetailedPermissions();

      String message = 'Stato Sistema Completo:\n\n';

      // Stato servizi di sistema
      message += '📶 Sistema:\n';
      message +=
          'Bluetooth: ${bluetoothEnabled ? "✅ Attivo" : "❌ Disattivo"}\n';
      message +=
          'Localizzazione: ${locationEnabled ? "✅ Attiva" : "❌ Disattiva"}\n';
      message +=
          'Android: ${detailedPermissions['androidVersion'] ?? "N/A"}\n\n';

      // Permessi
      message += '🔐 Permessi:\n';
      final locationPerm = detailedPermissions['location'] as bool? ?? false;
      final bluetoothScan =
          detailedPermissions['bluetoothScan'] as bool? ?? false;
      final bluetoothConnect =
          detailedPermissions['bluetoothConnect'] as bool? ?? false;
      final notifications =
          detailedPermissions['notifications'] as bool? ?? false;
      final backgroundLocation =
          detailedPermissions['backgroundLocation'] as bool? ?? false;

      message += 'Localizzazione: ${locationPerm ? "✅" : "❌"}\n';
      message +=
          'Localizzazione Background: ${backgroundLocation ? "✅" : "❌"}\n';
      message += 'Bluetooth Scan: ${bluetoothScan ? "✅" : "❌"}\n';
      message += 'Bluetooth Connect: ${bluetoothConnect ? "✅" : "❌"}\n';
      message += 'Notifiche: ${notifications ? "✅" : "❌"}\n\n';

      // Ottimizzazioni
      message += '🔋 Ottimizzazioni:\n';
      final batteryOptIgnored =
          detailedPermissions['batteryOptimizationIgnored'] as bool? ?? false;
      final canScheduleAlarms =
          detailedPermissions['canScheduleExactAlarms'] as bool? ?? false;

      message += 'Batteria non ottimizzata: ${batteryOptIgnored ? "✅" : "❌"}\n';
      message += 'Allarmi esatti: ${canScheduleAlarms ? "✅" : "❌"}\n\n';

      // Stato servizi
      message += '⚙️ Servizi:\n';
      message +=
          'Background Service: ${_isBackgroundServiceRunning ? "✅ Attivo" : "❌ Inattivo"}\n';
      message +=
          'Auto-start Background: ${_isBackgroundServiceEnabled ? "✅ Abilitato" : "❌ Disabilitato"}\n';
      message +=
          'Monitoring Foreground: ${_isForegroundMonitoringRunning ? "✅ Attivo" : "❌ Inattivo"}\n';

      // Raccomandazioni
      if (!batteryOptIgnored || !backgroundLocation || !locationPerm) {
        message += '\n⚠️ Raccomandazioni:\n';
        if (!batteryOptIgnored) {
          message += '• Disabilita ottimizzazione batteria\n';
        }
        if (!backgroundLocation) {
          message += '• Concedi permesso localizzazione background\n';
        }
        if (!locationPerm) {
          message += '• Concedi permesso localizzazione\n';
        }
      }

      showDialog(
        context: context,
        builder: (context) => AlertDialog(
          title: Row(
            children: [
              Icon(Icons.info_outline, color: Colors.blue),
              SizedBox(width: 8),
              Text('Diagnostica Sistema'),
            ],
          ),
          content: SingleChildScrollView(
            child: Text(
              message,
              style: TextStyle(fontFamily: 'monospace', fontSize: 12),
            ),
          ),
          actions: [
            if (!batteryOptIgnored)
              TextButton(
                onPressed: () async {
                  Navigator.pop(context);
                  await _beaconManager.requestIgnoreBatteryOptimization();
                },
                child: Text('Ottimizza Batteria'),
              ),
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: Text('Chiudi'),
            ),
          ],
        ),
      );
    } catch (e) {
      _showSnackBar('Errore nel controllo requisiti: $e', Colors.red);
    }
  }

  Future<void> _getLogs() async {
    try {
      final logs = await _beaconManager.getLogs();
      setState(() {
        _logs = logs;
      });

      _showLogsDialog();
    } catch (e) {
      _showSnackBar('Errore nel recupero dei log: $e', Colors.red);
    }
  }

  void _showLogsDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Log del sistema'),
          content: Container(
            width: double.maxFinite,
            height: 400,
            child: ListView.builder(
              itemCount: _logs.length,
              itemBuilder: (context, index) {
                return Card(
                  margin: EdgeInsets.symmetric(vertical: 2),
                  child: Padding(
                    padding: EdgeInsets.all(8),
                    child: Text(
                      _logs[index],
                      style: TextStyle(fontSize: 12, fontFamily: 'monospace'),
                    ),
                  ),
                );
              },
            ),
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(context).pop(),
              child: Text('Chiudi'),
            ),
          ],
        );
      },
    );
  }

  void _showSnackBar(String message, Color color) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(message),
        backgroundColor: color,
        duration: Duration(seconds: 3),
      ),
    );
  }

  // ===== UI COMPONENTS =====

  Widget _buildStatusCard() {
    return Card(
      elevation: 4,
      margin: EdgeInsets.all(16),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Stato del Sistema',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            SizedBox(height: 16),
            _buildStatusRow('Inizializzato', _isInitialized),
            Divider(),
            Text('Monitoraggio Foreground (Ranging):',
                style: TextStyle(fontWeight: FontWeight.bold)),
            _buildStatusRow(
                'Foreground Monitoring', _isForegroundMonitoringRunning),
            SizedBox(height: 8),
            Text('Servizio Background:',
                style: TextStyle(fontWeight: FontWeight.bold)),
            _buildStatusRow(
                'Background Service Running', _isBackgroundServiceRunning),
            _buildStatusRow(
                'Background Service Abilitato', _isBackgroundServiceEnabled),
            Divider(),
            Text(
                'Stato Regione: ${_monitoringState.toString().split('.').last.toUpperCase()}'),
            Text('Beacon Rilevati: ${_detectedBeacons.length}'),
            if (_isForegroundMonitoringRunning && _isBackgroundServiceRunning)
              Container(
                padding: EdgeInsets.all(8),
                margin: EdgeInsets.only(top: 8),
                decoration: BoxDecoration(
                  color: Colors.blue.withOpacity(0.1),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  children: [
                    Icon(Icons.info, color: Colors.blue, size: 16),
                    SizedBox(width: 8),
                    Expanded(
                        child: Text('Monitoraggio completo attivo',
                            style: TextStyle(color: Colors.blue))),
                  ],
                ),
              ),
          ],
        ),
      ),
    );
  }

  Widget _buildStatusRow(String label, bool status) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: 4),
      child: Row(
        children: [
          Icon(
            status ? Icons.check_circle : Icons.cancel,
            color: status ? Colors.green : Colors.red,
            size: 20,
          ),
          SizedBox(width: 8),
          Text(label),
        ],
      ),
    );
  }

  Widget _buildForegroundControls() {
    return Card(
      elevation: 4,
      margin: EdgeInsets.all(16),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Controlli Monitoraggio Foreground',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            SizedBox(height: 8),
            Text(
              'Il monitoraggio foreground rileva beacon in tempo reale quando l\'app è aperta',
              style: TextStyle(color: Colors.grey[600], fontSize: 12),
            ),
            SizedBox(height: 16),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isInitialized && !_isForegroundMonitoringRunning
                        ? _startForegroundMonitoring
                        : null,
                    icon: Icon(Icons.radar),
                    label: Text('Avvia Foreground'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.purple,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isInitialized && _isForegroundMonitoringRunning
                        ? _stopForegroundMonitoring
                        : null,
                    icon: Icon(Icons.stop),
                    label: Text('Ferma Foreground'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.orange,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildBackgroundControls() {
    return Card(
      elevation: 4,
      margin: EdgeInsets.all(16),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Controlli Servizio Background',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            SizedBox(height: 8),
            Text(
              'Il servizio background rileva beacon anche quando l\'app è chiusa',
              style: TextStyle(color: Colors.grey[600], fontSize: 12),
            ),
            SizedBox(height: 16),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isInitialized && !_isBackgroundServiceRunning
                        ? _startBackgroundService
                        : null,
                    icon: Icon(Icons.play_arrow),
                    label: Text('Avvia Background'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isInitialized && _isBackgroundServiceRunning
                        ? _stopBackgroundService
                        : null,
                    icon: Icon(Icons.stop),
                    label: Text('Arresta Background'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
            SizedBox(height: 8),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton.icon(
                onPressed: _isInitialized ? _restartBackgroundService : null,
                icon: Icon(Icons.refresh),
                label: Text('Riavvia Servizio Background'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.blue,
                  foregroundColor: Colors.white,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildCompleteControls() {
    return Card(
      elevation: 4,
      margin: EdgeInsets.all(16),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Controlli Monitoraggio Completo',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            SizedBox(height: 8),
            Text(
              'Avvia/ferma sia il monitoraggio foreground che il servizio background insieme',
              style: TextStyle(color: Colors.grey[600], fontSize: 12),
            ),
            SizedBox(height: 16),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isInitialized &&
                            !(_isForegroundMonitoringRunning &&
                                _isBackgroundServiceRunning)
                        ? _startCompleteMonitoring
                        : null,
                    icon: Icon(Icons.play_circle_fill),
                    label: Text('Avvia Tutto'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.indigo,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: _isInitialized &&
                            (_isForegroundMonitoringRunning ||
                                _isBackgroundServiceRunning)
                        ? _stopCompleteMonitoring
                        : null,
                    icon: Icon(Icons.stop_circle),
                    label: Text('Ferma Tutto'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.brown,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildBeaconsList() {
    return Card(
      elevation: 4,
      margin: EdgeInsets.all(16),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Beacon Rilevati (${_detectedBeacons.length})',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            SizedBox(height: 16),
            if (_detectedBeacons.isEmpty)
              Container(
                padding: EdgeInsets.all(20),
                child: Center(
                  child: Column(
                    children: [
                      Icon(Icons.bluetooth_searching,
                          size: 50, color: Colors.grey),
                      SizedBox(height: 8),
                      Text(
                        'Nessun beacon rilevato',
                        style: TextStyle(color: Colors.grey),
                      ),
                      if (!_isForegroundMonitoringRunning &&
                          !_isBackgroundServiceRunning)
                        Text(
                          'Avvia il monitoraggio per rilevare beacon',
                          style: TextStyle(color: Colors.grey, fontSize: 12),
                        ),
                    ],
                  ),
                ),
              )
            else
              ...(_detectedBeacons
                  .map((beacon) => _buildBeaconCard(beacon))
                  .toList()),
          ],
        ),
      ),
    );
  }

  Widget _buildBeaconCard(Beacon beacon) {
    return Card(
      margin: EdgeInsets.symmetric(vertical: 4),
      child: ListTile(
        leading: Icon(Icons.bluetooth, color: Colors.blue),
        title: Text('UUID: ${beacon.uuid}'),
        subtitle: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            if (beacon.major != null) Text('Major: ${beacon.major}'),
            if (beacon.minor != null) Text('Minor: ${beacon.minor}'),
            Text('Distanza: ${beacon.distance.toStringAsFixed(2)}m'),
            Text('RSSI: ${beacon.rssi} dBm'),
          ],
        ),
        trailing: Container(
          padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
          decoration: BoxDecoration(
            color: _getDistanceColor(beacon.distance),
            borderRadius: BorderRadius.circular(12),
          ),
          child: Text(
            _getDistanceText(beacon.distance),
            style: TextStyle(color: Colors.white, fontSize: 12),
          ),
        ),
      ),
    );
  }

  Color _getDistanceColor(double distance) {
    if (distance < 1.0) return Colors.green;
    if (distance < 3.0) return Colors.orange;
    return Colors.red;
  }

  String _getDistanceText(double distance) {
    if (distance < 1.0) return 'VICINO';
    if (distance < 3.0) return 'MEDIO';
    return 'LONTANO';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Brux88 Beacon Control'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          IconButton(
            onPressed: _updateStatus,
            icon: Icon(Icons.refresh),
            tooltip: 'Aggiorna stato',
          ),
          IconButton(
            onPressed: _checkSystemRequirements,
            icon: Icon(Icons.settings),
            tooltip: 'Controlla requisiti sistema',
          ),
          IconButton(
            onPressed: _getLogs,
            icon: Icon(Icons.list),
            tooltip: 'Visualizza log',
          ),
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            _buildStatusCard(),
            // Controllo Auto-restart
            Card(
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('Auto-restart Settings',
                        style: Theme.of(context).textTheme.headlineSmall),
                    SizedBox(height: 8),
                    SwitchListTile(
                      title: Text('Auto-restart abilitato'),
                      subtitle: Text(_autoRestartEnabled
                          ? 'Il servizio si riavvierà automaticamente se terminato'
                          : 'Il servizio NON si riavvierà automaticamente'),
                      value: _autoRestartEnabled,
                      onChanged: _isInitialized
                          ? (value) => _toggleAutoRestart()
                          : null,
                    ),
                  ],
                ),
              ),
            ),
            _buildForegroundControls(),
            _buildBackgroundControls(),
            _buildCompleteControls(),
            _buildBeaconsList(),
          ],
        ),
      ),
    );
  }

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



/*import 'package:brux88_beacon_example/permission_screen.dart';
import 'package:flutter/material.dart';
import 'package:brux88_beacon/brux88_beacon.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Beacon Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const BeaconHomePage(),
    );
  }
}

class BeaconHomePage extends StatefulWidget {
  const BeaconHomePage({Key? key}) : super(key: key);

  @override
  State<BeaconHomePage> createState() => _BeaconHomePageState();
}

class _BeaconHomePageState extends State<BeaconHomePage>
    with SingleTickerProviderStateMixin {
  // Chiave globale per lo Scaffold, che ci fornisce un contesto valido
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  final BeaconManager _beaconManager = BeaconManager();
  bool _isMonitoring = false;
  List<Beacon> _detectedBeacons = [];
  List<String> _logs = [];
  SelectedBeacon? _selectedBeacon;
  late TabController _tabController;
  bool _showDetectionNotifications = false;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
    _initBeaconManager().then((_) {
      _checkServiceStatus();
      _checkBatteryOptimization();
    });
  }

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

  Future<void> _checkBatteryOptimization() async {
    try {
      final isIgnored = await _beaconManager.isBatteryOptimizationIgnored();
      if (!isIgnored) {
        showDialog(
          context: context,
          builder: (context) => AlertDialog(
            title: Text('Ottimizzazione Batteria'),
            content: Text(
                'Per garantire il corretto funzionamento del monitoraggio beacon in background, ' +
                    'è necessario disattivare l\'ottimizzazione della batteria per questa app.'),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text('Non ora'),
              ),
              TextButton(
                onPressed: () async {
                  Navigator.of(context).pop();
                  await _beaconManager.requestIgnoreBatteryOptimization();
                },
                child: Text('Disattiva'),
              ),
            ],
          ),
        );
      }
    } catch (e) {
      _addLog("Errore nel controllo dell'ottimizzazione batteria: $e");
    }
  }

  Future<void> _checkServiceStatus() async {
    try {
      // Controlla se il monitoraggio è attivo
      final isRunning = await _beaconManager.isMonitoringRunning();

      setState(() {
        _isMonitoring = isRunning;
      });

      if (isRunning) {
        _addLog("Monitoraggio già attivo");
      } else if (_selectedBeacon != null && _selectedBeacon!.enabled) {
        // Se abbiamo un beacon selezionato ma il monitoraggio non è attivo, riavviamolo
        _addLog("Riavvio automatico del monitoraggio");
        await _beaconManager.startMonitoring();
        setState(() {
          _isMonitoring = true;
        });
      }
    } catch (e) {
      _addLog("Errore nel controllo dello stato del servizio: $e");
    }
  }

  Future<void> _initBeaconManager() async {
    try {
      _addLog("Inizializzazione beacon manager...");

      // Se il monitoraggio è attivo, fermalo prima di reinizializzare
      if (_isMonitoring) {
        _addLog(
            "Monitoraggio attivo rilevato, lo fermo prima di reinizializzare");
        try {
          await _beaconManager.stopMonitoring();
          await _beaconManager.cancelRecurringAlarm();
        } catch (e) {
          _addLog("Errore nel fermare il monitoraggio precedente: $e");
          // Continua comunque con l'inizializzazione
        }
      }

      // Prima inizializza in modo basico
      final initialized = await _beaconManager.initialize();
      if (!initialized) {
        _addLog("Errore durante l'inizializzazione del BeaconManager");
        return;
      }

      // Verifica i permessi dopo l'inizializzazione di base
      final permissionsStatus = await _beaconManager.checkPermissions();
      final allPermissionsGranted = !permissionsStatus.values.contains(false);

      if (!allPermissionsGranted) {
        _addLog("Permessi mancanti, richiedo i permessi necessari");

        // Richiedi permessi di posizione
        var locationStatus = await Permission.locationWhenInUse.request();
        _addLog("Permesso posizione: $locationStatus");

        if (locationStatus.isGranted) {
          var backgroundLocationStatus =
              await Permission.locationAlways.request();
          _addLog(
              "Permesso posizione in background: $backgroundLocationStatus");
        }

        // Richiedi permessi Bluetooth
        var bluetoothScanStatus = await Permission.bluetoothScan.request();
        _addLog("Permesso Bluetooth scan: $bluetoothScanStatus");

        var bluetoothConnectStatus =
            await Permission.bluetoothConnect.request();
        _addLog("Permesso Bluetooth connect: $bluetoothConnectStatus");

        // Richiedi permesso notifiche
        var notificationStatus = await Permission.notification.request();
        _addLog("Permesso notifiche: $notificationStatus");
      }

      // Ascolto dei beacon rilevati
      _beaconManager.beacons.listen((beacons) {
        if (mounted) {
          setState(() {
            _detectedBeacons = beacons;
          });
        }

        // Mostra toast per ogni beacon rilevato
        if (beacons.isNotEmpty) {
          for (var beacon in beacons) {
            Fluttertoast.showToast(
              msg: "Beacon rilevato: ${beacon.uuid}",
              toastLength: Toast.LENGTH_SHORT,
              gravity: ToastGravity.BOTTOM,
            );
          }
        }
      });

      // Ascolto dello stato di monitoraggio
      _beaconManager.monitoringState.listen((state) {
        _addLog("Stato monitoraggio: $state");
      });

      // Carica i log iniziali
      _loadLogs();

      // Carica il beacon selezionato
      _loadSelectedBeacon();

      // Verifica se il monitoraggio era attivo in precedenza
      final isMonitoring = await _beaconManager.isMonitoringRunning();
      setState(() {
        _isMonitoring = isMonitoring;
      });

      _addLog(
          "Inizializzazione beacon manager completata. Monitoraggio attivo: $_isMonitoring");

      // Se il monitoraggio era attivo, riavvialo
      if (_isMonitoring) {
        _addLog("Riavvio monitoraggio automaticamente...");
        try {
          await _beaconManager.startMonitoring();
          await _beaconManager.setupRecurringAlarm();
          _addLog("Monitoraggio riavviato con successo");
        } catch (e) {
          _addLog("Errore nel riavvio del monitoraggio: $e");
          setState(() {
            _isMonitoring = false;
          });
        }
      }
    } catch (e) {
      _addLog("Errore nell'inizializzazione: $e");
    }
  }

  Future<void> _loadSelectedBeacon() async {
    try {
      final beacon = await _beaconManager.getSelectedBeacon();
      setState(() {
        _selectedBeacon = beacon;
      });
      if (beacon != null) {
        _addLog("Beacon selezionato caricato: ${beacon.uuid}");
      }
    } catch (e) {
      _addLog("Errore nel caricamento del beacon selezionato: $e");
    }
  }

  Future<void> _loadLogs() async {
    try {
      final logs = await _beaconManager.getLogs();
      setState(() {
        _logs = logs;
      });
    } catch (e) {
      _addLog("Errore nel caricamento dei log: $e");
    }
  }

  void _addLog(String message) {
    setState(() {
      final timestamp = DateTime.now().toString().substring(0, 19);
      _logs.insert(0, "[$timestamp] $message");
    });
  }

  Future<void> _toggleMonitoring() async {
    try {
      if (_isMonitoring) {
        _addLog("Arresto monitoraggio...");

        // Imposta _isMonitoring = false prima di fermare il servizio
        // per evitare che i callback possano riattivarlo
        setState(() {
          _isMonitoring = false;
        });

        await _beaconManager.stopMonitoring();
        await _beaconManager.cancelRecurringAlarm(); // Cancella l'allarme
        _addLog("Monitoraggio fermato");
      } else {
        // Verifica nuovamente i permessi prima di avviare il monitoraggio
        final permissionsStatus = await _beaconManager.checkPermissions();
        final allPermissionsGranted = !permissionsStatus.values.contains(false);

        if (!allPermissionsGranted) {
          _addLog(
              "Richiesta di permessi mancanti prima di avviare il monitoraggio");

          // Richiedi i permessi di base
          await Permission.locationWhenInUse.request();
          if (await Permission.locationWhenInUse.isGranted) {
            await Permission.locationAlways.request();
          }
          await Permission.bluetoothScan.request();
          await Permission.bluetoothConnect.request();
          await Permission.notification.request();

          // Richiedi di ignorare l'ottimizzazione della batteria
          await _beaconManager.requestIgnoreBatteryOptimization();
        }

        // Richiedi il permesso per gli allarmi esatti
        await _beaconManager.requestExactAlarmPermission();

        _addLog("Avvio monitoraggio...");

        // Verifica che il BeaconManager sia inizializzato
        bool isInitialized = await _beaconManager.isInitialized();
        if (!isInitialized) {
          _addLog("BeaconManager non inizializzato, lo reinizializzo...");
          isInitialized = await _beaconManager.initialize();

          if (!isInitialized) {
            _addLog("Impossibile inizializzare il BeaconManager");
            throw Exception("Impossibile inizializzare il BeaconManager");
          }
        }

        // Avvia il monitoraggio
        await _beaconManager.startMonitoring();
        await _beaconManager.setupRecurringAlarm(); // Configura l'allarme
        _addLog("Monitoraggio avviato");

        setState(() {
          _isMonitoring = true;
        });

        // Verifica che il servizio sia effettivamente attivo
        await _checkServiceStatus();
      }
    } catch (e) {
      _addLog("Errore: $e");

      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Si è verificato un errore: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    }
  }

// Estrai i dialoghi in metodi separati
  Future<bool> showPermissionDialog(BuildContext context) async {
    return await showDialog<bool>(
          context: context,
          barrierDismissible: false,
          builder: (BuildContext dialogContext) {
            return AlertDialog(
              title: const Text('Permessi necessari'),
              content: const Text(
                  'Per monitorare i beacon, l\'app ha bisogno di tutti i permessi richiesti. '
                  'Vuoi concedere tutti i permessi necessari per continuare?'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.of(dialogContext).pop(false);
                  },
                  child: const Text('Annulla'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.of(dialogContext).pop(true);
                  },
                  child: const Text('Concedi permessi'),
                ),
              ],
            );
          },
        ) ??
        false; // Default a false se il dialogo viene chiuso in modo imprevisto
  }

  Future<bool> showContinueAnywayDialog(BuildContext context) async {
    return await showDialog<bool>(
          context: context,
          barrierDismissible: false,
          builder: (BuildContext dialogContext) {
            return AlertDialog(
              title: const Text('Permessi mancanti'),
              content: const Text(
                  'Alcuni permessi necessari non sono stati concessi. '
                  'Il monitoraggio potrebbe non funzionare correttamente. '
                  'Vuoi avviare comunque il monitoraggio?'),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.of(dialogContext).pop(false);
                  },
                  child: const Text('No'),
                ),
                TextButton(
                  onPressed: () {
                    Navigator.of(dialogContext).pop(true);
                  },
                  child: const Text('Sì, avvia comunque'),
                ),
              ],
            );
          },
        ) ??
        false; // Default a false se il dialogo viene chiuso in modo imprevisto
  }

  Future<void> requestAllPermissions() async {
    // Prima richiedi i permessi di base
    await Permission.locationWhenInUse.request();

    if (await Permission.locationWhenInUse.isGranted) {
      await Permission.locationAlways.request();
    }

    // Permessi Bluetooth (per Android 12+)
    await Permission.bluetoothScan.request();
    await Permission.bluetoothConnect.request();

    // Permessi notifiche (per Android 13+)
    await Permission.notification.request();

    // Richiedi di ignorare l'ottimizzazione della batteria
    await _beaconManager.requestIgnoreBatteryOptimization();
  }

  Future<void> _saveSelectedBeacon(Beacon beacon) async {
    try {
      final result = await _beaconManager.setBeaconToMonitor(
        uuid: beacon.uuid,
        major: beacon.major,
        minor: beacon.minor,
        enabled: true,
      );

      if (result) {
        _addLog("Beacon selezionato: ${beacon.uuid}");
        Fluttertoast.showToast(
          msg: "Beacon selezionato con successo",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.BOTTOM,
        );
        // Ricarica il beacon selezionato
        _loadSelectedBeacon();
      } else {
        _addLog("Errore nella selezione del beacon");
      }
    } catch (e) {
      _addLog("Errore: $e");
    }
  }

  Future<void> _clearSelectedBeacon() async {
    try {
      final result = await _beaconManager.clearSelectedBeacon();

      if (result) {
        setState(() {
          _selectedBeacon = null;
        });
        _addLog("Selezione beacon cancellata");
        Fluttertoast.showToast(
          msg: "Selezione beacon cancellata",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.BOTTOM,
        );
      } else {
        _addLog("Errore nella cancellazione del beacon selezionato");
      }
    } catch (e) {
      _addLog("Errore: $e");
    }
  }

  Future<void> _refreshLogs() async {
    _addLog("Aggiornamento log...");
    await _loadLogs();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: Text('Plugin Beacon Demo'),
        bottom: TabBar(
          controller: _tabController,
          tabs: [
            Tab(text: 'Beacon'),
            Tab(text: 'Permessi', icon: Icon(Icons.security)),
            Tab(text: 'Log'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          // Tab Beacon
          Column(
            children: [
              SwitchListTile(
                title: const Text('Notifiche di rilevamento beacon'),
                subtitle: const Text(
                    'Mostra notifiche quando vengono rilevati nuovi beacon'),
                value: _showDetectionNotifications,
                onChanged: (bool value) async {
                  // Chiama il metodo del plugin
                  await _beaconManager.setShowDetectionNotifications(value);

                  setState(() {
                    _showDetectionNotifications = value;
                    // Se hai un log, aggiungi anche un messaggio
                    if (mounted && _logs != null) {
                      _logs.add(
                          "Notifiche di rilevamento ${value ? 'abilitate' : 'disabilitate'}");
                    }
                  });
                },
              ),
              Padding(
                padding: EdgeInsets.all(16.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    ElevatedButton(
                      onPressed: _toggleMonitoring,
                      child: Text(_isMonitoring
                          ? 'Ferma monitoraggio'
                          : 'Avvia monitoraggio'),
                    ),
                  ],
                ),
              ),

              // Beacon selezionato
              if (_selectedBeacon != null)
                Card(
                  margin: EdgeInsets.all(16.0),
                  color: Colors.lightBlue[50],
                  child: Padding(
                    padding: EdgeInsets.all(16.0),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          'Beacon Selezionato',
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 16,
                          ),
                        ),
                        SizedBox(height: 8),
                        Text('UUID: ${_selectedBeacon!.uuid}'),
                        if (_selectedBeacon!.major != null)
                          Text('Major: ${_selectedBeacon!.major}'),
                        if (_selectedBeacon!.minor != null)
                          Text('Minor: ${_selectedBeacon!.minor}'),
                        SizedBox(height: 8),
                        Text(
                            'Stato: ${_selectedBeacon!.enabled ? 'Attivo' : 'Inattivo'}'),
                        SizedBox(height: 8),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.end,
                          children: [
                            TextButton(
                              onPressed: _clearSelectedBeacon,
                              child: Text('Cancella'),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),

              Expanded(
                child: _detectedBeacons.isEmpty
                    ? Center(child: Text('Nessun beacon rilevato'))
                    : ListView.builder(
                        itemCount: _detectedBeacons.length,
                        itemBuilder: (context, index) {
                          final beacon = _detectedBeacons[index];
                          return Card(
                            margin: EdgeInsets.symmetric(
                                horizontal: 16.0, vertical: 8.0),
                            child: ListTile(
                              title: Text('UUID: ${beacon.uuid}'),
                              subtitle: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  if (beacon.major != null)
                                    Text('Major: ${beacon.major}'),
                                  if (beacon.minor != null)
                                    Text('Minor: ${beacon.minor}'),
                                  Text(
                                      'Distanza: ${beacon.distance.toStringAsFixed(2)}m'),
                                  Text('RSSI: ${beacon.rssi} dBm'),
                                ],
                              ),
                              trailing: IconButton(
                                icon: Icon(Icons.save),
                                onPressed: () => _saveSelectedBeacon(beacon),
                                tooltip: 'Seleziona beacon',
                              ),
                            ),
                          );
                        },
                      ),
              ),
            ],
          ),
          // Scheda Permessi
          const PermissionScreen(),

          // Tab Log
          Column(
            children: [
              Padding(
                padding: EdgeInsets.all(16.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: [
                    ElevatedButton(
                      onPressed: _refreshLogs,
                      child: Text('Aggiorna Log'),
                    ),
                    ElevatedButton(
                      onPressed: () {
                        setState(() {
                          _logs.clear();
                          _addLog("Log cancellati");
                        });
                      },
                      child: Text('Cancella Log'),
                    ),
                  ],
                ),
              ),
              Expanded(
                child: _logs.isEmpty
                    ? Center(child: Text('Nessun log disponibile'))
                    : ListView.builder(
                        itemCount: _logs.length,
                        itemBuilder: (context, index) {
                          return Padding(
                            padding: EdgeInsets.symmetric(
                              horizontal: 16.0,
                              vertical: 4.0,
                            ),
                            child: Text(
                              _logs[index],
                              style: TextStyle(
                                fontFamily: 'monospace',
                                fontSize: 12,
                              ),
                            ),
                          );
                        },
                      ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}
*/
1
likes
140
points
40
downloads

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin for handling BLE beacons with background detection and reliable monitoring.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on brux88_beacon

Packages that implement brux88_beacon