Skip to content
All posts

Oct 11, 2026 · Mobile · 4 min read

Background location tracking in Flutter that survives bad networks

In on-demand delivery and logistics apps, real-time rider tracking is the backbone of customer trust. Customers want to see their parcel moving across a map, and operations dispatchers need to know which driver is closest to an incoming pickup. When developing in Flutter, achieving reliable tracking is notoriously difficult under real-world conditions.

In Nairobi and across East Africa, riders navigate cellular blind spots, underground basements, and patchy network coverage. Worse still, aggressive OEM Android power managers kill background services without warning, and frequent GPS polling rapidly drains motorbike riders' phones. If your mobile app tries to HTTP POST every GPS point individually over the network, you will drop points, drain batteries, and overwhelm your API.

Here is how to design a resilient, disk-backed telemetry queue in Flutter so recorded locations survive bad connectivity and the app being killed.

1. The Core Failure Modes of Mobile Telemetry

When tracking active drivers in the field, naive socket-only or direct-HTTP implementations fail because of three factors:

  • Spotty Connectivity: If an HTTP request fails while a rider enters a dead zone, an in-memory buffer risks being cleared or overflowing when memory pressure builds.
  • Process Termination: Android's Low Memory Killer (LMK) or aggressive battery optimizations (especially on brands like Xiaomi, Oppo, and Samsung) can kill your app while it is backgrounded. Any points stored purely in RAM are lost forever.
  • Thermal and Battery Throttling: Firing high-accuracy GPS fixes every 2 seconds while constantly waking the radio chip can drain a battery well before the end of a shift and trigger thermal throttling.

2. The Telemetry Data Model

Every location fix must be self-contained. Rather than sending just latitude and longitude, each record should contain client-side timestamps, accuracy thresholds, speed, bearing, and a unique identifier generated on the device.

3. A Disk-Backed Persistence Queue

Whenever the GPS provider yields a valid coordinate, never make a direct network call inside the listener. Instead, immediately write the record to a persistent local store—such as SQLite (sqflite) or an append-only file. A local write is cheap compared with a network round trip, and the coordinate survives even if the app is killed a second later.

4. Batching, Exponential Backoff, and Retention Policies

A separate background worker loop drains the queue. It fetches the oldest 20 to 50 pending records, packages them into a single HTTP POST batch payload, and sends them to your backend ingest API. Only when the server confirms the batch are those records deleted from the local queue; if the request fails, they stay put and the worker waits longer before each retry (exponential backoff) instead of hammering a struggling network.

What happens if a rider is offline for four hours? The queue might accumulate thousands of records. You must enforce a drop policy: discard inaccurate points (accuracy > 50m) and cap total storage by dropping fixes older than 12 hours once a size threshold is hit.

5. Battery-Adaptive Sampling

Not every minute requires identical GPS precision. When a driver is moving at 40 km/h, polling every 5 to 10 seconds is necessary for smooth navigation. When the rider has been idle at a merchant's pickup counter for five minutes, continuing that rate wastes battery.

By measuring accelerometer movement or velocity thresholds from the GPS hardware, transition your background service between Active Tracking (10s intervals) and Idle Geofencing (60s to 120s intervals). When battery drops below 15%, gracefully increase the sampling window to protect the driver's phone.

6. Idempotent Ingestion on the Server

Because network responses can drop on the return path after a server successfully saves a batch, the mobile uploader might re-send a batch. The backend API must accept points idempotently: store each point under its client-generated ID and silently skip any ID it has already saved, so a repeated batch changes nothing.

What to Measure in Production

  • Queue Depth: Track how many pending records exist locally. A sudden spike indicates network outages or API ingest degradation.
  • Sync Latency: Measure the gap between when a point was recorded on the device and when the server stored it.
  • Battery Impact: Monitor average percentage consumed per 8-hour shift across different device manufacturers.
  • Duplicate Ratio: Monitor how often the server intercepts client IDs that already exist.