Baseflow / Baseflow/flutter-geolocator

[Android] Expose LocationRequest maxUpdateAgeMillis so getPositionStream can deliver an initial historical location

Open
#1,784 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Dart
Stars
1.3k
Forks
803
Avg merge
8h 16m
Merged PRs (30d)
1

Description

### Platform(s)

- [x] Android

### Use case / Problem

On Android, the Fused Location Provider can deliver an "initial historical location" immediately when a new `requestLocationUpdates` subscription starts. Whether it does so is gated by [`LocationRequest.Builder.setMaxUpdateAgeMillis`](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.Builder#setMaxUpdateAgeMillis(long)), whose default is `IMPLICIT_MAX_UPDATE_AGE` — i.e. the maximum age of the initial fix equals the request interval.

`geolocator_android` never calls `setMaxUpdateAgeMillis` when building the request in `FusedLocationClient.java` — it only sets the priority, `setIntervalMillis`, `setMinUpdateIntervalMillis`, and `setMinUpdateDistanceMeters`. As a result, the initial-fix window is implicitly and invisibly tied to `AndroidSettings.intervalDuration`.

**Consequence.** Apps that need frequent updates and therefore pass a short `intervalDuration` (e.g. 1 s) unknowingly shrink the initial-historical-location window to ~1 s, effectively disabling initial delivery. At cold start, `getPositionStream` then stays silent until the FLP derives a completely fresh fix — which can take 10–30+ s (GNSS warmup) or indefinitely indoors — even when a perfectly recent fix exists in the FLP cache.

Confusingly, `Geolocator.getCurrentPosition()` appears to behave differently in the exact same situation — but only because it internally starts its own updates subscription with the default 5000 ms interval (see `MethodCallHandlerImpl.onGetCurrentPosition` / `LocationOptions.parseArguments`), giving it a 5× wider implicit initial-fix window.

The current workaround is to manually merge a one-shot `getCurrentPosition()` into the position stream.

### Steps to reproduce / Expected / Actual

1. On a device where another app obtained a location fix a few seconds ago, cold-start an app.
2. Subscribe to `Geolocator.getPositionStream(locationSettings: AndroidSettings(intervalDuration: Duration(seconds: 1)))`.

**Expected:** an immediate initial position event — the FLP has a recent cached fix that should qualify as an initial historical location.

**Actual:** no event is emitted until a freshly derived fix arrives, which can take tens of seconds outdoors or never happen indoors.

Observed with geolocator `^14` / current `geolocator_android` main branch.

### Proposal

Add a `maxUpdateAge` (`Duration?`) field to `AndroidSettings`, passed through to `LocationRequest.Builder.setMaxUpdateAgeMillis` on the builder path:

```dart
final stream = Geolocator.getPositionStream(
locationSettings: AndroidSettings(
intervalDuration: const Duration(seconds: 1),
// Accept an initial historical fix up to 30 s old.
maxUpdateAge: const Duration(seconds: 30),
),
);
```

```java
// FusedLocationClient.java (builder path)
if (locationOptions.getMaxUpdateAgeMillis() != null) {
builder.setMaxUpdateAgeMillis(locationOptions.getMaxUpdateAgeMillis());
}
```

On the legacy `LocationRequest.create()` path the setting could be documented as unsupported (or approximated, if feasible).

### Specific requirements or considerations

- Default `null` preserves today's implicit behavior, so the change is non-breaking.
- The key benefit is decoupling "how old may my first fix be" from "how often do I want updates" — two concerns that are currently coupled through an undocumented Play Services default.

### Additional information or context

- [`LocationRequest.Builder.setMaxUpdateAgeMillis` docs](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.Builder#setMaxUpdateAgeMillis(long))
- [`IMPLICIT_MAX_UPDATE_AGE`](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest#IMPLICIT_MAX_UPDATE_AGE) — "the implicit maximum update age is the same as the interval"

Contributor guide

Open the contributing guide

Research direction

Trace AndroidSettings through LocationOptions.parseArguments to the request construction in FusedLocationClient.java, starting with the builder path and the existing interval fields. Verify how a nullable value reaches setMaxUpdateAgeMillis, then check the legacy LocationRequest.create() path and document or handle its support as described. Done means the setting preserves null defaults and allows an explicit initial-fix age for getPositionStream.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, dart, flutter, java
Domain
mobile-dev
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.