beekon_flutter 0.0.5
beekon_flutter: ^0.0.5 copied to clipboard
Flutter plugin for the Beekon location SDK (Android + iOS).
beekon_flutter #
Flutter plugin for the Beekon background-location SDK. A thin, type-safe pass-through over the Android AAR (io.github.wayqteam:beekon) and the iOS BeekonKit Swift module. The public Dart surface mirrors the native 0.0.5 API 1:1.
Hosts read history and observe live state / locations / geofence crossings / sync health. Writes never cross into Dart — in the background the Flutter engine is not guaranteed to be alive, so the native libraries own persistence and upload end-to-end.
Install #
dependencies:
beekon_flutter: ^0.0.5
Native SDKs are pulled in transitively — Android via Maven Central (io.github.wayqteam:beekon), iOS via SwiftPM (BeekonKit from wayqteam/beekon-ios-binary).
Usage #
import 'package:beekon_flutter/beekon_flutter.dart';
// Configure is optional — BeekonConfig() defaults are 30 s / 100 m, balanced.
await Beekon.instance.configure(
const BeekonConfig(
minTimeBetweenLocationsSeconds: 30,
minDistanceBetweenLocationsMeters: 100,
accuracyMode: AccuracyMode.balanced,
whenStationary: StationaryMode.pause,
// Optional background upload:
// sync: SyncConfig(url: 'https://your.endpoint/ingest'),
notification: NotificationConfig(title: 'Tracking location'), // Android-only
),
);
// state is replay-1 and the single source of truth — start()/stop() never throw.
Beekon.instance.state.listen((BeekonState s) {
if (s is Stopped && s.reason == StopReason.permissionDenied) {
// request location permission, then call start() again
}
});
// Live fixes — broadcast, no replay (only while the engine is alive).
Beekon.instance.locations.listen(
(Location loc) => debugPrint('${loc.latitude}, ${loc.longitude}'),
);
await Beekon.instance.start();
// Resume a session that survived process death (call early in main()).
await Beekon.instance.resumeIfNeeded();
// Read persisted history (survives process death; the live stream does not).
final List<Location> rows = await Beekon.instance.getLocations(
from: DateTime.now().subtract(const Duration(hours: 1)),
to: DateTime.now(),
);
// Geofences (persisted; fire across start/stop cycles).
await Beekon.instance.addGeofences(<BeekonGeofence>[
const BeekonGeofence(
id: 'home', latitude: 37.77, longitude: -122.41, radiusMeters: 150,
),
]);
Beekon.instance.geofenceEvents.listen(
(GeofenceEvent e) => debugPrint('${e.type.name} ${e.geofenceId}'),
);
await Beekon.instance.stop();
Public surface #
Beekon.instance mirrors the native Beekon object (Android) / Beekon.shared actor (iOS).
| Group | Members |
|---|---|
| Lifecycle | configure(BeekonConfig), start(), stop(), resumeIfNeeded() |
| History | getLocations({from, to}), deleteLocations({before}), pendingUploadCount() |
| Sync | sync(), setExtras(Map<String,String>) |
| Geofences | addGeofences(List<BeekonGeofence>), removeGeofences(List<String>), listGeofences() |
| Streams | state (replay-1), locations, geofenceEvents, syncStatus (replay-1) |
Models: BeekonConfig (+ SyncConfig, NotificationConfig), Location, BeekonGeofence, GeofenceEvent; sealed BeekonState (Idle/Tracking/Stopped(StopReason)), sealed SyncStatus (SyncIdle/SyncPending/SyncFailed(SyncFailure)); enums AccuracyMode, StationaryMode, LocationTrigger, MotionState, ActivityType, Transition, LocationQuality. Only getLocations/deleteLocations/pendingUploadCount (→ StorageException) and addGeofences (→ InvalidGeofence) throw — permission/services problems surface on state, never as exceptions.
Required platform setup #
Android #
The plugin's manifest is empty — the SDK AAR declares the foreground service and all permissions, which merge into your app. The SDK auto-initializes (AndroidX Startup); there is no initialize(). Your app must request ACCESS_FINE_LOCATION (and on API 33+ POST_NOTIFICATIONS) at runtime before start(); observe state for Stopped(permissionDenied).
iOS #
iOS dependencies are distributed via Swift Package Manager — automatic on Flutter 3.44+, or enable once on 3.32–3.43 with flutter config --enable-swift-package-manager. CocoaPods is not supported.
Add to ios/Runner/Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Why your app needs in-use location.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Why your app needs background location.</string>
<key>NSMotionUsageDescription</key> <!-- only if detectActivity: true -->
<string>Why your app classifies activity.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>fetch</string> <!-- required for background sync -->
</array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>in.wayq.beekon.sync</string>
</array>
The plugin registers Beekon's background task at launch (during plugin registration) and installs the actor's cold-launch resume hooks — no app code needed for that.
Caveats & platform differences #
- Live streams stop in the background. When the Flutter engine is suspended,
state/locations/geofenceEvents/syncStatusgo quiet; native persistence keeps running. UsegetLocations(from, to)for data captured while you weren't subscribed. - Faithful passthrough.
Location.accuracy/speed/bearing/altitudearenullwhen the OS didn't report them — never substituted with0. No smoothing or filtering. minTimeBetweenLocationsSeconds == 0disables the time gate on Android; iOS floors it to 10 s.stationaryRadiusMetersis clamped to[5, 1000]by both.notificationis Android-only — iOS ignores it.resumeIfNeeded()maps to the native intent-aware resume on iOS; on Android it resumes from the persisted config (the foreground service is alsoSTART_STICKY).
Layout #
beekon_flutter/
├── pubspec.yaml
├── pigeons/beekon_messages.dart # Pigeon spec — single source of truth; re-gen via tool/pigeon.sh
├── lib/ # public Dart API (faithful 1:1 mirror of native 0.0.5)
├── android/ # Kotlin plugin glue (depends on the AAR)
├── ios/ # Swift plugin glue (SwiftPM only)
└── example/ # Flutter sample app (live / history / geofences)