[google_maps_flutter] Cloud-based map styling support (#3682)

Recreates https://github.com/flutter/plugins/pull/6553 form flutter/plugins which had approvals in-progress. 

Fixes https://github.com/flutter/flutter/issues/67631
This commit is contained in:
Alex Muramoto
2023-08-31 14:03:20 -07:00
committed by GitHub
parent c9a25843f5
commit cd94db1f08
10 changed files with 226 additions and 6 deletions

View File

@ -1,3 +1,7 @@
## 2.5.0
* Adds implementation for `cloudMapId` parameter to support cloud-based maps styling.
## 2.4.1 ## 2.4.1
* Adds pub topics to package metadata. * Adds pub topics to package metadata.

View File

@ -61,6 +61,11 @@ The Android implementation supports multiple
[platform view display modes](https://flutter.dev/docs/development/platform-integration/platform-views). [platform view display modes](https://flutter.dev/docs/development/platform-integration/platform-views).
For details, see [the Android README](https://pub.dev/packages/google_maps_flutter_android#display-mode). For details, see [the Android README](https://pub.dev/packages/google_maps_flutter_android#display-mode).
#### Cloud-based map styling
Cloud-based map styling works on Android only if `AndroidMapRenderer.latest` map renderer has been initialized.
For details, see [the Android README](https://pub.dev/packages/google_maps_flutter_android#map-renderer).
### iOS ### iOS
To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`: To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`:

View File

@ -407,6 +407,30 @@ void runTests() {
// TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled. // TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled.
// https://github.com/flutter/flutter/issues/57057 // https://github.com/flutter/flutter/issues/57057
skip: isAndroid || isWeb); skip: isAndroid || isWeb);
testWidgets(
'testCloudMapId',
(WidgetTester tester) async {
final Completer<int> mapIdCompleter = Completer<int>();
final Key key = GlobalKey();
await pumpMap(
tester,
GoogleMap(
key: key,
initialCameraPosition: kInitialCameraPosition,
onMapCreated: (GoogleMapController controller) {
mapIdCompleter.complete(controller.mapId);
},
cloudMapId: kCloudMapId,
),
);
await tester.pumpAndSettle();
// Await mapIdCompleter to finish to make sure map can be created with cloudMapId
await mapIdCompleter.future;
},
);
} }
/// Repeatedly checks an asynchronous value against a test condition. /// Repeatedly checks an asynchronous value against a test condition.

View File

@ -19,6 +19,9 @@ const double kInitialZoomLevel = 5;
const CameraPosition kInitialCameraPosition = const CameraPosition kInitialCameraPosition =
CameraPosition(target: kInitialMapCenter, zoom: kInitialZoomLevel); CameraPosition(target: kInitialMapCenter, zoom: kInitialZoomLevel);
// Dummy map ID
const String kCloudMapId = '000000000000000'; // Dummy map ID.
/// True if the test is running in an iOS device /// True if the test is running in an iOS device
final bool isIOS = defaultTargetPlatform == TargetPlatform.iOS; final bool isIOS = defaultTargetPlatform == TargetPlatform.iOS;

View File

@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project # Global platform version is set to 12 for this example project to support cloud-based maps styling
# platform :ios, '11.0' platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart'; import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart'; import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
@ -10,6 +12,7 @@ import 'animate_camera.dart';
import 'lite_mode.dart'; import 'lite_mode.dart';
import 'map_click.dart'; import 'map_click.dart';
import 'map_coordinates.dart'; import 'map_coordinates.dart';
import 'map_map_id.dart';
import 'map_ui.dart'; import 'map_ui.dart';
import 'marker_icons.dart'; import 'marker_icons.dart';
import 'move_camera.dart'; import 'move_camera.dart';
@ -39,6 +42,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
const SnapshotPage(), const SnapshotPage(),
const LiteModePage(), const LiteModePage(),
const TileOverlayPage(), const TileOverlayPage(),
const MapIdPage(),
]; ];
/// MapsDemo is the Main Application. /// MapsDemo is the Main Application.
@ -75,6 +79,38 @@ void main() {
GoogleMapsFlutterPlatform.instance; GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) { if (mapsImplementation is GoogleMapsFlutterAndroid) {
mapsImplementation.useAndroidViewSurface = true; mapsImplementation.useAndroidViewSurface = true;
initializeMapRenderer();
} }
runApp(const MaterialApp(home: MapsDemo())); runApp(const MaterialApp(home: MapsDemo()));
} }
Completer<AndroidMapRenderer?>? _initializedRendererCompleter;
/// Initializes map renderer to the `latest` renderer type for Android platform.
///
/// The renderer must be requested before creating GoogleMap instances,
/// as the renderer can be initialized only once per application context.
Future<AndroidMapRenderer?> initializeMapRenderer() async {
if (_initializedRendererCompleter != null) {
return _initializedRendererCompleter!.future;
}
final Completer<AndroidMapRenderer?> completer =
Completer<AndroidMapRenderer?>();
_initializedRendererCompleter = completer;
WidgetsFlutterBinding.ensureInitialized();
final GoogleMapsFlutterPlatform mapsImplementation =
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
unawaited(mapsImplementation
.initializeWithRenderer(AndroidMapRenderer.latest)
.then((AndroidMapRenderer initializedRenderer) =>
completer.complete(initializedRenderer)));
} else {
completer.complete(null);
}
return completer.future;
}

