diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 91ad60b5966..8755bc89eb9 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.0 + +* Converts additional platform calls to Pigeon. + ## 2.8.2 * Converts inspector interface platform calls to Pigeon. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m index e75fc046912..b5a992d1efc 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -113,25 +113,26 @@ - (void)testDictionaryFromPosition { XCTAssertEqualObjects(dictionary[@"tilt"], @75.0); } -- (void)testDictionaryFromPoint { +- (void)testPigeonPointForGCPoint { CGPoint point = CGPointMake(10, 20); - NSDictionary *dictionary = [FLTGoogleMapJSONConversions dictionaryFromPoint:point]; - const CGFloat accuracy = 0.0001; - XCTAssertEqualWithAccuracy([dictionary[@"x"] floatValue], point.x, accuracy); - XCTAssertEqualWithAccuracy([dictionary[@"y"] floatValue], point.y, accuracy); + FGMPlatformPoint *pigeonPoint = FGMGetPigeonPointForCGPoint(point); + XCTAssertEqualWithAccuracy(pigeonPoint.x, point.x, DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonPoint.y, point.y, DBL_EPSILON); } -- (void)testDictionaryFromCoordinateBounds { - XCTAssertNil([FLTGoogleMapJSONConversions dictionaryFromCoordinateBounds:nil]); - +- (void)testPigeonLatLngBoundsForCoordinateBounds { GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithCoordinate:CLLocationCoordinate2DMake(10, 20) coordinate:CLLocationCoordinate2DMake(30, 40)]; - NSDictionary *dictionary = [FLTGoogleMapJSONConversions dictionaryFromCoordinateBounds:bounds]; - NSArray *southwest = @[ @10, @20 ]; - NSArray *northeast = @[ @30, @40 ]; - XCTAssertEqualObjects(dictionary[@"southwest"], southwest); - XCTAssertEqualObjects(dictionary[@"northeast"], northeast); + FGMPlatformLatLngBounds *pigeonBounds = FGMGetPigeonLatLngBoundsForCoordinateBounds(bounds); + XCTAssertEqualWithAccuracy(pigeonBounds.southwest.latitude, bounds.southWest.latitude, + DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonBounds.southwest.longitude, bounds.southWest.longitude, + DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonBounds.northeast.latitude, bounds.northEast.latitude, + DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonBounds.northeast.longitude, bounds.northEast.longitude, + DBL_EPSILON); } - (void)testCameraPostionFromDictionary { @@ -151,19 +152,13 @@ - (void)testCameraPostionFromDictionary { XCTAssertEqualWithAccuracy(cameraPosition.viewingAngle, 5, accuracy); } -- (void)testPointFromDictionary { - XCTAssertNil([FLTGoogleMapJSONConversions cameraPostionFromDictionary:nil]); +- (void)testCGPointForPigeonPoint { + FGMPlatformPoint *pigeonPoint = [FGMPlatformPoint makeWithX:1.0 y:2.0]; - NSDictionary *dictionary = @{ - @"x" : @1, - @"y" : @2, - }; + CGPoint point = FGMGetCGPointForPigeonPoint(pigeonPoint); - CGPoint point = [FLTGoogleMapJSONConversions pointFromDictionary:dictionary]; - - const CGFloat accuracy = 0.001; - XCTAssertEqualWithAccuracy(point.x, 1, accuracy); - XCTAssertEqualWithAccuracy(point.y, 2, accuracy); + XCTAssertEqualWithAccuracy(pigeonPoint.x, point.x, DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonPoint.y, point.y, DBL_EPSILON); } - (void)testCoordinateBoundsFromLatLongs { @@ -188,18 +183,18 @@ - (void)testMapViewTypeFromTypeValue { XCTAssertEqual(kGMSTypeNone, [FLTGoogleMapJSONConversions mapViewTypeFromTypeValue:@5]); } -- (void)testCameraUpdateFromChannelValueNewCameraPosition { +- (void)testCameraUpdateFromArrayNewCameraPosition { NSArray *channelValue = @[ @"newCameraPosition", @{@"target" : @[ @1, @2 ], @"zoom" : @3, @"bearing" : @4, @"tilt" : @5} ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; [[classMockCameraUpdate expect] setCamera:[FLTGoogleMapJSONConversions cameraPostionFromDictionary:channelValue[1]]]; [classMockCameraUpdate stopMocking]; } -// TODO(cyanglaz): Fix the test for CameraUpdateFromChannelValue with the "NewLatlng" key. +// TODO(cyanglaz): Fix the test for cameraUpdateFromArray with the "NewLatlng" key. // 2 approaches have been tried and neither worked for the tests. // // 1. Use OCMock to vefiry that [GMSCameraUpdate setTarget:] is triggered with the correct value. @@ -213,10 +208,10 @@ - (void)testCameraUpdateFromChannelValueNewCameraPosition { // verified. // // The code in below test uses the 2nd approach. -- (void)skip_testCameraUpdateFromChannelValueNewLatLong { +- (void)skip_testCameraUpdateFromArrayNewLatLong { NSArray *channelValue = @[ @"newLatLng", @[ @1, @2 ] ]; - GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; GMSMapViewOptions *options = [[GMSMapViewOptions alloc] init]; options.frame = CGRectZero; @@ -230,7 +225,7 @@ - (void)skip_testCameraUpdateFromChannelValueNewLatLong { accuracy); // mapView.camera.target.longitude is still 6. } -- (void)testCameraUpdateFromChannelValueNewLatLngBounds { +- (void)testCameraUpdateFromArrayNewLatLngBounds { NSArray *latlong1 = @[ @1, @2 ]; NSArray *latlong2 = @[ @(3), @(4) ]; GMSCoordinateBounds *bounds = @@ -238,73 +233,73 @@ - (void)testCameraUpdateFromChannelValueNewLatLngBounds { NSArray *channelValue = @[ @"newLatLngBounds", @[ latlong1, latlong2 ], @20 ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; [[classMockCameraUpdate expect] fitBounds:bounds withPadding:20]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromChannelValueNewLatLngZoom { +- (void)testCameraUpdateFromArrayNewLatLngZoom { NSArray *channelValue = @[ @"newLatLngZoom", @[ @1, @2 ], @3 ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; [[classMockCameraUpdate expect] setTarget:CLLocationCoordinate2DMake(1, 2) zoom:3]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromChannelValueScrollBy { +- (void)testCameraUpdateFromArrayScrollBy { NSArray *channelValue = @[ @"scrollBy", @1, @2 ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValue]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValue]; [[classMockCameraUpdate expect] scrollByX:1 Y:2]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromChannelValueZoomBy { +- (void)testCameraUpdateFromArrayZoomBy { NSArray *channelValueNoPoint = @[ @"zoomBy", @1 ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; [[classMockCameraUpdate expect] zoomBy:1]; NSArray *channelValueWithPoint = @[ @"zoomBy", @1, @[ @2, @3 ] ]; - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueWithPoint]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueWithPoint]; [[classMockCameraUpdate expect] zoomBy:1 atPoint:CGPointMake(2, 3)]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromChannelValueZoomIn { +- (void)testCameraUpdateFromArrayZoomIn { NSArray *channelValueNoPoint = @[ @"zoomIn" ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; [[classMockCameraUpdate expect] zoomIn]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromChannelValueZoomOut { +- (void)testCameraUpdateFromArrayZoomOut { NSArray *channelValueNoPoint = @[ @"zoomOut" ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; [[classMockCameraUpdate expect] zoomOut]; [classMockCameraUpdate stopMocking]; } -- (void)testCameraUpdateFromChannelValueZoomTo { +- (void)testCameraUpdateFromArrayZoomTo { NSArray *channelValueNoPoint = @[ @"zoomTo", @1 ]; id classMockCameraUpdate = OCMClassMock([GMSCameraUpdate class]); - [FLTGoogleMapJSONConversions cameraUpdateFromChannelValue:channelValueNoPoint]; + [FLTGoogleMapJSONConversions cameraUpdateFromArray:channelValueNoPoint]; [[classMockCameraUpdate expect] zoomTo:1]; [classMockCameraUpdate stopMocking]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h index c6f9fc76eba..f6fd13cc16d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h @@ -5,11 +5,29 @@ #import #import +#import "messages.g.h" + NS_ASSUME_NONNULL_BEGIN /// Returns dict[key], or nil if dict[key] is NSNull. extern id _Nullable FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key); +/// Creates a CGPoint from its Pigeon equivalent. +extern CGPoint FGMGetCGPointForPigeonPoint(FGMPlatformPoint *point); + +/// Converts a CGPoint to its Pigeon equivalent. +extern FGMPlatformPoint *FGMGetPigeonPointForCGPoint(CGPoint point); + +/// Creates a CLLocationCoordinate2D from its Pigeon representation. +extern CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng); + +/// Converts a CLLocationCoordinate2D to its Pigeon representation. +extern FGMPlatformLatLng *FGMGetPigeonLatLngForCoordinate(CLLocationCoordinate2D coord); + +/// Converts a GMSCoordinateBounds to its Pigeon representation. +extern FGMPlatformLatLngBounds *FGMGetPigeonLatLngBoundsForCoordinateBounds( + GMSCoordinateBounds *bounds); + @interface FLTGoogleMapJSONConversions : NSObject + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong; @@ -20,13 +38,10 @@ extern id _Nullable FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key); + (NSArray *> *)holesFromPointsArray:(NSArray *)data; + (nullable NSDictionary *)dictionaryFromPosition: (nullable GMSCameraPosition *)position; -+ (NSDictionary *)dictionaryFromPoint:(CGPoint)point; -+ (nullable NSDictionary *)dictionaryFromCoordinateBounds:(nullable GMSCoordinateBounds *)bounds; + (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)channelValue; -+ (CGPoint)pointFromDictionary:(NSDictionary *)dictionary; + (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs; + (GMSMapViewType)mapViewTypeFromTypeValue:(NSNumber *)value; -+ (nullable GMSCameraUpdate *)cameraUpdateFromChannelValue:(NSArray *)channelValue; ++ (nullable GMSCameraUpdate *)cameraUpdateFromArray:(NSArray *)channelValue; /// Return GMS strokestyle object array populated using the patterns and stroke colors passed in. /// diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index f6ea7690222..a7adf833a8c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -10,6 +10,28 @@ id FGMGetValueOrNilFromDict(NSDictionary *dict, NSString *key) { return value == [NSNull null] ? nil : value; } +CGPoint FGMGetCGPointForPigeonPoint(FGMPlatformPoint *point) { + return CGPointMake(point.x, point.y); +} + +FGMPlatformPoint *FGMGetPigeonPointForCGPoint(CGPoint point) { + return [FGMPlatformPoint makeWithX:point.x y:point.y]; +} + +CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng) { + return CLLocationCoordinate2DMake(latLng.latitude, latLng.longitude); +} + +FGMPlatformLatLng *FGMGetPigeonLatLngForCoordinate(CLLocationCoordinate2D coord) { + return [FGMPlatformLatLng makeWithLatitude:coord.latitude longitude:coord.longitude]; +} + +FGMPlatformLatLngBounds *FGMGetPigeonLatLngBoundsForCoordinateBounds(GMSCoordinateBounds *bounds) { + return + [FGMPlatformLatLngBounds makeWithNortheast:FGMGetPigeonLatLngForCoordinate(bounds.northEast) + southwest:FGMGetPigeonLatLngForCoordinate(bounds.southWest)]; +} + @implementation FLTGoogleMapJSONConversions + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong { @@ -67,23 +89,6 @@ + (UIColor *)colorFromRGBA:(NSNumber *)numberColor { }; } -+ (NSDictionary *)dictionaryFromPoint:(CGPoint)point { - return @{ - @"x" : @(lroundf(point.x)), - @"y" : @(lroundf(point.y)), - }; -} - -+ (nullable NSDictionary *)dictionaryFromCoordinateBounds:(GMSCoordinateBounds *)bounds { - if (!bounds) { - return nil; - } - return @{ - @"southwest" : [FLTGoogleMapJSONConversions arrayFromLocation:[bounds southWest]], - @"northeast" : [FLTGoogleMapJSONConversions arrayFromLocation:[bounds northEast]], - }; -} - + (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)data { if (!data) { return nil; @@ -95,12 +100,6 @@ + (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictiona viewingAngle:[data[@"tilt"] doubleValue]]; } -+ (CGPoint)pointFromDictionary:(NSDictionary *)dictionary { - double x = [dictionary[@"x"] doubleValue]; - double y = [dictionary[@"y"] doubleValue]; - return CGPointMake(x, y); -} - + (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs { return [[GMSCoordinateBounds alloc] initWithCoordinate:[FLTGoogleMapJSONConversions locationFromLatLong:latlongs[0]] @@ -112,7 +111,7 @@ + (GMSMapViewType)mapViewTypeFromTypeValue:(NSNumber *)typeValue { return (GMSMapViewType)(value == 0 ? 5 : value); } -+ (nullable GMSCameraUpdate *)cameraUpdateFromChannelValue:(NSArray *)channelValue { ++ (nullable GMSCameraUpdate *)cameraUpdateFromArray:(NSArray *)channelValue { NSString *update = channelValue[0]; if ([update isEqualToString:@"newCameraPosition"]) { return [GMSCameraUpdate diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h index 23f00c9af15..edc1f9197e4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h @@ -5,6 +5,8 @@ #import #import +#import "messages.g.h" + NS_ASSUME_NONNULL_BEGIN @interface FLTGoogleMapTileOverlayController : NSObject @@ -28,9 +30,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar; -- (void)addTileOverlays:(NSArray *)tileOverlaysToAdd; -- (void)changeTileOverlays:(NSArray *)tileOverlaysToChange; -- (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers; +- (void)addJSONTileOverlays:(NSArray *> *)tileOverlaysToAdd; +- (void)addTileOverlays:(NSArray *)tileOverlaysToAdd; +- (void)changeTileOverlays:(NSArray *)tileOverlaysToChange; +- (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers; - (void)clearTileCacheWithIdentifier:(NSString *)identifier; - (nullable FLTGoogleMapTileOverlayController *)tileOverlayWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index c13ab1b57d6..44dd42c4768 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -192,8 +192,8 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel return self; } -- (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { - for (NSDictionary *tileOverlay in tileOverlaysToAdd) { +- (void)addJSONTileOverlays:(NSArray *> *)tileOverlaysToAdd { + for (NSDictionary *tileOverlay in tileOverlaysToAdd) { NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay]; FLTTileProviderController *tileProvider = [[FLTTileProviderController alloc] init:self.methodChannel @@ -206,18 +206,29 @@ - (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { } } -- (void)changeTileOverlays:(NSArray *)tileOverlaysToChange { - for (NSDictionary *tileOverlay in tileOverlaysToChange) { - NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay]; +- (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { + for (FGMPlatformTileOverlay *tileOverlay in tileOverlaysToAdd) { + NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay.json]; + FLTTileProviderController *tileProvider = + [[FLTTileProviderController alloc] init:self.methodChannel + withTileOverlayIdentifier:identifier]; + FLTGoogleMapTileOverlayController *controller = + [[FLTGoogleMapTileOverlayController alloc] initWithTileLayer:tileProvider + mapView:self.mapView + options:tileOverlay.json]; + self.tileOverlayIdentifierToController[identifier] = controller; + } +} + +- (void)changeTileOverlays:(NSArray *)tileOverlaysToChange { + for (FGMPlatformTileOverlay *tileOverlay in tileOverlaysToChange) { + NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay.json]; FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdentifierToController[identifier]; - if (!controller) { - continue; - } - [controller interpretTileOverlayOptions:tileOverlay]; + [controller interpretTileOverlayOptions:tileOverlay.json]; } } -- (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers { +- (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers { for (NSString *identifier in identifiers) { FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdentifierToController[identifier]; @@ -232,9 +243,6 @@ - (void)removeTileOverlayWithIdentifiers:(NSArray *)identifiers { - (void)clearTileCacheWithIdentifier:(NSString *)identifier { FLTGoogleMapTileOverlayController *controller = self.tileOverlayIdentifierToController[identifier]; - if (!controller) { - return; - } [controller clearTileCache]; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h index 6b67760fdaf..d9b798d5d47 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h @@ -5,6 +5,8 @@ #import #import +#import "messages.g.h" + // Defines circle controllable by Flutter. @interface FLTGoogleMapCircleController : NSObject - (instancetype)initCircleWithPosition:(CLLocationCoordinate2D)position @@ -19,9 +21,10 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar; -- (void)addCircles:(NSArray *)circlesToAdd; -- (void)changeCircles:(NSArray *)circlesToChange; -- (void)removeCircleWithIdentifiers:(NSArray *)identifiers; +- (void)addJSONCircles:(NSArray *> *)circlesToAdd; +- (void)addCircles:(NSArray *)circlesToAdd; +- (void)changeCircles:(NSArray *)circlesToChange; +- (void)removeCirclesWithIdentifiers:(NSArray *)identifiers; - (void)didTapCircleWithIdentifier:(NSString *)identifier; - (bool)hasCircleWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m index 1eb74a19559..1e42d91ac7d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m @@ -125,8 +125,8 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel return self; } -- (void)addCircles:(NSArray *)circlesToAdd { - for (NSDictionary *circle in circlesToAdd) { +- (void)addJSONCircles:(NSArray *> *)circlesToAdd { + for (NSDictionary *circle in circlesToAdd) { CLLocationCoordinate2D position = [FLTCirclesController getPosition:circle]; CLLocationDistance radius = [FLTCirclesController getRadius:circle]; NSString *circleId = [FLTCirclesController getCircleId:circle]; @@ -140,18 +140,30 @@ - (void)addCircles:(NSArray *)circlesToAdd { } } -- (void)changeCircles:(NSArray *)circlesToChange { - for (NSDictionary *circle in circlesToChange) { - NSString *circleId = [FLTCirclesController getCircleId:circle]; +- (void)addCircles:(NSArray *)circlesToAdd { + for (FGMPlatformCircle *circle in circlesToAdd) { + CLLocationCoordinate2D position = [FLTCirclesController getPosition:circle.json]; + CLLocationDistance radius = [FLTCirclesController getRadius:circle.json]; + NSString *circleId = [FLTCirclesController getCircleId:circle.json]; + FLTGoogleMapCircleController *controller = + [[FLTGoogleMapCircleController alloc] initCircleWithPosition:position + radius:radius + circleId:circleId + mapView:self.mapView + options:circle.json]; + self.circleIdToController[circleId] = controller; + } +} + +- (void)changeCircles:(NSArray *)circlesToChange { + for (FGMPlatformCircle *circle in circlesToChange) { + NSString *circleId = [FLTCirclesController getCircleId:circle.json]; FLTGoogleMapCircleController *controller = self.circleIdToController[circleId]; - if (!controller) { - continue; - } - [controller interpretCircleOptions:circle]; + [controller interpretCircleOptions:circle.json]; } } -- (void)removeCircleWithIdentifiers:(NSArray *)identifiers { +- (void)removeCirclesWithIdentifiers:(NSArray *)identifiers { for (NSString *identifier in identifiers) { FLTGoogleMapCircleController *controller = self.circleIdToController[identifier]; if (!controller) { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h index 2b7b1182b6b..4fdc08721d3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.h @@ -20,8 +20,6 @@ NS_ASSUME_NONNULL_BEGIN registrar:(NSObject *)registrar; - (void)showAtOrigin:(CGPoint)origin; - (void)hide; -- (void)animateWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate; -- (void)moveWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate; - (nullable GMSCameraPosition *)cameraPosition; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 039224466ca..453cd8c946b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -59,6 +59,31 @@ - (instancetype)initWithRegistrar:(NSObject *)registrar #pragma mark - +/// Implementation of the Pigeon maps API. +/// +/// This is a separate object from the maps controller because the Pigeon API registration keeps a +/// strong reference to the implementor, but as the FlutterPlatformView, the lifetime of the +/// FLTGoogleMapController instance is what needs to trigger Pigeon unregistration, so can't be +/// the target of the registration. +@interface FGMMapCallHandler : NSObject +- (instancetype)initWithMapController:(nonnull FLTGoogleMapController *)controller + messenger:(NSObject *)messenger + pigeonSuffix:(NSString *)suffix; +@end + +/// Private declarations. +// This is separate in case the above is made public in the future (e.g., for unit testing). +@interface FGMMapCallHandler () +/// The map controller this inspector corresponds to. +@property(nonatomic, weak) FLTGoogleMapController *controller; +/// The messenger this instance was registered with by Pigeon. +@property(nonatomic, copy) NSObject *messenger; +/// The suffix this instance was registered under with Pigeon. +@property(nonatomic, copy) NSString *pigeonSuffix; +@end + +#pragma mark - + /// Implementation of the Pigeon maps inspector API. /// /// This is a separate object from the maps controller because the Pigeon API registration keeps a @@ -100,6 +125,8 @@ @interface FLTGoogleMapController () // creation time and there's no mechanism to return non-fatal error details during platform view // initialization. @property(nonatomic, copy) NSString *styleError; +// The main Pigeon API implementation, separate to avoid lifetime extension. +@property(nonatomic, strong) FGMMapCallHandler *callHandler; // The inspector API implementation, separate to avoid lifetime extension. @property(nonatomic, strong) FGMMapInspector *inspector; @@ -142,11 +169,7 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView [NSString stringWithFormat:@"plugins.flutter.dev/google_maps_ios_%lld", viewId]; _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:registrar.messenger]; - __weak __typeof__(self) weakSelf = self; - [_channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) { - [weakSelf onMethodCall:call result:result]; - }]; - _mapView.delegate = weakSelf; + _mapView.delegate = self; _mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever; _registrar = registrar; _markersController = [[FLTMarkersController alloc] initWithMethodChannel:_channel @@ -166,28 +189,32 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView registrar:registrar]; id markersToAdd = args[@"markersToAdd"]; if ([markersToAdd isKindOfClass:[NSArray class]]) { - [_markersController addMarkers:markersToAdd]; + [_markersController addJSONMarkers:markersToAdd]; } id polygonsToAdd = args[@"polygonsToAdd"]; if ([polygonsToAdd isKindOfClass:[NSArray class]]) { - [_polygonsController addPolygons:polygonsToAdd]; + [_polygonsController addJSONPolygons:polygonsToAdd]; } id polylinesToAdd = args[@"polylinesToAdd"]; if ([polylinesToAdd isKindOfClass:[NSArray class]]) { - [_polylinesController addPolylines:polylinesToAdd]; + [_polylinesController addJSONPolylines:polylinesToAdd]; } id circlesToAdd = args[@"circlesToAdd"]; if ([circlesToAdd isKindOfClass:[NSArray class]]) { - [_circlesController addCircles:circlesToAdd]; + [_circlesController addJSONCircles:circlesToAdd]; } id tileOverlaysToAdd = args[@"tileOverlaysToAdd"]; if ([tileOverlaysToAdd isKindOfClass:[NSArray class]]) { - [_tileOverlaysController addTileOverlays:tileOverlaysToAdd]; + [_tileOverlaysController addJSONTileOverlays:tileOverlaysToAdd]; } [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; NSString *suffix = [NSString stringWithFormat:@"%lld", viewId]; + _callHandler = [[FGMMapCallHandler alloc] initWithMapController:self + messenger:registrar.messenger + pigeonSuffix:suffix]; + SetUpFGMMapsApiWithSuffix(registrar.messenger, _callHandler, suffix); _inspector = [[FGMMapInspector alloc] initWithMapController:self messenger:registrar.messenger pigeonSuffix:suffix]; @@ -199,6 +226,7 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView - (void)dealloc { // Unregister the API implementations so that they can be released; the registration created an // owning reference. + SetUpFGMMapsApiWithSuffix(_callHandler.messenger, nil, _callHandler.pigeonSuffix); SetUpFGMMapsInspectorApiWithSuffix(_inspector.messenger, nil, _inspector.pigeonSuffix); } @@ -227,192 +255,6 @@ - (void)observeValueForKeyPath:(NSString *)keyPath } } -- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([call.method isEqualToString:@"map#show"]) { - [self showAtOrigin:CGPointMake([call.arguments[@"x"] doubleValue], - [call.arguments[@"y"] doubleValue])]; - result(nil); - } else if ([call.method isEqualToString:@"map#hide"]) { - [self hide]; - result(nil); - } else if ([call.method isEqualToString:@"camera#animate"]) { - [self - animateWithCameraUpdate:[FLTGoogleMapJSONConversions - cameraUpdateFromChannelValue:call.arguments[@"cameraUpdate"]]]; - result(nil); - } else if ([call.method isEqualToString:@"camera#move"]) { - [self moveWithCameraUpdate:[FLTGoogleMapJSONConversions - cameraUpdateFromChannelValue:call.arguments[@"cameraUpdate"]]]; - result(nil); - } else if ([call.method isEqualToString:@"map#update"]) { - [self interpretMapOptions:call.arguments[@"options"]]; - result([FLTGoogleMapJSONConversions dictionaryFromPosition:[self cameraPosition]]); - } else if ([call.method isEqualToString:@"map#getVisibleRegion"]) { - if (self.mapView != nil) { - GMSVisibleRegion visibleRegion = self.mapView.projection.visibleRegion; - GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; - result([FLTGoogleMapJSONConversions dictionaryFromCoordinateBounds:bounds]); - } else { - result([FlutterError errorWithCode:@"GoogleMap uninitialized" - message:@"getVisibleRegion called prior to map initialization" - details:nil]); - } - } else if ([call.method isEqualToString:@"map#getScreenCoordinate"]) { - if (self.mapView != nil) { - CLLocationCoordinate2D location = - [FLTGoogleMapJSONConversions locationFromLatLong:call.arguments]; - CGPoint point = [self.mapView.projection pointForCoordinate:location]; - result([FLTGoogleMapJSONConversions dictionaryFromPoint:point]); - } else { - result([FlutterError errorWithCode:@"GoogleMap uninitialized" - message:@"getScreenCoordinate called prior to map initialization" - details:nil]); - } - } else if ([call.method isEqualToString:@"map#getLatLng"]) { - if (self.mapView != nil && call.arguments) { - CGPoint point = [FLTGoogleMapJSONConversions pointFromDictionary:call.arguments]; - CLLocationCoordinate2D latlng = [self.mapView.projection coordinateForPoint:point]; - result([FLTGoogleMapJSONConversions arrayFromLocation:latlng]); - } else { - result([FlutterError errorWithCode:@"GoogleMap uninitialized" - message:@"getLatLng called prior to map initialization" - details:nil]); - } - } else if ([call.method isEqualToString:@"map#waitForMap"]) { - result(nil); - } else if ([call.method isEqualToString:@"map#takeSnapshot"]) { - if (self.mapView != nil) { - UIGraphicsImageRenderer *renderer = - [[UIGraphicsImageRenderer alloc] initWithSize:self.mapView.bounds.size]; - // For some unknown reason mapView.layer::renderInContext API returns a blank image on iOS 17. - // So we have to use drawViewHierarchyInRect API. - UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) { - [self.mapView drawViewHierarchyInRect:self.mapView.bounds afterScreenUpdates:YES]; - }]; - result([FlutterStandardTypedData typedDataWithBytes:UIImagePNGRepresentation(image)]); - } else { - result([FlutterError errorWithCode:@"GoogleMap uninitialized" - message:@"takeSnapshot called prior to map initialization" - details:nil]); - } - } else if ([call.method isEqualToString:@"markers#update"]) { - id markersToAdd = call.arguments[@"markersToAdd"]; - if ([markersToAdd isKindOfClass:[NSArray class]]) { - [self.markersController addMarkers:markersToAdd]; - } - id markersToChange = call.arguments[@"markersToChange"]; - if ([markersToChange isKindOfClass:[NSArray class]]) { - [self.markersController changeMarkers:markersToChange]; - } - id markerIdsToRemove = call.arguments[@"markerIdsToRemove"]; - if ([markerIdsToRemove isKindOfClass:[NSArray class]]) { - [self.markersController removeMarkersWithIdentifiers:markerIdsToRemove]; - } - result(nil); - } else if ([call.method isEqualToString:@"markers#showInfoWindow"]) { - id markerId = call.arguments[@"markerId"]; - if ([markerId isKindOfClass:[NSString class]]) { - [self.markersController showMarkerInfoWindowWithIdentifier:markerId result:result]; - } else { - result([FlutterError errorWithCode:@"Invalid markerId" - message:@"showInfoWindow called with invalid markerId" - details:nil]); - } - } else if ([call.method isEqualToString:@"markers#hideInfoWindow"]) { - id markerId = call.arguments[@"markerId"]; - if ([markerId isKindOfClass:[NSString class]]) { - [self.markersController hideMarkerInfoWindowWithIdentifier:markerId result:result]; - } else { - result([FlutterError errorWithCode:@"Invalid markerId" - message:@"hideInfoWindow called with invalid markerId" - details:nil]); - } - } else if ([call.method isEqualToString:@"markers#isInfoWindowShown"]) { - id markerId = call.arguments[@"markerId"]; - if ([markerId isKindOfClass:[NSString class]]) { - [self.markersController isInfoWindowShownForMarkerWithIdentifier:markerId result:result]; - } else { - result([FlutterError errorWithCode:@"Invalid markerId" - message:@"isInfoWindowShown called with invalid markerId" - details:nil]); - } - } else if ([call.method isEqualToString:@"polygons#update"]) { - id polygonsToAdd = call.arguments[@"polygonsToAdd"]; - if ([polygonsToAdd isKindOfClass:[NSArray class]]) { - [self.polygonsController addPolygons:polygonsToAdd]; - } - id polygonsToChange = call.arguments[@"polygonsToChange"]; - if ([polygonsToChange isKindOfClass:[NSArray class]]) { - [self.polygonsController changePolygons:polygonsToChange]; - } - id polygonIdsToRemove = call.arguments[@"polygonIdsToRemove"]; - if ([polygonIdsToRemove isKindOfClass:[NSArray class]]) { - [self.polygonsController removePolygonWithIdentifiers:polygonIdsToRemove]; - } - result(nil); - } else if ([call.method isEqualToString:@"polylines#update"]) { - id polylinesToAdd = call.arguments[@"polylinesToAdd"]; - if ([polylinesToAdd isKindOfClass:[NSArray class]]) { - [self.polylinesController addPolylines:polylinesToAdd]; - } - id polylinesToChange = call.arguments[@"polylinesToChange"]; - if ([polylinesToChange isKindOfClass:[NSArray class]]) { - [self.polylinesController changePolylines:polylinesToChange]; - } - id polylineIdsToRemove = call.arguments[@"polylineIdsToRemove"]; - if ([polylineIdsToRemove isKindOfClass:[NSArray class]]) { - [self.polylinesController removePolylineWithIdentifiers:polylineIdsToRemove]; - } - result(nil); - } else if ([call.method isEqualToString:@"circles#update"]) { - id circlesToAdd = call.arguments[@"circlesToAdd"]; - if ([circlesToAdd isKindOfClass:[NSArray class]]) { - [self.circlesController addCircles:circlesToAdd]; - } - id circlesToChange = call.arguments[@"circlesToChange"]; - if ([circlesToChange isKindOfClass:[NSArray class]]) { - [self.circlesController changeCircles:circlesToChange]; - } - id circleIdsToRemove = call.arguments[@"circleIdsToRemove"]; - if ([circleIdsToRemove isKindOfClass:[NSArray class]]) { - [self.circlesController removeCircleWithIdentifiers:circleIdsToRemove]; - } - result(nil); - } else if ([call.method isEqualToString:@"tileOverlays#update"]) { - id tileOverlaysToAdd = call.arguments[@"tileOverlaysToAdd"]; - if ([tileOverlaysToAdd isKindOfClass:[NSArray class]]) { - [self.tileOverlaysController addTileOverlays:tileOverlaysToAdd]; - } - id tileOverlaysToChange = call.arguments[@"tileOverlaysToChange"]; - if ([tileOverlaysToChange isKindOfClass:[NSArray class]]) { - [self.tileOverlaysController changeTileOverlays:tileOverlaysToChange]; - } - id tileOverlayIdsToRemove = call.arguments[@"tileOverlayIdsToRemove"]; - if ([tileOverlayIdsToRemove isKindOfClass:[NSArray class]]) { - [self.tileOverlaysController removeTileOverlayWithIdentifiers:tileOverlayIdsToRemove]; - } - result(nil); - } else if ([call.method isEqualToString:@"tileOverlays#clearTileCache"]) { - id rawTileOverlayId = call.arguments[@"tileOverlayId"]; - [self.tileOverlaysController clearTileCacheWithIdentifier:rawTileOverlayId]; - result(nil); - } else if ([call.method isEqualToString:@"map#getZoomLevel"]) { - result(@(self.mapView.camera.zoom)); - } else if ([call.method isEqualToString:@"map#setStyle"]) { - id mapStyle = [call arguments]; - self.styleError = [self setMapStyle:(mapStyle == [NSNull null] ? nil : mapStyle)]; - if (self.styleError == nil) { - result(@[ @(YES) ]); - } else { - result(@[ @(NO), self.styleError ]); - } - } else if ([call.method isEqualToString:@"map#getStyleError"]) { - result(self.styleError); - } else { - result(FlutterMethodNotImplemented); - } -} - - (void)showAtOrigin:(CGPoint)origin { CGRect frame = {origin, self.mapView.frame.size}; self.mapView.frame = frame; @@ -423,14 +265,6 @@ - (void)hide { self.mapView.hidden = YES; } -- (void)animateWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate { - [self.mapView animateWithCameraUpdate:cameraUpdate]; -} - -- (void)moveWithCameraUpdate:(GMSCameraUpdate *)cameraUpdate { - [self.mapView moveCamera:cameraUpdate]; -} - - (GMSCameraPosition *)cameraPosition { if (self.trackCameraPosition) { return self.mapView.camera; @@ -503,19 +337,23 @@ - (void)setMyLocationButtonEnabled:(BOOL)enabled { self.mapView.settings.myLocationButton = enabled; } +/// Sets the map style, returing any error string as well as storing that error in `mapStyle` for +/// later access. - (NSString *)setMapStyle:(NSString *)mapStyle { + NSString *errorString = nil; if (mapStyle.length == 0) { self.mapView.mapStyle = nil; - return nil; - } - NSError *error; - GMSMapStyle *style = [GMSMapStyle styleWithJSONString:mapStyle error:&error]; - if (!style) { - return [error localizedDescription]; } else { - self.mapView.mapStyle = style; - return nil; + NSError *error; + GMSMapStyle *style = [GMSMapStyle styleWithJSONString:mapStyle error:&error]; + if (style) { + self.mapView.mapStyle = style; + } else { + errorString = [error localizedDescription]; + } } + self.styleError = errorString; + return errorString; } #pragma mark - GMSMapViewDelegate methods @@ -658,8 +496,204 @@ - (void)interpretMapOptions:(NSDictionary *)data { } NSString *style = FGMGetValueOrNilFromDict(data, @"style"); if (style) { - self.styleError = [self setMapStyle:style]; + [self setMapStyle:style]; + } +} + +@end + +#pragma mark - + +@implementation FGMMapCallHandler + +- (instancetype)initWithMapController:(nonnull FLTGoogleMapController *)controller + messenger:(NSObject *)messenger + pigeonSuffix:(NSString *)suffix { + self = [super init]; + if (self) { + _controller = controller; + _messenger = messenger; + _pigeonSuffix = suffix; + } + return self; +} + +- (void)waitForMapWithError:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + // No-op; this call just ensures synchronization with the platform thread. +} + +- (void)updateCirclesByAdding:(nonnull NSArray *)toAdd + changing:(nonnull NSArray *)toChange + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.circlesController addCircles:toAdd]; + [self.controller.circlesController changeCircles:toChange]; + [self.controller.circlesController removeCirclesWithIdentifiers:idsToRemove]; +} + +- (void)updateWithMapConfiguration:(nonnull FGMPlatformMapConfiguration *)configuration + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller interpretMapOptions:configuration.json]; +} + +- (void)updateMarkersByAdding:(nonnull NSArray *)toAdd + changing:(nonnull NSArray *)toChange + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.markersController addMarkers:toAdd]; + [self.controller.markersController changeMarkers:toChange]; + [self.controller.markersController removeMarkersWithIdentifiers:idsToRemove]; +} + +- (void)updatePolygonsByAdding:(nonnull NSArray *)toAdd + changing:(nonnull NSArray *)toChange + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.polygonsController addPolygons:toAdd]; + [self.controller.polygonsController changePolygons:toChange]; + [self.controller.polygonsController removePolygonWithIdentifiers:idsToRemove]; +} + +- (void)updatePolylinesByAdding:(nonnull NSArray *)toAdd + changing:(nonnull NSArray *)toChange + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.polylinesController addPolylines:toAdd]; + [self.controller.polylinesController changePolylines:toChange]; + [self.controller.polylinesController removePolylineWithIdentifiers:idsToRemove]; +} + +- (void)updateTileOverlaysByAdding:(nonnull NSArray *)toAdd + changing:(nonnull NSArray *)toChange + removing:(nonnull NSArray *)idsToRemove + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + [self.controller.tileOverlaysController addTileOverlays:toAdd]; + [self.controller.tileOverlaysController changeTileOverlays:toChange]; + [self.controller.tileOverlaysController removeTileOverlayWithIdentifiers:idsToRemove]; +} + +- (nullable FGMPlatformLatLng *) + latLngForScreenCoordinate:(nonnull FGMPlatformPoint *)screenCoordinate + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + if (!self.controller.mapView) { + *error = [FlutterError errorWithCode:@"GoogleMap uninitialized" + message:@"getLatLng called prior to map initialization" + details:nil]; + return nil; + } + CGPoint point = FGMGetCGPointForPigeonPoint(screenCoordinate); + CLLocationCoordinate2D latlng = [self.controller.mapView.projection coordinateForPoint:point]; + return FGMGetPigeonLatLngForCoordinate(latlng); +} + +- (nullable FGMPlatformPoint *) + screenCoordinatesForLatLng:(nonnull FGMPlatformLatLng *)latLng + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + if (!self.controller.mapView) { + *error = [FlutterError errorWithCode:@"GoogleMap uninitialized" + message:@"getScreenCoordinate called prior to map initialization" + details:nil]; + return nil; + } + CLLocationCoordinate2D location = FGMGetCoordinateForPigeonLatLng(latLng); + CGPoint point = [self.controller.mapView.projection pointForCoordinate:location]; + return FGMGetPigeonPointForCGPoint(point); +} + +- (nullable FGMPlatformLatLngBounds *)visibleMapRegion: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + if (!self.controller.mapView) { + *error = [FlutterError errorWithCode:@"GoogleMap uninitialized" + message:@"getVisibleRegion called prior to map initialization" + details:nil]; + return nil; + } + GMSVisibleRegion visibleRegion = self.controller.mapView.projection.visibleRegion; + GMSCoordinateBounds *bounds = [[GMSCoordinateBounds alloc] initWithRegion:visibleRegion]; + return FGMGetPigeonLatLngBoundsForCoordinateBounds(bounds); +} + +- (void)moveCameraWithUpdate:(nonnull FGMPlatformCameraUpdate *)cameraUpdate + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromArray:cameraUpdate.json]; + if (!update) { + *error = [FlutterError errorWithCode:@"Invalid update" + message:@"Unrecognized camera update" + details:cameraUpdate.json]; + return; + } + [self.controller.mapView moveCamera:update]; +} + +- (void)animateCameraWithUpdate:(nonnull FGMPlatformCameraUpdate *)cameraUpdate + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + GMSCameraUpdate *update = [FLTGoogleMapJSONConversions cameraUpdateFromArray:cameraUpdate.json]; + if (!update) { + *error = [FlutterError errorWithCode:@"Invalid update" + message:@"Unrecognized camera update" + details:cameraUpdate.json]; + return; + } + [self.controller.mapView animateWithCameraUpdate:update]; +} + +- (nullable NSNumber *)currentZoomLevel:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return @(self.controller.mapView.camera.zoom); +} + +- (void)showInfoWindowForMarkerWithIdentifier:(nonnull NSString *)markerId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [self.controller.markersController showMarkerInfoWindowWithIdentifier:markerId error:error]; +} + +- (void)hideInfoWindowForMarkerWithIdentifier:(nonnull NSString *)markerId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [self.controller.markersController hideMarkerInfoWindowWithIdentifier:markerId error:error]; +} + +- (nullable NSNumber *) + isShowingInfoWindowForMarkerWithIdentifier:(nonnull NSString *)markerId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + return [self.controller.markersController isInfoWindowShownForMarkerWithIdentifier:markerId + error:error]; +} + +- (nullable NSString *)setStyle:(nonnull NSString *)style + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return [self.controller setMapStyle:style]; +} + +- (nullable NSString *)lastStyleError:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { + return self.controller.styleError; +} + +- (void)clearTileCacheForOverlayWithIdentifier:(nonnull NSString *)tileOverlayId + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [self.controller.tileOverlaysController clearTileCacheWithIdentifier:tileOverlayId]; +} + +- (nullable FlutterStandardTypedData *)takeSnapshotWithError: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { + GMSMapView *mapView = self.controller.mapView; + if (!mapView) { + *error = [FlutterError errorWithCode:@"GoogleMap uninitialized" + message:@"takeSnapshot called prior to map initialization" + details:nil]; + return nil; } + UIGraphicsImageRenderer *renderer = + [[UIGraphicsImageRenderer alloc] initWithSize:mapView.bounds.size]; + // For some unknown reason mapView.layer::renderInContext API returns a blank image on iOS 17. + // So we have to use drawViewHierarchyInRect API. + UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *context) { + [mapView drawViewHierarchyInRect:mapView.bounds afterScreenUpdates:YES]; + }]; + NSData *imageData = UIImagePNGRepresentation(image); + return imageData ? [FlutterStandardTypedData typedDataWithBytes:imageData] : nil; } @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index a33d48073dd..67fe1245a93 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -4,7 +4,9 @@ #import #import + #import "GoogleMapController.h" +#import "messages.g.h" NS_ASSUME_NONNULL_BEGIN @@ -24,9 +26,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar; -- (void)addMarkers:(NSArray *)markersToAdd; -- (void)changeMarkers:(NSArray *)markersToChange; -- (void)removeMarkersWithIdentifiers:(NSArray *)identifiers; +- (void)addJSONMarkers:(NSArray *> *)markersToAdd; +- (void)addMarkers:(NSArray *)markersToAdd; +- (void)changeMarkers:(NSArray *)markersToChange; +- (void)removeMarkersWithIdentifiers:(NSArray *)identifiers; - (BOOL)didTapMarkerWithIdentifier:(NSString *)identifier; - (void)didStartDraggingMarkerWithIdentifier:(NSString *)identifier location:(CLLocationCoordinate2D)coordinate; @@ -35,10 +38,17 @@ NS_ASSUME_NONNULL_BEGIN - (void)didDragMarkerWithIdentifier:(NSString *)identifier location:(CLLocationCoordinate2D)coordinate; - (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)identifier; -- (void)showMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result; -- (void)hideMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result; -- (void)isInfoWindowShownForMarkerWithIdentifier:(NSString *)identifier - result:(FlutterResult)result; +- (void)showMarkerInfoWindowWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error; +- (void)hideMarkerInfoWindowWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error; +/// Returns whether or not the info window for the marker with the given identifier is shown. +/// +/// If there is no such marker, returns nil and sets error. +- (nullable NSNumber *) + isInfoWindowShownForMarkerWithIdentifier:(NSString *)identifier + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error; @end NS_ASSUME_NONNULL_END diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 1bc7d2a8db8..02e6a09e1c6 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -452,8 +452,8 @@ - (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel return self; } -- (void)addMarkers:(NSArray *)markersToAdd { - for (NSDictionary *marker in markersToAdd) { +- (void)addJSONMarkers:(NSArray *> *)markersToAdd { + for (NSDictionary *marker in markersToAdd) { CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker]; NSString *identifier = marker[@"markerId"]; FLTGoogleMapMarkerController *controller = @@ -467,20 +467,32 @@ - (void)addMarkers:(NSArray *)markersToAdd { } } -- (void)changeMarkers:(NSArray *)markersToChange { - for (NSDictionary *marker in markersToChange) { - NSString *identifier = marker[@"markerId"]; +- (void)addMarkers:(NSArray *)markersToAdd { + for (FGMPlatformMarker *marker in markersToAdd) { + CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker.json]; + NSString *identifier = marker.json[@"markerId"]; + FLTGoogleMapMarkerController *controller = + [[FLTGoogleMapMarkerController alloc] initMarkerWithPosition:position + identifier:identifier + mapView:self.mapView]; + [controller interpretMarkerOptions:marker.json + registrar:self.registrar + screenScale:[self getScreenScale]]; + self.markerIdentifierToController[identifier] = controller; + } +} + +- (void)changeMarkers:(NSArray *)markersToChange { + for (FGMPlatformMarker *marker in markersToChange) { + NSString *identifier = marker.json[@"markerId"]; FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; - if (!controller) { - continue; - } - [controller interpretMarkerOptions:marker + [controller interpretMarkerOptions:marker.json registrar:self.registrar screenScale:[self getScreenScale]]; } } -- (void)removeMarkersWithIdentifiers:(NSArray *)identifiers { +- (void)removeMarkersWithIdentifiers:(NSArray *)identifiers { for (NSString *identifier in identifiers) { FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (!controller) { @@ -554,39 +566,44 @@ - (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)identifier { } } -- (void)showMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result { +- (void)showMarkerInfoWindowWithIdentifier:(NSString *)identifier + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (controller) { [controller showInfoWindow]; - result(nil); } else { - result([FlutterError errorWithCode:@"Invalid markerId" - message:@"showInfoWindow called with invalid markerId" - details:nil]); + *error = [FlutterError errorWithCode:@"Invalid markerId" + message:@"showInfoWindow called with invalid markerId" + details:nil]; } } -- (void)hideMarkerInfoWindowWithIdentifier:(NSString *)identifier result:(FlutterResult)result { +- (void)hideMarkerInfoWindowWithIdentifier:(NSString *)identifier + error: + (FlutterError *_Nullable __autoreleasing *_Nonnull)error { FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (controller) { [controller hideInfoWindow]; - result(nil); } else { - result([FlutterError errorWithCode:@"Invalid markerId" - message:@"hideInfoWindow called with invalid markerId" - details:nil]); + *error = [FlutterError errorWithCode:@"Invalid markerId" + message:@"hideInfoWindow called with invalid markerId" + details:nil]; } } -- (void)isInfoWindowShownForMarkerWithIdentifier:(NSString *)identifier - result:(FlutterResult)result { +- (nullable NSNumber *) + isInfoWindowShownForMarkerWithIdentifier:(NSString *)identifier + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { FLTGoogleMapMarkerController *controller = self.markerIdentifierToController[identifier]; if (controller) { - result(@([controller isInfoWindowShown])); + return @([controller isInfoWindowShown]); } else { - result([FlutterError errorWithCode:@"Invalid markerId" - message:@"isInfoWindowShown called with invalid markerId" - details:nil]); + *error = [FlutterError errorWithCode:@"Invalid markerId" + message:@"isInfoWindowShown called with invalid markerId" + details:nil]; + return nil; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h index bd0c9110200..842a6da939c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h @@ -5,6 +5,8 @@ #import #import +#import "messages.g.h" + // Defines polygon controllable by Flutter. @interface FLTGoogleMapPolygonController : NSObject - (instancetype)initPolygonWithPath:(GMSMutablePath *)path @@ -17,9 +19,10 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar; -- (void)addPolygons:(NSArray *)polygonsToAdd; -- (void)changePolygons:(NSArray *)polygonsToChange; -- (void)removePolygonWithIdentifiers:(NSArray *)identifiers; +- (void)addJSONPolygons:(NSArray *> *)polygonsToAdd; +- (void)addPolygons:(NSArray *)polygonsToAdd; +- (void)changePolygons:(NSArray *)polygonsToChange; +- (void)removePolygonWithIdentifiers:(NSArray *)identifiers; - (void)didTapPolygonWithIdentifier:(NSString *)identifier; - (bool)hasPolygonWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m index d8dc47def60..b48e67ea8a7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m @@ -140,8 +140,8 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel return self; } -- (void)addPolygons:(NSArray *)polygonsToAdd { - for (NSDictionary *polygon in polygonsToAdd) { +- (void)addJSONPolygons:(NSArray *> *)polygonsToAdd { + for (NSDictionary *polygon in polygonsToAdd) { GMSMutablePath *path = [FLTPolygonsController getPath:polygon]; NSString *identifier = polygon[@"polygonId"]; FLTGoogleMapPolygonController *controller = @@ -153,18 +153,28 @@ - (void)addPolygons:(NSArray *)polygonsToAdd { } } -- (void)changePolygons:(NSArray *)polygonsToChange { - for (NSDictionary *polygon in polygonsToChange) { - NSString *identifier = polygon[@"polygonId"]; +- (void)addPolygons:(NSArray *)polygonsToAdd { + for (FGMPlatformPolygon *polygon in polygonsToAdd) { + GMSMutablePath *path = [FLTPolygonsController getPath:polygon.json]; + NSString *identifier = polygon.json[@"polygonId"]; + FLTGoogleMapPolygonController *controller = + [[FLTGoogleMapPolygonController alloc] initPolygonWithPath:path + identifier:identifier + mapView:self.mapView]; + [controller interpretPolygonOptions:polygon.json registrar:self.registrar]; + self.polygonIdentifierToController[identifier] = controller; + } +} + +- (void)changePolygons:(NSArray *)polygonsToChange { + for (FGMPlatformPolygon *polygon in polygonsToChange) { + NSString *identifier = polygon.json[@"polygonId"]; FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; - if (!controller) { - continue; - } - [controller interpretPolygonOptions:polygon registrar:self.registrar]; + [controller interpretPolygonOptions:polygon.json registrar:self.registrar]; } } -- (void)removePolygonWithIdentifiers:(NSArray *)identifiers { +- (void)removePolygonWithIdentifiers:(NSArray *)identifiers { for (NSString *identifier in identifiers) { FLTGoogleMapPolygonController *controller = self.polygonIdentifierToController[identifier]; if (!controller) { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h index 63061392e5a..06c47800084 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h @@ -5,6 +5,8 @@ #import #import +#import "messages.g.h" + // Defines polyline controllable by Flutter. @interface FLTGoogleMapPolylineController : NSObject - (instancetype)initPolylineWithPath:(GMSMutablePath *)path @@ -23,9 +25,10 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel mapView:(GMSMapView *)mapView registrar:(NSObject *)registrar; -- (void)addPolylines:(NSArray *)polylinesToAdd; -- (void)changePolylines:(NSArray *)polylinesToChange; -- (void)removePolylineWithIdentifiers:(NSArray *)identifiers; +- (void)addJSONPolylines:(NSArray *> *)polylinesToAdd; +- (void)addPolylines:(NSArray *)polylinesToAdd; +- (void)changePolylines:(NSArray *)polylinesToChange; +- (void)removePolylineWithIdentifiers:(NSArray *)identifiers; - (void)didTapPolylineWithIdentifier:(NSString *)identifier; - (bool)hasPolylineWithIdentifier:(NSString *)identifier; @end diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m index a97f372d6fd..6f8b7b3be71 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m @@ -135,8 +135,9 @@ - (instancetype)init:(FlutterMethodChannel *)methodChannel } return self; } -- (void)addPolylines:(NSArray *)polylinesToAdd { - for (NSDictionary *polyline in polylinesToAdd) { + +- (void)addJSONPolylines:(NSArray *> *)polylinesToAdd { + for (NSDictionary *polyline in polylinesToAdd) { GMSMutablePath *path = [FLTPolylinesController pathForPolyline:polyline]; NSString *identifier = polyline[@"polylineId"]; FLTGoogleMapPolylineController *controller = @@ -147,17 +148,29 @@ - (void)addPolylines:(NSArray *)polylinesToAdd { self.polylineIdentifierToController[identifier] = controller; } } -- (void)changePolylines:(NSArray *)polylinesToChange { - for (NSDictionary *polyline in polylinesToChange) { - NSString *identifier = polyline[@"polylineId"]; + +- (void)addPolylines:(NSArray *)polylinesToAdd { + for (FGMPlatformPolyline *polyline in polylinesToAdd) { + GMSMutablePath *path = [FLTPolylinesController pathForPolyline:polyline.json]; + NSString *identifier = polyline.json[@"polylineId"]; + FLTGoogleMapPolylineController *controller = + [[FLTGoogleMapPolylineController alloc] initPolylineWithPath:path + identifier:identifier + mapView:self.mapView]; + [controller interpretPolylineOptions:polyline.json registrar:self.registrar]; + self.polylineIdentifierToController[identifier] = controller; + } +} + +- (void)changePolylines:(NSArray *)polylinesToChange { + for (FGMPlatformPolyline *polyline in polylinesToChange) { + NSString *identifier = polyline.json[@"polylineId"]; FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; - if (!controller) { - continue; - } - [controller interpretPolylineOptions:polyline registrar:self.registrar]; + [controller interpretPolylineOptions:polyline.json registrar:self.registrar]; } } -- (void)removePolylineWithIdentifiers:(NSArray *)identifiers { + +- (void)removePolylineWithIdentifiers:(NSArray *)identifiers { for (NSString *identifier in identifiers) { FLTGoogleMapPolylineController *controller = self.polylineIdentifierToController[identifier]; if (!controller) { @@ -167,6 +180,7 @@ - (void)removePolylineWithIdentifiers:(NSArray *)identifiers { [self.polylineIdentifierToController removeObjectForKey:identifier]; } } + - (void)didTapPolylineWithIdentifier:(NSString *)identifier { if (!identifier) { return; @@ -177,12 +191,14 @@ - (void)didTapPolylineWithIdentifier:(NSString *)identifier { } [self.methodChannel invokeMethod:@"polyline#onTap" arguments:@{@"polylineId" : identifier}]; } + - (bool)hasPolylineWithIdentifier:(NSString *)identifier { if (!identifier) { return false; } return self.polylineIdentifierToController[identifier] != nil; } + + (GMSMutablePath *)pathForPolyline:(NSDictionary *)polyline { NSArray *pointArray = polyline[@"points"]; NSArray *points = [FLTGoogleMapJSONConversions pointsFromLatLongs:pointArray]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h index 80a9279add8..3315002fc7d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v20.0.1), do not edit directly. +// Autogenerated from Pigeon (v20.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @@ -13,9 +13,124 @@ NS_ASSUME_NONNULL_BEGIN +@class FGMPlatformCameraUpdate; +@class FGMPlatformCircle; +@class FGMPlatformMarker; +@class FGMPlatformPolygon; +@class FGMPlatformPolyline; +@class FGMPlatformTileOverlay; +@class FGMPlatformLatLng; +@class FGMPlatformLatLngBounds; +@class FGMPlatformMapConfiguration; +@class FGMPlatformPoint; @class FGMPlatformTileLayer; @class FGMPlatformZoomRange; +/// Pigeon representation of a CameraUpdate. +@interface FGMPlatformCameraUpdate : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The update data, as JSON. This should only be set from +/// CameraUpdate.toJson, and the native code must intepret it according to the +/// internal implementation details of the CameraUpdate class. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of the Circle class. +@interface FGMPlatformCircle : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The circle data, as JSON. This should only be set from +/// Circle.toJson, and the native code must intepret it according to the +/// internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of the Marker class. +@interface FGMPlatformMarker : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The marker data, as JSON. This should only be set from +/// Marker.toJson, and the native code must intepret it according to the +/// internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of the Polygon class. +@interface FGMPlatformPolygon : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The polygon data, as JSON. This should only be set from +/// Polygon.toJson, and the native code must intepret it according to the +/// internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of the Polyline class. +@interface FGMPlatformPolyline : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The polyline data, as JSON. This should only be set from +/// Polyline.toJson, and the native code must intepret it according to the +/// internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of the TileOverlay class. +@interface FGMPlatformTileOverlay : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The tile overlay data, as JSON. This should only be set from +/// TileOverlay.toJson, and the native code must intepret it according to the +/// internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon equivalent of LatLng. +@interface FGMPlatformLatLng : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithLatitude:(double)latitude longitude:(double)longitude; +@property(nonatomic, assign) double latitude; +@property(nonatomic, assign) double longitude; +@end + +/// Pigeon equivalent of LatLngBounds. +@interface FGMPlatformLatLngBounds : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithNortheast:(FGMPlatformLatLng *)northeast + southwest:(FGMPlatformLatLng *)southwest; +@property(nonatomic, strong) FGMPlatformLatLng *northeast; +@property(nonatomic, strong) FGMPlatformLatLng *southwest; +@end + +/// Pigeon equivalent of MapConfiguration. +@interface FGMPlatformMapConfiguration : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithJson:(id)json; +/// The configuration options, as JSON. This should only be set from +/// _jsonForMapConfiguration, and the native code must intepret it according +/// to the internal implementation details of that method. +@property(nonatomic, strong) id json; +@end + +/// Pigeon representation of an x,y coordinate. +@interface FGMPlatformPoint : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithX:(double)x y:(double)y; +@property(nonatomic, assign) double x; +@property(nonatomic, assign) double y; +@end + /// Pigeon equivalent of GMSTileLayer properties. @interface FGMPlatformTileLayer : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -30,7 +145,7 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, assign) NSInteger zIndex; @end -/// Possible outcomes of launching a URL. +/// Pigeon equivalent of MinMaxZoomPreference. @interface FGMPlatformZoomRange : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. - (instancetype)init NS_UNAVAILABLE; @@ -42,6 +157,108 @@ NS_ASSUME_NONNULL_BEGIN /// The codec used by all APIs. NSObject *FGMGetMessagesCodec(void); +/// Interface for non-test interactions with the native SDK. +/// +/// For test-only state queries, see [MapsInspectorApi]. +@protocol FGMMapsApi +/// Returns once the map instance is available. +- (void)waitForMapWithError:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the map's configuration options. +/// +/// Only non-null configuration values will result in updates; options with +/// null values will remain unchanged. +- (void)updateWithMapConfiguration:(FGMPlatformMapConfiguration *)configuration + error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of circles on the map. +- (void)updateCirclesByAdding:(NSArray *)toAdd + changing:(NSArray *)toChange + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of markers on the map. +- (void)updateMarkersByAdding:(NSArray *)toAdd + changing:(NSArray *)toChange + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of polygonss on the map. +- (void)updatePolygonsByAdding:(NSArray *)toAdd + changing:(NSArray *)toChange + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of polylines on the map. +- (void)updatePolylinesByAdding:(NSArray *)toAdd + changing:(NSArray *)toChange + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; +/// Updates the set of tile overlays on the map. +- (void)updateTileOverlaysByAdding:(NSArray *)toAdd + changing:(NSArray *)toChange + removing:(NSArray *)idsToRemove + error:(FlutterError *_Nullable *_Nonnull)error; +/// Gets the screen coordinate for the given map location. +/// +/// @return `nil` only when `error != nil`. +- (nullable FGMPlatformPoint *)screenCoordinatesForLatLng:(FGMPlatformLatLng *)latLng + error:(FlutterError *_Nullable *_Nonnull)error; +/// Gets the map location for the given screen coordinate. +/// +/// @return `nil` only when `error != nil`. +- (nullable FGMPlatformLatLng *)latLngForScreenCoordinate:(FGMPlatformPoint *)screenCoordinate + error:(FlutterError *_Nullable *_Nonnull)error; +/// Gets the map region currently displayed on the map. +/// +/// @return `nil` only when `error != nil`. +- (nullable FGMPlatformLatLngBounds *)visibleMapRegion:(FlutterError *_Nullable *_Nonnull)error; +/// Moves the camera according to [cameraUpdate] immediately, with no +/// animation. +- (void)moveCameraWithUpdate:(FGMPlatformCameraUpdate *)cameraUpdate + error:(FlutterError *_Nullable *_Nonnull)error; +/// Moves the camera according to [cameraUpdate], animating the update. +- (void)animateCameraWithUpdate:(FGMPlatformCameraUpdate *)cameraUpdate + error:(FlutterError *_Nullable *_Nonnull)error; +/// Gets the current map zoom level. +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)currentZoomLevel:(FlutterError *_Nullable *_Nonnull)error; +/// Show the info window for the marker with the given ID. +- (void)showInfoWindowForMarkerWithIdentifier:(NSString *)markerId + error:(FlutterError *_Nullable *_Nonnull)error; +/// Hide the info window for the marker with the given ID. +- (void)hideInfoWindowForMarkerWithIdentifier:(NSString *)markerId + error:(FlutterError *_Nullable *_Nonnull)error; +/// Returns true if the marker with the given ID is currently displaying its +/// info window. +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *) + isShowingInfoWindowForMarkerWithIdentifier:(NSString *)markerId + error:(FlutterError *_Nullable *_Nonnull)error; +/// Sets the style to the given map style string, where an empty string +/// indicates that the style should be cleared. +/// +/// If there was an error setting the style, such as an invalid style string, +/// returns the error message. +- (nullable NSString *)setStyle:(NSString *)style error:(FlutterError *_Nullable *_Nonnull)error; +/// Returns the error string from the last attempt to set the map style, if +/// any. +/// +/// This allows checking asynchronously for initial style failures, as there +/// is no way to return failures from map initialization. +- (nullable NSString *)lastStyleError:(FlutterError *_Nullable *_Nonnull)error; +/// Clears the cache of tiles previously requseted from the tile provider. +- (void)clearTileCacheForOverlayWithIdentifier:(NSString *)tileOverlayId + error:(FlutterError *_Nullable *_Nonnull)error; +/// Takes a snapshot of the map and returns its image data. +- (nullable FlutterStandardTypedData *)takeSnapshotWithError: + (FlutterError *_Nullable *_Nonnull)error; +@end + +extern void SetUpFGMMapsApi(id binaryMessenger, + NSObject *_Nullable api); + +extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger, + NSObject *_Nullable api, + NSString *messageChannelSuffix); + /// Inspector API only intended for use in integration tests. @protocol FGMMapsInspectorApi /// @return `nil` only when `error != nil`. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m index eb7ab77ffac..843fc11ead5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v20.0.1), do not edit directly. +// Autogenerated from Pigeon (v20.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "messages.g.h" @@ -30,6 +30,66 @@ static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { return (result == [NSNull null]) ? nil : result; } +@interface FGMPlatformCameraUpdate () ++ (FGMPlatformCameraUpdate *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraUpdate *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformCircle () ++ (FGMPlatformCircle *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformMarker () ++ (FGMPlatformMarker *)fromList:(NSArray *)list; ++ (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformPolygon () ++ (FGMPlatformPolygon *)fromList:(NSArray *)list; ++ (nullable FGMPlatformPolygon *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformPolyline () ++ (FGMPlatformPolyline *)fromList:(NSArray *)list; ++ (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformTileOverlay () ++ (FGMPlatformTileOverlay *)fromList:(NSArray *)list; ++ (nullable FGMPlatformTileOverlay *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformLatLng () ++ (FGMPlatformLatLng *)fromList:(NSArray *)list; ++ (nullable FGMPlatformLatLng *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformLatLngBounds () ++ (FGMPlatformLatLngBounds *)fromList:(NSArray *)list; ++ (nullable FGMPlatformLatLngBounds *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformMapConfiguration () ++ (FGMPlatformMapConfiguration *)fromList:(NSArray *)list; ++ (nullable FGMPlatformMapConfiguration *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface FGMPlatformPoint () ++ (FGMPlatformPoint *)fromList:(NSArray *)list; ++ (nullable FGMPlatformPoint *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformTileLayer () + (FGMPlatformTileLayer *)fromList:(NSArray *)list; + (nullable FGMPlatformTileLayer *)nullableFromList:(NSArray *)list; @@ -42,6 +102,226 @@ + (nullable FGMPlatformZoomRange *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@implementation FGMPlatformCameraUpdate ++ (instancetype)makeWithJson:(id)json { + FGMPlatformCameraUpdate *pigeonResult = [[FGMPlatformCameraUpdate alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformCameraUpdate *)fromList:(NSArray *)list { + FGMPlatformCameraUpdate *pigeonResult = [[FGMPlatformCameraUpdate alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformCameraUpdate *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraUpdate fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformCircle ++ (instancetype)makeWithJson:(id)json { + FGMPlatformCircle *pigeonResult = [[FGMPlatformCircle alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformCircle *)fromList:(NSArray *)list { + FGMPlatformCircle *pigeonResult = [[FGMPlatformCircle alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformCircle *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCircle fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformMarker ++ (instancetype)makeWithJson:(id)json { + FGMPlatformMarker *pigeonResult = [[FGMPlatformMarker alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformMarker *)fromList:(NSArray *)list { + FGMPlatformMarker *pigeonResult = [[FGMPlatformMarker alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformMarker *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformMarker fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformPolygon ++ (instancetype)makeWithJson:(id)json { + FGMPlatformPolygon *pigeonResult = [[FGMPlatformPolygon alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformPolygon *)fromList:(NSArray *)list { + FGMPlatformPolygon *pigeonResult = [[FGMPlatformPolygon alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformPolygon *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformPolygon fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformPolyline ++ (instancetype)makeWithJson:(id)json { + FGMPlatformPolyline *pigeonResult = [[FGMPlatformPolyline alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformPolyline *)fromList:(NSArray *)list { + FGMPlatformPolyline *pigeonResult = [[FGMPlatformPolyline alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformPolyline fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformTileOverlay ++ (instancetype)makeWithJson:(id)json { + FGMPlatformTileOverlay *pigeonResult = [[FGMPlatformTileOverlay alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformTileOverlay *)fromList:(NSArray *)list { + FGMPlatformTileOverlay *pigeonResult = [[FGMPlatformTileOverlay alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformTileOverlay *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformTileOverlay fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformLatLng ++ (instancetype)makeWithLatitude:(double)latitude longitude:(double)longitude { + FGMPlatformLatLng *pigeonResult = [[FGMPlatformLatLng alloc] init]; + pigeonResult.latitude = latitude; + pigeonResult.longitude = longitude; + return pigeonResult; +} ++ (FGMPlatformLatLng *)fromList:(NSArray *)list { + FGMPlatformLatLng *pigeonResult = [[FGMPlatformLatLng alloc] init]; + pigeonResult.latitude = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.longitude = [GetNullableObjectAtIndex(list, 1) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformLatLng *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformLatLng fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.latitude), + @(self.longitude), + ]; +} +@end + +@implementation FGMPlatformLatLngBounds ++ (instancetype)makeWithNortheast:(FGMPlatformLatLng *)northeast + southwest:(FGMPlatformLatLng *)southwest { + FGMPlatformLatLngBounds *pigeonResult = [[FGMPlatformLatLngBounds alloc] init]; + pigeonResult.northeast = northeast; + pigeonResult.southwest = southwest; + return pigeonResult; +} ++ (FGMPlatformLatLngBounds *)fromList:(NSArray *)list { + FGMPlatformLatLngBounds *pigeonResult = [[FGMPlatformLatLngBounds alloc] init]; + pigeonResult.northeast = GetNullableObjectAtIndex(list, 0); + pigeonResult.southwest = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable FGMPlatformLatLngBounds *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformLatLngBounds fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.northeast ?: [NSNull null], + self.southwest ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformMapConfiguration ++ (instancetype)makeWithJson:(id)json { + FGMPlatformMapConfiguration *pigeonResult = [[FGMPlatformMapConfiguration alloc] init]; + pigeonResult.json = json; + return pigeonResult; +} ++ (FGMPlatformMapConfiguration *)fromList:(NSArray *)list { + FGMPlatformMapConfiguration *pigeonResult = [[FGMPlatformMapConfiguration alloc] init]; + pigeonResult.json = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable FGMPlatformMapConfiguration *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformMapConfiguration fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.json ?: [NSNull null], + ]; +} +@end + +@implementation FGMPlatformPoint ++ (instancetype)makeWithX:(double)x y:(double)y { + FGMPlatformPoint *pigeonResult = [[FGMPlatformPoint alloc] init]; + pigeonResult.x = x; + pigeonResult.y = y; + return pigeonResult; +} ++ (FGMPlatformPoint *)fromList:(NSArray *)list { + FGMPlatformPoint *pigeonResult = [[FGMPlatformPoint alloc] init]; + pigeonResult.x = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.y = [GetNullableObjectAtIndex(list, 1) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformPoint *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformPoint fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.x), + @(self.y), + ]; +} +@end + @implementation FGMPlatformTileLayer + (instancetype)makeWithVisible:(BOOL)visible fadeIn:(BOOL)fadeIn @@ -105,8 +385,28 @@ @implementation FGMMessagesPigeonCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 129: - return [FGMPlatformTileLayer fromList:[self readValue]]; + return [FGMPlatformCameraUpdate fromList:[self readValue]]; case 130: + return [FGMPlatformCircle fromList:[self readValue]]; + case 131: + return [FGMPlatformMarker fromList:[self readValue]]; + case 132: + return [FGMPlatformPolygon fromList:[self readValue]]; + case 133: + return [FGMPlatformPolyline fromList:[self readValue]]; + case 134: + return [FGMPlatformTileOverlay fromList:[self readValue]]; + case 135: + return [FGMPlatformLatLng fromList:[self readValue]]; + case 136: + return [FGMPlatformLatLngBounds fromList:[self readValue]]; + case 137: + return [FGMPlatformMapConfiguration fromList:[self readValue]]; + case 138: + return [FGMPlatformPoint fromList:[self readValue]]; + case 139: + return [FGMPlatformTileLayer fromList:[self readValue]]; + case 140: return [FGMPlatformZoomRange fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -118,12 +418,42 @@ @interface FGMMessagesPigeonCodecWriter : FlutterStandardWriter @end @implementation FGMMessagesPigeonCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + if ([value isKindOfClass:[FGMPlatformCameraUpdate class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + } else if ([value isKindOfClass:[FGMPlatformCircle class]]) { [self writeByte:130]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { + [self writeByte:131]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { + [self writeByte:133]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { + [self writeByte:134]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { + [self writeByte:135]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { + [self writeByte:136]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { + [self writeByte:137]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { + [self writeByte:138]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + [self writeByte:139]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + [self writeByte:140]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -151,6 +481,544 @@ - (FlutterStandardReader *)readerWithData:(NSData *)data { }); return sSharedObject; } +void SetUpFGMMapsApi(id binaryMessenger, NSObject *api) { + SetUpFGMMapsApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpFGMMapsApiWithSuffix(id binaryMessenger, + NSObject *api, NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 + ? [NSString stringWithFormat:@".%@", messageChannelSuffix] + : @""; + /// Returns once the map instance is available. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.waitForMap", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(waitForMapWithError:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(waitForMapWithError:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + [api waitForMapWithError:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Updates the map's configuration options. + /// + /// Only non-null configuration values will result in updates; options with + /// null values will remain unchanged. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.updateMapConfiguration", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(updateWithMapConfiguration:error:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(updateWithMapConfiguration:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FGMPlatformMapConfiguration *arg_configuration = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api updateWithMapConfiguration:arg_configuration error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Updates the set of circles on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateCircles", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateCirclesByAdding:changing:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updateCirclesByAdding:changing:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_toChange = GetNullableObjectAtIndex(args, 1); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api updateCirclesByAdding:arg_toAdd + changing:arg_toChange + removing:arg_idsToRemove + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Updates the set of markers on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateMarkers", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateMarkersByAdding:changing:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updateMarkersByAdding:changing:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_toChange = GetNullableObjectAtIndex(args, 1); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api updateMarkersByAdding:arg_toAdd + changing:arg_toChange + removing:arg_idsToRemove + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Updates the set of polygonss on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.updatePolygons", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updatePolygonsByAdding:changing:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updatePolygonsByAdding:changing:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_toChange = GetNullableObjectAtIndex(args, 1); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api updatePolygonsByAdding:arg_toAdd + changing:arg_toChange + removing:arg_idsToRemove + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Updates the set of polylines on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.updatePolylines", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updatePolylinesByAdding: + changing:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updatePolylinesByAdding:changing:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_toChange = GetNullableObjectAtIndex(args, 1); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api updatePolylinesByAdding:arg_toAdd + changing:arg_toChange + removing:arg_idsToRemove + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Updates the set of tile overlays on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.updateTileOverlays", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(updateTileOverlaysByAdding: + changing:removing:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(updateTileOverlaysByAdding:changing:removing:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSArray *arg_toAdd = GetNullableObjectAtIndex(args, 0); + NSArray *arg_toChange = GetNullableObjectAtIndex(args, 1); + NSArray *arg_idsToRemove = GetNullableObjectAtIndex(args, 2); + FlutterError *error; + [api updateTileOverlaysByAdding:arg_toAdd + changing:arg_toChange + removing:arg_idsToRemove + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Gets the screen coordinate for the given map location. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.getScreenCoordinate", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(screenCoordinatesForLatLng:error:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(screenCoordinatesForLatLng:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FGMPlatformLatLng *arg_latLng = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + FGMPlatformPoint *output = [api screenCoordinatesForLatLng:arg_latLng error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Gets the map location for the given screen coordinate. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getLatLng", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert( + [api respondsToSelector:@selector(latLngForScreenCoordinate:error:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(latLngForScreenCoordinate:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FGMPlatformPoint *arg_screenCoordinate = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + FGMPlatformLatLng *output = [api latLngForScreenCoordinate:arg_screenCoordinate + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Gets the map region currently displayed on the map. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.getVisibleRegion", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(visibleMapRegion:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(visibleMapRegion:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + FGMPlatformLatLngBounds *output = [api visibleMapRegion:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Moves the camera according to [cameraUpdate] immediately, with no + /// animation. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.moveCamera", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(moveCameraWithUpdate:error:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(moveCameraWithUpdate:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FGMPlatformCameraUpdate *arg_cameraUpdate = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api moveCameraWithUpdate:arg_cameraUpdate error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Moves the camera according to [cameraUpdate], animating the update. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.animateCamera", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(animateCameraWithUpdate:error:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(animateCameraWithUpdate:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + FGMPlatformCameraUpdate *arg_cameraUpdate = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api animateCameraWithUpdate:arg_cameraUpdate error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Gets the current map zoom level. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getZoomLevel", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(currentZoomLevel:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(currentZoomLevel:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api currentZoomLevel:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Show the info window for the marker with the given ID. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.showInfoWindow", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(showInfoWindowForMarkerWithIdentifier:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(showInfoWindowForMarkerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_markerId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api showInfoWindowForMarkerWithIdentifier:arg_markerId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Hide the info window for the marker with the given ID. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.hideInfoWindow", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(hideInfoWindowForMarkerWithIdentifier:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(hideInfoWindowForMarkerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_markerId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api hideInfoWindowForMarkerWithIdentifier:arg_markerId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Returns true if the marker with the given ID is currently displaying its + /// info window. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.isInfoWindowShown", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(isShowingInfoWindowForMarkerWithIdentifier: + error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(isShowingInfoWindowForMarkerWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_markerId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSNumber *output = [api isShowingInfoWindowForMarkerWithIdentifier:arg_markerId + error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Sets the style to the given map style string, where an empty string + /// indicates that the style should be cleared. + /// + /// If there was an error setting the style, such as an invalid style string, + /// returns the error message. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.setStyle", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(setStyle:error:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(setStyle:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_style = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + NSString *output = [api setStyle:arg_style error:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Returns the error string from the last attempt to set the map style, if + /// any. + /// + /// This allows checking asynchronously for initial style failures, as there + /// is no way to return failures from map initialization. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.getLastStyleError", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(lastStyleError:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(lastStyleError:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSString *output = [api lastStyleError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Clears the cache of tiles previously requseted from the tile provider. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios." + @"MapsApi.clearTileCache", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(clearTileCacheForOverlayWithIdentifier:error:)], + @"FGMMapsApi api (%@) doesn't respond to " + @"@selector(clearTileCacheForOverlayWithIdentifier:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSString *arg_tileOverlayId = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api clearTileCacheForOverlayWithIdentifier:arg_tileOverlayId error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Takes a snapshot of the map and returns its image data. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.takeSnapshot", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FGMGetMessagesCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(takeSnapshotWithError:)], + @"FGMMapsApi api (%@) doesn't respond to @selector(takeSnapshotWithError:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + FlutterStandardTypedData *output = [api takeSnapshotWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} void SetUpFGMMapsInspectorApi(id binaryMessenger, NSObject *api) { SetUpFGMMapsInspectorApiWithSuffix(binaryMessenger, api, @""); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index f0db5e99b12..dd8cb856f82 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -17,6 +17,11 @@ import 'messages.g.dart'; // TODO(stuartmorgan): Remove the dependency on platform interface toJson // methods. Channel serialization details should all be package-internal. +/// The non-test implementation of `_apiProvider`. +MapsApi _productionApiProvider(int mapId) { + return MapsApi(messageChannelSuffix: mapId.toString()); +} + /// Error thrown when an unknown map ID is provided to a method channel API. class UnknownMapIDError extends Error { /// Creates an assertion error with the provided [mapId] and optional @@ -40,6 +45,11 @@ class UnknownMapIDError extends Error { /// An implementation of [GoogleMapsFlutterPlatform] for iOS. class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { + /// Creates a new Android maps implementation instance. + GoogleMapsFlutterIOS({ + @visibleForTesting MapsApi Function(int mapId)? apiProvider, + }) : _apiProvider = apiProvider ?? _productionApiProvider; + /// Registers the iOS implementation of GoogleMapsFlutterPlatform. static void registerWith() { GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterIOS(); @@ -49,13 +59,18 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { // Every method call passes the int mapId final Map _channels = {}; - /// Accesses the MethodChannel associated to the passed mapId. - MethodChannel _channel(int mapId) { - final MethodChannel? channel = _channels[mapId]; - if (channel == null) { + final Map _hostMaps = {}; + + // A method to create MapsApi instances, which can be overridden for testing. + final MapsApi Function(int mapId) _apiProvider; + + /// Accesses the MapsApi associated to the passed mapId. + MapsApi _hostApi(int mapId) { + final MapsApi? api = _hostMaps[mapId]; + if (api == null) { throw UnknownMapIDError(mapId); } - return channel; + return api; } // Keep a collection of mapId to a map of TileOverlays. @@ -75,10 +90,23 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return channel; } + /// Returns the API instance for [mapId], creating it if it doesn't already + /// exist. + @visibleForTesting + MapsApi ensureApiInitialized(int mapId) { + MapsApi? api = _hostMaps[mapId]; + if (api == null) { + api = _apiProvider(mapId); + _hostMaps[mapId] ??= api; + } + return api; + } + @override Future init(int mapId) { - final MethodChannel channel = ensureChannelInitialized(mapId); - return channel.invokeMethod('map#waitForMap'); + ensureChannelInitialized(mapId); + final MapsApi hostApi = ensureApiInitialized(mapId); + return hostApi.waitForMap(); } @override @@ -269,17 +297,22 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return (call.arguments as Map).cast(); } + @override + Future updateMapConfiguration( + MapConfiguration configuration, { + required int mapId, + }) { + return updateMapOptions(_jsonForMapConfiguration(configuration), + mapId: mapId); + } + @override Future updateMapOptions( Map optionsUpdate, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'map#update', - { - 'options': optionsUpdate, - }, - ); + return _hostApi(mapId) + .updateMapConfiguration(PlatformMapConfiguration(json: optionsUpdate)); } @override @@ -287,9 +320,10 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { MarkerUpdates markerUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'markers#update', - markerUpdates.toJson(), + return _hostApi(mapId).updateMarkers( + markerUpdates.markersToAdd.map(_platformMarkerFromMarker).toList(), + markerUpdates.markersToChange.map(_platformMarkerFromMarker).toList(), + markerUpdates.markerIdsToRemove.map((MarkerId id) => id.value).toList(), ); } @@ -298,9 +332,12 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { PolygonUpdates polygonUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'polygons#update', - polygonUpdates.toJson(), + return _hostApi(mapId).updatePolygons( + polygonUpdates.polygonsToAdd.map(_platformPolygonFromPolygon).toList(), + polygonUpdates.polygonsToChange.map(_platformPolygonFromPolygon).toList(), + polygonUpdates.polygonIdsToRemove + .map((PolygonId id) => id.value) + .toList(), ); } @@ -309,9 +346,16 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { PolylineUpdates polylineUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'polylines#update', - polylineUpdates.toJson(), + return _hostApi(mapId).updatePolylines( + polylineUpdates.polylinesToAdd + .map(_platformPolylineFromPolyline) + .toList(), + polylineUpdates.polylinesToChange + .map(_platformPolylineFromPolyline) + .toList(), + polylineUpdates.polylineIdsToRemove + .map((PolylineId id) => id.value) + .toList(), ); } @@ -320,9 +364,10 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { CircleUpdates circleUpdates, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'circles#update', - circleUpdates.toJson(), + return _hostApi(mapId).updateCircles( + circleUpdates.circlesToAdd.map(_platformCircleFromCircle).toList(), + circleUpdates.circlesToChange.map(_platformCircleFromCircle).toList(), + circleUpdates.circleIdsToRemove.map((CircleId id) => id.value).toList(), ); } @@ -339,9 +384,16 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { final _TileOverlayUpdates updates = _TileOverlayUpdates.from(previousSet, newTileOverlays); _tileOverlays[mapId] = keyTileOverlayId(newTileOverlays); - return _channel(mapId).invokeMethod( - 'tileOverlays#update', - updates.toJson(), + return _hostApi(mapId).updateTileOverlays( + updates.tileOverlaysToAdd + .map(_platformTileOverlayFromTileOverlay) + .toList(), + updates.tileOverlaysToChange + .map(_platformTileOverlayFromTileOverlay) + .toList(), + updates.tileOverlayIdsToRemove + .map((TileOverlayId id) => id.value) + .toList(), ); } @@ -350,10 +402,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { TileOverlayId tileOverlayId, { required int mapId, }) { - return _channel(mapId) - .invokeMethod('tileOverlays#clearTileCache', { - 'tileOverlayId': tileOverlayId.value, - }); + return _hostApi(mapId).clearTileCache(tileOverlayId.value); } @override @@ -361,10 +410,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { required int mapId, }) { - return _channel(mapId) - .invokeMethod('camera#animate', { - 'cameraUpdate': cameraUpdate.toJson(), - }); + return _hostApi(mapId) + .animateCamera(PlatformCameraUpdate(json: cameraUpdate.toJson())); } @override @@ -372,9 +419,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { CameraUpdate cameraUpdate, { required int mapId, }) { - return _channel(mapId).invokeMethod('camera#move', { - 'cameraUpdate': cameraUpdate.toJson(), - }); + return _hostApi(mapId) + .moveCamera(PlatformCameraUpdate(json: cameraUpdate.toJson())); } @override @@ -382,11 +428,10 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { String? mapStyle, { required int mapId, }) async { - final List successAndError = (await _channel(mapId) - .invokeMethod>('map#setStyle', mapStyle))!; - final bool success = successAndError[0] as bool; - if (!success) { - throw MapStyleException(successAndError[1] as String); + final String? errorDescription = + await _hostApi(mapId).setStyle(mapStyle ?? ''); + if (errorDescription != null) { + throw MapStyleException(errorDescription); } } @@ -394,12 +439,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { Future getVisibleRegion({ required int mapId, }) async { - final Map latLngBounds = (await _channel(mapId) - .invokeMapMethod('map#getVisibleRegion'))!; - final LatLng southwest = LatLng.fromJson(latLngBounds['southwest'])!; - final LatLng northeast = LatLng.fromJson(latLngBounds['northeast'])!; - - return LatLngBounds(northeast: northeast, southwest: southwest); + return _latLngBoundsFromPlatformLatLngBounds( + await _hostApi(mapId).getVisibleRegion()); } @override @@ -407,11 +448,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { LatLng latLng, { required int mapId, }) async { - final Map point = (await _channel(mapId) - .invokeMapMethod( - 'map#getScreenCoordinate', latLng.toJson()))!; - - return ScreenCoordinate(x: point['x']!, y: point['y']!); + return _screenCoordinateFromPlatformPoint(await _hostApi(mapId) + .getScreenCoordinate(_platformLatLngFromLatLng(latLng))); } @override @@ -419,10 +457,8 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { ScreenCoordinate screenCoordinate, { required int mapId, }) async { - final List latLng = (await _channel(mapId) - .invokeMethod>( - 'map#getLatLng', screenCoordinate.toJson()))!; - return LatLng(latLng[0] as double, latLng[1] as double); + return _latLngFromPlatformLatLng(await _hostApi(mapId) + .getLatLng(_platformPointFromScreenCoordinate(screenCoordinate))); } @override @@ -430,8 +466,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { MarkerId markerId, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'markers#showInfoWindow', {'markerId': markerId.value}); + return _hostApi(mapId).showInfoWindow(markerId.value); } @override @@ -439,38 +474,34 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { MarkerId markerId, { required int mapId, }) { - return _channel(mapId).invokeMethod( - 'markers#hideInfoWindow', {'markerId': markerId.value}); + return _hostApi(mapId).hideInfoWindow(markerId.value); } @override Future isMarkerInfoWindowShown( MarkerId markerId, { required int mapId, - }) async { - return (await _channel(mapId).invokeMethod( - 'markers#isInfoWindowShown', - {'markerId': markerId.value}))!; + }) { + return _hostApi(mapId).isInfoWindowShown(markerId.value); } @override Future getZoomLevel({ required int mapId, - }) async { - return (await _channel(mapId).invokeMethod('map#getZoomLevel'))!; + }) { + return _hostApi(mapId).getZoomLevel(); } @override Future takeSnapshot({ required int mapId, }) { - return _channel(mapId).invokeMethod('map#takeSnapshot'); + return _hostApi(mapId).takeSnapshot(); } @override Future getStyleError({required int mapId}) { - final MethodChannel channel = ensureChannelInitialized(mapId); - return channel.invokeMethod('map#getStyleError'); + return _hostApi(mapId).getLastStyleError(); } Widget _buildView( @@ -581,6 +612,54 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { GoogleMapsInspectorPlatform.instance = GoogleMapsInspectorIOS((int mapId) => MapsInspectorApi(messageChannelSuffix: mapId.toString())); } + + static LatLng _latLngFromPlatformLatLng(PlatformLatLng latLng) { + return LatLng(latLng.latitude, latLng.longitude); + } + + static PlatformLatLng _platformLatLngFromLatLng(LatLng latLng) { + return PlatformLatLng( + latitude: latLng.latitude, longitude: latLng.longitude); + } + + static ScreenCoordinate _screenCoordinateFromPlatformPoint( + PlatformPoint point) { + return ScreenCoordinate(x: point.x.round(), y: point.y.round()); + } + + static PlatformPoint _platformPointFromScreenCoordinate( + ScreenCoordinate coordinate) { + return PlatformPoint( + x: coordinate.x.toDouble(), y: coordinate.y.toDouble()); + } + + static LatLngBounds _latLngBoundsFromPlatformLatLngBounds( + PlatformLatLngBounds bounds) { + return LatLngBounds( + southwest: _latLngFromPlatformLatLng(bounds.southwest), + northeast: _latLngFromPlatformLatLng(bounds.northeast)); + } + + static PlatformCircle _platformCircleFromCircle(Circle circle) { + return PlatformCircle(json: circle.toJson()); + } + + static PlatformMarker _platformMarkerFromMarker(Marker marker) { + return PlatformMarker(json: marker.toJson()); + } + + static PlatformPolygon _platformPolygonFromPolygon(Polygon polygon) { + return PlatformPolygon(json: polygon.toJson()); + } + + static PlatformPolyline _platformPolylineFromPolyline(Polyline polyline) { + return PlatformPolyline(json: polyline.toJson()); + } + + static PlatformTileOverlay _platformTileOverlayFromTileOverlay( + TileOverlay tileOverlay) { + return PlatformTileOverlay(json: tileOverlay.toJson()); + } } Map _jsonForMapConfiguration(MapConfiguration config) { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart index 0f54169c937..b327880eb9d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v20.0.1), do not edit directly. +// Autogenerated from Pigeon (v20.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -18,6 +18,262 @@ PlatformException _createConnectionError(String channelName) { ); } +/// Pigeon representation of a CameraUpdate. +class PlatformCameraUpdate { + PlatformCameraUpdate({ + required this.json, + }); + + /// The update data, as JSON. This should only be set from + /// CameraUpdate.toJson, and the native code must intepret it according to the + /// internal implementation details of the CameraUpdate class. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformCameraUpdate decode(Object result) { + result as List; + return PlatformCameraUpdate( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Circle class. +class PlatformCircle { + PlatformCircle({ + required this.json, + }); + + /// The circle data, as JSON. This should only be set from + /// Circle.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformCircle decode(Object result) { + result as List; + return PlatformCircle( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Marker class. +class PlatformMarker { + PlatformMarker({ + required this.json, + }); + + /// The marker data, as JSON. This should only be set from + /// Marker.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformMarker decode(Object result) { + result as List; + return PlatformMarker( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Polygon class. +class PlatformPolygon { + PlatformPolygon({ + required this.json, + }); + + /// The polygon data, as JSON. This should only be set from + /// Polygon.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformPolygon decode(Object result) { + result as List; + return PlatformPolygon( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the Polyline class. +class PlatformPolyline { + PlatformPolyline({ + required this.json, + }); + + /// The polyline data, as JSON. This should only be set from + /// Polyline.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformPolyline decode(Object result) { + result as List; + return PlatformPolyline( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of the TileOverlay class. +class PlatformTileOverlay { + PlatformTileOverlay({ + required this.json, + }); + + /// The tile overlay data, as JSON. This should only be set from + /// TileOverlay.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformTileOverlay decode(Object result) { + result as List; + return PlatformTileOverlay( + json: result[0]!, + ); + } +} + +/// Pigeon equivalent of LatLng. +class PlatformLatLng { + PlatformLatLng({ + required this.latitude, + required this.longitude, + }); + + double latitude; + + double longitude; + + Object encode() { + return [ + latitude, + longitude, + ]; + } + + static PlatformLatLng decode(Object result) { + result as List; + return PlatformLatLng( + latitude: result[0]! as double, + longitude: result[1]! as double, + ); + } +} + +/// Pigeon equivalent of LatLngBounds. +class PlatformLatLngBounds { + PlatformLatLngBounds({ + required this.northeast, + required this.southwest, + }); + + PlatformLatLng northeast; + + PlatformLatLng southwest; + + Object encode() { + return [ + northeast, + southwest, + ]; + } + + static PlatformLatLngBounds decode(Object result) { + result as List; + return PlatformLatLngBounds( + northeast: result[0]! as PlatformLatLng, + southwest: result[1]! as PlatformLatLng, + ); + } +} + +/// Pigeon equivalent of MapConfiguration. +class PlatformMapConfiguration { + PlatformMapConfiguration({ + required this.json, + }); + + /// The configuration options, as JSON. This should only be set from + /// _jsonForMapConfiguration, and the native code must intepret it according + /// to the internal implementation details of that method. + Object json; + + Object encode() { + return [ + json, + ]; + } + + static PlatformMapConfiguration decode(Object result) { + result as List; + return PlatformMapConfiguration( + json: result[0]!, + ); + } +} + +/// Pigeon representation of an x,y coordinate. +class PlatformPoint { + PlatformPoint({ + required this.x, + required this.y, + }); + + double x; + + double y; + + Object encode() { + return [ + x, + y, + ]; + } + + static PlatformPoint decode(Object result) { + result as List; + return PlatformPoint( + x: result[0]! as double, + y: result[1]! as double, + ); + } +} + /// Pigeon equivalent of GMSTileLayer properties. class PlatformTileLayer { PlatformTileLayer({ @@ -55,7 +311,7 @@ class PlatformTileLayer { } } -/// Possible outcomes of launching a URL. +/// Pigeon equivalent of MinMaxZoomPreference. class PlatformZoomRange { PlatformZoomRange({ required this.min, @@ -86,12 +342,42 @@ class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is PlatformTileLayer) { + if (value is PlatformCameraUpdate) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformCircle) { buffer.putUint8(130); writeValue(buffer, value.encode()); + } else if (value is PlatformMarker) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is PlatformPolygon) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is PlatformPolyline) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is PlatformTileOverlay) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is PlatformLatLng) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is PlatformLatLngBounds) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is PlatformMapConfiguration) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is PlatformPoint) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else if (value is PlatformTileLayer) { + buffer.putUint8(139); + writeValue(buffer, value.encode()); + } else if (value is PlatformZoomRange) { + buffer.putUint8(140); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -101,8 +387,28 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformCameraUpdate.decode(readValue(buffer)!); case 130: + return PlatformCircle.decode(readValue(buffer)!); + case 131: + return PlatformMarker.decode(readValue(buffer)!); + case 132: + return PlatformPolygon.decode(readValue(buffer)!); + case 133: + return PlatformPolyline.decode(readValue(buffer)!); + case 134: + return PlatformTileOverlay.decode(readValue(buffer)!); + case 135: + return PlatformLatLng.decode(readValue(buffer)!); + case 136: + return PlatformLatLngBounds.decode(readValue(buffer)!); + case 137: + return PlatformMapConfiguration.decode(readValue(buffer)!); + case 138: + return PlatformPoint.decode(readValue(buffer)!); + case 139: + return PlatformTileLayer.decode(readValue(buffer)!); + case 140: return PlatformZoomRange.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -110,6 +416,568 @@ class _PigeonCodec extends StandardMessageCodec { } } +/// Interface for non-test interactions with the native SDK. +/// +/// For test-only state queries, see [MapsInspectorApi]. +class MapsApi { + /// Constructor for [MapsApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + MapsApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : __pigeon_binaryMessenger = binaryMessenger, + __pigeon_messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String __pigeon_messageChannelSuffix; + + /// Returns once the map instance is available. + Future waitForMap() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.waitForMap$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the map's configuration options. + /// + /// Only non-null configuration values will result in updates; options with + /// null values will remain unchanged. + Future updateMapConfiguration( + PlatformMapConfiguration configuration) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateMapConfiguration$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([configuration]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of circles on the map. + Future updateCircles(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateCircles$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of markers on the map. + Future updateMarkers(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateMarkers$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of polygonss on the map. + Future updatePolygons(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updatePolygons$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of polylines on the map. + Future updatePolylines(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updatePolylines$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Updates the set of tile overlays on the map. + Future updateTileOverlays(List toAdd, + List toChange, List idsToRemove) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.updateTileOverlays$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([toAdd, toChange, idsToRemove]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Gets the screen coordinate for the given map location. + Future getScreenCoordinate(PlatformLatLng latLng) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getScreenCoordinate$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([latLng]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as PlatformPoint?)!; + } + } + + /// Gets the map location for the given screen coordinate. + Future getLatLng(PlatformPoint screenCoordinate) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getLatLng$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([screenCoordinate]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as PlatformLatLng?)!; + } + } + + /// Gets the map region currently displayed on the map. + Future getVisibleRegion() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getVisibleRegion$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as PlatformLatLngBounds?)!; + } + } + + /// Moves the camera according to [cameraUpdate] immediately, with no + /// animation. + Future moveCamera(PlatformCameraUpdate cameraUpdate) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.moveCamera$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraUpdate]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Moves the camera according to [cameraUpdate], animating the update. + Future animateCamera(PlatformCameraUpdate cameraUpdate) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.animateCamera$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([cameraUpdate]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Gets the current map zoom level. + Future getZoomLevel() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getZoomLevel$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as double?)!; + } + } + + /// Show the info window for the marker with the given ID. + Future showInfoWindow(String markerId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.showInfoWindow$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([markerId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Hide the info window for the marker with the given ID. + Future hideInfoWindow(String markerId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.hideInfoWindow$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([markerId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Returns true if the marker with the given ID is currently displaying its + /// info window. + Future isInfoWindowShown(String markerId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.isInfoWindowShown$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([markerId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } + + /// Sets the style to the given map style string, where an empty string + /// indicates that the style should be cleared. + /// + /// If there was an error setting the style, such as an invalid style string, + /// returns the error message. + Future setStyle(String style) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.setStyle$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([style]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + /// Returns the error string from the last attempt to set the map style, if + /// any. + /// + /// This allows checking asynchronously for initial style failures, as there + /// is no way to return failures from map initialization. + Future getLastStyleError() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.getLastStyleError$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + /// Clears the cache of tiles previously requseted from the tile provider. + Future clearTileCache(String tileOverlayId) async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.clearTileCache$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([tileOverlayId]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Takes a snapshot of the map and returns its image data. + Future takeSnapshot() async { + final String __pigeon_channelName = + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsApi.takeSnapshot$__pigeon_messageChannelSuffix'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as Uint8List?); + } + } +} + /// Inspector API only intended for use in integration tests. class MapsInspectorApi { /// Constructor for [MapsInspectorApi]. The [binaryMessenger] named argument is diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index 5e394763400..b6321bf5b4c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -12,6 +12,117 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) +/// Pigeon representation of a CameraUpdate. +class PlatformCameraUpdate { + PlatformCameraUpdate(this.json); + + /// The update data, as JSON. This should only be set from + /// CameraUpdate.toJson, and the native code must intepret it according to the + /// internal implementation details of the CameraUpdate class. + // TODO(stuartmorgan): Update the google_maps_platform_interface CameraUpdate + // class to provide a structured representation of an update. Currently it + // uses JSON as its only state, so there is no way to preserve structure. + // This wrapper class exists as a placeholder for now to at least provide + // type safety in the top-level call's arguments. + final Object json; +} + +/// Pigeon equivalent of the Circle class. +class PlatformCircle { + PlatformCircle(this.json); + + /// The circle data, as JSON. This should only be set from + /// Circle.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the Marker class. +class PlatformMarker { + PlatformMarker(this.json); + + /// The marker data, as JSON. This should only be set from + /// Marker.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the Polygon class. +class PlatformPolygon { + PlatformPolygon(this.json); + + /// The polygon data, as JSON. This should only be set from + /// Polygon.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the Polyline class. +class PlatformPolyline { + PlatformPolyline(this.json); + + /// The polyline data, as JSON. This should only be set from + /// Polyline.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of the TileOverlay class. +class PlatformTileOverlay { + PlatformTileOverlay(this.json); + + /// The tile overlay data, as JSON. This should only be set from + /// TileOverlay.toJson, and the native code must intepret it according to the + /// internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon equivalent of LatLng. +class PlatformLatLng { + PlatformLatLng({required this.latitude, required this.longitude}); + + final double latitude; + final double longitude; +} + +/// Pigeon equivalent of LatLngBounds. +class PlatformLatLngBounds { + PlatformLatLngBounds({required this.northeast, required this.southwest}); + + final PlatformLatLng northeast; + final PlatformLatLng southwest; +} + +/// Pigeon equivalent of MapConfiguration. +class PlatformMapConfiguration { + PlatformMapConfiguration({required this.json}); + + /// The configuration options, as JSON. This should only be set from + /// _jsonForMapConfiguration, and the native code must intepret it according + /// to the internal implementation details of that method. + // TODO(stuartmorgan): Replace this with structured data. This exists only to + // allow incremental migration to Pigeon. + final Object json; +} + +/// Pigeon representation of an x,y coordinate. +class PlatformPoint { + PlatformPoint({required this.x, required this.y}); + + final double x; + final double y; +} + /// Pigeon equivalent of GMSTileLayer properties. class PlatformTileLayer { PlatformTileLayer({ @@ -27,7 +138,7 @@ class PlatformTileLayer { final int zIndex; } -/// Possible outcomes of launching a URL. +/// Pigeon equivalent of MinMaxZoomPreference. class PlatformZoomRange { PlatformZoomRange({required this.min, required this.max}); @@ -35,6 +146,123 @@ class PlatformZoomRange { final double max; } +/// Interface for non-test interactions with the native SDK. +/// +/// For test-only state queries, see [MapsInspectorApi]. +@HostApi() +abstract class MapsApi { + /// Returns once the map instance is available. + void waitForMap(); + + /// Updates the map's configuration options. + /// + /// Only non-null configuration values will result in updates; options with + /// null values will remain unchanged. + @ObjCSelector('updateWithMapConfiguration:') + void updateMapConfiguration(PlatformMapConfiguration configuration); + + /// Updates the set of circles on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updateCirclesByAdding:changing:removing:') + void updateCircles(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of markers on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updateMarkersByAdding:changing:removing:') + void updateMarkers(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of polygonss on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updatePolygonsByAdding:changing:removing:') + void updatePolygons(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of polylines on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updatePolylinesByAdding:changing:removing:') + void updatePolylines(List toAdd, + List toChange, List idsToRemove); + + /// Updates the set of tile overlays on the map. + // TODO(stuartmorgan): Make the generic type non-nullable once supported. + // https://github.com/flutter/flutter/issues/97848 + // The consuming code treats the entries as non-nullable. + @ObjCSelector('updateTileOverlaysByAdding:changing:removing:') + void updateTileOverlays(List toAdd, + List toChange, List idsToRemove); + + /// Gets the screen coordinate for the given map location. + @ObjCSelector('screenCoordinatesForLatLng:') + PlatformPoint getScreenCoordinate(PlatformLatLng latLng); + + /// Gets the map location for the given screen coordinate. + @ObjCSelector('latLngForScreenCoordinate:') + PlatformLatLng getLatLng(PlatformPoint screenCoordinate); + + /// Gets the map region currently displayed on the map. + @ObjCSelector('visibleMapRegion') + PlatformLatLngBounds getVisibleRegion(); + + /// Moves the camera according to [cameraUpdate] immediately, with no + /// animation. + @ObjCSelector('moveCameraWithUpdate:') + void moveCamera(PlatformCameraUpdate cameraUpdate); + + /// Moves the camera according to [cameraUpdate], animating the update. + @ObjCSelector('animateCameraWithUpdate:') + void animateCamera(PlatformCameraUpdate cameraUpdate); + + /// Gets the current map zoom level. + @ObjCSelector('currentZoomLevel') + double getZoomLevel(); + + /// Show the info window for the marker with the given ID. + @ObjCSelector('showInfoWindowForMarkerWithIdentifier:') + void showInfoWindow(String markerId); + + /// Hide the info window for the marker with the given ID. + @ObjCSelector('hideInfoWindowForMarkerWithIdentifier:') + void hideInfoWindow(String markerId); + + /// Returns true if the marker with the given ID is currently displaying its + /// info window. + @ObjCSelector('isShowingInfoWindowForMarkerWithIdentifier:') + bool isInfoWindowShown(String markerId); + + /// Sets the style to the given map style string, where an empty string + /// indicates that the style should be cleared. + /// + /// If there was an error setting the style, such as an invalid style string, + /// returns the error message. + @ObjCSelector('setStyle:') + String? setStyle(String style); + + /// Returns the error string from the last attempt to set the map style, if + /// any. + /// + /// This allows checking asynchronously for initial style failures, as there + /// is no way to return failures from map initialization. + @ObjCSelector('lastStyleError') + String? getLastStyleError(); + + /// Clears the cache of tiles previously requseted from the tile provider. + @ObjCSelector('clearTileCacheForOverlayWithIdentifier:') + void clearTileCache(String tileOverlayId); + + /// Takes a snapshot of the map and returns its image data. + Uint8List? takeSnapshot(); +} + /// Inspector API only intended for use in integration tests. @HostApi() abstract class MapsInspectorApi { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index 7060af1202e..a70cc3e8f8b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.8.2 +version: 2.9.0 environment: sdk: ^3.2.3 @@ -24,8 +24,10 @@ dependencies: dev_dependencies: async: ^2.5.0 + build_runner: ^2.4.11 flutter_test: sdk: flutter + mockito: 5.4.4 pigeon: ^20.0.1 plugin_platform_interface: ^2.1.7 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart index c9f21a79e4c..eff422240c1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart @@ -8,33 +8,23 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_maps_flutter_ios/google_maps_flutter_ios.dart'; +import 'package:google_maps_flutter_ios/src/messages.g.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'google_maps_flutter_ios_test.mocks.dart'; + +@GenerateNiceMocks(>[MockSpec()]) void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late List log; - - setUp(() async { - log = []; - }); - - /// Initializes a map with the given ID and canned responses, logging all - /// calls to [log]. - void configureMockMap( - GoogleMapsFlutterIOS maps, { - required int mapId, - required Future? Function(MethodCall call) handler, - }) { - final MethodChannel channel = maps.ensureChannelInitialized(mapId); - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( - channel, - (MethodCall methodCall) { - log.add(methodCall.method); - return handler(methodCall); - }, - ); + (GoogleMapsFlutterIOS, MockMapsApi) setUpMockMap({required int mapId}) { + final MockMapsApi api = MockMapsApi(); + final GoogleMapsFlutterIOS maps = + GoogleMapsFlutterIOS(apiProvider: (_) => api); + maps.ensureApiInitialized(mapId); + return (maps, api); } Future sendPlatformMessage( @@ -51,39 +41,398 @@ void main() { expect(GoogleMapsFlutterPlatform.instance, isA()); }); - // Calls each method that uses invokeMethod with a return type other than - // void to ensure that the casting/nullability handling succeeds. - // - // TODO(stuartmorgan): Remove this once there is real test coverage of - // each method, since that would cover this issue. - test('non-void invokeMethods handle types correctly', () async { - const int mapId = 0; - final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); - configureMockMap(maps, mapId: mapId, - handler: (MethodCall methodCall) async { - switch (methodCall.method) { - case 'map#getLatLng': - return [1.0, 2.0]; - case 'markers#isInfoWindowShown': - return true; - case 'map#getZoomLevel': - return 2.5; - case 'map#takeSnapshot': - return null; - } - }); - - await maps.getLatLng(const ScreenCoordinate(x: 0, y: 0), mapId: mapId); - await maps.isMarkerInfoWindowShown(const MarkerId(''), mapId: mapId); - await maps.getZoomLevel(mapId: mapId); - await maps.takeSnapshot(mapId: mapId); - // Check that all the invokeMethod calls happened. - expect(log, [ - 'map#getLatLng', - 'markers#isInfoWindowShown', - 'map#getZoomLevel', - 'map#takeSnapshot', - ]); + test('init calls waitForMap', () async { + final MockMapsApi api = MockMapsApi(); + final GoogleMapsFlutterIOS maps = + GoogleMapsFlutterIOS(apiProvider: (_) => api); + + await maps.init(1); + + verify(api.waitForMap()); + }); + + test('getScreenCoordinate converts and passes values correctly', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Arbitrary values that are all different from each other. + const LatLng latLng = LatLng(10, 20); + const ScreenCoordinate expectedCoord = ScreenCoordinate(x: 30, y: 40); + when(api.getScreenCoordinate(any)).thenAnswer((_) async => PlatformPoint( + x: expectedCoord.x.toDouble(), y: expectedCoord.y.toDouble())); + + final ScreenCoordinate coord = + await maps.getScreenCoordinate(latLng, mapId: mapId); + expect(coord, expectedCoord); + final VerificationResult verification = + verify(api.getScreenCoordinate(captureAny)); + final PlatformLatLng passedLatLng = + verification.captured[0] as PlatformLatLng; + expect(passedLatLng.latitude, latLng.latitude); + expect(passedLatLng.longitude, latLng.longitude); + }); + + test('getLatLng converts and passes values correctly', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Arbitrary values that are all different from each other. + const LatLng expectedLatLng = LatLng(10, 20); + const ScreenCoordinate coord = ScreenCoordinate(x: 30, y: 40); + when(api.getLatLng(any)).thenAnswer((_) async => PlatformLatLng( + latitude: expectedLatLng.latitude, + longitude: expectedLatLng.longitude)); + + final LatLng latLng = await maps.getLatLng(coord, mapId: mapId); + expect(latLng, expectedLatLng); + final VerificationResult verification = verify(api.getLatLng(captureAny)); + final PlatformPoint passedCoord = verification.captured[0] as PlatformPoint; + expect(passedCoord.x, coord.x); + expect(passedCoord.y, coord.y); + }); + + test('getVisibleRegion converts and passes values correctly', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Arbitrary values that are all different from each other. + final LatLngBounds expectedBounds = LatLngBounds( + southwest: const LatLng(10, 20), northeast: const LatLng(30, 40)); + when(api.getVisibleRegion()).thenAnswer((_) async => PlatformLatLngBounds( + southwest: PlatformLatLng( + latitude: expectedBounds.southwest.latitude, + longitude: expectedBounds.southwest.longitude), + northeast: PlatformLatLng( + latitude: expectedBounds.northeast.latitude, + longitude: expectedBounds.northeast.longitude))); + + final LatLngBounds bounds = await maps.getVisibleRegion(mapId: mapId); + expect(bounds, expectedBounds); + }); + + test('moveCamera calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.scrollBy(10, 20); + await maps.moveCamera(update, mapId: mapId); + + final VerificationResult verification = verify(api.moveCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + expect(passedUpdate.json, update.toJson()); + }); + + test('animateCamera calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final CameraUpdate update = CameraUpdate.scrollBy(10, 20); + await maps.animateCamera(update, mapId: mapId); + + final VerificationResult verification = + verify(api.animateCamera(captureAny)); + final PlatformCameraUpdate passedUpdate = + verification.captured[0] as PlatformCameraUpdate; + expect(passedUpdate.json, update.toJson()); + }); + + test('getZoomLevel passes values correctly', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const double expectedZoom = 4.2; + when(api.getZoomLevel()).thenAnswer((_) async => expectedZoom); + + final double zoom = await maps.getZoomLevel(mapId: mapId); + expect(zoom, expectedZoom); + }); + + test('showInfoWindow calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const String markedId = 'a_marker'; + await maps.showMarkerInfoWindow(const MarkerId(markedId), mapId: mapId); + + verify(api.showInfoWindow(markedId)); + }); + + test('hideInfoWindow calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const String markedId = 'a_marker'; + await maps.hideMarkerInfoWindow(const MarkerId(markedId), mapId: mapId); + + verify(api.hideInfoWindow(markedId)); + }); + + test('isInfoWindowShown calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const String markedId = 'a_marker'; + when(api.isInfoWindowShown(markedId)).thenAnswer((_) async => true); + + expect( + await maps.isMarkerInfoWindowShown(const MarkerId(markedId), + mapId: mapId), + true); + }); + + test('takeSnapshot calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + final Uint8List fakeSnapshot = Uint8List(10); + when(api.takeSnapshot()).thenAnswer((_) async => fakeSnapshot); + + expect(await maps.takeSnapshot(mapId: mapId), fakeSnapshot); + }); + + test('clearTileCache calls through', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const String tileOverlayId = 'overlay'; + await maps.clearTileCache(const TileOverlayId(tileOverlayId), mapId: mapId); + + verify(api.clearTileCache(tileOverlayId)); + }); + + test('updateMapConfiguration passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Set some arbitrary options. + const MapConfiguration config = MapConfiguration( + compassEnabled: true, + liteModeEnabled: false, + mapType: MapType.terrain, + ); + await maps.updateMapConfiguration(config, mapId: mapId); + + final VerificationResult verification = + verify(api.updateMapConfiguration(captureAny)); + final PlatformMapConfiguration passedConfig = + verification.captured[0] as PlatformMapConfiguration; + final Map passedConfigJson = + passedConfig.json as Map; + // Each set option should be present. + expect(passedConfigJson['compassEnabled'], true); + expect(passedConfigJson['liteModeEnabled'], false); + expect(passedConfigJson['mapType'], MapType.terrain.index); + // Spot-check that unset options are not be present. + expect(passedConfigJson['myLocationEnabled'], isNull); + expect(passedConfigJson['cameraTargetBounds'], isNull); + expect(passedConfigJson['padding'], isNull); + }); + + test('updateMapOptions passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + // Set some arbitrary options. + final Map config = { + 'compassEnabled': true, + 'liteModeEnabled': false, + 'mapType': MapType.terrain.index, + }; + await maps.updateMapOptions(config, mapId: mapId); + + final VerificationResult verification = + verify(api.updateMapConfiguration(captureAny)); + final PlatformMapConfiguration passedConfig = + verification.captured[0] as PlatformMapConfiguration; + final Map passedConfigJson = + passedConfig.json as Map; + // Each set option should be present. + expect(passedConfigJson['compassEnabled'], true); + expect(passedConfigJson['liteModeEnabled'], false); + expect(passedConfigJson['mapType'], MapType.terrain.index); + // Spot-check that unset options are not be present. + expect(passedConfigJson['myLocationEnabled'], isNull); + expect(passedConfigJson['cameraTargetBounds'], isNull); + expect(passedConfigJson['padding'], isNull); + }); + + test('updateCircles passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Circle object1 = Circle(circleId: CircleId('1')); + const Circle object2old = Circle(circleId: CircleId('2')); + final Circle object2new = object2old.copyWith(radiusParam: 42); + const Circle object3 = Circle(circleId: CircleId('3')); + await maps.updateCircles( + CircleUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updateCircles(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.circleId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updateMarkers passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Marker object1 = Marker(markerId: MarkerId('1')); + const Marker object2old = Marker(markerId: MarkerId('2')); + final Marker object2new = object2old.copyWith(rotationParam: 42); + const Marker object3 = Marker(markerId: MarkerId('3')); + await maps.updateMarkers( + MarkerUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updateMarkers(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.markerId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updatePolygons passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Polygon object1 = Polygon(polygonId: PolygonId('1')); + const Polygon object2old = Polygon(polygonId: PolygonId('2')); + final Polygon object2new = object2old.copyWith(strokeWidthParam: 42); + const Polygon object3 = Polygon(polygonId: PolygonId('3')); + await maps.updatePolygons( + PolygonUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updatePolygons(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.polygonId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updatePolylines passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const Polyline object1 = Polyline(polylineId: PolylineId('1')); + const Polyline object2old = Polyline(polylineId: PolylineId('2')); + final Polyline object2new = object2old.copyWith(widthParam: 42); + const Polyline object3 = Polyline(polylineId: PolylineId('3')); + await maps.updatePolylines( + PolylineUpdates.from( + {object1, object2old}, {object2new, object3}), + mapId: mapId); + + final VerificationResult verification = + verify(api.updatePolylines(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.polylineId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); + }); + + test('updateTileOverlays passes expected arguments', () async { + const int mapId = 1; + final (GoogleMapsFlutterIOS maps, MockMapsApi api) = + setUpMockMap(mapId: mapId); + + const TileOverlay object1 = TileOverlay(tileOverlayId: TileOverlayId('1')); + const TileOverlay object2old = + TileOverlay(tileOverlayId: TileOverlayId('2')); + final TileOverlay object2new = object2old.copyWith(zIndexParam: 42); + const TileOverlay object3 = TileOverlay(tileOverlayId: TileOverlayId('3')); + // Pre-set the initial state, since this update method doesn't take the old + // state. + await maps.updateTileOverlays( + newTileOverlays: {object1, object2old}, mapId: mapId); + clearInteractions(api); + + await maps.updateTileOverlays( + newTileOverlays: {object2new, object3}, mapId: mapId); + + final VerificationResult verification = + verify(api.updateTileOverlays(captureAny, captureAny, captureAny)); + final List toAdd = + verification.captured[0] as List; + final List toChange = + verification.captured[1] as List; + final List toRemove = verification.captured[2] as List; + // Object one should be removed. + expect(toRemove.length, 1); + expect(toRemove.first, object1.tileOverlayId.value); + // Object two should be changed. + expect(toChange.length, 1); + expect(toChange.first?.json, object2new.toJson()); + // Object 3 should be added. + expect(toAdd.length, 1); + expect(toAdd.first?.json, object3.toJson()); }); test('markers send drag event to correct streams', () async { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart new file mode 100644 index 00000000000..ca2dbb086af --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.mocks.dart @@ -0,0 +1,354 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; +import 'dart:typed_data' as _i4; + +import 'package:google_maps_flutter_ios/src/messages.g.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakePlatformPoint_0 extends _i1.SmartFake implements _i2.PlatformPoint { + _FakePlatformPoint_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePlatformLatLng_1 extends _i1.SmartFake + implements _i2.PlatformLatLng { + _FakePlatformLatLng_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakePlatformLatLngBounds_2 extends _i1.SmartFake + implements _i2.PlatformLatLngBounds { + _FakePlatformLatLngBounds_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [MapsApi]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMapsApi extends _i1.Mock implements _i2.MapsApi { + @override + _i3.Future waitForMap() => (super.noSuchMethod( + Invocation.method( + #waitForMap, + [], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateMapConfiguration( + _i2.PlatformMapConfiguration? configuration) => + (super.noSuchMethod( + Invocation.method( + #updateMapConfiguration, + [configuration], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateCircles( + List<_i2.PlatformCircle?>? toAdd, + List<_i2.PlatformCircle?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateCircles, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateMarkers( + List<_i2.PlatformMarker?>? toAdd, + List<_i2.PlatformMarker?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateMarkers, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updatePolygons( + List<_i2.PlatformPolygon?>? toAdd, + List<_i2.PlatformPolygon?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updatePolygons, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updatePolylines( + List<_i2.PlatformPolyline?>? toAdd, + List<_i2.PlatformPolyline?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updatePolylines, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future updateTileOverlays( + List<_i2.PlatformTileOverlay?>? toAdd, + List<_i2.PlatformTileOverlay?>? toChange, + List? idsToRemove, + ) => + (super.noSuchMethod( + Invocation.method( + #updateTileOverlays, + [ + toAdd, + toChange, + idsToRemove, + ], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future<_i2.PlatformPoint> getScreenCoordinate( + _i2.PlatformLatLng? latLng) => + (super.noSuchMethod( + Invocation.method( + #getScreenCoordinate, + [latLng], + ), + returnValue: _i3.Future<_i2.PlatformPoint>.value(_FakePlatformPoint_0( + this, + Invocation.method( + #getScreenCoordinate, + [latLng], + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.PlatformPoint>.value(_FakePlatformPoint_0( + this, + Invocation.method( + #getScreenCoordinate, + [latLng], + ), + )), + ) as _i3.Future<_i2.PlatformPoint>); + + @override + _i3.Future<_i2.PlatformLatLng> getLatLng( + _i2.PlatformPoint? screenCoordinate) => + (super.noSuchMethod( + Invocation.method( + #getLatLng, + [screenCoordinate], + ), + returnValue: _i3.Future<_i2.PlatformLatLng>.value(_FakePlatformLatLng_1( + this, + Invocation.method( + #getLatLng, + [screenCoordinate], + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.PlatformLatLng>.value(_FakePlatformLatLng_1( + this, + Invocation.method( + #getLatLng, + [screenCoordinate], + ), + )), + ) as _i3.Future<_i2.PlatformLatLng>); + + @override + _i3.Future<_i2.PlatformLatLngBounds> getVisibleRegion() => + (super.noSuchMethod( + Invocation.method( + #getVisibleRegion, + [], + ), + returnValue: _i3.Future<_i2.PlatformLatLngBounds>.value( + _FakePlatformLatLngBounds_2( + this, + Invocation.method( + #getVisibleRegion, + [], + ), + )), + returnValueForMissingStub: _i3.Future<_i2.PlatformLatLngBounds>.value( + _FakePlatformLatLngBounds_2( + this, + Invocation.method( + #getVisibleRegion, + [], + ), + )), + ) as _i3.Future<_i2.PlatformLatLngBounds>); + + @override + _i3.Future moveCamera(_i2.PlatformCameraUpdate? cameraUpdate) => + (super.noSuchMethod( + Invocation.method( + #moveCamera, + [cameraUpdate], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future animateCamera(_i2.PlatformCameraUpdate? cameraUpdate) => + (super.noSuchMethod( + Invocation.method( + #animateCamera, + [cameraUpdate], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future getZoomLevel() => (super.noSuchMethod( + Invocation.method( + #getZoomLevel, + [], + ), + returnValue: _i3.Future.value(0.0), + returnValueForMissingStub: _i3.Future.value(0.0), + ) as _i3.Future); + + @override + _i3.Future showInfoWindow(String? markerId) => (super.noSuchMethod( + Invocation.method( + #showInfoWindow, + [markerId], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future hideInfoWindow(String? markerId) => (super.noSuchMethod( + Invocation.method( + #hideInfoWindow, + [markerId], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future isInfoWindowShown(String? markerId) => (super.noSuchMethod( + Invocation.method( + #isInfoWindowShown, + [markerId], + ), + returnValue: _i3.Future.value(false), + returnValueForMissingStub: _i3.Future.value(false), + ) as _i3.Future); + + @override + _i3.Future setStyle(String? style) => (super.noSuchMethod( + Invocation.method( + #setStyle, + [style], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future getLastStyleError() => (super.noSuchMethod( + Invocation.method( + #getLastStyleError, + [], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future clearTileCache(String? tileOverlayId) => (super.noSuchMethod( + Invocation.method( + #clearTileCache, + [tileOverlayId], + ), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) as _i3.Future); + + @override + _i3.Future<_i4.Uint8List?> takeSnapshot() => (super.noSuchMethod( + Invocation.method( + #takeSnapshot, + [], + ), + returnValue: _i3.Future<_i4.Uint8List?>.value(), + returnValueForMissingStub: _i3.Future<_i4.Uint8List?>.value(), + ) as _i3.Future<_i4.Uint8List?>); +}