Skip to content

Methods

Import the facade — every method below is a static method on it:

import 'package:bgeo_background_geolocation/bgeo_background_geolocation.dart' as bg;

Event listeners (onLocation, onProviderChange, onHttp, onGeofence, onGeofencesChange, …) have their own page: see the Events reference. This page covers every documented method, grouped into: Lifecycle, Geolocation, Permissions & sensors, Odometer, HTTP & upload queue, Authorization, Geofences, Logging, Events management, and Headless — see the Geofencing guide for proximity slicing and DWELL semantics alongside the geofence methods below.

All methods at a glance

MethodPurpose
ready()Apply config at launch; auto-resumes tracking if it was left enabled.
setConfig()Merge config into the live tracker.
start()Begin tracking.
stop()Stop tracking.
getState()Read the current state snapshot without side effects.
changePace()Force the motion state machine into moving/stationary.
getCurrentPosition()Request a single fix.
watchPosition()Start a continuous fix stream via a callback.
stopWatchPosition()Stop the watchPosition() stream.
requestPermission()Run the platform permission-request flow.
requestTemporaryFullAccuracy()iOS: request temporary full accuracy after Reduced Accuracy.
getProviderState()One-shot snapshot of the location provider/authorization.
isPowerSaveMode()Read the OS power-saving state.
getOdometer()Read the cumulative odometer, metres.
setOdometer()Set the odometer to a new value.
resetOdometer()Zero the odometer (alias for setOdometer(0.0)).
sync()Manually drain the upload queue.
getLocations()Read every queued record, oldest-first.
destroyLocations()Delete every queued record.
getCount()Count of records currently queued.
destroyLocation()Delete one queued record by uuid.
insertLocation()Manually enqueue a location-shaped record.
getAuthState()Read the tokens the native uploader currently holds.
addGeofence()Add (or overwrite) one geofence.
addGeofences()Bulk add/overwrite geofences.
removeGeofence()Remove one geofence by identifier.
removeGeofences()Remove every geofence.
getGeofences()Read the full persisted geofence set.
geofenceExists()Whether a geofence with a given identifier is persisted.
loggerWrite app-side log lines (.error/.warn/.info/.debug/.verbose).
getLog()Read persisted log entries, newest-first.
destroyLog()Delete every persisted log entry.
uploadLog()Manually trigger a log upload.
removeListeners()Detach every listener and any active watchPosition().
registerHeadlessTask()Android: register a task invoked while the Dart isolate isn’t running.

Futures, not callbacks

Every method on this page (other than watchPosition(), which is callback-only by design — see below) is a plain Future-returning Dart method with no success/failure callback parameters. There is no RN-style dual callback-and-promise contract to reason about here: await it or attach .then()/.catch() as usual, and a rejected Future always completes with an error (typically a PlatformException) rather than resolving with it.

try {
final state = await bg.BackgroundGeolocation.ready(bg.Config(
desiredAccuracy: bg.desiredAccuracyHigh,
distanceFilter: 30,
stopOnTerminate: false,
startOnBoot: true,
url: 'https://your-server.example/locations',
));
print('${state.enabled}');
} catch (error) {
print('ready failed: $error');
}

Lifecycle

ready()

static Future<State> ready(Config config)
ParameterTypeDescription
configConfigFull SDK configuration.

Returns: Future<State> — applies config and, if tracking was previously enabled, auto-resumes it. In a release build with an invalid or missing license key, the returned Future completes with a PlatformException whose code is one of the license* codes.

final state = await bg.BackgroundGeolocation.ready(bg.Config(
desiredAccuracy: bg.desiredAccuracyHigh,
distanceFilter: 30,
stopOnTerminate: false,
startOnBoot: true,
url: 'https://your-server.example/locations',
));
print('${state.enabled} ${state.raw['trackingActive']}');

setConfig()

static Future<State> setConfig(Config config)
ParameterTypeDescription
configConfigPartial or full configuration — unset fields are omitted from the native payload, so existing/persisted values apply. Merged into the live tracker.

