abi_beacon 0.1.0
abi_beacon: ^0.1.0 copied to clipboard
Android iBeacon background monitoring with a resilient foreground service, OEM manufacturer detection, and battery/permission state management.
abi_beacon #
Android iBeacon background monitoring plugin for Flutter. Runs a resilient foreground service that keeps scanning when the app is closed, survives OEM battery killers, and handles Android Doze automatically.
Platform support: Android only (API 24+). iOS is not supported yet — the iOS implementation is a stub: its methods are no-ops and
eventsnever emits. Do not ship this plugin expecting iOS functionality.
Features #
- Background monitoring — foreground service keeps scanning even when the app is closed or the screen is off
- Region detection —
EntryEvent/ExitEventfor iBeacon UUID regions, with optional major/minor filter - Ranging — continuous
RangeEventwith RSSI and estimated distance per beacon per cycle - Sealed event stream — type-safe Dart 3 pattern matching across all event subtypes
- OEM battery management — detects manufacturer family (Xiaomi, Huawei, Samsung, etc.) and deep-links to the right settings screen
- Permission management — runtime permission requests with per-permission status tracking
- Battery & power state — query Doze exemption, power save mode, and location services
Getting started #
Installation #
dependencies:
abi_beacon: ^0.1.0
Your app's minSdkVersion must be 24 or higher (set in android/app/build.gradle).
Android permissions #
Most permissions ship inside the plugin's own manifest and are merged into your app automatically — you do not need to copy them. The plugin already declares: BLUETOOTH, BLUETOOTH_ADMIN, BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, FOREGROUND_SERVICE, FOREGROUND_SERVICE_LOCATION, POST_NOTIFICATIONS, WAKE_LOCK, RECEIVE_BOOT_COMPLETED, and REQUEST_IGNORE_BATTERY_OPTIMIZATIONS.
The foreground service runs with
foregroundServiceType="location", so on Android 14+ it requiresFOREGROUND_SERVICE_LOCATION— already declared by the plugin.
The only permission you must add yourself is background location, because it is sensitive and cannot be granted implicitly. Add it to your android/app/src/main/AndroidManifest.xml (inside <manifest>) if you need to keep scanning while the app is not in the foreground:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Background location runtime flow (Android 11+)
ACCESS_BACKGROUND_LOCATION cannot be requested in the same dialog as foreground location. The required flow is:
- Request foreground location first (
AbiBeacon.requestPermissions()handles the standard permissions). - Then request background location separately — on Android 11+ the OS does not show a dialog and instead sends the user to the app settings screen, where they must choose "Allow all the time". Explain this to the user before sending them there.
Without "Allow all the time", monitoring still works while the app is open but stops when it goes to the background.
Usage #
Basic setup #
import 'package:abi_beacon/abi_beacon.dart';
// 1. Request permissions
final perms = await AbiBeacon.requestPermissions();
if (!perms.allGranted) {
// perms.deniedPermissions — list of what is still missing
return;
}
// 2. Initialize with your beacon UUID
await AbiBeacon.initialize(BeaconConfig(
uuid: 'B9407F30-F5F8-466E-AFF9-25556B57FE6D',
// major: 100, // optional — null = any
// minor: 200, // optional — null = any
scanPeriod: const Duration(milliseconds: 1100),
exitPeriod: const Duration(seconds: 4),
notificationTitle: 'Beacon Monitor',
notificationText: 'Scanning for beacons...',
));
// 3. Start the foreground service
await AbiBeacon.startMonitoring();
// 4. Listen to events
AbiBeacon.events.listen((event) => switch (event) {
EntryEvent(:final timestamp) => print('Entered region at $timestamp'),
ExitEvent(:final timestamp) => print('Exited region at $timestamp'),
RangeEvent(:final beacons, :final nearest) => print('${beacons.length} beacons — nearest: $nearest'),
ServiceStartedEvent() => print('Service started'),
ServiceStoppedEvent() => print('Service stopped'),
ErrorEvent(:final message) => print('Error: $message'),
UnknownEvent(:final rawType) => print('Unknown event: $rawType'),
});
OEM battery optimization #
Manufacturers like Xiaomi, Huawei, Samsung, and OnePlus apply proprietary battery restrictions that can kill background services even when Android Doze exemption is granted. Use DeviceInfo to guide users to the right screen:
final info = await AbiBeacon.getDeviceInfo();
if (info.oem.isAggressive) {
// Show a dialog explaining why the user needs to whitelist the app
await AbiBeacon.openOemSetting(OemSettingType.autostart);
await AbiBeacon.openOemSetting(OemSettingType.batterySaver);
}
// Also request standard Android Doze exemption
final isExempt = await AbiBeacon.isBatteryOptimizationDisabled();
if (!isExempt) {
await AbiBeacon.requestDisableBatteryOptimization();
}
Checking current status #
final status = await AbiBeacon.getCurrentStatus();
print('Service running: ${status.isRunning}');
print('Region state: ${status.regionState}'); // inside / outside / unknown
print('Last event: ${status.lastEventTime}');
print('Nearest beacon: ${status.nearestBeacon}');
Stopping #
await AbiBeacon.stopMonitoring();
API reference #
AbiBeacon (static API) #
Lifecycle
| Method | Returns | Description |
|---|---|---|
initialize(BeaconConfig) |
Future<void> |
Persist beacon configuration. Call before startMonitoring. |
startMonitoring() |
Future<bool> |
Start the foreground service. Returns true on success. |
stopMonitoring() |
Future<bool> |
Stop the foreground service. |
isMonitoring() |
Future<bool> |
true if the service is currently running. |
getCurrentStatus() |
Future<MonitoringStatus> |
Snapshot of service state, region, and nearest beacon. |
events |
Stream<BeaconEvent> |
Real-time stream of monitoring events. |
Device state
| Method | Returns | Description |
|---|---|---|
getDeviceInfo() |
Future<DeviceInfo> |
Device info and detected OEM family. |
getRequiredPermissions() |
Future<List<String>> |
Permissions required for this Android version. |
checkPermissions() |
Future<PermissionsSnapshot> |
Current permission state without requesting. |
requestPermissions() |
Future<PermissionsSnapshot> |
Request all required permissions from the user. |
isBatteryOptimizationDisabled() |
Future<bool> |
true if Doze exemption is granted. |
requestDisableBatteryOptimization() |
Future<void> |
Open system Doze exemption dialog. |
isPowerSaveModeEnabled() |
Future<bool> |
true if global power save mode is active. |
isLocationServiceEnabled() |
Future<bool> |
true if device location services are on. |
openOemSetting(OemSettingType) |
Future<bool> |
Open a manufacturer settings screen. Returns true if opened. |
BeaconEvent subtypes #
All events extend the sealed class BeaconEvent. Use Dart 3 pattern matching:
| Subtype | Key properties | Triggered when |
|---|---|---|
EntryEvent |
timestamp, regionId |
Device enters the monitored region |
ExitEvent |
timestamp, regionId |
Device exits the monitored region |
RangeEvent |
timestamp, beacons, nearest |
Ranging cycle completes (every scanPeriod) |
ServiceStartedEvent |
timestamp |
Foreground service starts |
ServiceStoppedEvent |
timestamp |
Foreground service stops |
ErrorEvent |
timestamp, message |
Native layer reports an error |
UnknownEvent |
timestamp, rawType |
Unrecognized event type (forward compat) |
BeaconConfig #
| Parameter | Type | Default | Description |
|---|---|---|---|
uuid |
String |
required | iBeacon proximity UUID |
major |
int? |
null |
Major filter. null = match any. |
minor |
int? |
null |
Minor filter. null = match any. |
scanPeriod |
Duration |
1100ms |
Duration of each BLE scan cycle |
exitPeriod |
Duration |
4s |
No-signal time before firing ExitEvent. Min recommended: 3s |
notificationTitle |
String |
'iBeacon Monitor' |
Foreground service notification title |
notificationText |
String |
'Buscando beacon...' |
Foreground service notification body |
notificationIcon |
String? |
null |
Drawable/mipmap resource name (e.g. 'ic_launcher') |
OEM support #
| Manufacturer | OemFamily value |
isAggressive |
Notes |
|---|---|---|---|
| Xiaomi / MIUI | xiaomi |
true |
Requires autostart + no battery restrictions |
| Huawei / EMUI | huawei |
true |
Requires protected apps setting |
| Honor | honor |
true |
Similar to Huawei |
| Samsung / One UI | samsung |
true |
Requires disabling battery optimization |
| OnePlus | oneplus |
true |
Requires autostart |
| OPPO / ColorOS | oppo |
true |
Requires autostart |
| Realme | realme |
true |
Requires autostart |
| Vivo / FuntouchOS | vivo |
true |
Requires autostart |
| ASUS / ZenUI | asus |
true |
Requires autostart |
| Stock Android | stock |
false |
Standard Doze exemption is sufficient |
Why abi_beacon? #
Most Flutter beacon packages wrap the AltBeacon Android library or similar native SDKs, which introduces two problems in production:
1. OEM battery killers are not handled #
Standard Android Doze exemption is not enough. Manufacturers like Xiaomi, Huawei, Samsung, and OnePlus ship proprietary battery managers that kill background services independently of Android's own Doze. This is the real reason beacon monitoring "works in the demo but fails for real users."
abi_beacon is the only Flutter beacon plugin that addresses this at the API level:
| Other packages | abi_beacon | |
|---|---|---|
| Background foreground service | Most do | ✓ |
| Request Doze exemption | Some do | ✓ |
| Detect OEM manufacturer | ✗ | ✓ |
Know if OEM is aggressive (isAggressive) |
✗ | ✓ |
| Deep-link to OEM autostart settings | ✗ | ✓ |
| Deep-link to OEM protected apps (Huawei/Honor) | ✗ | ✓ |
| Permissions + battery state in one package | ✗ | ✓ |
2. License — MIT with AltBeacon notice #
abi_beacon is MIT licensed. However, the Android layer wraps AltBeacon android-beacon-library (org.altbeacon:android-beacon-library:2.20.5), which is Apache License 2.0.
This is the same situation as every other Flutter beacon plugin that uses AltBeacon. Apache 2.0 does not restrict commercial use, but it does require:
- Including a copy of the Apache 2.0 license in your app's distribution
- Including an attribution notice for AltBeacon
This is a known, well-understood requirement in the beacon ecosystem. Most enterprise legal teams approve Apache 2.0 dependencies without escalation.
Additional information #
- Issues & feature requests: GitHub Issues
- Example app: See the
example/directory for a complete working demo. - License: MIT. The Android layer uses AltBeacon android-beacon-library (Apache 2.0) — commercial use is permitted, attribution notice required.