Platform(s)
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, 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
- On a device where another app obtained a location fix a few seconds ago, cold-start an app.
- 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:
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),
),
);
// 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
Platform(s)
Use case / Problem
On Android, the Fused Location Provider can deliver an "initial historical location" immediately when a new
requestLocationUpdatessubscription starts. Whether it does so is gated byLocationRequest.Builder.setMaxUpdateAgeMillis, whose default isIMPLICIT_MAX_UPDATE_AGE— i.e. the maximum age of the initial fix equals the request interval.geolocator_androidnever callssetMaxUpdateAgeMilliswhen building the request inFusedLocationClient.java— it only sets the priority,setIntervalMillis,setMinUpdateIntervalMillis, andsetMinUpdateDistanceMeters. As a result, the initial-fix window is implicitly and invisibly tied toAndroidSettings.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,getPositionStreamthen 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 (seeMethodCallHandlerImpl.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
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/ currentgeolocator_androidmain branch.Proposal
Add a
maxUpdateAge(Duration?) field toAndroidSettings, passed through toLocationRequest.Builder.setMaxUpdateAgeMillison the builder path:On the legacy
LocationRequest.create()path the setting could be documented as unsupported (or approximated, if feasible).Specific requirements or considerations
nullpreserves today's implicit behavior, so the change is non-breaking.Additional information or context
LocationRequest.Builder.setMaxUpdateAgeMillisdocsIMPLICIT_MAX_UPDATE_AGE— "the implicit maximum update age is the same as the interval"