Returns: Future<State> — the merged state. Some keys (e.g. locationFilterPolicy, kalmanProfile) only take effect the next time the filter is rebuilt at tracking start/stop, not immediately — see the Config reference for which keys apply live.

await bg.BackgroundGeolocation.setConfig(bg.Config(distanceFilter: 50));

start()

static Future<State> start()

Returns: Future<State> — begins tracking. Completes with a PlatformException carrying a license* code in a release build with an invalid key.

await bg.BackgroundGeolocation.start();

stop()

static Future<State> stop()

Returns: Future<State> — stops tracking.

await bg.BackgroundGeolocation.stop();

getState()

static Future<State> getState()

Returns: Future<State> — the current state snapshot without side effects.

final state = await bg.BackgroundGeolocation.getState();
print('${state.enabled} ${state.isMoving} ${state.odometer}');

changePace()

static Future<void> changePace(bool isMoving)
ParameterTypeDescription
isMovingboolForce the motion state machine into moving (true) or stationary (false).

Returns: Future<void> — completes with a PlatformException(DISABLED) while tracking is currently stopped. See the tracking lifecycle guide for how this interacts with the automatic motion-detection state machine.

await bg.BackgroundGeolocation.changePace(true); // force moving

Geolocation

getCurrentPosition()

static Future<Location> getCurrentPosition([CurrentPositionOptions? options])
ParameterTypeDescription
optionsCurrentPositionOptions?Optional. Omit entirely for engine defaults.

Returns: Future<Location> — a single fix. Like ready()/start(), completes with a PlatformException carrying a license* code in a release build with an invalid key.

final location = await bg.BackgroundGeolocation.getCurrentPosition(
bg.CurrentPositionOptions(
samples: 3,
timeout: 30, // seconds
desiredAccuracy: bg.desiredAccuracyHigh,
),
);

watchPosition()

static Future<void> watchPosition(
void Function(Location) onLocation, {
void Function(int errorCode)? onError,
WatchPositionOptions? options,
})
ParameterTypeDescription
onLocationvoid Function(Location)Called for every fix in the continuous stream.
onErrorvoid Function(int errorCode)?Optional. Called with a numeric error code.
optionsWatchPositionOptions?Optional.

Returns: Future<void> — this resolves as soon as the platform-channel call that arms the watch completes; the fixes themselves only ever reach you via the onLocation callback, never through the returned Future. Internally, fixes from watchPosition() ride the same native location event as onLocation() on the Events reference, but are distinguished by an extras['watch'] marker the native side sets; only fixes carrying that marker reach your onLocation callback here. Calling watchPosition() again first cancels the previous watch’s subscriptions before re-arming — only one active watch is supported at a time.

If the underlying watchPosition platform call throws (e.g. a license* rejection in a release build), the error is caught internally and forwarded to onError instead of rejecting the returned Future — but only as a bare int: the catch handler converts the PlatformException via int.tryParse(exception.code), so a numeric error code round-trips correctly, while a non-numeric code (every license* value is a string like 'LICENSE_MISSING', not a numeric string) fails to parse and onError receives 0 instead. In that case the original PlatformException/its string code is not otherwise surfaced through this method — prefer ready()/start()/getCurrentPosition() if you need to distinguish the specific license* failure reason.

await bg.BackgroundGeolocation.watchPosition(
(location) => print('fix ${location.coords}'),
onError: (errorCode) => print('watch error $errorCode'),
options: bg.WatchPositionOptions(interval: 2000, desiredAccuracy: bg.desiredAccuracyHigh),
);

stopWatchPosition()

static Future<void> stopWatchPosition()

Returns: Future<void> — cancels the onLocation/onError subscriptions registered by watchPosition() and stops the native watch.

await bg.BackgroundGeolocation.stopWatchPosition();

Permissions & sensors

requestPermission()

static Future<int> requestPermission()

Returns: Future<int> — one of the authorizationStatus* constants, resolved by the platform’s native permission-request flow. See the permissions guide for the full store-declaration requirements on both platforms.

