From 1477eabbbe65a80a2e54d9b58c01af57e042def4 Mon Sep 17 00:00:00 2001 From: Luka S Date: Thu, 3 Aug 2023 14:03:02 +0100 Subject: [PATCH] Added `InteractiveFlag.doubleTapDragZoom` (#1603) * Added `InteractiveFlag.doubleTapDragZoom` Improved example app's Interactive Flags page Bumped version to match latest on pub.dev * Syntactic improvements to example app's Interactive Flags page --- example/lib/pages/interactive_test_page.dart | 210 +++++++++--------- .../flutter_map_interactive_viewer.dart | 16 +- lib/src/gestures/interactive_flag.dart | 15 +- pubspec.yaml | 2 +- 4 files changed, 121 insertions(+), 122 deletions(-) diff --git a/example/lib/pages/interactive_test_page.dart b/example/lib/pages/interactive_test_page.dart index 883b2af73..86486cb7d 100644 --- a/example/lib/pages/interactive_test_page.dart +++ b/example/lib/pages/interactive_test_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_example/widgets/drawer.dart'; import 'package:latlong2/latlong.dart'; @@ -9,128 +10,103 @@ class InteractiveTestPage extends StatefulWidget { const InteractiveTestPage({Key? key}) : super(key: key); @override - State createState() { - return _InteractiveTestPageState(); - } + State createState() => _InteractiveTestPageState(); } class _InteractiveTestPageState extends State { - // Enable pinchZoom and doubleTapZoomBy by default - int flags = InteractiveFlag.pinchZoom | InteractiveFlag.doubleTapZoom; - - MapEvent? _latestEvent; - - @override - void initState() { - super.initState(); - } + static const availableFlags = { + 'Movement': { + InteractiveFlag.drag: 'Drag', + InteractiveFlag.flingAnimation: 'Fling', + InteractiveFlag.pinchMove: 'Pinch', + }, + 'Zooming': { + InteractiveFlag.pinchZoom: 'Pinch', + InteractiveFlag.scrollWheelZoom: 'Scroll', + InteractiveFlag.doubleTapZoom: 'Double tap', + InteractiveFlag.doubleTapDragZoom: '+ drag', + }, + 'Rotation': { + InteractiveFlag.rotate: 'Twist', + }, + }; - void onMapEvent(MapEvent mapEvent) { - if (mapEvent is! MapEventMove && mapEvent is! MapEventRotate) { - // do not flood console with move and rotate events - debugPrint(_eventName(mapEvent)); - } - - setState(() { - _latestEvent = mapEvent; - }); - } + int flags = InteractiveFlag.drag | InteractiveFlag.pinchZoom; + bool keyboardCursorRotate = false; - void updateFlags(int flag) { - if (InteractiveFlag.hasFlag(flags, flag)) { - // remove flag from flags - flags &= ~flag; - } else { - // add flag to flags - flags |= flag; - } - } + MapEvent? _latestEvent; @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Test out Interactive flags!')), + appBar: AppBar(title: const Text('Interactive Flags')), drawer: buildDrawer(context, InteractiveTestPage.route), body: Padding( padding: const EdgeInsets.all(8), child: Column( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MaterialButton( - color: InteractiveFlag.hasDrag(flags) - ? Colors.greenAccent - : Colors.redAccent, - onPressed: () { - setState(() { - updateFlags(InteractiveFlag.drag); - }); - }, - child: const Text('Drag'), - ), - MaterialButton( - color: InteractiveFlag.hasFlingAnimation(flags) - ? Colors.greenAccent - : Colors.redAccent, - onPressed: () { - setState(() { - updateFlags(InteractiveFlag.flingAnimation); - }); - }, - child: const Text('Fling'), - ), - MaterialButton( - color: InteractiveFlag.hasPinchMove(flags) - ? Colors.greenAccent - : Colors.redAccent, - onPressed: () { - setState(() { - updateFlags(InteractiveFlag.pinchMove); - }); - }, - child: const Text('Pinch move'), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MaterialButton( - color: InteractiveFlag.hasDoubleTapZoom(flags) - ? Colors.greenAccent - : Colors.redAccent, - onPressed: () { - setState(() { - updateFlags(InteractiveFlag.doubleTapZoom); - }); - }, - child: const Text('Double tap zoom'), - ), - MaterialButton( - color: InteractiveFlag.hasRotate(flags) - ? Colors.greenAccent - : Colors.redAccent, - onPressed: () { - setState(() { - updateFlags(InteractiveFlag.rotate); - }); - }, - child: const Text('Rotate'), - ), - MaterialButton( - color: InteractiveFlag.hasPinchZoom(flags) - ? Colors.greenAccent - : Colors.redAccent, - onPressed: () { - setState(() { - updateFlags(InteractiveFlag.pinchZoom); - }); - }, - child: const Text('Pinch zoom'), - ), - ], + Flex( + direction: MediaQuery.of(context).size.width >= 600 + ? Axis.horizontal + : Axis.vertical, + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: availableFlags.entries + .map( + (category) => Column( + children: [ + Text( + category.key, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ...category.value.entries.map( + (e) => Column( + children: [ + Checkbox.adaptive( + value: + InteractiveFlag.hasFlag(e.key, flags), + onChanged: (enabled) { + if (!enabled!) { + setState(() => flags &= ~e.key); + return; + } + setState(() => flags |= e.key); + }, + ), + Text(e.value), + ], + ), + ), + if (category.key == 'Rotation') ...[ + Column( + children: [ + Checkbox.adaptive( + value: keyboardCursorRotate, + onChanged: (enabled) => setState( + () => keyboardCursorRotate = enabled!), + ), + const Text('Cursor & CTRL'), + ], + ), + ] + ].interleave(const SizedBox(width: 12)).toList() + ..removeLast(), + ) + ], + ), + ) + .interleave( + MediaQuery.of(context).size.width >= 600 + ? null + : const SizedBox(height: 12), + ) + .whereType() + .toList(), ), + const Divider(), Padding( padding: const EdgeInsets.only(top: 8, bottom: 8), child: Center( @@ -140,14 +116,21 @@ class _InteractiveTestPageState extends State { ), ), ), - Flexible( + Expanded( child: FlutterMap( options: MapOptions( - onMapEvent: onMapEvent, + onMapEvent: (evt) => setState(() => _latestEvent = evt), initialCenter: const LatLng(51.5, -0.09), initialZoom: 11, interactionOptions: InteractionOptions( flags: flags, + isCursorRotationKeyboardKeyTrigger: (key) => + keyboardCursorRotate && + { + LogicalKeyboardKey.control, + LogicalKeyboardKey.controlLeft, + LogicalKeyboardKey.controlRight + }.contains(key), ), ), children: [ @@ -210,3 +193,12 @@ class _InteractiveTestPageState extends State { } } } + +extension _IterableExt on Iterable { + Iterable interleave(E separator) sync* { + for (int i = 0; i < length; i++) { + yield elementAt(i); + if (i < length) yield separator; + } + } +} diff --git a/lib/src/gestures/flutter_map_interactive_viewer.dart b/lib/src/gestures/flutter_map_interactive_viewer.dart index 3e488c0de..da1ef6d44 100644 --- a/lib/src/gestures/flutter_map_interactive_viewer.dart +++ b/lib/src/gestures/flutter_map_interactive_viewer.dart @@ -40,7 +40,7 @@ class FlutterMapInteractiveViewerState extends State with TickerProviderStateMixin { static const int _kMinFlingVelocity = 800; static const _kDoubleTapZoomDuration = 200; - static const doubleTapDelay = Duration(milliseconds: 350); + static const doubleTapDelay = Duration(milliseconds: 250); final _positionedTapController = PositionedTapController(); final _gestureArenaTeam = GestureArenaTeam(); @@ -210,13 +210,11 @@ class FlutterMapInteractiveViewerState widget.controller.moveEnded(MapEventSource.interactiveFlagsChanged); } - if (oldOptions.isCursorRotationKeyboardKeyTrigger != - newOptions.isCursorRotationKeyboardKeyTrigger) { - ServicesBinding.instance.keyboard - .removeHandler(keyboardRotationTriggerKeyHandler); - ServicesBinding.instance.keyboard - .addHandler(keyboardRotationTriggerKeyHandler); - } + // No way to detect whether two functions are equal, so assume they aren't + ServicesBinding.instance.keyboard + .removeHandler(keyboardRotationTriggerKeyHandler); + ServicesBinding.instance.keyboard + .addHandler(keyboardRotationTriggerKeyHandler); } Map _createGestures({ @@ -841,7 +839,7 @@ class FlutterMapInteractiveViewerState _doubleTapHoldMaxDelay?.cancel(); final flags = _interactionOptions.flags; - if (InteractiveFlag.hasPinchZoom(flags)) { + if (InteractiveFlag.hasDoubleTapDragZoom(flags)) { final verticalOffset = (_focalStartLocal - details.localFocalPoint).dy; final newZoom = _mapZoomStart - verticalOffset / 360 * _camera.zoom; diff --git a/lib/src/gestures/interactive_flag.dart b/lib/src/gestures/interactive_flag.dart index b88e33018..f6d5058cf 100644 --- a/lib/src/gestures/interactive_flag.dart +++ b/lib/src/gestures/interactive_flag.dart @@ -1,4 +1,4 @@ -import 'package:flutter_map/src/map/options.dart'; +import 'package:flutter_map/flutter_map.dart'; /// Use [InteractiveFlag] to disable / enable certain events Use /// [InteractiveFlag.all] to enable all events, use [InteractiveFlag.none] to @@ -35,14 +35,19 @@ abstract class InteractiveFlag { /// Enable zooming with a single-finger double tap gesture static const int doubleTapZoom = 1 << 4; + /// Enable zooming with a single-finger double-tap-drag gesture + /// + /// The associated [MapEventSource] is [MapEventSource.doubleTapHold]. + static const int doubleTapDragZoom = 1 << 5; + /// Enable zooming with a mouse scroll wheel - static const int scrollWheelZoom = 1 << 5; + static const int scrollWheelZoom = 1 << 6; /// Enable rotation with two-finger twist gesture /// /// For controlling rotation where a keyboard/cursor combination is used, see /// [InteractionOptions.isCursorRotationKeyboardKeyTrigger]. - static const int rotate = 1 << 6; + static const int rotate = 1 << 7; /// Flags pertaining to gestures which require multiple fingers. static const _multiFingerFlags = pinchMove | pinchZoom | rotate; @@ -71,6 +76,10 @@ abstract class InteractiveFlag { /// True if the [pinchZoom] interactive flag is enabled. static bool hasPinchZoom(int flags) => hasFlag(flags, pinchZoom); + /// True if the [doubleTapDragZoom] interactive flag is enabled. + static bool hasDoubleTapDragZoom(int flags) => + hasFlag(flags, doubleTapDragZoom); + /// True if the [doubleTapZoom] interactive flag is enabled. static bool hasDoubleTapZoom(int flags) => hasFlag(flags, doubleTapZoom); diff --git a/pubspec.yaml b/pubspec.yaml index 77d9aaced..f9783d6e4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_map description: A versatile mapping package for Flutter, that's simple and easy to learn, yet completely customizable and configurable. -version: 6.0.0-dev.1 +version: 6.0.0-dev.2 repository: https://github.com/fleaflet/flutter_map issue_tracker: https://github.com/fleaflet/flutter_map/issues