Adonna – FeasyBeacon Flutter Plugin
Adonna is a Flutter plugin that wraps Feasycom's FeasyBeacon SDKs for Android and iOS, providing a clean Dart API to scan, configure, and update BLE beacons (iBeacon, Eddystone, AltBeacon). It includes comprehensive background monitoring that detects beacon power-on events and button presses by reading actual advertising intervals and comparing them to configurable thresholds.
- FeasyBeacon Android SDK docs: https://document.feasycom.com/web/#/130/955
- FeasyBeacon iOS SDK docs: https://document.feasycom.com/web/#/130/954
Status: Production Ready ✅ Android and iOS now have 100% feature parity with identical background monitoring logic, comprehensive event detection, and professional UI.
Features
✅ 100% SDK Implementation: Complete parity with native Android and iOS SDKs
✅ Cross-Platform: Unified Dart API for both platforms
✅ BLE Scanning: Discover nearby FeasyBeacon devices
✅ Device Management: Connect, configure, and control beacons
✅ Beacon Configuration: Full CRUD operations for iBeacon, Eddystone, and AltBeacon
✅ OTA Updates: Over-the-air firmware updates
✅ Comprehensive Background Monitoring:
- Android: Foreground service with full beacon lifecycle detection
- iOS: Core Location region monitoring with fast connection and interval reading
- Event Types:
beacon_turned_on,button_pressed,beacon_normal,connection_failed,interval_read_failed - Smart Detection: Connect to beacon, read advertising interval, compare to threshold for button press vs normal operation
✅ Real-time Events: Streams for scan results, background events, OTA progress, and device data
✅ Professional Example App: Tabbed interface with color-coded event display and comprehensive monitoring
Platform Support Matrix
| Feature | Android | iOS | Notes |
|---|---|---|---|
| BLE Scanning | ✅ | ✅ | Full support |
| Device Connection | ✅ | ✅ | Full support |
| Parameter Setting | ✅ | ✅ | All setters implemented |
| Beacon CRUD | ✅ | ✅ | Full support |
| OTA Updates | ✅ | ✅ | Full support |
| Background Monitoring | ✅ | ✅ | Identical logic on both platforms |
| Low-level Communication | ✅ | ✅ | Packet streaming |
| Device Info | ✅ | ✅ | Parameter loading |
Install
Add to your app's pubspec.yaml:
dependencies:
adonna: ^0.2.0
permission_handler: ^11.3.1 # Recommended for runtime permissions
Quick Start
import 'package:adonna/adonna.dart';
// Start scanning (Android + iOS)
await FeasyBeacon.startScan();
final scanSub = FeasyBeacon.scanStream.listen((e) {
// e: {address?, name?, rssi, hasIBeacon, hasEddystone, hasAltBeacon}
});
// Comprehensive background monitoring (Android + iOS)
await FeasyBeacon.startBackgroundMonitoring(config: {
'thresholdMs': 200, // ≤200ms = button pressed, >200ms = normal operation
'favoriteAddresses': ['AA:BB:CC:DD:EE:FF'], // Only monitor favorite devices
'notificationTitle': 'Adonna Monitoring',
'notificationText': 'Monitoring favorite beacons',
});
final bgSub = FeasyBeacon.backgroundEvents.listen((e) {
// e: {address, name, rssi, eventType, intervalMs, latitude?, longitude?, timestamp}
// eventType: 'beacon_turned_on', 'button_pressed', 'beacon_normal', 'connection_failed', 'interval_read_failed'
switch (e['eventType']) {
case 'beacon_turned_on':
print('Beacon ${e['name']} just turned on!');
break;
case 'button_pressed':
print('Button pressed on ${e['name']}! Interval: ${e['intervalMs']}ms');
break;
case 'beacon_normal':
print('${e['name']} operating normally. Interval: ${e['intervalMs']}ms');
break;
}
});
// Connect and read device info (platform-supported)
final connected = await FeasyBeacon.connect(address: 'AA:BB:CC:DD:EE:FF', pin: '000000');
if (connected) {
final info = await FeasyBeacon.getDeviceInfo();
}
Android Setup
- Target Android 12+ permissions as needed:
BLUETOOTH_SCAN,BLUETOOTH_CONNECTACCESS_COARSE_LOCATIONand/orACCESS_FINE_LOCATIONPOST_NOTIFICATIONS(Android 13+)- For background monitoring:
ACCESS_BACKGROUND_LOCATIONrecommended
- Background monitoring runs as a foreground service and displays a persistent notification.
Permissions example (manifest excerpt):
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_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" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Request runtime permissions (example using permission_handler):
await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
Permission.notification,
].request();
iOS Setup
- Add the following keys to your
Info.plist:NSBluetoothAlwaysUsageDescriptionNSBluetoothPeripheralUsageDescriptionNSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescription
- Background monitoring is now fully implemented on iOS with identical logic to Android:
- Uses Core Location for iBeacon region monitoring
- Automatically connects to detected beacons
- Reads advertising interval parameters
- Emits same event types as Android
- iOS helpers:
// Optional: set 128-bit decrypt key
final ok = await FeasyBeacon.setupDecryptKey(your16ByteKey);
// Optional: filter iBeacon UUIDs
await FeasyBeacon.setIBeaconUUIDs(['xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx']);
final uuids = await FeasyBeacon.getIBeaconUUIDs();
API Overview
Scanning
startScan(),stopScan()scanStream: emits{ address?, name?, rssi, hasIBeacon, hasEddystone, hasAltBeacon }
Background monitor (Android + iOS)
startBackgroundMonitoring({ thresholdMs, favoriteAddresses, notificationTitle, notificationText })stopBackgroundMonitoring()backgroundEvents: emits comprehensive event data:{ address, name, rssi, eventType, intervalMs, latitude?, longitude?, timestamp }- Event Types:
beacon_turned_on,button_pressed,beacon_normal,connection_failed,interval_read_failed - Smart Detection: Automatically connects to beacons, reads advertising interval, determines event type based on threshold
Connectivity & Parameters (Android + iOS*)
connect(address, { pin = '000000' }),disconnect(),isConnected()setDeviceName(name),setDevicePin(pin)setBroadcastInterval(intervalHex)setTlm(enabled)setBroadcastExtensions(enabled)setFirmwareKey(command),setAppKey(key)(Android)setTxPower(value),setGsensor(enabled),setKey(enabled),setPhy(value),setLongRange(enabled),setConnectable(on)saveBeaconInfo()
Beacon configuration (Android + iOS*)
addBeaconInfo({...})// supports iBeacon/Eddystone/AltBeacon fieldsdeleteBeaconInfo(index)beaconBroadcastParameters()getDeviceInfo(isTxPower: true|false)restore()
OTA (Android + iOS*)
checkDfuFile(dfuBytes)→ boolstartOtaUpdate(dfuBytes, { reset = false })→ boolotaProgressStream→ int (0..100)
Streams (Android + iOS)
packetStream:{ str, hex, raw, timestamp }deviceInfoStream:{ name, value }
iOS helpers
setupDecryptKey(key128)→ boolsetIBeaconUUIDs(uuids)/getIBeaconUUIDs()
Example App
The included example app demonstrates all plugin features with a modern, tabbed interface:
🕵️ Devices Tab
- Live BLE Scanning: Start/stop scanning with real-time device discovery
- Device Information: Display device name, MAC address, RSSI, and beacon types
- Connection Controls: Connect to devices with automatic scan stopping
- Favorite Management: Add/remove devices from favorites with heart icons
❤️ Favorites Tab
- Persistent Storage: Devices saved using SharedPreferences
- Swipe to Delete: Remove favorites with swipe gestures
- Quick Access: Connect to favorite devices directly
- Auto-Add: Devices automatically added to favorites when connected
⚙️ Background Tab
- Android Background Monitoring: Foreground service with interval detection
- iOS Background Monitoring: Core Location region monitoring configuration
- Parameter Configuration: UUID, major, and threshold settings
- Event Logging: Real-time display of background detection events
🔧 Connected Device Features
- Device Info: Load and display all device parameters
- OTA Updates: Trigger firmware updates with file picker
- Parameter Setting: Configure device name, interval, power, and more
- Status Display: Connected device information in bottom bar
🎯 Key Features
- Material 3 Design: Modern UI with improved user experience
- State Management: Proper handling of connection states and UI updates
- Permission Handling: Automatic request for Bluetooth, location, and notifications
- Error Handling: User-friendly error messages and status updates
Expected test flow:
- Start Scan → tap a device in the list to select it (name often shows as "Unknown"), Stop Scan to keep the list stable
- Enter PIN (default
000000) → Connect → Get Info - Start Background → briefly press the beacon's button (fast interval ~100ms for ~35s) → observe a local notification with location and an event in the app
- Stop Background → Disconnect
Notes:
- The scan list updates frequently and may reorder while scanning; Stop Scan before connecting for a stable list.
- iOS background monitoring is now fully implemented with identical logic to Android.
🚀 Comprehensive Background Monitoring
Adonna provides identical background monitoring logic on both Android and iOS, making it the first Flutter plugin to offer true cross-platform beacon lifecycle detection.
Event Detection System
5 Distinct Event Types:
- 🟢
beacon_turned_on: New favorite beacon detected (power-on) - 🟠
button_pressed: Fast advertising interval detected (button pressed) - 🔵
beacon_normal: Normal advertising interval (standard operation) - 🔴
connection_failed: Failed to connect to beacon - 🟡
interval_read_failed: Failed to read advertising interval
Smart Detection Logic
Threshold-Based Classification:
- ≤threshold: Button pressed (fast advertising = 100-200ms)
- >threshold: Normal operation (slow advertising = 1300ms+)
- Default threshold: 200ms for optimal detection
Detection Flow:
- Beacon Appears →
beacon_turned_onevent - Connect & Read → Connect to beacon, read advertising interval
- Classify Event → Compare interval to threshold
- Emit Event → Send appropriate event type with full details
Cross-Platform Consistency
Android Implementation:
- Foreground service with continuous scanning
- Automatic connection to favorite devices
- Real-time interval reading and classification
- Location-based notifications
iOS Implementation:
- Core Location iBeacon region monitoring
- Fast Core Bluetooth connection on detection
- Identical interval reading and classification
- Same event types and data structure
Unified API:
// Same code works identically on both platforms
await FeasyBeacon.startBackgroundMonitoring(config: {
'thresholdMs': 200,
'favoriteAddresses': ['AA:BB:CC:DD:EE:FF'],
});
FeasyBeacon.backgroundEvents.listen((event) {
// event.eventType, event.intervalMs, event.timestamp, etc.
// Works the same on Android and iOS!
});
Professional Event Display
The example app includes a color-coded event system:
- Green icons for power-on events
- Orange icons for button press detection
- Blue icons for normal operation
- Red icons for connection failures
- Amber icons for reading failures
Each event shows:
- Device name and MAC address
- Actual advertising interval value
- Precise timestamp
- Event classification
- Location coordinates (when available)
API Table (condensed)
| Group | Method | Platforms |
|---|---|---|
| Scan | startScan(), stopScan(), scanStream |
Android, iOS |
| Background | startBackgroundMonitoring(config), stopBackgroundMonitoring(), backgroundEvents |
Android, iOS ✅ |
| Connect | connect(address, {pin}), disconnect(), isConnected() |
Android, iOS |
| Params | setDeviceName, setDevicePin, setBroadcastInterval, setTlm, setBroadcastExtensions, setTxPower, setGsensor, setKey, setPhy, setLongRange, setConnectable |
Android, iOS* (some platform-specific) |
| Beacon CRUD | addBeaconInfo(map), updateBeaconInfo(map, index), deleteBeaconInfo(index), getBeaconInfo(index), beaconBroadcastParameters() |
Android, iOS |
| OTA | checkDfuFile(bytes), startOtaUpdate(bytes, {reset}), otaProgressStream |
Android, iOS |
| Streams | packetStream, deviceInfoStream |
Android, iOS |
| iOS Helpers | setupDecryptKey(key128), setIBeaconUUIDs(uuids), getIBeaconUUIDs() |
iOS |
* Some setters are not supported on iOS (e.g., setConnectable) and will return an unimplemented error.
Roadmap
- iOS: expand optional setters support where applicable
- Vendor native binaries for pub.dev distribution and finalize packaging
- Expand tests and documentation
Troubleshooting
- Android 12+: ensure
BLUETOOTH_SCANandBLUETOOTH_CONNECTpermissions are granted at runtime. - Background monitoring requires a foreground service notification and may require
ACCESS_BACKGROUND_LOCATIONdepending on OEM policies. - iOS background monitoring is now fully supported with identical functionality to Android.
- If
permission_handlerreports denied permissions, guide users to system settings.
Android Gradle Plugin 8.x and vendored AARs:
- AGP 8.x warns about direct local AARs in library modules. The plugin compiles with vendored SDKs, but some app builds may require explicit inclusion of AARs at the app level.
- If you see “Could not find :FeasyBeaconLibrary-release:” in your app, add the AARs/JAR to your app module as files() dependencies (see the example app
android/app/build.gradle.kts).
License
- Plugin code: MIT (see LICENSE)
- Feasycom native SDKs are property of Feasycom. Verify licensing before redistribution in your app or a redistributed package.
Libraries
- adonna
- A Flutter plugin that wraps the Feasycom FeasyBeacon SDK for Android and iOS.
- feasybeacon_flutter_sdk