final status = await bg.BackgroundGeolocation.requestPermission();
if (status == bg.authorizationStatusAlways) {
await bg.BackgroundGeolocation.start();
}

requestTemporaryFullAccuracy()

iOS
static Future<int> requestTemporaryFullAccuracy(String purpose)
ParameterTypeDescription
purposeStringMust exactly match a key in the app’s NSLocationTemporaryUsageDescriptionDictionary in Info.plist — see the permissions guide.

Returns: Future<int> — resolves accuracyAuthorizationFull (0) or accuracyAuthorizationReduced (1) on iOS 14+. Resolves 0 unconditionally on Android and on iOS below 14.

try {
final accuracy =
await bg.BackgroundGeolocation.requestTemporaryFullAccuracy('DeliverFullAccuracy');
} catch (error) {
print('temporary accuracy request failed: $error');
}

getProviderState()

static Future<ProviderChangeEvent> getProviderState()

Returns: Future<ProviderChangeEvent> — a one-shot snapshot of the location provider. status is one of the authorizationStatus* constants; enabled/gps/network reflect whether location services and the individual GPS/network providers are currently on. This is the same shape delivered by onProviderChange.

final provider = await bg.BackgroundGeolocation.getProviderState();
print('${provider.status} ${provider.enabled} ${provider.gps}');

On iOS, enabled, gps, and network all reflect the same underlying “location services enabled” flag — there is no independent GPS-vs-network provider distinction on that platform. On Android they’re independent: gps and network reflect the actual state of each provider.

isPowerSaveMode()

static Future<bool> isPowerSaveMode()

Returns: Future<bool> — the current OS power-saving state: Android battery saver, or iOS Low Power Mode.

final saving = await bg.BackgroundGeolocation.isPowerSaveMode();

Odometer

getOdometer()

static Future<double> getOdometer()

Returns: Future<double> — the current odometer reading in metres.

final metres = await bg.BackgroundGeolocation.getOdometer();

setOdometer()

static Future<Location> setOdometer(double value)
ParameterTypeDescription
valuedoubleNew odometer value, in metres.

Returns: Future<Location> — resolves with the current location at the moment the odometer was set. That Location is a sample fix (sample: true, event: 'odometer'), not a record from the ambient tracking stream.

await bg.BackgroundGeolocation.setOdometer(0.0);

resetOdometer()

static Future<Location> resetOdometer()

Returns: Future<Location> — zeroes the odometer. This is exactly setOdometer(0.0): a thin Dart-side alias with no separate native call, so it resolves the current Location for the same reason setOdometer() does (Transistor semantics — the odometer methods resolve the reference fix used to (re)anchor the reading, not the odometer value itself).

await bg.BackgroundGeolocation.resetOdometer();

HTTP & upload queue

See the HTTP upload & authorization guide for the full picture of how url, autoSync, and the SQLite-backed queue interact.

sync()

static Future<List<Location>> sync()

Returns: Future<List<Location>> — manually drains the upload queue. The resolved list is a snapshot of the queue taken before the drain starts, not what remains afterward: internally, sync() first calls getLocations() to read the currently-queued records, then tells the native side to drain the queue via a separate sync channel call, and finally resolves with the records it captured in that first read (Transistor semantics). If more records are enqueued concurrently, they are not included in the resolved list even though they may also be uploaded in the same drain.

final drained = await bg.BackgroundGeolocation.sync();
print('uploading ${drained.length} queued records');

getLocations()

static Future<List<Location>> getLocations()

Returns: Future<List<Location>> — every record currently queued for upload, oldest-first.

final queued = await bg.BackgroundGeolocation.getLocations();

destroyLocations()

static Future<int> destroyLocations()

Returns: Future<int> — deletes every queued record and resolves with the number removed.

final removed = await bg.BackgroundGeolocation.destroyLocations();

getCount()

static Future<int> getCount()

Returns: Future<int> — the number of records currently queued for upload.

