Skip to content

Commit

Permalink
Added InteractiveFlag.doubleTapDragZoom (#1603)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
JaffaKetchup authored Aug 3, 2023
1 parent 9907251 commit 1477eab
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 122 deletions.
210 changes: 101 additions & 109 deletions example/lib/pages/interactive_test_page.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<InteractiveTestPage> {
// 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: <Widget>[
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: <Widget>[
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<Widget?>(
(category) => Column(
children: [
Text(
category.key,
style: const TextStyle(fontWeight: FontWeight.bold),
),
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
...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<Widget>()
.toList(),
),
const Divider(),
Padding(
padding: const EdgeInsets.only(top: 8, bottom: 8),
child: Center(
Expand All @@ -140,14 +116,21 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
),
),
),
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: [
Expand Down Expand Up @@ -210,3 +193,12 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
}
}
}

extension _IterableExt<E> on Iterable<E> {
Iterable<E> interleave(E separator) sync* {
for (int i = 0; i < length; i++) {
yield elementAt(i);
if (i < length) yield separator;
}
}
}
16 changes: 7 additions & 9 deletions lib/src/gestures/flutter_map_interactive_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class FlutterMapInteractiveViewerState
extends State<FlutterMapInteractiveViewer> 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();
Expand Down Expand Up @@ -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<Type, GestureRecognizerFactory> _createGestures({
Expand Down Expand Up @@ -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;

Expand Down
15 changes: 12 additions & 3 deletions lib/src/gestures/interactive_flag.dart
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 1477eab

Please sign in to comment.