Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[google_maps_flutter] Cloud-based map styling support #3682

Merged
merged 6 commits into from
Aug 31, 2023
Merged
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,3 +1,7 @@
## 2.5.0

* Adds implementation for `cloudMapId` parameter to support cloud-based maps styling.

## 2.4.1

* Adds pub topics to package metadata.
Expand Down
5 changes: 5 additions & 0 deletions packages/google_maps_flutter/google_maps_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ 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 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 @@ -407,6 +407,30 @@ void runTests() {
// TODO(cyanglaz): un-skip the test when we can test this on CI with API key enabled.
// https://github.com/flutter/flutter/issues/57057
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const double kInitialZoomLevel = 5;
const CameraPosition kInitialCameraPosition =
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
final bool isIOS = defaultTargetPlatform == TargetPlatform.iOS;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# Global platform version is set to 12 for this example project to support cloud-based maps styling
platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
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,38 @@ void main() {
GoogleMapsFlutterPlatform.instance;
if (mapsImplementation is GoogleMapsFlutterAndroid) {
mapsImplementation.useAndroidViewSurface = true;
initializeMapRenderer();
}
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,
amuramoto marked this conversation as resolved.
Show resolved Hide resolved
/// 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;
}
Original file line number Diff line number Diff line change
@@ -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()}".'),
amuramoto marked this conversation as resolved.
Show resolved Hide resolved
),
];

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 @@ -18,7 +18,7 @@ dependencies:
# 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.
path: ../
google_maps_flutter_android: ^2.1.10
google_maps_flutter_android: ^2.5.0
google_maps_flutter_platform_interface: ^2.4.0

dev_dependencies:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class GoogleMap extends StatefulWidget {
this.onCameraIdle,
this.onTap,
this.onLongPress,
this.cloudMapId,
});

/// Callback method for when the map is ready to be used.
Expand Down Expand Up @@ -292,6 +293,12 @@ class GoogleMap extends StatefulWidget {
/// See [WebGestureHandling] for more details.
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;
amuramoto marked this conversation as resolved.
Show resolved Hide resolved

/// Creates a [State] for this [GoogleMap].
@override
State createState() => _GoogleMapState();
Expand Down Expand Up @@ -548,5 +555,6 @@ MapConfiguration _configurationFromMapWidget(GoogleMap map) {
indoorViewEnabled: map.indoorViewEnabled,
trafficEnabled: map.trafficEnabled,
buildingsEnabled: map.buildingsEnabled,
cloudMapId: map.cloudMapId,
);
}
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/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
version: 2.4.1
version: 2.5.0

environment:
sdk: ">=3.0.0 <4.0.0"
Expand All @@ -21,8 +21,8 @@ flutter:
dependencies:
flutter:
sdk: flutter
google_maps_flutter_android: ^2.1.10
google_maps_flutter_ios: ^2.1.10
google_maps_flutter_android: ^2.5.0
google_maps_flutter_ios: ^2.3.0
google_maps_flutter_platform_interface: ^2.4.0
google_maps_flutter_web: ^0.5.2

Expand Down