final count = await bg.BackgroundGeolocation.getCount();

destroyLocation()

static Future<void> destroyLocation(String uuid)
ParameterTypeDescription
uuidStringThe queued record’s uuid.

Returns: Future<void> — deletes one queued record. Completes with a PlatformException(NOT_FOUND) if no queued record has that uuid.

try {
await bg.BackgroundGeolocation.destroyLocation(location.uuid);
} catch (error) {
// error is a PlatformException with code 'NOT_FOUND' if it was already gone
// (e.g. already synced and pruned).
}

insertLocation()

static Future<void> insertLocation(Map<String, dynamic> location)
ParameterTypeDescription
locationMap<String, dynamic>A location-shaped record to enqueue manually.

Returns: Future<void> — enqueues the record through the normal persist path, so autoSync applies to it exactly like a tracked fix.

await bg.BackgroundGeolocation.insertLocation({
'uuid': 'manual-checkin-1',
'timestamp': DateTime.now().toUtc().toIso8601String(),
'coords': {'latitude': 52.23, 'longitude': 21.01, 'accuracy': 5},
});

Authorization

getAuthState()

static Future<AuthState> getAuthState()

Returns: Future<AuthState> — the tokens the native uploader currently holds, from Config.authorization. The native side may have refreshed them while the app was killed (a background HTTP 401/403 triggers refreshUrl with no Dart isolate involved), so your app should prefer this over its own persisted copy on foreground to avoid resuming with a token the server has already invalidated. See the HTTP guide’s authorization section and onAuthorization for the corresponding push notification of refresh outcomes.

final auth = await bg.BackgroundGeolocation.getAuthState();
// On app foreground, reconcile auth.accessToken/auth.refreshToken against your in-memory/store copy.

Geofences

Full CRUD is available at any time (tracking does not need to be started); geofences persist in the SDK’s own SQLite database and survive app restarts and reboots. Only a proximity-sliced subset is registered with the OS at any moment — see the Geofencing guide for proximity slicing, the platform registration budget, and DWELL semantics. Transitions themselves are delivered via the onGeofence/onGeofencesChange events — see the Events reference; they are not documented on this page.

addGeofence()

static Future<void> addGeofence(Geofence geofence)
ParameterTypeDescription
geofenceGeofenceidentifier, radius, latitude, longitude, and optionally notifyOnEntry/notifyOnExit/notifyOnDwell/loiteringDelay/extras.

Returns: Future<void> — adds one geofence. An existing geofence with the same identifier is overwritten and re-registered with the fresh values rather than duplicated. The native engine validates the geofence — see the Geofencing guide for the validation rules.

await bg.BackgroundGeolocation.addGeofence(bg.Geofence(
identifier: 'home',
radius: 150,
latitude: 52.2297,
longitude: 21.0122,
notifyOnEntry: true,
notifyOnExit: true,
));

addGeofences()

static Future<void> addGeofences(List<Geofence> geofences)
ParameterTypeDescription
geofencesList<Geofence>Bulk variant of addGeofence().

Returns: Future<void> — adds (or overwrites, per-identifier) every geofence in the list.

await bg.BackgroundGeolocation.addGeofences([
bg.Geofence(identifier: 'home', radius: 150, latitude: 52.2297, longitude: 21.0122, notifyOnEntry: true),
bg.Geofence(identifier: 'office', radius: 100, latitude: 52.2319, longitude: 21.0067, notifyOnEntry: true, notifyOnExit: true),
]);

removeGeofence()

static Future<void> removeGeofence(String identifier)
ParameterTypeDescription
identifierStringThe geofence’s identifier.

Returns: Future<void> — removes the one geofence matching identifier.

await bg.BackgroundGeolocation.removeGeofence('office');

removeGeofences()

static Future<void> removeGeofences()

Returns: Future<void> — removes all geofences (Transistor semantics) — the whole persisted set, not just the subset currently registered with the OS.

await bg.BackgroundGeolocation.removeGeofences();

getGeofences()

