diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md index c47578b208..c785625997 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.0 + +* Adds options for gesture handling and tilt controls on web. + ## 2.3.0 * Adds a `cloudMapId` parameter to support cloud-based map styling. @@ -58,7 +62,7 @@ ## 2.1.5 -Removes dependency on `meta`. +* Removes dependency on `meta`. ## 2.1.4 diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart index 5580e9eea9..3ec973fd7d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/map_configuration.dart @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart'; -import 'ui.dart'; +import '../../google_maps_flutter_platform_interface.dart'; /// Configuration options for the GoogleMaps user interface. @immutable @@ -15,6 +15,7 @@ class MapConfiguration { /// as either a full configuration selection, or an update to an existing /// configuration where only non-null values are updated. const MapConfiguration({ + this.webGestureHandling, this.compassEnabled, this.mapToolbarEnabled, this.cameraTargetBounds, @@ -23,6 +24,7 @@ class MapConfiguration { this.rotateGesturesEnabled, this.scrollGesturesEnabled, this.tiltGesturesEnabled, + this.fortyFiveDegreeImageryEnabled, this.trackCameraPosition, this.zoomControlsEnabled, this.zoomGesturesEnabled, @@ -36,6 +38,11 @@ class MapConfiguration { this.cloudMapId, }); + /// This setting controls how the API handles gestures on the map. Web only. + /// + /// See [WebGestureHandling] for more details. + final WebGestureHandling? webGestureHandling; + /// True if the compass UI should be shown. final bool? compassEnabled; @@ -48,18 +55,25 @@ class MapConfiguration { /// The type of the map. final MapType? mapType; - /// The prefered zoom range. + /// The preferred zoom range. final MinMaxZoomPreference? minMaxZoomPreference; /// True if rotate gestures should be enabled. final bool? rotateGesturesEnabled; /// True if scroll gestures should be enabled. + /// + /// Android/iOS only. For web, see [webGestureHandling]. final bool? scrollGesturesEnabled; /// True if tilt gestures should be enabled. final bool? tiltGesturesEnabled; + /// True if 45 degree imagery should be enabled. + /// + /// Web only. + final bool? fortyFiveDegreeImageryEnabled; + /// True if camera position changes should trigger notifications. final bool? trackCameraPosition; @@ -67,6 +81,8 @@ class MapConfiguration { final bool? zoomControlsEnabled; /// True if zoom gestures should be enabled. + /// + /// Android/iOS only. For web, see [webGestureHandling]. final bool? zoomGesturesEnabled; /// True if the map should use Lite Mode, showing a limited-interactivity @@ -101,6 +117,9 @@ class MapConfiguration { /// that are different from [other]. MapConfiguration diffFrom(MapConfiguration other) { return MapConfiguration( + webGestureHandling: webGestureHandling != other.webGestureHandling + ? webGestureHandling + : null, compassEnabled: compassEnabled != other.compassEnabled ? compassEnabled : null, mapToolbarEnabled: mapToolbarEnabled != other.mapToolbarEnabled @@ -124,6 +143,10 @@ class MapConfiguration { tiltGesturesEnabled: tiltGesturesEnabled != other.tiltGesturesEnabled ? tiltGesturesEnabled : null, + fortyFiveDegreeImageryEnabled: + fortyFiveDegreeImageryEnabled != other.fortyFiveDegreeImageryEnabled + ? fortyFiveDegreeImageryEnabled + : null, trackCameraPosition: trackCameraPosition != other.trackCameraPosition ? trackCameraPosition : null, @@ -158,6 +181,7 @@ class MapConfiguration { /// replacing the previous values. MapConfiguration applyDiff(MapConfiguration diff) { return MapConfiguration( + webGestureHandling: diff.webGestureHandling ?? webGestureHandling, compassEnabled: diff.compassEnabled ?? compassEnabled, mapToolbarEnabled: diff.mapToolbarEnabled ?? mapToolbarEnabled, cameraTargetBounds: diff.cameraTargetBounds ?? cameraTargetBounds, @@ -168,6 +192,8 @@ class MapConfiguration { scrollGesturesEnabled: diff.scrollGesturesEnabled ?? scrollGesturesEnabled, tiltGesturesEnabled: diff.tiltGesturesEnabled ?? tiltGesturesEnabled, + fortyFiveDegreeImageryEnabled: + diff.fortyFiveDegreeImageryEnabled ?? fortyFiveDegreeImageryEnabled, trackCameraPosition: diff.trackCameraPosition ?? trackCameraPosition, zoomControlsEnabled: diff.zoomControlsEnabled ?? zoomControlsEnabled, zoomGesturesEnabled: diff.zoomGesturesEnabled ?? zoomGesturesEnabled, @@ -185,6 +211,7 @@ class MapConfiguration { /// True if no options are set. bool get isEmpty => + webGestureHandling == null && compassEnabled == null && mapToolbarEnabled == null && cameraTargetBounds == null && @@ -193,6 +220,7 @@ class MapConfiguration { rotateGesturesEnabled == null && scrollGesturesEnabled == null && tiltGesturesEnabled == null && + fortyFiveDegreeImageryEnabled == null && trackCameraPosition == null && zoomControlsEnabled == null && zoomGesturesEnabled == null && @@ -214,6 +242,7 @@ class MapConfiguration { return false; } return other is MapConfiguration && + webGestureHandling == other.webGestureHandling && compassEnabled == other.compassEnabled && mapToolbarEnabled == other.mapToolbarEnabled && cameraTargetBounds == other.cameraTargetBounds && @@ -222,6 +251,7 @@ class MapConfiguration { rotateGesturesEnabled == other.rotateGesturesEnabled && scrollGesturesEnabled == other.scrollGesturesEnabled && tiltGesturesEnabled == other.tiltGesturesEnabled && + fortyFiveDegreeImageryEnabled == other.fortyFiveDegreeImageryEnabled && trackCameraPosition == other.trackCameraPosition && zoomControlsEnabled == other.zoomControlsEnabled && zoomGesturesEnabled == other.zoomGesturesEnabled && @@ -236,7 +266,8 @@ class MapConfiguration { } @override - int get hashCode => Object.hash( + int get hashCode => Object.hashAll([ + webGestureHandling, compassEnabled, mapToolbarEnabled, cameraTargetBounds, @@ -245,6 +276,7 @@ class MapConfiguration { rotateGesturesEnabled, scrollGesturesEnabled, tiltGesturesEnabled, + fortyFiveDegreeImageryEnabled, trackCameraPosition, zoomControlsEnabled, zoomGesturesEnabled, @@ -256,5 +288,5 @@ class MapConfiguration { trafficEnabled, buildingsEnabled, cloudMapId, - ); + ]); } diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart index 0beb7d747e..1f1916b1c5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/types.dart @@ -34,3 +34,4 @@ export 'utils/marker.dart'; export 'utils/polygon.dart'; export 'utils/polyline.dart'; export 'utils/tile_overlay.dart'; +export 'web_gesture_handling.dart'; diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/web_gesture_handling.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/web_gesture_handling.dart new file mode 100644 index 0000000000..c978f91530 --- /dev/null +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/lib/src/types/web_gesture_handling.dart @@ -0,0 +1,22 @@ +// 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. + +/// This setting controls how the API handles gestures on the map +enum WebGestureHandling { + /// Scroll events and one-finger touch gestures scroll the page, and do not + /// zoom or pan the map. Two-finger touch gestures pan and zoom the map. + /// Scroll events with a ctrl key or ⌘ key pressed zoom the map. In this mode + /// the map cooperates with the page. + cooperative, + + /// All touch gestures and scroll events pan or zoom the map. + greedy, + + /// The map cannot be panned or zoomed by user gestures. + none, + + /// (default) Gesture handling is either cooperative or greedy, depending on + /// whether the page is scrollable or in an iframe. + auto, +} diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml index a8d7b126ca..9b79a532eb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/google_maps_f issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.3.0 +version: 2.4.0 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart index e57d2c73ee..2a53b8c0bb 100644 --- a/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_platform_interface/test/types/map_configuration_test.dart @@ -12,6 +12,7 @@ void main() { group('diffs', () { // A options instance with every field set, to test diffs against. final MapConfiguration diffBase = MapConfiguration( + webGestureHandling: WebGestureHandling.auto, compassEnabled: false, mapToolbarEnabled: false, cameraTargetBounds: CameraTargetBounds(LatLngBounds( @@ -21,6 +22,7 @@ void main() { rotateGesturesEnabled: false, scrollGesturesEnabled: false, tiltGesturesEnabled: false, + fortyFiveDegreeImageryEnabled: false, trackCameraPosition: false, zoomControlsEnabled: false, zoomGesturesEnabled: false, @@ -58,6 +60,23 @@ void main() { expect(updated.cloudMapId, null); }); + test('handle webGestureHandling', () async { + const MapConfiguration diff = + MapConfiguration(webGestureHandling: WebGestureHandling.none); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); + // A diff applied to non-empty options should update that field. + expect(updated.webGestureHandling, WebGestureHandling.none); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); + }); + test('handle compassEnabled', () async { const MapConfiguration diff = MapConfiguration(compassEnabled: true); @@ -66,8 +85,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.compassEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle mapToolbarEnabled', () async { @@ -78,8 +101,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.mapToolbarEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle cameraTargetBounds', () async { @@ -93,8 +120,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.cameraTargetBounds, newBounds); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle mapType', () async { @@ -106,8 +137,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.mapType, MapType.satellite); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle minMaxZoomPreference', () async { @@ -120,8 +155,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.minMaxZoomPreference, newZoomPref); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle rotateGesturesEnabled', () async { @@ -133,8 +172,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.rotateGesturesEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle scrollGesturesEnabled', () async { @@ -146,8 +189,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.scrollGesturesEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle tiltGesturesEnabled', () async { @@ -158,8 +205,29 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.tiltGesturesEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); + }); + + test('handle fortyFiveDegreeImageryEnabled', () async { + const MapConfiguration diff = + MapConfiguration(fortyFiveDegreeImageryEnabled: true); + + const MapConfiguration empty = MapConfiguration(); + final MapConfiguration updated = diffBase.applyDiff(diff); + + // A diff applied to empty options should be the diff itself. + expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); + // A diff applied to non-empty options should update that field. + expect(updated.fortyFiveDegreeImageryEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle trackCameraPosition', () async { @@ -170,8 +238,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.trackCameraPosition, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle zoomControlsEnabled', () async { @@ -182,8 +254,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.zoomControlsEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle zoomGesturesEnabled', () async { @@ -194,8 +270,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.zoomGesturesEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle liteModeEnabled', () async { @@ -206,8 +286,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.liteModeEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle myLocationEnabled', () async { @@ -218,8 +302,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.myLocationEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle myLocationButtonEnabled', () async { @@ -231,8 +319,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.myLocationButtonEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle padding', () async { @@ -245,8 +337,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.padding, newPadding); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle indoorViewEnabled', () async { @@ -257,8 +353,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.indoorViewEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle trafficEnabled', () async { @@ -269,8 +369,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.trafficEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle buildingsEnabled', () async { @@ -281,8 +385,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.buildingsEnabled, true); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); test('handle cloudMapId', () async { @@ -293,8 +401,12 @@ void main() { // A diff applied to empty options should be the diff itself. expect(empty.applyDiff(diff), diff); + // The diff from empty options should be the diff itself. + expect(diff.diffFrom(empty), diff); // A diff applied to non-empty options should update that field. expect(updated.cloudMapId, _kCloudMapId); + // The hash code should change. + expect(empty.hashCode, isNot(diff.hashCode)); }); });