advanced_background_locator 0.1.0
advanced_background_locator: ^0.1.0 copied to clipboard
Flutter plugin for location tracking that keeps running in the background (Android foreground service, iOS background location updates).
advanced_background_locator #
A Flutter plugin that keeps receiving location updates while the app is in the background.
- Android: a foreground service (
foregroundServiceType="location") with a persistent notification runs a background Flutter isolate. Locations come from the Fused Location Provider (Google Play services) or the platformLocationManager. - iOS:
CLLocationManagerwithallowsBackgroundLocationUpdates, plus significant location changes and region monitoring to relaunch the app.
Your callbacks run in a separate background isolate, not in your UI isolate. Use IsolateNameServer/SendPort (see the example) to send data back to the UI.
Requirements #
| Minimum | |
|---|---|
| Flutter / Dart | 3.32 / 3.8 |
| Android | API 24, built with compileSdk 36 |
| iOS | 13.0 |
Installation #
dependencies:
advanced_background_locator: ^0.1.0
Android setup #
The plugin manifest already declares the foreground service, the boot receiver and these permissions:
ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, FOREGROUND_SERVICE, FOREGROUND_SERVICE_LOCATION, POST_NOTIFICATIONS, WAKE_LOCK, RECEIVE_BOOT_COMPLETED.
If you upgraded from 0.0.x, remove the IsolateHolderService <service> and BootBroadcastReceiver <receiver> entries from your app manifest.
-
If you need location while the app is not visible and want tracking to resume after a reboot, declare background location in
android/app/src/main/AndroidManifest.xml. Google Play reviews apps that use it, so only add it if your feature needs it:<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> -
Make the main activity
singleTopso notification taps reach the running app:<activity android:name=".MainActivity" android:launchMode="singleTop" ...> -
Request permissions at runtime (for example with
permission_handler) before callingregisterLocationUpdate:ACCESS_FINE_LOCATIONorACCESS_COARSE_LOCATION(required, otherwise aPERMISSION_DENIEDPlatformExceptionis thrown),POST_NOTIFICATIONSon Android 13+ (otherwise the service runs but its notification is hidden),- optionally
ACCESS_BACKGROUND_LOCATION("Allow all the time"), which Android 11+ only grants in a separate request after foreground location.
Platform rules to keep in mind:
- Android 12+: a foreground service cannot be started while the app is in the background. Call
registerLocationUpdatewhile your UI is visible; otherwise aSERVICE_START_FAILEDPlatformExceptionis thrown. - Android 14+: the service is started with the
locationtype, which requires a granted location permission at that moment. - Reboot: tracking is resumed after
BOOT_COMPLETEDonly if it was running before the reboot (it was not stopped withunRegisterLocationUpdate) and background location is granted. wakeLockTime(minutes) limits how long the partial wake lock is held.
iOS setup #
The iOS implementation could not be compiled on the maintainer's current (Windows) machine for 0.1.0. Please report any build problems.
-
Add usage descriptions and the location background mode to
ios/Runner/Info.plist:<key>NSLocationWhenInUseUsageDescription</key> <string>Why your app needs location while in use.</string> <key>NSLocationAlwaysAndWhenInUseUsageDescription</key> <string>Why your app needs location in the background.</string> <key>UIBackgroundModes</key> <array> <string>location</string> </array> -
Register plugins for the background isolate in
AppDelegate.swift, so plugins such aspath_providerwork in your callbacks:import Flutter import UIKit import advanced_background_locator func registerPlugins(registry: FlutterPluginRegistry) { if !registry.hasPlugin("BackgroundLocatorPlugin") { GeneratedPluginRegistrant.register(with: registry) } } @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) BackgroundLocatorPlugin.setPluginRegistrantCallback(registerPlugins) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } -
registerLocationUpdatecallsrequestAlwaysAuthorization. iOS first grants "While Using" and may later ask the user to upgrade to "Always".
Usage #
All callbacks must be top-level or static functions annotated with @pragma('vm:entry-point'), so they can be looked up from the background isolate (and are not removed by tree shaking in release builds). Passing a closure throws an ArgumentError.
import 'package:advanced_background_locator/advanced_background_locator.dart';
@pragma('vm:entry-point')
void onLocation(LocationDto location) {
// Runs in the background isolate.
print('${location.latitude}, ${location.longitude}');
}
@pragma('vm:entry-point')
void onInit(Map<String, dynamic> data) {}
@pragma('vm:entry-point')
void onDispose() {}
@pragma('vm:entry-point')
void onNotificationTap() {}
Future<void> start() async {
await AdvancedBackgroundLocator.initialize();
await AdvancedBackgroundLocator.registerLocationUpdate(
onLocation,
initCallback: onInit,
initDataCallback: {'startedAt': DateTime.now().toIso8601String()},
disposeCallback: onDispose,
autoStop: false, // true: stop when the app goes to the background
iosSettings: const IOSSettings(
accuracy: LocationAccuracy.NAVIGATION,
distanceFilter: 0,
showsBackgroundLocationIndicator: true,
stopWithTerminate: false,
),
androidSettings: const AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5, // seconds
distanceFilter: 0, // meters
wakeLockTime: 60, // minutes
client: LocationClient.google, // or LocationClient.android
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Tracking location',
notificationMsg: 'Your location is being recorded',
notificationIcon: '', // mipmap/drawable name, defaults to the app icon
notificationTapCallback: onNotificationTap,
),
),
);
}
Future<void> stop() => AdvancedBackgroundLocator.unRegisterLocationUpdate();
Other APIs:
AdvancedBackgroundLocator.isServiceRunning()/isRegisterLocationUpdate()AdvancedBackgroundLocator.updateNotificationText(title:, msg:, bigMsg:)(Android only)
LocationDto #
latitude, longitude, accuracy, altitude, speed, speedAccuracy, heading, time (milliseconds since epoch), isMocked (Android, iOS 15+), provider (Android only, empty on iOS).
Accuracy mapping #
LocationAccuracy |
Android (Priority) |
iOS |
|---|---|---|
POWERSAVE |
PRIORITY_PASSIVE |
kCLLocationAccuracyKilometer |
LOW |
PRIORITY_LOW_POWER |
kCLLocationAccuracyHundredMeters |
BALANCED |
PRIORITY_BALANCED_POWER_ACCURACY |
kCLLocationAccuracyNearestTenMeters |
HIGH |
PRIORITY_HIGH_ACCURACY |
kCLLocationAccuracyBest |
NAVIGATION |
PRIORITY_HIGH_ACCURACY |
kCLLocationAccuracyBestForNavigation |
Example #
See the example directory for a complete app that logs locations to a file and shows them in the UI.
License #
MIT, see LICENSE.