KasyKasy

Background location

GPS keeps publishing the user's position even with the app minimized. Standalone module, wired automatically into Drive.

Background location is a standalone module: it captures and publishes the user's GPS even while the app is minimized, something the kit's normal location tracking (used by Drive) doesn't do on its own. It works with or without Drive in the project, is native only (iOS/Android), and doesn't use any paid library, it runs on top of the geolocator package already included in the kit.

Background tracking only exists in the native app. On the web, the browser doesn't keep GPS running with the tab minimized, that's a browser limitation, not a kit limitation. Web still views the position normally (Drive already shows the driver's position to whoever is watching from the web), it just can't publish its own location in the background.

Implementation

Via MCP: ask the assistant "add background location to my project". It runs add_feature for you. See Kasy MCP.

  1. Enable the module (not included in Quick mode by default, same as Drive):

    kasy add background_location        # add
    kasy remove background_location     # remove
    kasy add --list                     # list what's enabled

    kasy add background_location inserts the native permissions on both sides: ACCESS_BACKGROUND_LOCATION + FOREGROUND_SERVICE_LOCATION in AndroidManifest.xml, and NSLocationAlwaysAndWhenInUseUsageDescription + location in Info.plist's UIBackgroundModes. The template does not ship with this by default, it's opt-in: requesting "always" location without a real need gets an app rejected in App Store/Play Store review.

  2. If the project already has Drive, the module wires in on its own: DriveLocationGateway starts using background tracking automatically, no manual edit needed. If you remove the module later, Drive goes back to foreground-only tracking, nothing breaks.

  3. Without Drive, the module works the same way, nothing calls it by default. Use BackgroundLocationController (core/background_location/) in your own code: start() to begin tracking, positions to listen to the stream, stop() to stop.

  4. Before shipping, review the disclosure screen copy (Google Play requirement: "prominent disclosure", explain the reason before the native permission prompt). The default text lives in lib/i18n/*.i18n.json, key background_location.disclosure_message: adjust it for your actual use case.

  5. Test on a physical device, there's no way to confirm this in a simulator or on the web:

    • Android: flutter run on a device or emulator, grant "Allow all the time", minimize the app (don't kill it) and confirm the persistent notification appears and position keeps reaching the backend.
    • iOS: needs to be a physical device (the simulator doesn't reliably hold background execution). Install via Xcode with your developer account, no need for TestFlight just to test it yourself.

Information

How it works

The engine is BackgroundLocationController (core/background_location/background_location_provider.dart), built on top of geolocator, the same library the rest of the kit already uses for location, just configured with AndroidSettings.foregroundNotificationConfig (Android) and AppleSettings.allowBackgroundLocationUpdates (iOS), the two pieces that make the GPS keep delivering position with the app minimized.

Permission is requested in two steps, because both operating systems require it:

  • Android: first requests regular location (ACCESS_FINE_LOCATION), then, in a second call, requests ACCESS_BACKGROUND_LOCATION. Android hasn't allowed requesting both together since version 11.
  • iOS: first grants "While Using", then the system offers the upgrade to "Always". It only appears after some real usage time, not immediately.

Integration with Drive

The entry point is a single file: features/drive/providers/drive_location_gateway.dart. When withBackgroundLocation is on, watchPosition() uses BackgroundLocationController instead of the default KasyLocation; when it's off, it falls back to the usual behavior. No other part of Drive (route, camera, ETA, screens) changes, since all of it already consumed position through a shared stream, not directly from the GPS.

Installing the two features in either order works the same, the CLI handles the integration both ways.

Driver only, today. driver_mode_notifier.dart is what calls watchPosition(), triggered when the driver goes online, it's the only Drive flow that needs continuous position (the passenger tracks the driver's car moving on the map). The passenger never runs this engine: their position is captured once, at request time, and never updated again, even with the app in the foreground, because today no Drive screen or piece of data depends on tracking the passenger moving. Extending it to the passenger is possible (the engine is generic, see Drive), but it's only worth it with a real feature behind it (e.g. showing the passenger walking to the meeting point), asking for "always" permission with no real use works against you in store review.

If the person denies the "always" permission

The app keeps working normally with foreground-only location, exactly as it would without the module installed. There's no blocking screen and nothing stops working, background tracking just stays unavailable until the person enables it (system settings).

Across the 3 backends

This module is purely device-side, it doesn't change the schema or endpoints on any of the 3 backends (Firebase, Supabase, API). The published data stays the same (lat, lng, status), it just starts arriving even with the app minimized.

Last updated on 08/23/2026