diff --git a/example/lib/map_ui.dart b/example/lib/map_ui.dart index 034ee089e..b10a0334a 100644 --- a/example/lib/map_ui.dart +++ b/example/lib/map_ui.dart @@ -5,6 +5,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:collection/collection.dart'; import 'package:mapbox_gl/mapbox_gl.dart'; import 'main.dart'; @@ -61,6 +62,7 @@ class MapUiBodyState extends State { ]; bool _rotateGesturesEnabled = true; bool _scrollGesturesEnabled = true; + bool? _doubleClickToZoomEnabled; bool _tiltGesturesEnabled = true; bool _zoomGesturesEnabled = true; bool _myLocationEnabled = true; @@ -214,6 +216,28 @@ class MapUiBodyState extends State { ); } + Widget _doubleClickToZoomToggler() { + final stateInfo = _doubleClickToZoomEnabled == null + ? "disable" + : _doubleClickToZoomEnabled! + ? 'unset' + : 'enable'; + return TextButton( + child: Text('$stateInfo double click to zoom'), + onPressed: () { + setState(() { + if (_doubleClickToZoomEnabled == null) { + _doubleClickToZoomEnabled = false; + } else if (!_doubleClickToZoomEnabled!) { + _doubleClickToZoomEnabled = true; + } else { + _doubleClickToZoomEnabled = null; + } + }); + }, + ); + } + Widget _tiltToggler() { return TextButton( child: Text('${_tiltGesturesEnabled ? 'disable' : 'enable'} tilt'), @@ -282,10 +306,11 @@ class MapUiBodyState extends State { } _drawFill(List features) async { - Map feature = features[0]; - if (feature['geometry']['type'] == 'Polygon') { - var coordinates = feature['geometry']['coordinates']; - List> geometry = coordinates + Map? feature = + features.firstWhereOrNull((f) => f['geometry']['type'] == 'Polygon'); + + if (feature != null) { + List> geometry = feature['geometry']['coordinates'] .map( (ll) => ll.map((l) => LatLng(l[1], l[0])).toList().cast()) .toList() @@ -317,6 +342,7 @@ class MapUiBodyState extends State { scrollGesturesEnabled: _scrollGesturesEnabled, tiltGesturesEnabled: _tiltGesturesEnabled, zoomGesturesEnabled: _zoomGesturesEnabled, + doubleClickZoomEnabled: _doubleClickToZoomEnabled, myLocationEnabled: _myLocationEnabled, myLocationTrackingMode: _myLocationTrackingMode, myLocationRenderMode: MyLocationRenderMode.GPS, @@ -365,15 +391,7 @@ class MapUiBodyState extends State { }, ); - final List listViewChildren = [ - Center( - child: SizedBox( - width: _mapExpanded ? null : 300.0, - height: 200.0, - child: mapboxMap, - ), - ), - ]; + final List listViewChildren = []; if (mapController != null) { listViewChildren.addAll( @@ -393,6 +411,7 @@ class MapUiBodyState extends State { _zoomBoundsToggler(), _rotateToggler(), _scrollToggler(), + _doubleClickToZoomToggler(), _tiltToggler(), _zoomToggler(), _myLocationToggler(), @@ -401,8 +420,22 @@ class MapUiBodyState extends State { ], ); } - return ListView( - children: listViewChildren, + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: SizedBox( + width: _mapExpanded ? null : 300.0, + height: 200.0, + child: mapboxMap, + ), + ), + Expanded( + child: ListView( + children: listViewChildren, + ), + ) + ], ); } diff --git a/lib/mapbox_gl.dart b/lib/mapbox_gl.dart index 18bbed3dc..6d388c930 100644 --- a/lib/mapbox_gl.dart +++ b/lib/mapbox_gl.dart @@ -13,6 +13,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:collection/collection.dart'; import 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart'; export 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart' diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index c15f6b4bb..ce11c2a1e 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -24,6 +24,7 @@ class MapboxMap extends StatefulWidget { this.scrollGesturesEnabled = true, this.zoomGesturesEnabled = true, this.tiltGesturesEnabled = true, + this.doubleClickZoomEnabled, this.trackCameraPosition = false, this.myLocationEnabled = false, this.myLocationTrackingMode = MyLocationTrackingMode.None, @@ -115,6 +116,12 @@ class MapboxMap extends StatefulWidget { /// True if the map view should respond to tilt gestures. final bool tiltGesturesEnabled; + /// Set to true to forcefully disable/enable if map should respond to double + /// click to zoom. + /// + /// This takes presedence over zoomGesturesEnabled. Only supported for web. + final bool? doubleClickZoomEnabled; + /// True if you want to be notified of map camera movements by the MapboxMapController. Default is false. /// /// If this is set to true and the user pans/zooms/rotates the map, MapboxMapController (which is a ChangeNotifier) @@ -311,11 +318,12 @@ class _MapboxMapOptions { this.cameraTargetBounds, this.styleString, this.minMaxZoomPreference, - this.rotateGesturesEnabled, - this.scrollGesturesEnabled, - this.tiltGesturesEnabled, + required this.rotateGesturesEnabled, + required this.scrollGesturesEnabled, + required this.tiltGesturesEnabled, + required this.zoomGesturesEnabled, + required this.doubleClickZoomEnabled, this.trackCameraPosition, - this.zoomGesturesEnabled, this.myLocationEnabled, this.myLocationTrackingMode, this.myLocationRenderMode, @@ -337,6 +345,8 @@ class _MapboxMapOptions { tiltGesturesEnabled: map.tiltGesturesEnabled, trackCameraPosition: map.trackCameraPosition, zoomGesturesEnabled: map.zoomGesturesEnabled, + doubleClickZoomEnabled: + map.doubleClickZoomEnabled ?? map.zoomGesturesEnabled, myLocationEnabled: map.myLocationEnabled, myLocationTrackingMode: map.myLocationTrackingMode, myLocationRenderMode: map.myLocationRenderMode, @@ -356,15 +366,17 @@ class _MapboxMapOptions { final MinMaxZoomPreference? minMaxZoomPreference; - final bool? rotateGesturesEnabled; + final bool rotateGesturesEnabled; - final bool? scrollGesturesEnabled; + final bool scrollGesturesEnabled; - final bool? tiltGesturesEnabled; + final bool tiltGesturesEnabled; - final bool? trackCameraPosition; + final bool zoomGesturesEnabled; + + final bool doubleClickZoomEnabled; - final bool? zoomGesturesEnabled; + final bool? trackCameraPosition; final bool? myLocationEnabled; @@ -382,6 +394,14 @@ class _MapboxMapOptions { final Point? attributionButtonMargins; + final _gestureGroup = { + 'rotateGesturesEnabled', + 'scrollGesturesEnabled', + 'tiltGesturesEnabled', + 'zoomGesturesEnabled', + 'doubleClickZoomEnabled' + }; + Map toMap() { final Map optionsMap = {}; @@ -403,10 +423,13 @@ class _MapboxMapOptions { addIfNonNull('cameraTargetBounds', cameraTargetBounds?.toJson()); addIfNonNull('styleString', styleString); addIfNonNull('minMaxZoomPreference', minMaxZoomPreference?.toJson()); + addIfNonNull('rotateGesturesEnabled', rotateGesturesEnabled); addIfNonNull('scrollGesturesEnabled', scrollGesturesEnabled); addIfNonNull('tiltGesturesEnabled', tiltGesturesEnabled); addIfNonNull('zoomGesturesEnabled', zoomGesturesEnabled); + addIfNonNull('doubleClickZoomEnabled', doubleClickZoomEnabled); + addIfNonNull('trackCameraPosition', trackCameraPosition); addIfNonNull('myLocationEnabled', myLocationEnabled); addIfNonNull('myLocationTrackingMode', myLocationTrackingMode?.index); @@ -422,8 +445,22 @@ class _MapboxMapOptions { Map updatesMap(_MapboxMapOptions newOptions) { final Map prevOptionsMap = toMap(); - return newOptions.toMap() - ..removeWhere( - (String key, dynamic value) => prevOptionsMap[key] == value); + final newOptionsMap = newOptions.toMap(); + + // if any gesture is updated also all other gestures have to the saved to + // the update + + final gesturesRequireUpdate = + _gestureGroup.any((key) => newOptionsMap[key] != prevOptionsMap[key]); + + return newOptionsMap + ..removeWhere((String key, dynamic value) { + if (_gestureGroup.contains(key)) return !gesturesRequireUpdate; + final oldValue = prevOptionsMap[key]; + if (oldValue is List && value is List) { + return listEquals(oldValue, value); + } + return oldValue == value; + }); } } diff --git a/mapbox_gl_web/lib/src/convert.dart b/mapbox_gl_web/lib/src/convert.dart index 0816d191f..f51d40779 100644 --- a/mapbox_gl_web/lib/src/convert.dart +++ b/mapbox_gl_web/lib/src/convert.dart @@ -26,21 +26,23 @@ class Convert { sink.setMinMaxZoomPreference(options['minMaxZoomPreference'][0], options['minMaxZoomPreference'][1]); } - if (options.containsKey('rotateGesturesEnabled')) { - sink.setRotateGesturesEnabled(options['rotateGesturesEnabled']); - } - if (options.containsKey('scrollGesturesEnabled')) { - sink.setScrollGesturesEnabled(options['scrollGesturesEnabled']); - } - if (options.containsKey('tiltGesturesEnabled')) { - sink.setTiltGesturesEnabled(options['tiltGesturesEnabled']); + if (options['rotateGesturesEnabled'] != null && + options['scrollGesturesEnabled'] != null && + options['tiltGesturesEnabled'] != null && + options['zoomGesturesEnabled'] != null && + options['doubleClickZoomEnabled'] != null) { + sink.setGestures( + rotateGesturesEnabled: options['rotateGesturesEnabled'], + scrollGesturesEnabled: options['scrollGesturesEnabled'], + tiltGesturesEnabled: options['tiltGesturesEnabled'], + zoomGesturesEnabled: options['zoomGesturesEnabled'], + doubleClickZoomEnabled: options['doubleClickZoomEnabled']); } + if (options.containsKey('trackCameraPosition')) { sink.setTrackCameraPosition(options['trackCameraPosition']); } - if (options.containsKey('zoomGesturesEnabled')) { - sink.setZoomGesturesEnabled(options['zoomGesturesEnabled']); - } + if (options.containsKey('myLocationEnabled')) { sink.setMyLocationEnabled(options['myLocationEnabled']); } diff --git a/mapbox_gl_web/lib/src/mapbox_map_controller.dart b/mapbox_gl_web/lib/src/mapbox_map_controller.dart index cd9f48023..9785a2e24 100644 --- a/mapbox_gl_web/lib/src/mapbox_map_controller.dart +++ b/mapbox_gl_web/lib/src/mapbox_map_controller.dart @@ -291,11 +291,14 @@ class MapboxMapController extends MapboxGlPlatform if (filter != null) { options['filter'] = filter; } + + // avoid issues with the js point type + final pointAsList = [point.x, point.y]; return _map - .queryRenderedFeatures([point, point], options) + .queryRenderedFeatures([pointAsList, pointAsList], options) .map((feature) => { 'type': 'Feature', - 'id': feature.id as int?, + 'id': feature.id, 'geometry': { 'type': feature.geometry.type, 'coordinates': feature.geometry.coordinates, @@ -318,8 +321,8 @@ class MapboxMapController extends MapboxGlPlatform } return _map .queryRenderedFeatures([ - Point(rect.left, rect.bottom), - Point(rect.right, rect.top), + [rect.left, rect.bottom], + [rect.right, rect.top], ], options) .map((feature) => { 'type': 'Feature', @@ -696,30 +699,6 @@ class MapboxMapController extends MapboxGlPlatform } } - @override - void setRotateGesturesEnabled(bool rotateGesturesEnabled) { - if (rotateGesturesEnabled) { - _map.dragRotate.enable(); - _map.touchZoomRotate.enableRotation(); - _map.keyboard.enable(); - } else { - _map.dragRotate.disable(); - _map.touchZoomRotate.disableRotation(); - _map.keyboard.disable(); - } - } - - @override - void setScrollGesturesEnabled(bool scrollGesturesEnabled) { - if (scrollGesturesEnabled) { - _map.dragPan.enable(); - _map.keyboard.enable(); - } else { - _map.dragPan.disable(); - _map.keyboard.disable(); - } - } - @override void setStyleString(String? styleString) { //remove old mouseenter callbacks to avoid multicalling @@ -737,39 +716,11 @@ class MapboxMapController extends MapboxGlPlatform } } - @override - void setTiltGesturesEnabled(bool tiltGesturesEnabled) { - if (tiltGesturesEnabled) { - _map.dragRotate.enable(); - _map.keyboard.enable(); - } else { - _map.dragRotate.disable(); - _map.keyboard.disable(); - } - } - @override void setTrackCameraPosition(bool trackCameraPosition) { _trackCameraPosition = trackCameraPosition; } - @override - void setZoomGesturesEnabled(bool zoomGesturesEnabled) { - if (zoomGesturesEnabled) { - _map.doubleClickZoom.enable(); - _map.boxZoom.enable(); - _map.scrollZoom.enable(); - _map.touchZoomRotate.enable(); - _map.keyboard.enable(); - } else { - _map.doubleClickZoom.disable(); - _map.boxZoom.disable(); - _map.scrollZoom.disable(); - _map.touchZoomRotate.disable(); - _map.keyboard.disable(); - } - } - @override Future toScreenLocation(LatLng latLng) async { var screenPosition = @@ -907,6 +858,60 @@ class MapboxMapController extends MapboxGlPlatform _map.getCanvas().style.cursor = ''; } + @override + void setGestures( + {required bool rotateGesturesEnabled, + required bool scrollGesturesEnabled, + required bool tiltGesturesEnabled, + required bool zoomGesturesEnabled, + required bool doubleClickZoomEnabled}) { + if (rotateGesturesEnabled && + scrollGesturesEnabled && + tiltGesturesEnabled && + zoomGesturesEnabled) { + _map.keyboard.enable(); + } else { + _map.keyboard.disable(); + } + + if (scrollGesturesEnabled) { + _map.dragPan.enable(); + } else { + _map.dragPan.disable(); + } + + if (zoomGesturesEnabled) { + _map.doubleClickZoom.enable(); + _map.boxZoom.enable(); + _map.scrollZoom.enable(); + _map.touchZoomRotate.enable(); + } else { + _map.doubleClickZoom.disable(); + _map.boxZoom.disable(); + _map.scrollZoom.disable(); + _map.touchZoomRotate.disable(); + } + + if (doubleClickZoomEnabled) { + _map.doubleClickZoom.enable(); + } else { + _map.doubleClickZoom.disable(); + } + + if (rotateGesturesEnabled) { + _map.touchZoomRotate.enableRotation(); + } else { + _map.touchZoomRotate.disableRotation(); + } + + // dragRotate is shared by both gestures + if (tiltGesturesEnabled && rotateGesturesEnabled) { + _map.dragRotate.enable(); + } else { + _map.dragRotate.disable(); + } + } + @override Future addSource(String sourceId, SourceProperties source) async { _map.addSource(sourceId, source.toJson()); diff --git a/mapbox_gl_web/lib/src/options_sink.dart b/mapbox_gl_web/lib/src/options_sink.dart index 6ab61d901..e12f85a1e 100644 --- a/mapbox_gl_web/lib/src/options_sink.dart +++ b/mapbox_gl_web/lib/src/options_sink.dart @@ -11,16 +11,16 @@ abstract class MapboxMapOptionsSink { void setMinMaxZoomPreference(num? min, num? max); - void setRotateGesturesEnabled(bool rotateGesturesEnabled); - - void setScrollGesturesEnabled(bool scrollGesturesEnabled); - - void setTiltGesturesEnabled(bool tiltGesturesEnabled); + void setGestures({ + required bool rotateGesturesEnabled, + required bool scrollGesturesEnabled, + required bool tiltGesturesEnabled, + required bool zoomGesturesEnabled, + required bool doubleClickZoomEnabled, + }); void setTrackCameraPosition(bool trackCameraPosition); - void setZoomGesturesEnabled(bool zoomGesturesEnabled); - void setMyLocationEnabled(bool myLocationEnabled); void setMyLocationTrackingMode(int myLocationTrackingMode);