#241 improved google map refresh on size change

This commit is contained in:
Thibault Deckers 2022-05-13 17:05:05 +09:00
parent fecfa45a29
commit 406e99b13f
2 changed files with 69 additions and 55 deletions

View file

@ -429,8 +429,6 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
void _toggleOverlay() => _overlayVisible.value = !_overlayVisible.value; void _toggleOverlay() => _overlayVisible.value = !_overlayVisible.value;
// TODO TLAD [map] as of Flutter v2.5.1 / google_maps_flutter v2.0.10, toggling overlay changes the size of the map, which is an issue for Google map on Android 12
// cf https://github.com/flutter/flutter/issues/90556
Future<void> _onOverlayVisibleChange({bool animate = true}) async { Future<void> _onOverlayVisibleChange({bool animate = true}) async {
if (_overlayVisible.value) { if (_overlayVisible.value) {
if (animate) { if (animate) {

View file

@ -57,6 +57,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
final Map<MarkerKey<T>, Uint8List> _markerBitmaps = {}; final Map<MarkerKey<T>, Uint8List> _markerBitmaps = {};
final StreamController<MarkerKey<T>> _markerBitmapReadyStreamController = StreamController.broadcast(); final StreamController<MarkerKey<T>> _markerBitmapReadyStreamController = StreamController.broadcast();
Uint8List? _dotMarkerBitmap; Uint8List? _dotMarkerBitmap;
final ValueNotifier<Size> _sizeNotifier = ValueNotifier(Size.zero);
ValueNotifier<ZoomedBounds> get boundsNotifier => widget.boundsNotifier; ValueNotifier<ZoomedBounds> get boundsNotifier => widget.boundsNotifier;
@ -68,6 +69,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
_sizeNotifier.addListener(_onSizeChange);
_registerWidget(widget); _registerWidget(widget);
} }
@ -83,6 +85,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
_unregisterWidget(widget); _unregisterWidget(widget);
_serviceMapController?.dispose(); _serviceMapController?.dispose();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_sizeNotifier.removeListener(_onSizeChange);
super.dispose(); super.dispose();
} }
@ -109,7 +112,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
case AppLifecycleState.detached: case AppLifecycleState.detached:
break; break;
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
// workaround for blank Google map when resuming app // workaround for blank map when resuming app
// cf https://github.com/flutter/flutter/issues/40284 // cf https://github.com/flutter/flutter/issues/40284
_serviceMapController?.setMapStyle(null); _serviceMapController?.setMapStyle(null);
break; break;
@ -166,60 +169,65 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
return ValueListenableBuilder<double>( return ValueListenableBuilder<double>(
valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1), valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1),
builder: (context, overlayOpacity, child) { builder: (context, overlayOpacity, child) {
return GoogleMap( return LayoutBuilder(
initialCameraPosition: CameraPosition( builder: (context, constraints) {
bearing: -bounds.rotation, _sizeNotifier.value = constraints.biggest;
target: _toServiceLatLng(bounds.projectedCenter), return GoogleMap(
zoom: bounds.zoom, initialCameraPosition: CameraPosition(
), bearing: -bounds.rotation,
onMapCreated: (controller) async { target: _toServiceLatLng(bounds.projectedCenter),
_serviceMapController = controller; zoom: bounds.zoom,
final zoom = await controller.getZoomLevel();
await _updateVisibleRegion(zoom: zoom, rotation: bounds.rotation);
if (mounted) {
setState(() {});
}
},
// compass disabled to use provider agnostic controls
compassEnabled: false,
mapToolbarEnabled: false,
mapType: _toMapType(widget.style),
minMaxZoomPreference: MinMaxZoomPreference(widget.minZoom, widget.maxZoom),
rotateGesturesEnabled: true,
scrollGesturesEnabled: interactive,
// zoom controls disabled to use provider agnostic controls
zoomControlsEnabled: false,
zoomGesturesEnabled: interactive,
// lite mode disabled because it lacks camera animation
liteModeEnabled: false,
// tilt disabled to match leaflet
tiltGesturesEnabled: false,
myLocationEnabled: false,
myLocationButtonEnabled: false,
markers: {
...markers,
if (dotLocation != null && _dotMarkerBitmap != null)
Marker(
markerId: const MarkerId('dot'),
anchor: const Offset(.5, .5),
consumeTapEvents: true,
icon: BitmapDescriptor.fromBytes(_dotMarkerBitmap!),
position: _toServiceLatLng(dotLocation),
zIndex: 1,
)
},
// TODO TLAD [geotiff] may use ground overlay instead when this is fixed: https://github.com/flutter/flutter/issues/26479
tileOverlays: {
if (overlayEntry != null && overlayEntry.canOverlay)
TileOverlay(
tileOverlayId: TileOverlayId(overlayEntry.id),
tileProvider: GmsGeoTiffTileProvider(overlayEntry),
transparency: 1 - overlayOpacity,
), ),
onMapCreated: (controller) async {
_serviceMapController = controller;
final zoom = await controller.getZoomLevel();
await _updateVisibleRegion(zoom: zoom, rotation: bounds.rotation);
if (mounted) {
setState(() {});
}
},
// compass disabled to use provider agnostic controls
compassEnabled: false,
mapToolbarEnabled: false,
mapType: _toMapType(widget.style),
minMaxZoomPreference: MinMaxZoomPreference(widget.minZoom, widget.maxZoom),
rotateGesturesEnabled: true,
scrollGesturesEnabled: interactive,
// zoom controls disabled to use provider agnostic controls
zoomControlsEnabled: false,
zoomGesturesEnabled: interactive,
// lite mode disabled because it lacks camera animation
liteModeEnabled: false,
// tilt disabled to match leaflet
tiltGesturesEnabled: false,
myLocationEnabled: false,
myLocationButtonEnabled: false,
markers: {
...markers,
if (dotLocation != null && _dotMarkerBitmap != null)
Marker(
markerId: const MarkerId('dot'),
anchor: const Offset(.5, .5),
consumeTapEvents: true,
icon: BitmapDescriptor.fromBytes(_dotMarkerBitmap!),
position: _toServiceLatLng(dotLocation),
zIndex: 1,
)
},
// TODO TLAD [geotiff] may use ground overlay instead when this is fixed: https://github.com/flutter/flutter/issues/26479
tileOverlays: {
if (overlayEntry != null && overlayEntry.canOverlay)
TileOverlay(
tileOverlayId: TileOverlayId(overlayEntry.id),
tileProvider: GmsGeoTiffTileProvider(overlayEntry),
transparency: 1 - overlayOpacity,
),
},
onCameraMove: (position) => _updateVisibleRegion(zoom: position.zoom, rotation: -position.bearing),
onCameraIdle: _onIdle,
onTap: (position) => widget.onMapTap?.call(_fromServiceLatLng(position)),
);
}, },
onCameraMove: (position) => _updateVisibleRegion(zoom: position.zoom, rotation: -position.bearing),
onCameraIdle: _onIdle,
onTap: (position) => widget.onMapTap?.call(_fromServiceLatLng(position)),
); );
}, },
); );
@ -229,6 +237,14 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
); );
} }
// sometimes the map does not properly update after changing the widget size,
// so we monitor the size and force refreshing after an arbitrary small delay
Future<void> _onSizeChange() async {
await Future.delayed(const Duration(milliseconds: 100));
debugPrint('refresh map for size=${_sizeNotifier.value}');
await _serviceMapController?.setMapStyle(null);
}
void _onIdle() { void _onIdle() {
if (!mounted) return; if (!mounted) return;
widget.controller?.notifyIdle(bounds); widget.controller?.notifyIdle(bounds);