Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[google_maps_flutter] Cloud-based map styling support #6553

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.3.0

* Adds implementation for `cloudMapId` parameter to support cloud-based maps styling.
* Updates minimum Flutter version to 3.0.

## 2.2.3
Expand Down
4 changes: 4 additions & 0 deletions packages/google_maps_flutter/google_maps_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ The Android implementation supports multiple
[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).

#### Cloud-based map styling
Cloud-based map styling works on Android platform 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

To set up, specify your API key in the application delegate `ios/Runner/AppDelegate.m`:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const LatLng _kInitialMapCenter = LatLng(0, 0);
const double _kInitialZoomLevel = 5;
const CameraPosition _kInitialCameraPosition =
CameraPosition(target: _kInitialMapCenter, zoom: _kInitialZoomLevel);
const String _kCloudMapId = '000000000000000'; // Dummy map ID.

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Expand Down Expand Up @@ -1161,6 +1162,32 @@ void main() {
expect(tileOverlayInfo1, isNull);
},
);

testWidgets(
'testCloudMapId',
(WidgetTester tester) async {
final Completer<int> mapIdCompleter = Completer<int>();
final Key key = GlobalKey();

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: GoogleMap(
key: key,
initialCameraPosition: _kInitialCameraPosition,
onMapCreated: (GoogleMapController controller) {
mapIdCompleter.complete(controller.mapId);
},
cloudMapId: _kCloudMapId,
),
),
);

// Await mapIdCompleter to finish to make sure map can be created with styledMapId
// Styled map
await mapIdCompleter.future;
},
);
}

class _DebugTileProvider implements TileProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:flutter/material.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';
Expand All @@ -10,6 +12,7 @@ import 'animate_camera.dart';
import 'lite_mode.dart';
import 'map_click.dart';
import 'map_coordinates.dart';
import 'map_map_id.dart';
import 'map_ui.dart';
import 'marker_icons.dart';
import 'move_camera.dart';
Expand Down Expand Up @@ -39,6 +42,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
const SnapshotPage(),
const LiteModePage(),
const TileOverlayPage(),
const MapIdPage(),
];

/// MapsDemo is the Main Application.
Expand Down Expand Up @@ -75,6 +79,34 @@ void main() {
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
mapsImplementation.useAndroidViewSurface = true;
initializeMapRenderer();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README should be updated to explain the requirement here, and this should have a comment referring to that to explain why it's here.

Copy link
Contributor Author

@jokerttu jokerttu Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Map renderers are already explained under google_maps_flutter_android package README.
I can update the app-facing plugin README and add information that cloud-based mapid works only on Android if latest renderer is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
runApp(const MaterialApp(home: MapsDemo()));
}

Completer<AndroidMapRenderer?>? _initializedRendererCompleter;

/// Initializes map renderer to with `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;
}

_initializedRendererCompleter = Completer<AndroidMapRenderer?>();

WidgetsFlutterBinding.ensureInitialized();

final GoogleMapsFlutterPlatform mapsImplementation =
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
mapsImplementation.initializeWithRenderer(AndroidMapRenderer.latest).then(
(AndroidMapRenderer initializedRenderer) =>
_initializedRendererCompleter!.complete(initializedRenderer));
} else {
_initializedRendererCompleter!.complete(null);
}

return _initializedRendererCompleter!.future;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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/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({Key? key}) : super(key: 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 (Platform.isAndroid)
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()}".'),
),
if (Platform.isIOS)
const Padding(
padding: EdgeInsets.all(10.0),
child:
Text('On iOS, cloud based map styling works only if iOS platform '
'version 12 or above is targeted in project Podfile. '
"Run command 'pod update GoogleMaps' to update plugin"),
)
];

return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: columnChildren,
);
}

@override
void dispose() {
_mapIdController.dispose();
super.dispose();
}

