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] Add ground overlay support for Android and iOS #6909

Closed
wants to merge 12 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
NS_ASSUME_NONNULL_BEGIN

/// Queue-specific context data to be associated with the capture session queue.
extern const char* FLTCaptureSessionQueueSpecific;
extern const char *FLTCaptureSessionQueueSpecific;

/// Ensures the given block to be run on the main queue.
/// If caller site is already on the main queue, the block will be run
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.8.0

* Add ground overlay support for Android and iOS.

## 2.7.0

* Adds support for BitmapDescriptor classes `AssetMapBitmap` and `BytesMapBitmap`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ android {

defaultConfig {
applicationId "io.flutter.plugins.googlemapsexample"
minSdkVersion 20
minSdkVersion flutter.minSdkVersion
targetSdkVersion 28
multiDexEnabled true
versionCode flutterVersionCode.toInteger()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// 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 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'page.dart';

class GroundOverlayPage extends GoogleMapExampleAppPage {
const GroundOverlayPage({Key? key})
: super(const Icon(Icons.map), 'Ground overlay', key: key);

@override
Widget build(BuildContext context) {
return const GroundOverlayBody();
}
}

class GroundOverlayBody extends StatefulWidget {
const GroundOverlayBody({super.key});

@override
State<StatefulWidget> createState() => GroundOverlayBodyState();
}

class GroundOverlayBodyState extends State<GroundOverlayBody> {
GroundOverlayBodyState();

GoogleMapController? controller;
BitmapDescriptor? _overlayImage;
double _bearing = 0;
double _opacity = 1.0;

// ignore: use_setters_to_change_properties
void _onMapCreated(GoogleMapController controller) {
this.controller = controller;
}

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

void _removeGroundOverlay() {
setState(() {
_overlayImage = null;
});
}

void _addGroundOverlay() {
BitmapDescriptor.fromAssetImage(
ImageConfiguration.empty,
'assets/red_square.png',
).then((BitmapDescriptor bitmap) {
setState(() {
_overlayImage = bitmap;
});
});
}

@override
Widget build(BuildContext context) {
final Set<GroundOverlay> overlays = <GroundOverlay>{
if (_overlayImage != null)
GroundOverlay.fromPosition(
groundOverlayId: const GroundOverlayId('ground_overlay_1'),
bitmap: _overlayImage,
position: const LatLng(59.935460, 30.325177),
width: 200,
bearing: _bearing,
opacity: _opacity,
),
if (_overlayImage != null)
GroundOverlay.fromBounds(
LatLngBounds(
southwest: const LatLng(59.945460, 30.335177),
northeast: const LatLng(59.946460, 30.336177)),
groundOverlayId: const GroundOverlayId('ground_overlay_3'),
bitmap: _overlayImage,
),
};
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Center(
child: SizedBox(
width: 350.0,
height: 500.0,
child: GoogleMap(
initialCameraPosition: const CameraPosition(
target: LatLng(59.935460, 30.325177),
zoom: 15.0,
),
groundOverlays: overlays,
onMapCreated: _onMapCreated,
),
),
),
...<Widget>[
if (overlays.isEmpty)
TextButton(
onPressed: _addGroundOverlay,
child: const Text('Add ground overlay'),
),
if (overlays.isNotEmpty)
TextButton(
onPressed: _removeGroundOverlay,
child: const Text('Remove ground overlay'),
),
if (overlays.isNotEmpty)
const Padding(padding: EdgeInsets.all(8), child: Text('Bearing')),
if (overlays.isNotEmpty)
Slider(
label: 'Bearing',
value: _bearing,
max: 360,
onChanged: (double value) {
setState(() {
_bearing = value;
});
},
),
if (overlays.isNotEmpty)
const Padding(
padding: EdgeInsets.all(8),
child: Text('Opacity'),
),
if (overlays.isNotEmpty)
Slider(
label: 'Opacity',
value: _opacity * 100,
max: 100,
onChanged: (double value) {
setState(() {
_opacity = value / 100.0;
});
},
),
],
],
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:google_maps_flutter_android/google_maps_flutter_android.dart';
import 'package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart';

import 'animate_camera.dart';
import 'ground_overlay.dart';
import 'lite_mode.dart';
import 'map_click.dart';
import 'map_coordinates.dart';
Expand Down Expand Up @@ -42,6 +43,7 @@ final List<GoogleMapExampleAppPage> _allPages = <GoogleMapExampleAppPage>[
const SnapshotPage(),
const LiteModePage(),
const TileOverlayPage(),
const GroundOverlayPage(),
const MapIdPage(),
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,8 @@ flutter:
uses-material-design: true
assets:
- assets/

# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins
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}, google_maps_flutter_web: {path: ../../../google_maps_flutter/google_maps_flutter_web}}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export 'package:google_maps_flutter_platform_interface/google_maps_flutter_platf
Cap,
Circle,
CircleId,
GroundOverlay,
GroundOverlayId,
InfoWindow,
JointType,
LatLng,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ class GoogleMapController {
GoogleMapsFlutterPlatform.instance
.onCircleTap(mapId: mapId)
.listen((CircleTapEvent e) => _googleMapState.onCircleTap(e.value));
GoogleMapsFlutterPlatform.instance.onGroundOverlayTap(mapId: mapId).listen(
(GroundOverlayTapEvent e) =>
_googleMapState.onGroundOverlayTap(e.value));
GoogleMapsFlutterPlatform.instance
.onTap(mapId: mapId)
.listen((MapTapEvent e) => _googleMapState.onTap(e.position));
Expand Down Expand Up @@ -159,6 +162,18 @@ class GoogleMapController {
.clearTileCache(tileOverlayId, mapId: mapId);
}

/// Updates ground overlay configuration.
///
/// Change listeners are notified once the update has been made on the
/// platform side.
///
/// The returned [Future] completes after listeners have been notified.
Future<void> _updateGroundOverlays(
GroundOverlayUpdates groundOverlayUpdates) {
return GoogleMapsFlutterPlatform.instance
.updateGroundOverlays(groundOverlayUpdates, mapId: mapId);
}

/// Starts an animated change of the map camera position.
///
/// The returned [Future] completes after the change has been started on the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class GoogleMap extends StatefulWidget {
this.circles = const <Circle>{},
this.onCameraMoveStarted,
this.tileOverlays = const <TileOverlay>{},
this.groundOverlays = const <GroundOverlay>{},
this.onCameraMove,
this.onCameraIdle,
this.onTap,
Expand Down Expand Up @@ -217,6 +218,9 @@ class GoogleMap extends StatefulWidget {
/// Tile overlays to be placed on the map.
final Set<TileOverlay> tileOverlays;

/// Ground overlays to be placed on the map.
final Set<GroundOverlay> groundOverlays;

/// Called when the camera starts moving.
///
/// This can be initiated by the following:
Expand Down Expand Up @@ -328,6 +332,8 @@ class _GoogleMapState extends State<GoogleMap> {
Map<PolygonId, Polygon> _polygons = <PolygonId, Polygon>{};
Map<PolylineId, Polyline> _polylines = <PolylineId, Polyline>{};
Map<CircleId, Circle> _circles = <CircleId, Circle>{};
Map<GroundOverlayId, GroundOverlay> _groundOverlays =
<GroundOverlayId, GroundOverlay>{};
late MapConfiguration _mapConfiguration;

@override
Expand All @@ -347,6 +353,7 @@ class _GoogleMapState extends State<GoogleMap> {
polygons: widget.polygons,
polylines: widget.polylines,
circles: widget.circles,
groundOverlays: widget.groundOverlays,
),
mapConfiguration: _mapConfiguration,
);
Expand All @@ -360,6 +367,7 @@ class _GoogleMapState extends State<GoogleMap> {
_polygons = keyByPolygonId(widget.polygons);
_polylines = keyByPolylineId(widget.polylines);
_circles = keyByCircleId(widget.circles);
_groundOverlays = keyByGroundOverlayId(widget.groundOverlays);
}

@override
Expand All @@ -382,6 +390,7 @@ class _GoogleMapState extends State<GoogleMap> {
_updatePolylines();
_updateCircles();
_updateTileOverlays();
_updateGroundOverlays();
}

Future<void> _updateOptions() async {
Expand Down Expand Up @@ -428,6 +437,14 @@ class _GoogleMapState extends State<GoogleMap> {
unawaited(controller._updateTileOverlays(widget.tileOverlays));
}

Future<void> _updateGroundOverlays() async {
final GoogleMapController controller = await _controller.future;

unawaited(controller._updateGroundOverlays(GroundOverlayUpdates.from(
_groundOverlays.values.toSet(), widget.groundOverlays)));
_groundOverlays = keyByGroundOverlayId(widget.groundOverlays);
}

Future<void> onPlatformViewCreated(int id) async {
final GoogleMapController controller = await GoogleMapController.init(
id,
Expand All @@ -436,6 +453,7 @@ class _GoogleMapState extends State<GoogleMap> {
);
_controller.complete(controller);
unawaited(_updateTileOverlays());
unawaited(_updateGroundOverlays());
final MapCreatedCallback? onMapCreated = widget.onMapCreated;
if (onMapCreated != null) {
onMapCreated(controller);
Expand Down Expand Up @@ -519,6 +537,17 @@ class _GoogleMapState extends State<GoogleMap> {
}
}

void onGroundOverlayTap(GroundOverlayId groundOverlayId) {
final GroundOverlay? groundOverlay = _groundOverlays[groundOverlayId];
if (groundOverlay == null) {
throw UnknownMapObjectIdError('groundOverlay', groundOverlayId, 'onTap');
}
final VoidCallback? onTap = groundOverlay.onTap;
if (onTap != null) {
onTap();
}
}

void onInfoWindowTap(MarkerId markerId) {
final Marker? marker = _markers[markerId];
if (marker == null) {
Expand Down
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.7.0
version: 2.8.0

environment:
sdk: ^3.4.0
Expand Down Expand Up @@ -40,3 +40,8 @@ topics:
# The example deliberately includes limited-use secrets.
false_secrets:
- /example/web/index.html

# FOR TESTING AND INITIAL REVIEW ONLY. DO NOT MERGE.
# See https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changing-federated-plugins
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}, google_maps_flutter_web: {path: ../../google_maps_flutter/google_maps_flutter_web}}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.10.0

* Add ground overlay support for Android and iOS.

## 2.9.0

* Adds support for BitmapDescriptor classes `AssetMapBitmap` and `BytesMapBitmap`.
Expand Down
Loading