[google_maps_flutter] Fix the visual jarring during the first gesture on the map (#2629)

diff --git a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
index 5b3e350..bdc8a73 100644
--- a/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
+++ b/packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.5.26+3
+
+* iOS: observe the bounds update for the `GMSMapView` to reset the camera setting.
+* Update UI related e2e tests to wait for camera update on the platform thread.
+
 ## 0.5.26+2
 
 * Fix UIKit availability warnings and CocoaPods podspec lint warnings.
diff --git a/packages/google_maps_flutter/google_maps_flutter/example/test_driver/google_maps_e2e.dart b/packages/google_maps_flutter/google_maps_flutter/example/test_driver/google_maps_e2e.dart
index 3b78d2f..2eccbc4 100644
--- a/packages/google_maps_flutter/google_maps_flutter/example/test_driver/google_maps_e2e.dart
+++ b/packages/google_maps_flutter/google_maps_flutter/example/test_driver/google_maps_e2e.dart
@@ -383,6 +383,53 @@
     expect(scrollGesturesEnabled, true);
   });
 
+  testWidgets('testInitialCenterLocationAtCenter', (WidgetTester tester) async {
+    final Completer<GoogleMapController> mapControllerCompleter =
+        Completer<GoogleMapController>();
+    final Key key = GlobalKey();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: GoogleMap(
+          key: key,
+          initialCameraPosition: _kInitialCameraPosition,
+          onMapCreated: (GoogleMapController controller) {
+            mapControllerCompleter.complete(controller);
+          },
+        ),
+      ),
+    );
+    final GoogleMapController mapController =
+        await mapControllerCompleter.future;
+
+    await tester.pumpAndSettle();
+    // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+    // in `mapRendered`.
+    // https://github.com/flutter/flutter/issues/54758
+    await Future.delayed(Duration(seconds: 1));
+
+    ScreenCoordinate coordinate =
+        await mapController.getScreenCoordinate(_kInitialCameraPosition.target);
+    Rect rect = tester.getRect(find.byKey(key));
+    if (Platform.isIOS) {
+      // On iOS, the coordinate value from the GoogleMapSdk doesn't include the devicePixelRatio`.
+      // So we don't need to do the conversion like we did below for other platforms.
+      expect(coordinate.x, (rect.center.dx - rect.topLeft.dx).round());
+      expect(coordinate.y, (rect.center.dy - rect.topLeft.dy).round());
+    } else {
+      expect(
+          coordinate.x,
+          ((rect.center.dx - rect.topLeft.dx) *
+                  tester.binding.window.devicePixelRatio)
+              .round());
+      expect(
+          coordinate.y,
+          ((rect.center.dy - rect.topLeft.dy) *
+                  tester.binding.window.devicePixelRatio)
+              .round());
+    }
+  });
+
   testWidgets('testGetVisibleRegion', (WidgetTester tester) async {
     final Key key = GlobalKey();
     final LatLngBounds zeroLatLngBounds = LatLngBounds(
@@ -401,13 +448,8 @@
         },
       ),
     ));
-    // We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
-    // initialization. https://github.com/flutter/flutter/issues/24806
-    // This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
-    // still being investigated.
-    // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-    // https://github.com/flutter/flutter/issues/27550
-    await tester.pumpAndSettle(const Duration(seconds: 3));
+    await tester.pumpAndSettle();
+
     final GoogleMapController mapController =
         await mapControllerCompleter.future;
 
@@ -707,13 +749,11 @@
 
     final GoogleMapController controller = await controllerCompleter.future;
 
-    // We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
-    // initialization. https://github.com/flutter/flutter/issues/24806
-    // This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
-    // still being investigated.
-    // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-    // https://github.com/flutter/flutter/issues/27550
-    await tester.pumpAndSettle(const Duration(seconds: 3));
+    await tester.pumpAndSettle();
+    // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+    // in `mapRendered`.
+    // https://github.com/flutter/flutter/issues/54758
+    await Future.delayed(Duration(seconds: 1));
 
     final LatLngBounds visibleRegion = await controller.getVisibleRegion();
     final LatLng topLeft =
@@ -744,13 +784,11 @@
 
     final GoogleMapController controller = await controllerCompleter.future;
 
-    // We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
-    // initialization. https://github.com/flutter/flutter/issues/24806
-    // This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
-    // still being investigated.
-    // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-    // https://github.com/flutter/flutter/issues/27550
-    await tester.pumpAndSettle(const Duration(seconds: 3));
+    await tester.pumpAndSettle();
+    // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+    // in `mapRendered`.
+    // https://github.com/flutter/flutter/issues/54758
+    await Future.delayed(Duration(seconds: 1));
 
     double zoom = await controller.getZoomLevel();
     expect(zoom, _kInitialZoomLevel);
@@ -778,13 +816,11 @@
     ));
     final GoogleMapController controller = await controllerCompleter.future;
 
