Example app
The SDK’s source repo ships a full example app under example/ — a dev/debug
tool, not a template to copy into your project. It depends on the plugin the
same way a real consuming app would: a path: dependency in its
pubspec.yaml, resolved straight from the repo’s lib/, so editing plugin
source is immediately reflected in the example on the next hot restart
without requiring a pub publish step.
Unlike the React Native example (a three-tab map/settings/logs app), the Flutter example is deliberately minimal: a single screen that exercises every major API surface — lifecycle, permissions, a one-shot fix, pace control, manual sync, and the native log — without a map view or a persisted-config editor.
Running it
cd exampleflutter pub getflutter runBy default the app runs local-only: no url is set, so fixes are
filtered, persisted, and delivered to onLocation, but nothing uploads. To
point it at a server, pass the URL at build/run time via --dart-define:
flutter run --dart-define=BGEO_URL=https://your-server.example.com/locationsString.fromEnvironment('BGEO_URL') reads that value at compile time; when
it’s empty, the example passes url: null to Config, which is exactly
persist-only mode (see the HTTP guide).
Both a debug Android build and an iOS simulator/debug build run
unlicensed — no real license key is
required. The example’s manifest/Info.plist license entries are already
set to the literal string EVALUATION on both platforms, which is honoured
identically to an unset key in a debug build; swap in a real BGEO1... key
only if you build a release binary from this example.
Tour of the screen
The entire UI is one Scaffold: a switch, a row of buttons, and a scrolling
log list.
Tracking switch
A SwitchListTile at the top reflects state.enabled — the boolean every
lifecycle method (ready(), start(), stop()) resolves with. Flipping it
calls start() or
stop() and updates the switch from
the returned State, rather than tracking a separate local boolean:
Future<void> _toggleTracking(bool value) async { final state = value ? await BackgroundGeolocation.start() : await BackgroundGeolocation.stop(); setState(() => _enabled = state.enabled);}Action buttons
A Wrap of five buttons covers the rest of the one-shot API surface:
- Request Permission — calls
requestPermission()directly; the resolved authorization status isn’t displayed, just requested. - Get Current Position — calls
getCurrentPosition()and appends the resulting coordinates to the log. - Change Pace — calls
changePace(true), forcing the engine into the moving state regardless of actual motion. - Sync — calls
sync()to drain the upload queue on demand. - Log → console — calls
getLog()and prints every entry’s timestamp, source/event tag, and message viadebugPrint, rather than rendering them on screen.
Event log
Six listeners are wired up once in initState/_init —
onLocation,
onMotionChange,
onHeartbeat,
onHttp,
onProviderChange, and
onConnectivityChange
— each formatting a one-line summary and prepending it to an in-memory list
rendered by a ListView.builder below the buttons, newest-first. This is a
live event feed, not the native log store: it only shows events received
while the screen is mounted, and it’s cleared on hot restart. For the
native, persisted log store (what getLog()/log upload actually retain),
see the logging guide.
All six subscriptions are cancelled together in dispose() via
removeListeners(), and
a headless task is registered once at startup — see
Boot & killed-app behavior: Android headless tasks
for what that task can and can’t do.
Connecting to the BGeo web console
The example app has no built-in “link device” screen — unlike the React
Native example’s Settings-tab linking flow, wiring this app up to the
web console means calling
setConfig() yourself with the
url, logUrl, and authorization values a registration-code exchange
returns. See the web console guide’s linking section
for the exact request/response shape and the equivalent Dart snippet.