Skip to content

Commit cd94db1

Browse files
authored
[google_maps_flutter] Cloud-based map styling support (flutter#3682)
Recreates flutter/plugins#6553 form flutter/plugins which had approvals in-progress. Fixes flutter#67631
1 parent c9a2584 commit cd94db1

File tree

10 files changed

+226
-6
lines changed

10 files changed

+226
-6
lines changed

packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.5.0
2+
3+
* Adds implementation for `cloudMapId` parameter to support cloud-based maps styling.
4+
15
## 2.4.1
26

37
* Adds pub topics to package metadata.

packages/google_maps_flutter/google_maps_flutter/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ The Android implementation supports multiple
6161
[platform view display modes](https://flutter.dev/docs/development/platform-integration/platform-views).
6262
For details, see [the Android README](https://pub.dev/packages/google_maps_flutter_android#display-mode).
6363

64+
#### Cloud-based map styling
65+
66+
Cloud-based map styling works on Android only if `AndroidMapRenderer.latest` map renderer has been initialized.
67+
For details, see [the Android README](https://pub.dev/packages/google_maps_flutter_android#map-renderer).
68+
6469
### iOS
6570

6671
To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`:

packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/maps_controller.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,30 @@ void runTests() {
407407
// TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled.
408408
// https://github.com/flutter/flutter/issues/57057
409409
skip: isAndroid || isWeb);
410+
411+
testWidgets(
412+
'testCloudMapId',
413+
(WidgetTester tester) async {
414+
final Completer<int> mapIdCompleter = Completer<int>();
415+
final Key key = GlobalKey();
416+
417+
await pumpMap(
418+
tester,
419+
GoogleMap(
420+
key: key,
421+
initialCameraPosition: kInitialCameraPosition,
422+
onMapCreated: (GoogleMapController controller) {
423+
mapIdCompleter.complete(controller.mapId);
424+
},
425+
cloudMapId: kCloudMapId,
426+
),
427+
);
428+
await tester.pumpAndSettle();
429+
430+
// Await mapIdCompleter to finish to make sure map can be created with cloudMapId
431+
await mapIdCompleter.future;
432+
},
433+
);
410434
}
411435

412436
/// Repeatedly checks an asynchronous value against a test condition.

packages/google_maps_flutter/google_maps_flutter/example/integration_test/src/shared.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ const double kInitialZoomLevel = 5;
1919
const CameraPosition kInitialCameraPosition =
2020
CameraPosition(target: kInitialMapCenter, zoom: kInitialZoomLevel);
2121

22+
// Dummy map ID
23+
const String kCloudMapId = '000000000000000'; // Dummy map ID.
24+
2225
/// True if the test is running in an iOS device
2326
final bool isIOS = defaultTargetPlatform == TargetPlatform.iOS;
2427

packages/google_maps_flutter/google_maps_flutter/example/ios/Podfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# Uncomment this line to define a global platform for your project
2-
# platform :ios, '11.0'
1+
# Global platform version is set to 12 for this example project to support cloud-based maps styling
2+
platform :ios, '12.0'
33

44
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

packages/google_maps_flutter/google_maps_flutter/example/lib/main.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
6+
57
import 'package:flutter/material.dart';
68
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
79
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';
@@ -10,6 +12,7 @@ import 'animate_camera.dart';
1012
import 'lite_mode.dart';
1113
import 'map_click.dart';
1214
import 'map_coordinates.dart';
15+
import 'map_map_id.dart';
1316
import 'map_ui.dart';
1417
import 'marker_icons.dart';
1518
import 'move_camera.dart';
@@ -39,6 +42,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
3942
const SnapshotPage(),
4043
const LiteModePage(),
4144
const TileOverlayPage(),
45+
const MapIdPage(),
4246
];
4347

4448
/// MapsDemo is the Main Application.
@@ -75,6 +79,38 @@ void main() {
7579
GoogleMapsFlutterPlatform.instance;
7680
if (mapsImplementation is GoogleMapsFlutterAndroid) {
7781
mapsImplementation.useAndroidViewSurface = true;
82+
initializeMapRenderer();
7883
}
7984
runApp(const MaterialApp(home: MapsDemo()));
8085
}
86+
87+
Completer<AndroidMapRenderer?>? _initializedRendererCompleter;
88+
89+
/// Initializes map renderer to the `latest` renderer type for Android platform.
90+
///
91+
/// The renderer must be requested before creating GoogleMap instances,
92+
/// as the renderer can be initialized only once per application context.
93+
Future<AndroidMapRenderer?> initializeMapRenderer() async {
94+
if (_initializedRendererCompleter != null) {
95+
return _initializedRendererCompleter!.future;
96+
}
97+
98+
final Completer<AndroidMapRenderer?> completer =
99+
Completer<AndroidMapRenderer?>();
100+
_initializedRendererCompleter = completer;
101+
102+
WidgetsFlutterBinding.ensureInitialized();
103+
104+
final GoogleMapsFlutterPlatform mapsImplementation =
105+
GoogleMapsFlutterPlatform.instance;
106+
if (mapsImplementation is GoogleMapsFlutterAndroid) {
107+
unawaited(mapsImplementation
108+
.initializeWithRenderer(AndroidMapRenderer.latest)
109+
.then((AndroidMapRenderer initializedRenderer) =>
110+
completer.complete(initializedRenderer)));
111+
} else {
112+
completer.complete(null);
113+
}
114+
115+
return completer.future;
116+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// ignore_for_file: public_member_api_docs
6+
7+
import 'dart:io';
8+
9+
import 'package:flutter/foundation.dart';
10+
import 'package:flutter/material.dart';
11+
import 'package:google_maps_flutter/google_maps_flutter.dart';
12+
import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
13+
import 'main.dart';
14+
import 'page.dart';
15+
16+
class MapIdPage extends GoogleMapExampleAppPage {
17+
const MapIdPage({Key? key})
18+
: super(const Icon(Icons.map), 'Cloud-based maps styling', key: key);
19+
20+
@override
21+
Widget build(BuildContext context) {
22+
return const MapIdBody();
23+
}
24+
}
25+
26+
class MapIdBody extends StatefulWidget {
27+
const MapIdBody({super.key});
28+
29+
@override
30+
State<StatefulWidget> createState() => MapIdBodyState();
31+
}
32+
33+
const LatLng _kMapCenter = LatLng(52.4478, -3.5402);
34+
35+
class MapIdBodyState extends State<MapIdBody> {
36+
GoogleMapController? controller;
37+
38+
Key _key = const Key('mapId#');
39+
String? _mapId;
40+
final TextEditingController _mapIdController = TextEditingController();
41+
AndroidMapRenderer? _initializedRenderer;
42+
43+
@override
44+
void initState() {
45+
initializeMapRenderer()
46+
.then<void>((AndroidMapRenderer? initializedRenderer) => setState(() {
47+
_initializedRenderer = initializedRenderer;
48+
}));
49+
super.initState();
50+
}
51+
52+
String _getInitializedsRendererType() {
53+
switch (_initializedRenderer) {
54+
case AndroidMapRenderer.latest:
55+
return 'latest';
56+
case AndroidMapRenderer.legacy:
57+
return 'legacy';
58+
case AndroidMapRenderer.platformDefault:
59+
case null:
60+
break;
61+
}
62+
return 'unknown';
63+
}
64+
65+
void _setMapId() {
66+
setState(() {
67+
_mapId = _mapIdController.text;
68+
69+
// Change key to initialize new map instance for new mapId.
70+
_key = Key(_mapId ?? 'mapId#');
71+
});
72+
}
73+
74+
@override
75+
Widget build(BuildContext context) {
76+
final GoogleMap googleMap = GoogleMap(
77+
onMapCreated: _onMapCreated,
78+
initialCameraPosition: const CameraPosition(
79+
target: _kMapCenter,
80+
zoom: 7.0,
81+
),
82+
key: _key,
83+
cloudMapId: _mapId);
84+
85+
final List<Widget> columnChildren = <Widget>[
86+
Padding(
87+
padding: const EdgeInsets.all(10.0),
88+
child: Center(
89+
child: SizedBox(
90+
width: 300.0,
91+
height: 200.0,
92+
child: googleMap,
93+
),
94+
),
95+
),
96+
Padding(
97+
padding: const EdgeInsets.all(10.0),
98+
child: TextField(
99+
controller: _mapIdController,
100+
decoration: const InputDecoration(
101+
hintText: 'Map Id',
102+
),
103+
)),
104+
Padding(
105+
padding: const EdgeInsets.all(10.0),
106+
child: ElevatedButton(
107+
onPressed: () => _setMapId(),
108+
child: const Text(
109+
'Press to use specified map Id',
110+
),
111+
)),
112+
if (!kIsWeb &&
113+
Platform.isAndroid &&
114+
_initializedRenderer != AndroidMapRenderer.latest)
115+
Padding(
116+
padding: const EdgeInsets.all(10.0),
117+
child: Text(
118+
'On Android, Cloud-based maps styling only works with "latest" renderer.\n\n'
119+
'Current initialized renderer is "${_getInitializedsRendererType()}".'),
120+
),
121+
];
122+
123+
return Column(
124+
crossAxisAlignment: CrossAxisAlignment.stretch,
125+
children: columnChildren,
126+
);
127+
}
128+
129+
@override
130+
void dispose() {
131+
_mapIdController.dispose();
132+
super.dispose();
133+
}
134+
135+
void _onMapCreated(GoogleMapController controllerParam) {
136+
setState(() {
137+
controller = controllerParam;
138+
});
139+
}
140+
}

packages/google_maps_flutter/google_maps_flutter/example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ dependencies:
1818
# The example app is bundled with the plugin so we use a path dependency on
1919
# the parent directory to use the current plugin's version.
2020
path: ../
21-
google_maps_flutter_android: ^2.1.10
21+
google_maps_flutter_android: ^2.5.0
2222
google_maps_flutter_platform_interface: ^2.4.0
2323

2424
dev_dependencies:

packages/google_maps_flutter/google_maps_flutter/lib/src/google_map.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class GoogleMap extends StatefulWidget {
125125
this.onCameraIdle,
126126
this.onTap,
127127
this.onLongPress,
128+
this.cloudMapId,
128129
});
129130

130131
/// Callback method for when the map is ready to be used.
@@ -292,6 +293,12 @@ class GoogleMap extends StatefulWidget {
292293
/// See [WebGestureHandling] for more details.
293294
final WebGestureHandling? webGestureHandling;
294295

296+
/// Identifier that's associated with a specific cloud-based map style.
297+
///
298+
/// See https://developers.google.com/maps/documentation/get-map-id
299+
/// for more details.
300+
final String? cloudMapId;
301+
295302
/// Creates a [State] for this [GoogleMap].
296303
@override
297304
State createState() => _GoogleMapState();
@@ -548,5 +555,6 @@ MapConfiguration _configurationFromMapWidget(GoogleMap map) {
548555
indoorViewEnabled: map.indoorViewEnabled,
549556
trafficEnabled: map.trafficEnabled,
550557
buildingsEnabled: map.buildingsEnabled,
558+
cloudMapId: map.cloudMapId,
551559
);
552560
}

packages/google_maps_flutter/google_maps_flutter/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: google_maps_flutter
22
description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
33
repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
5-
version: 2.4.1
5+
version: 2.5.0
66

77
environment:
88
sdk: ">=3.0.0 <4.0.0"
@@ -21,8 +21,8 @@ flutter:
2121
dependencies:
2222
flutter:
2323
sdk: flutter
24-
google_maps_flutter_android: ^2.1.10
25-
google_maps_flutter_ios: ^2.1.10
24+
google_maps_flutter_android: ^2.5.0
25+
google_maps_flutter_ios: ^2.3.0
2626
google_maps_flutter_platform_interface: ^2.4.0
2727
google_maps_flutter_web: ^0.5.2
2828

0 commit comments

Comments
 (0)