mirror of
https://github.com/flutter/packages.git
synced 2025-06-27 21:28:33 +08:00
[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:
@ -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.
|
||||
|
@ -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`:
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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';
|
||||
@ -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';
|
||||
@ -39,6 +42,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
|
||||
const SnapshotPage(),
|
||||
const LiteModePage(),
|
||||
const TileOverlayPage(),
|
||||
const MapIdPage(),
|
||||
];
|
||||
|
||||
/// MapsDemo is the Main Application.
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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.
|
||||
@ -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();
|
||||
@ -548,5 +555,6 @@ MapConfiguration _configurationFromMapWidget(GoogleMap map) {
|
||||
indoorViewEnabled: map.indoorViewEnabled,
|
||||
trafficEnabled: map.trafficEnabled,
|
||||
buildingsEnabled: map.buildingsEnabled,
|
||||
cloudMapId: map.cloudMapId,
|
||||
);
|
||||
}
|
||||
|
@ -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"
|
||||
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user