static Future<List<Geofence>> getGeofences()

Returns: Future<List<Geofence>> — the full persisted set, not just the proximity-sliced subset currently registered with the OS.

final geofences = await bg.BackgroundGeolocation.getGeofences();

geofenceExists()

static Future<bool> geofenceExists(String identifier)
ParameterTypeDescription
identifierStringThe geofence’s identifier.

Returns: Future<bool> — whether a geofence with that identifier is currently persisted.

final exists = await bg.BackgroundGeolocation.geofenceExists('home');

Logging

See the logging & debugging guide for retention, upload cadence, and field reference.

logger

static const Logger logger;
class Logger {
Future<void> error(String message, [Map<String, dynamic>? data]);
Future<void> warn(String message, [Map<String, dynamic>? data]);
Future<void> info(String message, [Map<String, dynamic>? data]);
Future<void> debug(String message, [Map<String, dynamic>? data]);
Future<void> verbose(String message, [Map<String, dynamic>? data]);
}
ParameterTypeDescription
messageStringLog line text.
dataMap<String, dynamic>?Optional. JSON-encoded and stored alongside the line.

Returns: each method returns Future<void> and never rejects — every call is wrapped in a try/catch that swallows the error, so a logging call must never throw into app code. App-written lines are persisted in the same on-device store as native engine logs (tagged src: "js" for wire-format parity with the RN/Transistor SDKs) and uploaded through the same batches described in the logging guide.

bg.BackgroundGeolocation.logger.warn('geofence sync deferred', {'pendingCount': 3});

getLog()

static Future<List<LogEntry>> getLog({int limit = 500})
ParameterTypeDescription
limitintOptional, named. Maximum number of entries to return. Default 500.

Returns: Future<List<LogEntry>> — persisted log entries, newest-first.

final recent = await bg.BackgroundGeolocation.getLog(limit: 100);

destroyLog()

static Future<int> destroyLog()

Returns: Future<int> — deletes all persisted log entries and resolves with the number of rows removed.

await bg.BackgroundGeolocation.destroyLog();

uploadLog()

static Future<int> uploadLog()

Returns: Future<int> — manually triggers a log upload and resolves with the number of rows handed to the flusher (not necessarily the number successfully delivered — retryable failures stay queued).

await bg.BackgroundGeolocation.uploadLog();

Events management

removeListeners()

static Future<void> removeListeners()

Returns: Future<void> — cancels every subscription registered through any on* method on the Events reference (including geofence listeners), and also cancels any active watchPosition() subscription, exactly as if stopWatchPosition() had been called. Call this from your top-level teardown (e.g. on sign-out or State.dispose()) rather than cancelling listeners one at a time.

await bg.BackgroundGeolocation.removeListeners();

Headless

registerHeadlessTask()

Android
static Future<void> registerHeadlessTask(HeadlessTask task)
typedef HeadlessTask = Future<void> Function(HeadlessEvent event);
ParameterTypeDescription
taskHeadlessTaskInvoked in a background isolate when an event fires while the Dart isolate is not running. Must be a top-level function or a static method — annotate it with @pragma('vm:entry-point').

Returns: Future<void>. On Android, this resolves a CallbackHandle for task via PluginUtilities.getCallbackHandle and registers it with the native side so a background FlutterEngine can be booted on a killed-app event and invoke it — see the boot & killed-app guide for when this fires. If task is a closure or instance method, PluginUtilities.getCallbackHandle cannot resolve a handle for it, and the call throws an ArgumentError immediately, before any platform channel call — this is a synchronous Dart-side check, not a rejected Future. iOS has no headless dispatch service at all: the task is retained on the Dart side but never registered with a background isolate, so it is not invoked while the app process isn’t running.

@pragma('vm:entry-point')
Future<void> headlessTask(bg.HeadlessEvent event) async {
if (event.name == 'heartbeat') {
print('headless heartbeat ${event.params}');
}
}
await bg.BackgroundGeolocation.registerHeadlessTask(headlessTask);