Brux88 Beacon
A robust Flutter plugin for handling BLE beacons (iBeacon, AltBeacon, Eddystone) with reliable background detection and monitoring capabilities.
Features
- 📱 Cross-platform: Works on both Android and iOS
- 🔄 Background monitoring: Reliable beacon detection even when the app is in the background
- 🎛️ Background service control: Start, stop, and restart background service independently
- 🔔 Notifications: Customizable notifications for region entry/exit events
- 🔋 Battery-optimized: Configurable scan settings to balance between detection speed and battery usage
- 🎯 Region filtering: Monitor specific beacons or all beacons in range
- 📏 Distance estimation: Get accurate distance measurements for ranging
- 🔒 Permission handling: Integrated permission management for location and Bluetooth
- 📊 Comprehensive data: Access to UUID, Major, Minor, RSSI, and TxPower values
- 🔄 Boot persistence: Automatic restart of monitoring service after device reboot
- 🛠️ Debug tools: Built-in logging system for troubleshooting
Getting Started
Installation
Add this to your package's pubspec.yaml
file:
dependencies:
brux88_beacon: ^0.1.3
Platform-specific setup
Android
Add the following permissions to your AndroidManifest.xml
file:
<!-- Bluetooth permissions -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Location permissions (required for BLE scan) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- Background service permissions -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- Feature required for BLE -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
iOS
Add the following to your Info.plist
file:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to scan for nearby beacons.</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app uses Bluetooth to scan for nearby beacons.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app uses your location to detect beacons even when in the background.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses your location to detect nearby beacons.</string>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>location</string>
</array>
Usage
Basic Setup
import 'package:brux88_beacon/brux88_beacon.dart';
// Create BeaconManager instance
final beaconManager = BeaconManager();
// Initialize the beacon manager
await beaconManager.initialize();
// Request necessary permissions
await beaconManager.requestPermissions();
Background Service Control
The plugin provides independent control over the background service:
// Start background service only
await beaconManager.startBackgroundService();
// Stop background service
await beaconManager.stopBackgroundService();
// Restart background service
await beaconManager.restartBackgroundService();
// Check if background service is running
bool isRunning = await beaconManager.isBackgroundServiceRunning();
// Enable/disable background service auto-start
await beaconManager.setBackgroundServiceEnabled(true);
bool isEnabled = await beaconManager.isBackgroundServiceEnabled();
Foreground Monitoring
// Start foreground monitoring (includes ranging)
await beaconManager.startMonitoring();
// Stop all monitoring
await beaconManager.stopMonitoring();
// Check if monitoring is active
bool isMonitoring = await beaconManager.isMonitoringRunning();
Monitoring Specific Beacons
// Set a specific beacon to monitor
await beaconManager.setBeaconToMonitor(
uuid: "F7826DA6-4FA2-4E98-8024-BC5B71E0893E",
major: "1",
minor: "2",
enabled: true,
);
// Clear selected beacon (monitor all beacons)
await beaconManager.clearSelectedBeacon();
Listening for Events
// Listen for beacon detection events
beaconManager.beacons.listen((List<Beacon> beacons) {
for (var beacon in beacons) {
print("Found beacon: ${beacon.uuid}, distance: ${beacon.distance}m");
}
});
// Listen for region monitoring events
beaconManager.monitoringState.listen((MonitoringState state) {
switch (state) {
case MonitoringState.inside:
print("Entered beacon region");
break;
case MonitoringState.outside:
print("Exited beacon region");
break;
case MonitoringState.unknown:
print("Region state unknown");
break;
}
});
Configuring Scan Settings
// Configure scan settings for battery optimization
await beaconManager.setScanSettings(
ScanSettings(
backgroundScanPeriod: 1100, // Scan period in milliseconds
backgroundBetweenScanPeriod: 5000, // Time between scans in milliseconds
foregroundScanPeriod: 1100, // Foreground scan period
foregroundBetweenScanPeriod: 0, // Time between foreground scans (0 = continuous)
maxTrackingAge: 5000, // Maximum age for beacon tracking
),
);
Battery Optimization
// Check if battery optimization is ignored
bool isIgnored = await beaconManager.isBatteryOptimizationIgnored();
// Request to ignore battery optimization
if (!isIgnored) {
await beaconManager.requestIgnoreBatteryOptimization();
}
// Set up recurring alarms to keep the service alive
await beaconManager.setupRecurringAlarm();
Debug and Logging
// Enable debug mode for detailed logs
await beaconManager.enableDebugMode();
// Get logs for troubleshooting
List<String> logs = await beaconManager.getLogs();
for (var log in logs) {
print(log);
}
Complete Example
Here's a complete example showing how to create a beacon monitoring app with background service control:
import 'package:flutter/material.dart';
import 'package:brux88_beacon/brux88_beacon.dart';
class BeaconControlPage extends StatefulWidget {
@override
_BeaconControlPageState createState() => _BeaconControlPageState();
}
class _BeaconControlPageState extends State<BeaconControlPage> {
final BeaconManager _beaconManager = BeaconManager();
bool _isInitialized = false;
bool _isBackgroundServiceRunning = false;
List<Beacon> _detectedBeacons = [];
@override
void initState() {
super.initState();
_initializeBeaconManager();
_setupListeners();
}
Future<void> _initializeBeaconManager() async {
final initialized = await _beaconManager.initialize();
setState(() {
_isInitialized = initialized;
});
if (initialized) {
await _updateStatus();
}
}
void _setupListeners() {
_beaconManager.beacons.listen((beacons) {
setState(() {
_detectedBeacons = beacons;
});
});
}
Future<void> _updateStatus() async {
final isBackgroundRunning = await _beaconManager.isBackgroundServiceRunning();
setState(() {
_isBackgroundServiceRunning = isBackgroundRunning;
});
}
Future<void> _startBackgroundService() async {
await _beaconManager.requestPermissions();
// Check battery optimization
final isBatteryOptimized = await _beaconManager.isBatteryOptimizationIgnored();
if (!isBatteryOptimized) {
await _beaconManager.requestIgnoreBatteryOptimization();
}
final success = await _beaconManager.startBackgroundService();
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Background service started')),
);
await _updateStatus();
}
}
Future<void> _stopBackgroundService() async {
final success = await _beaconManager.stopBackgroundService();
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Background service stopped')),
);
await _updateStatus();
}
}
Future<void> _restartBackgroundService() async {
final success = await _beaconManager.restartBackgroundService();
if (success) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Background service restarted')),
);
await _updateStatus();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Beacon Control'),
actions: [
IconButton(
onPressed: _updateStatus,
icon: Icon(Icons.refresh),
),
],
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
// Status Card
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Status', style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 8),
Text('Initialized: $_isInitialized'),
Text('Background Service: $_isBackgroundServiceRunning'),
Text('Beacons Detected: ${_detectedBeacons.length}'),
],
),
),
),
SizedBox(height: 16),
// Control Buttons
Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Background Service Control',
style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: _isInitialized && !_isBackgroundServiceRunning
? _startBackgroundService
: null,
child: Text('Start Background'),
),
),
SizedBox(width: 8),
Expanded(
child: ElevatedButton(
onPressed: _isInitialized && _isBackgroundServiceRunning
? _stopBackgroundService
: null,
child: Text('Stop Background'),
),
),
],
),
SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _isInitialized ? _restartBackgroundService : null,
child: Text('Restart Background Service'),
),
),
],
),
),
),
SizedBox(height: 16),
// Detected Beacons
Expanded(
child: Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Detected Beacons',
style: Theme.of(context).textTheme.headlineSmall),
SizedBox(height: 8),
if (_detectedBeacons.isEmpty)
Expanded(
child: Center(
child: Text('No beacons detected'),
),
)
else
Expanded(
child: ListView.builder(
itemCount: _detectedBeacons.length,
itemBuilder: (context, index) {
final beacon = _detectedBeacons[index];
return ListTile(
leading: Icon(Icons.bluetooth),
title: Text('UUID: ${beacon.uuid}'),
subtitle: Text(
'Distance: ${beacon.distance.toStringAsFixed(2)}m\n'
'RSSI: ${beacon.rssi} dBm'
),
trailing: Text('${beacon.distance < 1 ? "NEAR" : beacon.distance < 3 ? "MID" : "FAR"}'),
);
},
),
),
],
),
),
),
),
],
),
),
);
}
@override
void dispose() {
_beaconManager.dispose();
super.dispose();
}
}
Advanced Features
Region Management
// Start monitoring a specific region
await beaconManager.startMonitoringForRegion(
BeaconRegion(
identifier: "myRegion",
uuid: "F7826DA6-4FA2-4E98-8024-BC5B71E0893E",
major: "1",
minor: "2",
),
);
// Stop monitoring a region
await beaconManager.stopMonitoringForRegion("myRegion");
// Get all monitored regions
List<BeaconRegion> regions = await beaconManager.getMonitoredRegions();
Notification Settings
// Configure notification settings
await beaconManager.setNotificationSettings(
NotificationSettings(
enabled: true,
channelId: 'beacon_monitoring_channel',
channelName: 'Beacon Monitoring',
channelDescription: 'Notifications for beacon monitoring',
importance: 2, // 1 = low, 2 = default, 3 = high
showBadge: false,
entryTitle: 'Beacon Detected',
entryMessage: 'You have entered a beacon region',
exitTitle: 'Beacon Lost',
exitMessage: 'You have left a beacon region',
),
);
// Control detection notifications separately from service notifications
await beaconManager.setShowDetectionNotifications(true);
Supported Beacon Types
- Apple iBeacon: The most common format used by iOS devices and many beacon manufacturers
- AltBeacon: Open source beacon format
- Eddystone: Google's beacon format (UID and URL variants)
Background Service Management
The plugin provides granular control over background services:
- Independent Control: Start/stop background service independently from foreground monitoring
- Battery Optimization: Background service uses optimized scan intervals to preserve battery
- Auto-restart: Service automatically restarts after device reboot if enabled
- Status Monitoring: Real-time status updates for service state
Performance Considerations
Battery Optimization
- Use longer scan intervals for background monitoring
- Request battery optimization exclusion for reliable background operation
- Configure appropriate scan settings based on your use case
Scan Settings Recommendations
// Battery-friendly settings
ScanSettings(
backgroundScanPeriod: 1100,
backgroundBetweenScanPeriod: 30000, // 30 seconds between scans
foregroundScanPeriod: 1100,
foregroundBetweenScanPeriod: 0, // Continuous when in foreground
)
// High-precision settings (more battery usage)
ScanSettings(
backgroundScanPeriod: 1100,
backgroundBetweenScanPeriod: 5000, // 5 seconds between scans
foregroundScanPeriod: 1100,
foregroundBetweenScanPeriod: 0,
)
Troubleshooting
Common Issues
Background Monitoring Not Working
- Ensure all permissions are granted
- Check battery optimization settings
- Verify Bluetooth and Location are enabled
- Enable debug mode to view detailed logs
await beaconManager.enableDebugMode();
List<String> logs = await beaconManager.getLogs();
Beacons Not Detected
- Verify beacon format compatibility (iBeacon, AltBeacon, Eddystone)
- Check signal strength and distance
- Ensure beacon is advertising correctly
- Review scan settings
Service Stops Unexpectedly
- Request battery optimization exclusion
- Set up recurring alarms for service restart
- Check device-specific power management settings
Debug Information
// Check system status
bool bluetoothEnabled = await beaconManager.isBluetoothEnabled();
bool locationEnabled = await beaconManager.isLocationEnabled();
Map<String, bool> permissions = await beaconManager.checkPermissions();
// Get detailed logs
await beaconManager.enableDebugMode();
List<String> logs = await beaconManager.getLogs();
Platform Differences
Android
- Uses AltBeacon library for beacon detection
- Requires foreground service for background operation
- Battery optimization exclusion recommended
- Supports all beacon formats
iOS
- Uses CoreLocation and CoreBluetooth APIs
- Background execution managed by iOS
- Limited background scanning time
- Optimized for iBeacon format
Migration Guide
From 0.1.2 to 0.1.3
- Added independent background service control methods
- Enhanced notification management
- Improved battery optimization handling
No breaking changes - all existing code continues to work.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- The Android implementation uses the AltBeacon library
- The iOS implementation uses native CoreLocation and CoreBluetooth APIs
Support
For support, please open an issue on GitHub or contact the maintainer.
Note: This plugin is actively maintained and tested on Flutter 3.3.0+. For older Flutter versions, please use an earlier version of this plugin.