View File

@ -0,0 +1,140 @@
// 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.
// ignore_for_file: public_member_api_docs
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'main.dart';
import 'page.dart';
class MapIdPage extends GoogleMapExampleAppPage {
const MapIdPage({Key? key})
: super(const Icon(Icons.map), 'Cloud-based maps styling', key: key);
@override
Widget build(BuildContext context) {
return const MapIdBody();
}
}
class MapIdBody extends StatefulWidget {
const MapIdBody({super.key});
@override
State<StatefulWidget> createState() => MapIdBodyState();
}
const LatLng _kMapCenter = LatLng(52.4478, -3.5402);
class MapIdBodyState extends State<MapIdBody> {
GoogleMapController? controller;
Key _key = const Key('mapId#');
String? _mapId;
final TextEditingController _mapIdController = TextEditingController();
AndroidMapRenderer? _initializedRenderer;
@override
void initState() {
initializeMapRenderer()
.then<void>((AndroidMapRenderer? initializedRenderer) => setState(() {
_initializedRenderer = initializedRenderer;
}));
super.initState();
}
String _getInitializedsRendererType() {
switch (_initializedRenderer) {
case AndroidMapRenderer.latest:
return 'latest';
case AndroidMapRenderer.legacy:
return 'legacy';
case AndroidMapRenderer.platformDefault:
case null:
break;
}
return 'unknown';
}
void _setMapId() {
setState(() {
_mapId = _mapIdController.text;
// Change key to initialize new map instance for new mapId.
_key = Key(_mapId ?? 'mapId#');
});
}
@override
Widget build(BuildContext context) {
final GoogleMap googleMap = GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: _kMapCenter,
zoom: 7.0,
),
key: _key,
cloudMapId: _mapId);
final List<Widget> columnChildren = <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: Center(
child: SizedBox(
width: 300.0,
height: 200.0,
child: googleMap,
),
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
controller: _mapIdController,
decoration: const InputDecoration(
hintText: 'Map Id',
),
)),
Padding(
padding: const EdgeInsets.all(10.0),
child: ElevatedButton(
onPressed: () => _setMapId(),
child: const Text(
'Press to use specified map Id',
),
)),
if (!kIsWeb &&
Platform.isAndroid &&
_initializedRenderer != AndroidMapRenderer.latest)
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
'On Android, Cloud-based maps styling only works with "latest" renderer.\n\n'
'Current initialized renderer is "${_getInitializedsRendererType()}".'),
),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: columnChildren,
);
}
@override
void dispose() {
_mapIdController.dispose();
super.dispose();
}
void _onMapCreated(GoogleMapController controllerParam) {
setState(() {
controller = controllerParam;
});
}
}

View File

@ -18,7 +18,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on # The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version. # the parent directory to use the current plugin's version.
path: ../ path: ../
google_maps_flutter_android: ^2.1.10 google_maps_flutter_android: ^2.5.0
google_maps_flutter_platform_interface: ^2.4.0 google_maps_flutter_platform_interface: ^2.4.0
dev_dependencies: dev_dependencies:

View File

@ -125,6 +125,7 @@ class GoogleMap extends StatefulWidget {
this.onCameraIdle, this.onCameraIdle,
this.onTap, this.onTap,
this.onLongPress, this.onLongPress,
this.cloudMapId,
}); });
/// Callback method for when the map is ready to be used. /// Callback method for when the map is ready to be used.
@ -292,6 +293,12 @@ class GoogleMap extends StatefulWidget {
/// See [WebGestureHandling] for more details. /// See [WebGestureHandling] for more details.
final WebGestureHandling? webGestureHandling; final WebGestureHandling? webGestureHandling;
/// Identifier that's associated with a specific cloud-based map style.
///
/// See https://developers.google.com/maps/documentation/get-map-id
/// for more details.
final String? cloudMapId;
/// Creates a [State] for this [GoogleMap]. /// Creates a [State] for this [GoogleMap].
@override @override
State createState() => _GoogleMapState(); State createState() => _GoogleMapState();
@ -548,5 +555,6 @@ MapConfiguration _configurationFromMapWidget(GoogleMap map) {
indoorViewEnabled: map.indoorViewEnabled, indoorViewEnabled: map.indoorViewEnabled,
trafficEnabled: map.trafficEnabled, trafficEnabled: map.trafficEnabled,
buildingsEnabled: map.buildingsEnabled, buildingsEnabled: map.buildingsEnabled,
cloudMapId: map.cloudMapId,
); );
} }

View File

@ -2,7 +2,7 @@ name: google_maps_flutter
description: A Flutter plugin for integrating Google Maps in iOS and Android applications. description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
version: 2.4.1 version: 2.5.0
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=3.0.0 <4.0.0"
@ -21,8 +21,8 @@ flutter:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
google_maps_flutter_android: ^2.1.10 google_maps_flutter_android: ^2.5.0
google_maps_flutter_ios: ^2.1.10 google_maps_flutter_ios: ^2.3.0
google_maps_flutter_platform_interface: ^2.4.0 google_maps_flutter_platform_interface: ^2.4.0
google_maps_flutter_web: ^0.5.2 google_maps_flutter_web: ^0.5.2