-    // We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
-    // initialization. https://github.com/flutter/flutter/issues/24806
-    // This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
-    // still being investigated.
-    // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-    // https://github.com/flutter/flutter/issues/27550
-    await tester.pumpAndSettle(const Duration(seconds: 3));
+    await tester.pumpAndSettle();
+    // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+    // in `mapRendered`.
+    // https://github.com/flutter/flutter/issues/54758
+    await Future.delayed(Duration(seconds: 1));
 
     final LatLngBounds visibleRegion = await controller.getVisibleRegion();
     final LatLng northWest = LatLng(
@@ -818,13 +854,11 @@
             home: Scaffold(
                 body: SizedBox(height: 400, width: 400, child: map)))));
 
-    // We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
-    // initialization. https://github.com/flutter/flutter/issues/24806
-    // This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
-    // still being investigated.
-    // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-    // https://github.com/flutter/flutter/issues/27550
-    await tester.pumpAndSettle(const Duration(seconds: 3));
+    await tester.pumpAndSettle();
+    // TODO(cyanglaz): Remove this after we added `mapRendered` callback, and `mapControllerCompleter.complete(controller)` above should happen
+    // in `mapRendered`.
+    // https://github.com/flutter/flutter/issues/54758
+    await Future.delayed(Duration(seconds: 1));
 
     // Simple call to make sure that the app hasn't crashed.
     final LatLngBounds bounds1 = await controller.getVisibleRegion();
diff --git a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m
index 59bfaa0..321ddd3 100644
--- a/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m
+++ b/packages/google_maps_flutter/google_maps_flutter/ios/Classes/GoogleMapController.m
@@ -50,10 +50,6 @@
   FlutterMethodChannel* _channel;
   BOOL _trackCameraPosition;
   NSObject<FlutterPluginRegistrar>* _registrar;
-  // Used for the temporary workaround for a bug that the camera is not properly positioned at
-  // initialization. https://github.com/flutter/flutter/issues/24806
-  // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-  // https://github.com/flutter/flutter/issues/27550
   BOOL _cameraDidInitialSetup;
   FLTMarkersController* _markersController;
   FLTPolygonsController* _polygonsController;
@@ -119,9 +115,36 @@
 }
 
 - (UIView*)view {
+  [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil];
   return _mapView;
 }
 
+- (void)observeValueForKeyPath:(NSString*)keyPath
+                      ofObject:(id)object
+                        change:(NSDictionary*)change
+                       context:(void*)context {
+  if (_cameraDidInitialSetup) {
+    // We only observe the frame for initial setup.
+    [_mapView removeObserver:self forKeyPath:@"frame"];
+    return;
+  }
+  if (object == _mapView && [keyPath isEqualToString:@"frame"]) {
+    CGRect bounds = _mapView.bounds;
+    if (CGRectEqualToRect(bounds, CGRectZero)) {
+      // The workaround is to fix an issue that the camera location is not current when
+      // the size of the map is zero at initialization.
+      // So We only care about the size of the `_mapView`, ignore the frame changes when the size is
+      // zero.
+      return;
+    }
+    _cameraDidInitialSetup = YES;
+    [_mapView removeObserver:self forKeyPath:@"frame"];
+    [_mapView moveCamera:[GMSCameraUpdate setCamera:_mapView.camera]];
+  } else {
+    [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+  }
+}
+
 - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
   if ([call.method isEqualToString:@"map#show"]) {
     [self showAtX:ToDouble(call.arguments[@"x"]) Y:ToDouble(call.arguments[@"y"])];
@@ -437,16 +460,6 @@
 }
 
 - (void)mapView:(GMSMapView*)mapView didChangeCameraPosition:(GMSCameraPosition*)position {
-  if (!_cameraDidInitialSetup) {
-    // We suspected a bug in the iOS Google Maps SDK caused the camera is not properly positioned at
-    // initialization. https://github.com/flutter/flutter/issues/24806
-    // This temporary workaround fix is provided while the actual fix in the Google Maps SDK is
-    // still being investigated.
-    // TODO(cyanglaz): Remove this temporary fix once the Maps SDK issue is resolved.
-    // https://github.com/flutter/flutter/issues/27550
-    _cameraDidInitialSetup = YES;
-    [mapView moveCamera:[GMSCameraUpdate setCamera:_mapView.camera]];
-  }
   if (_trackCameraPosition) {
     [_channel invokeMethod:@"camera#onMove" arguments:@{@"position" : PositionToJson(position)}];
   }
@@ -511,8 +524,8 @@
 
 static NSDictionary* PointToJson(CGPoint point) {
   return @{
-    @"x" : @((int)point.x),
-    @"y" : @((int)point.y),
+    @"x" : @(lroundf(point.x)),
+    @"y" : @(lroundf(point.y)),
   };
 }
 
diff --git a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
index 3d5f7b5..8c50efc 100644
--- a/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
+++ b/packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
@@ -1,7 +1,7 @@
 name: google_maps_flutter
 description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
 homepage: https://github.com/flutter/plugins/tree/master/packages/google_maps_flutter/google_maps_flutter
-version: 0.5.26+2
+version: 0.5.26+3
 
 dependencies:
   flutter: