Skip to content

Commit

Permalink
[google_maps_flutter] Cloud-based map styling support (#3682)
Browse files Browse the repository at this point in the history
Recreates flutter/plugins#6553 form flutter/plugins which had approvals in-progress. 

Fixes flutter/flutter#67631
  • Loading branch information
amuramoto committed Aug 31, 2023
1 parent c9a2584 commit cd94db1
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 6 deletions.
4 changes: 4 additions & 0 deletions packages/google_maps_flutter/google_maps_flutter/CHANGELOG.md
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,
/// 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()}".'),
),
];

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;

/// 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,
);
}
6 changes: 3 additions & 3 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/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

0 comments on commit cd94db1

Please sign in to comment.