-
Notifications
You must be signed in to change notification settings - Fork 28.2k
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
add minTaps argument to BaseTapAndDragGestureRecognizer and TapAndPanGestureRecognizer #164922
base: master
Are you sure you want to change the base?
Conversation
After some testing, I see that you can't combine Full exampleimport 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
/// Flutter code sample for [TapAndPanGestureRecognizer].
void main() {
runApp(const TapAndDragToZoomApp());
}
class TapAndDragToZoomApp extends StatelessWidget {
const TapAndDragToZoomApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(body: Center(child: TapAndDragToZoomWidget(child: MyBoxWidget()))),
);
}
}
class MyBoxWidget extends StatelessWidget {
const MyBoxWidget({super.key});
@override
Widget build(BuildContext context) {
return Container(color: Colors.blueAccent, height: 100.0, width: 100.0);
}
}
// This widget will scale its child up when it detects a drag up, after a
// double tap/click. It will scale the widget down when it detects a drag down,
// after a double tap. Dragging down and then up after a double tap/click will
// zoom the child in/out. The scale of the child will be reset when the drag ends.
class TapAndDragToZoomWidget extends StatefulWidget {
const TapAndDragToZoomWidget({super.key, required this.child});
final Widget child;
@override
State<TapAndDragToZoomWidget> createState() => _TapAndDragToZoomWidgetState();
}
class _TapAndDragToZoomWidgetState extends State<TapAndDragToZoomWidget> {
final double scaleMultiplier = -0.0001;
double _currentScale = 1.0;
Offset? _previousDragPosition;
static double _keepScaleWithinBounds(double scale) {
const double minScale = 0.1;
const double maxScale = 30;
if (scale <= 0) {
return minScale;
}
if (scale >= 30) {
return maxScale;
}
return scale;
}
void _zoomLogic(Offset currentDragPosition) {
final double dx = (_previousDragPosition!.dx - currentDragPosition.dx).abs();
final double dy = (_previousDragPosition!.dy - currentDragPosition.dy).abs();
if (dx > dy) {
// Ignore horizontal drags.
_previousDragPosition = currentDragPosition;
return;
}
if (currentDragPosition.dy < _previousDragPosition!.dy) {
// Zoom out on drag up.
setState(() {
_currentScale += currentDragPosition.dy * scaleMultiplier;
_currentScale = _keepScaleWithinBounds(_currentScale);
});
} else {
// Zoom in on drag down.
setState(() {
_currentScale -= currentDragPosition.dy * scaleMultiplier;
_currentScale = _keepScaleWithinBounds(_currentScale);
});
}
_previousDragPosition = currentDragPosition;
}
@override
Widget build(BuildContext context) {
return RawGestureDetector(
gestures: <Type, GestureRecognizerFactory>{
TapAndPanGestureRecognizer:
GestureRecognizerFactoryWithHandlers<TapAndPanGestureRecognizer>(
() => TapAndPanGestureRecognizer(
//minTaps: 2,
),
(TapAndPanGestureRecognizer instance) {
instance
..onTapDown = (TapDragDownDetails details) {
_previousDragPosition = details.globalPosition;
}
..onTapUp = (TapDragUpDetails details) {
//print("Tap Up");
}
..onDragStart = (TapDragStartDetails details) {
print("Drag Start");
_zoomLogic(details.globalPosition);
}
..onDragUpdate = (TapDragUpdateDetails details) {
print("Drag Update");
_zoomLogic(details.globalPosition);
}
..onDragEnd = (TapDragEndDetails details) {
print("Drag End");
setState(() {
_currentScale = 1.0;
});
_previousDragPosition = null;
};
},
),
DoubleTapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
() => DoubleTapGestureRecognizer(),
(DoubleTapGestureRecognizer instance) {
instance
..onDoubleTap = () {
print('Double Tap');
};
},
),
},
child: Transform.scale(scale: _currentScale, child: widget.child),
);
}
}
I wonder if it would make sense to add a clean way to recognize double or n taps within User taps three times then drags: You could interpret this in many ways:
Google earth does the first and Google photos does the second. With the current implementation, there is no way to let the third tap fall through back to the gesture arena as the beginning of a new gesture (such as a drag). Besides this, the current method of detecting a double tap with Clickint _tapUpCount = 0;
..onTapUp = (TapDragUpDetails details) {
_tapUpCount++;
if(_tapUpCount == 2){
performDoubleTap();
}
}
..onDragStart = (TapDragStartDetails details) {
//don't scale if a double tap occurred.
if(_tapUpCount <2){
_zoomLogic(details.globalPosition);
}
}
..onDragUpdate = (TapDragUpdateDetails details) {
//don't scale if a double tap occurred.
if(_tapUpCount <2){
_zoomLogic(details.globalPosition);
}
}
..onDragEnd = (TapDragEndDetails details) {
//don't scale if a double tap occurred.
if(_tapUpCount <2){
//...
}
};
It would be nice if there was a way to say "After n taps up, complete the gesture and fire the n-tapsUp callback, something like: (TapAndPanGestureRecognizer instance) {
instance
..nTaps = 2
..onNTaps(){
performDoubleTap();
} After All of this doesn't need to be part of the PR of course, I only mention this because in light of these issues, maybe my own solution is not good either and should be part of some entirely new gesture recognizer specifically meant for double tap, double-tap zoom and scale all in one. To be clean I do think the current PR is useful and would allow for replicating the Google photos behavior and even the Google earth behavior if you are able to perform a drag from the TapDragUpdate, eg. via a |
This adds a
minTaps
parameter toBaseTapAndDragGestureRecognizer
andTapAndPanGestureRecognizer
, allowingScaleGestureRecognizer
to resolve on single taps andTapAndPanGestureRecognizer
to only win when there is a double tap first by settingminTaps
to 2. See the related issue "Add double tap and pan/double tap and drag gesture (#164889)"Closes #164889
Note: we should probably check that
minTaps
<=maxConsecutiveTap
. Not sure where to check for that. I guess we would add setters formaxConsecutiveTap
andminTaps
and check there?Before
After
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.