Skip to content

Commit 3ee5f0a

Browse files
authored
[google_maps_flutter_ios] Fix iOS regression where updating a marker hides its info window (#9127)
Fixes a regression introduced on version 2.12.0: _Adds support for marker clustering_ (#6186), where visible info windows are not visible after marker updates. Adds tests accordingly. Fixes flutter/flutter#167459 Related to flutter/flutter#159471 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 813b3be commit 3ee5f0a

File tree

5 files changed

+138
-14
lines changed

5 files changed

+138
-14
lines changed

packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.15.2
2+
3+
* Fixes regression where updating a marker hides its info window.
4+
15
## 2.15.1
26

37
* Fixes regression in displaying info windows.

packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/integration_test/google_maps_test.dart

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,65 @@ void main() {
842842
expect(iwVisibleStatus, false);
843843
});
844844

845+
testWidgets('updating a marker does not hide its info window',
846+
(WidgetTester tester) async {
847+
final Key key = GlobalKey();
848+
const Marker marker = Marker(
849+
markerId: MarkerId('marker'),
850+
infoWindow: InfoWindow(title: 'InfoWindow'));
851+
Set<Marker> markers = <Marker>{marker};
852+
853+
const ClusterManager clusterManager =
854+
ClusterManager(clusterManagerId: ClusterManagerId('cluster_manager'));
855+
final Set<ClusterManager> clusterManagers = <ClusterManager>{
856+
clusterManager
857+
};
858+
859+
final Completer<ExampleGoogleMapController> controllerCompleter =
860+
Completer<ExampleGoogleMapController>();
861+
862+
await tester.pumpWidget(Directionality(
863+
textDirection: TextDirection.ltr,
864+
child: ExampleGoogleMap(
865+
key: key,
866+
initialCameraPosition: const CameraPosition(target: LatLng(10.0, 15.0)),
867+
markers: markers,
868+
clusterManagers: clusterManagers,
869+
onMapCreated: (ExampleGoogleMapController googleMapController) {
870+
controllerCompleter.complete(googleMapController);
871+
},
872+
),
873+
));
874+
875+
final ExampleGoogleMapController controller =
876+
await controllerCompleter.future;
877+
878+
await controller.showMarkerInfoWindow(marker.markerId);
879+
bool iwVisibleStatus =
880+
await controller.isMarkerInfoWindowShown(marker.markerId);
881+
expect(iwVisibleStatus, true);
882+
883+
// Update marker and ensure the info window remains visible when added to a
884+
// cluster manager.
885+
final Marker updatedMarker = marker.copyWith(
886+
alphaParam: 0.5,
887+
clusterManagerIdParam: clusterManager.clusterManagerId,
888+
);
889+
markers = <Marker>{updatedMarker};
890+
891+
await tester.pumpWidget(Directionality(
892+
textDirection: TextDirection.ltr,
893+
child: ExampleGoogleMap(
894+
key: key,
895+
initialCameraPosition: _kInitialCameraPosition,
896+
clusterManagers: clusterManagers,
897+
markers: Set<Marker>.of(markers)),
898+
));
899+
900+
iwVisibleStatus = await controller.isMarkerInfoWindowShown(marker.markerId);
901+
expect(iwVisibleStatus, true);
902+
});
903+
845904
testWidgets('testTakeSnapshot', (WidgetTester tester) async {
846905
final Completer<ExampleGoogleMapController> controllerCompleter =
847906
Completer<ExampleGoogleMapController>();
@@ -1110,6 +1169,46 @@ void main() {
11101169
expect(markersAmountForClusterManager, markersPerClusterManager);
11111170
}
11121171

1172+
// Move marker from the first cluster manager to the last.
1173+
final MarkerId markerIdToMove = markers.entries
1174+
.firstWhere((MapEntry<MarkerId, Marker> entry) =>
1175+
entry.value.clusterManagerId ==
1176+
clusterManagers.first.clusterManagerId)
1177+
.key;
1178+
markers[markerIdToMove] = _copyMarkerWithClusterManagerId(
1179+
markers[markerIdToMove]!, clusterManagers.last.clusterManagerId);
1180+
1181+
await tester.pumpWidget(Directionality(
1182+
textDirection: TextDirection.ltr,
1183+
child: ExampleGoogleMap(
1184+
key: key,
1185+
initialCameraPosition: _kInitialCameraPosition,
1186+
clusterManagers: clusterManagers,
1187+
markers: Set<Marker>.of(markers.values)),
1188+
));
1189+
1190+
{
1191+
final List<Cluster> clusters = await inspector.getClusters(
1192+
mapId: controller.mapId,
1193+
clusterManagerId: clusterManagers.first.clusterManagerId);
1194+
final int markersAmountForClusterManager = clusters
1195+
.map<int>((Cluster cluster) => cluster.count)
1196+
.reduce((int value, int element) => value + element);
1197+
//Check that first cluster manager has one less marker.
1198+
expect(markersAmountForClusterManager, markersPerClusterManager - 1);
1199+
}
1200+
1201+
{
1202+
final List<Cluster> clusters = await inspector.getClusters(
1203+
mapId: controller.mapId,
1204+
clusterManagerId: clusterManagers.last.clusterManagerId);
1205+
final int markersAmountForClusterManager = clusters
1206+
.map<int>((Cluster cluster) => cluster.count)
1207+
.reduce((int value, int element) => value + element);
1208+
// Check that last cluster manager has one more marker.
1209+
expect(markersAmountForClusterManager, markersPerClusterManager + 1);
1210+
}
1211+
11131212
// Remove markers from clusterManagers and test that clusterManagers are empty.
11141213
for (final MapEntry<MarkerId, Marker> entry in markers.entries) {
11151214
markers[entry.key] = _copyMarkerWithClusterManagerId(entry.value, null);

packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ NS_ASSUME_NONNULL_BEGIN
1616
@property(assign, nonatomic, readonly) BOOL consumeTapEvents;
1717
- (instancetype)initWithMarker:(GMSMarker *)marker
1818
markerIdentifier:(NSString *)markerIdentifier
19-
clusterManagerIdentifier:(nullable NSString *)clusterManagerIdentifier
2019
mapView:(GMSMapView *)mapView;
2120
- (void)showInfoWindow;
2221
- (void)hideInfoWindow;

packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,20 @@ @implementation FLTGoogleMapMarkerController
2424

2525
- (instancetype)initWithMarker:(GMSMarker *)marker
2626
markerIdentifier:(NSString *)markerIdentifier
27-
clusterManagerIdentifier:(nullable NSString *)clusterManagerIdentifier
2827
mapView:(GMSMapView *)mapView {
2928
self = [super init];
3029
if (self) {
3130
_marker = marker;
3231
_markerIdentifier = [markerIdentifier copy];
33-
_clusterManagerIdentifier = [clusterManagerIdentifier copy];
3432
_mapView = mapView;
35-
FGMSetIdentifiersToMarkerUserData(_markerIdentifier, _clusterManagerIdentifier, _marker);
3633
}
3734
return self;
3835
}
3936

37+
- (void)setClusterManagerIdentifier:(nullable NSString *)clusterManagerIdentifier {
38+
_clusterManagerIdentifier = clusterManagerIdentifier;
39+
}
40+
4041
- (void)showInfoWindow {
4142
self.mapView.selectedMarker = self.marker;
4243
}
@@ -110,6 +111,7 @@ - (void)setZIndex:(int)zIndex {
110111
- (void)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker
111112
registrar:(NSObject<FlutterPluginRegistrar> *)registrar
112113
screenScale:(CGFloat)screenScale {
114+
[self setClusterManagerIdentifier:platformMarker.clusterManagerId];
113115
[self setAlpha:platformMarker.alpha];
114116
[self setAnchor:FGMGetCGPointForPigeonPoint(platformMarker.anchor)];
115117
[self setDraggable:platformMarker.draggable];
@@ -126,6 +128,12 @@ - (void)updateFromPlatformMarker:(FGMPlatformMarker *)platformMarker
126128
[self setInfoWindowTitle:infoWindow.title snippet:infoWindow.snippet];
127129
}
128130

131+
// Set the marker's user data with current identifiers.
132+
FGMSetIdentifiersToMarkerUserData(self.markerIdentifier, self.clusterManagerIdentifier,
133+
self.marker);
134+
135+
// Ensure setVisible is called last as it adds the marker to the map,
136+
// and must be done after all other parameters are set.
129137
[self setVisible:platformMarker.visible];
130138
}
131139

@@ -173,7 +181,6 @@ - (void)addMarker:(FGMPlatformMarker *)markerToAdd {
173181
FLTGoogleMapMarkerController *controller =
174182
[[FLTGoogleMapMarkerController alloc] initWithMarker:marker
175183
markerIdentifier:markerIdentifier
176-
clusterManagerIdentifier:clusterManagerIdentifier
177184
mapView:self.mapView];
178185
[controller updateFromPlatformMarker:markerToAdd
179186
registrar:self.registrar
@@ -196,20 +203,35 @@ - (void)changeMarkers:(NSArray<FGMPlatformMarker *> *)markersToChange {
196203

197204
- (void)changeMarker:(FGMPlatformMarker *)markerToChange {
198205
NSString *markerIdentifier = markerToChange.markerId;
199-
NSString *clusterManagerIdentifier = markerToChange.clusterManagerId;
200206

201207
FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[markerIdentifier];
202208
if (!controller) {
203209
return;
204210
}
211+
212+
NSString *clusterManagerIdentifier = markerToChange.clusterManagerId;
205213
NSString *previousClusterManagerIdentifier = [controller clusterManagerIdentifier];
206-
if (![previousClusterManagerIdentifier isEqualToString:clusterManagerIdentifier]) {
207-
[self removeMarker:markerIdentifier];
208-
[self addMarker:markerToChange];
209-
} else {
210-
[controller updateFromPlatformMarker:markerToChange
211-
registrar:self.registrar
212-
screenScale:[self getScreenScale]];
214+
[controller updateFromPlatformMarker:markerToChange
215+
registrar:self.registrar
216+
screenScale:[self getScreenScale]];
217+
218+
if ([controller.marker conformsToProtocol:@protocol(GMUClusterItem)]) {
219+
if (previousClusterManagerIdentifier &&
220+
![clusterManagerIdentifier isEqualToString:previousClusterManagerIdentifier]) {
221+
// Remove marker from previous cluster manager if its cluster manager identifier is removed or
222+
// changed.
223+
GMUClusterManager *clusterManager = [_clusterManagersController
224+
clusterManagerWithIdentifier:previousClusterManagerIdentifier];
225+
[clusterManager removeItem:(id<GMUClusterItem>)controller.marker];
226+
}
227+
228+
if (clusterManagerIdentifier &&
229+
![previousClusterManagerIdentifier isEqualToString:clusterManagerIdentifier]) {
230+
// Add marker to cluster manager if its cluster manager identifier has changed.
231+
GMUClusterManager *clusterManager =
232+
[_clusterManagersController clusterManagerWithIdentifier:clusterManagerIdentifier];
233+
[clusterManager addItem:(id<GMUClusterItem>)controller.marker];
234+
}
213235
}
214236
}
215237

packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: google_maps_flutter_ios
22
description: iOS implementation of the google_maps_flutter plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
5-
version: 2.15.1
5+
version: 2.15.2
66

77
environment:
88
sdk: ^3.4.0

0 commit comments

Comments
 (0)