void _onMapCreated(GoogleMapController controllerParam) {
setState(() {
controller = controllerParam;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,15 @@ flutter:
uses-material-design: true
assets:
- assets/


# FOR TESTING ONLY. DO NOT MERGE.
dependency_overrides:
google_maps_flutter:
path: ../../../google_maps_flutter/google_maps_flutter
google_maps_flutter_android:
path: ../../../google_maps_flutter/google_maps_flutter_android
google_maps_flutter_ios:
path: ../../../google_maps_flutter/google_maps_flutter_ios
google_maps_flutter_platform_interface:
path: ../../../google_maps_flutter/google_maps_flutter_platform_interface
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class GoogleMap extends StatefulWidget {
this.onCameraIdle,
this.onTap,
this.onLongPress,
this.cloudMapId,
}) : assert(initialCameraPosition != null),
super(key: key);

Expand Down Expand Up @@ -283,6 +284,12 @@ class GoogleMap extends StatefulWidget {
/// were not claimed by any other gesture recognizer.
final Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers;

/// 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].
@override
State createState() => _GoogleMapState();
Expand Down Expand Up @@ -553,5 +560,6 @@ MapConfiguration _configurationFromMapWidget(GoogleMap map) {
indoorViewEnabled: map.indoorViewEnabled,
trafficEnabled: map.trafficEnabled,
buildingsEnabled: map.buildingsEnabled,
cloudMapId: map.cloudMapId,
);
}
13 changes: 11 additions & 2 deletions packages/google_maps_flutter/google_maps_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: google_maps_flutter
description: A Flutter plugin for integrating Google Maps in iOS and Android applications.
repository: https://github.com/flutter/plugins/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
version: 2.2.3
version: 2.3.0

environment:
sdk: ">=2.14.0 <3.0.0"
Expand All @@ -20,11 +20,20 @@ dependencies:
flutter:
sdk: flutter
google_maps_flutter_android: ^2.1.10
google_maps_flutter_ios: ^2.1.10
google_maps_flutter_platform_interface: ^2.2.1

dev_dependencies:
flutter_test:
sdk: flutter
plugin_platform_interface: ^2.0.0
stream_transform: ^2.0.0


# FOR TESTING ONLY. DO NOT MERGE.
dependency_overrides:
google_maps_flutter_android:
path: ../../google_maps_flutter/google_maps_flutter_android
google_maps_flutter_ios:
path: ../../google_maps_flutter/google_maps_flutter_ios
google_maps_flutter_platform_interface:
path: ../../google_maps_flutter/google_maps_flutter_platform_interface
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.5.0

* Adds implementation for `cloudMapId` parameter to support cloud-based map styling.
* Updates minimum Flutter version to 3.0.

## 2.4.3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ static void interpretGoogleMapOptions(Object o, GoogleMapOptionsSink sink) {
if (buildingsEnabled != null) {
sink.setBuildingsEnabled(toBoolean(buildingsEnabled));
}
final Object cloudMapId = data.get("cloudMapId");
if (buildingsEnabled != null) {
sink.setMapId(toString(cloudMapId));
}
}

/** Returns the dartMarkerId of the interpreted marker. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,9 @@ public void setInitialCircles(Object initialCircles) {
public void setInitialTileOverlays(List<Map<String, ?>> initialTileOverlays) {
this.initialTileOverlays = initialTileOverlays;
}

@Override
public void setMapId(String mapId) {
options.mapId(mapId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,11 @@ public void setInitialTileOverlays(List<Map<String, ?>> initialTileOverlays) {
}
}

@Override
public void setMapId(String mapId) {
Log.e(TAG, "Cannot change MapId after map is initialized.");
}

private void updateInitialTileOverlays() {
tileOverlaysController.addTileOverlays(initialTileOverlays);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,6 @@ interface GoogleMapOptionsSink {
void setInitialCircles(Object initialCircles);

void setInitialTileOverlays(List<Map<String, ?>> initialTileOverlays);

void setMapId(String mapId);
}
Loading