FB-01: fix ambient GPS's first fix not correcting the initial zoom-2
Emulator verification caught a real bug the widget tests missed: the shell background map builds before any location fix has resolved, so ambientPosition starts null and the initial camera locks at zoom 2. The first fix to arrive only re-centered (preserving that stale zoom forever) instead of also jumping to ambientZoom.
This commit is contained in:
@@ -153,10 +153,19 @@ class _RideMapState extends State<RideMap> with WidgetsBindingObserver {
|
|||||||
} else if (widget.ambientPosition != null &&
|
} else if (widget.ambientPosition != null &&
|
||||||
widget.ambientPosition != old.ambientPosition) {
|
widget.ambientPosition != old.ambientPosition) {
|
||||||
// FB-01: no recorded path yet -- chase the ambient GPS fix instead, the same way
|
// FB-01: no recorded path yet -- chase the ambient GPS fix instead, the same way
|
||||||
// a recording is chased above.
|
// a recording is chased above. The *first* fix has to also correct the zoom, not
|
||||||
|
// just the center: the very first build (before any fix has arrived) has no
|
||||||
|
// ambient position yet either, so `bounds == null` locks the initial camera at
|
||||||
|
// zoom 2 -- without this, every later fix would re-center at that same
|
||||||
|
// never-corrected zoom 2 forever, since a plain re-center only ever preserves
|
||||||
|
// whatever zoom the map is already at.
|
||||||
|
final isFirstFix = old.ambientPosition == null;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (!mounted || !_following) return;
|
if (!mounted || !_following) return;
|
||||||
_controller.move(widget.ambientPosition!, _controller.camera.zoom);
|
_controller.move(
|
||||||
|
widget.ambientPosition!,
|
||||||
|
isFirstFix ? ambientZoom : _controller.camera.zoom,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -250,6 +250,45 @@ void main() {
|
|||||||
expect(center.longitude, closeTo(fix2.longitude, 1e-9));
|
expect(center.longitude, closeTo(fix2.longitude, 1e-9));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('the first ambient fix to arrive after construction corrects the '
|
||||||
|
'zoom, not just the center -- a plain re-center would otherwise leave the '
|
||||||
|
'map at the initial zoom-2 forever', (tester) async {
|
||||||
|
// Reproduces a real device bug found in emulator verification: the shell
|
||||||
|
// background map builds before any location permission/fix has resolved, so
|
||||||
|
// `ambientPosition` starts null and the initial camera locks at zoom 2 (the
|
||||||
|
// `bounds == null` fallback). The first fix to actually arrive must jump to
|
||||||
|
// `ambientZoom`, not just re-center at whatever zoom the map already happens
|
||||||
|
// to be sitting at.
|
||||||
|
const fix = ll.LatLng(51.0, -114.0);
|
||||||
|
|
||||||
|
Widget build(ll.LatLng? ambient) => MaterialApp(
|
||||||
|
theme: ripprTheme(),
|
||||||
|
home: Scaffold(
|
||||||
|
body: RideMap(
|
||||||
|
points: const [],
|
||||||
|
segments: const [],
|
||||||
|
showEmptyLabel: false,
|
||||||
|
follow: true,
|
||||||
|
ambientPosition: ambient,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(build(null));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
var map = tester.widget<FlutterMap>(find.byType(FlutterMap));
|
||||||
|
expect(map.mapController!.camera.zoom, 2);
|
||||||
|
|
||||||
|
await tester.pumpWidget(build(fix));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
map = tester.widget<FlutterMap>(find.byType(FlutterMap));
|
||||||
|
expect(map.mapController!.camera.zoom, ambientZoom);
|
||||||
|
expect(map.mapController!.camera.center.latitude, closeTo(fix.latitude, 1e-9));
|
||||||
|
expect(map.mapController!.camera.center.longitude, closeTo(fix.longitude, 1e-9));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('a manual pan cancels ambient following the same way it cancels '
|
testWidgets('a manual pan cancels ambient following the same way it cancels '
|
||||||
'recording-follow', (tester) async {
|
'recording-follow', (tester) async {
|
||||||
const fix1 = ll.LatLng(51.0, -114.0);
|
const fix1 = ll.LatLng(51.0, -114.0);
|
||||||
|
|||||||
Reference in New Issue
Block a user