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

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

example/lib/main.dart

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
0
points
42
downloads

Publisher

unverified uploader

Weekly Downloads

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

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on brux88_beacon

Packages that implement brux88_beacon