我希望能在Flutter小部件中检测三次轻拍(甚至更多),但是GestureDetector只内置了双击检测。
在小部件上检测三次轻拍的最简单方法是什么?
(我想要不停地点击屏幕上的某个部分以解锁一些开发人员选项)
我希望能在Flutter小部件中检测三次轻拍(甚至更多),但是GestureDetector只内置了双击检测。
在小部件上检测三次轻拍的最简单方法是什么?
(我想要不停地点击屏幕上的某个部分以解锁一些开发人员选项)
这个有点懒,实际上并不那么难
// init
int lastTap = DateTime.now().millisecondsSinceEpoch;
int consecutiveTaps = 0;
GestureDetector(
onTap: () {
int now = DateTime.now().millisecondsSinceEpoch;
if (now - lastTap < 1000) {
print("Consecutive tap");
consecutiveTaps ++;
print("taps = " + consecutiveTaps.toString());
if (consecutiveTaps > 4){
// Do something
}
} else {
consecutiveTaps = 0;
}
lastTap = now;
},
child: ...
)
我尝试了这里提到的方法,但对我没有用。无论点击次数如何,GestureDetector onTap只被调用一次。可能在flutter中有些变化(我在beta频道上)。 然而,我深入研究了flutter的源代码并找到了解决方案(https://api.flutter.dev/flutter/gestures/SerialTapGestureRecognizer-class.html):
import "package:flutter/gestures.dart";
import 'package:flutter/material.dart';
RawGestureDetector(
gestures: {
SerialTapGestureRecognizer:
GestureRecognizerFactoryWithHandlers<SerialTapGestureRecognizer>(
SerialTapGestureRecognizer.new,
(SerialTapGestureRecognizer instance) {
instance.onSerialTapDown = (SerialTapDownDetails details) {
if (details.count == 3) print('Consecutive tap 3');
};
})
},
// child: // your widget,
);
相关解决方案。 这是基于Listener widget的灵活可重复使用的多点触控小部件,它报告原始指针事件:
class AppMultipleTap extends StatefulWidget {
final Widget child;
final VoidCallback onMultipleTap;
final int taps;
final Duration duration;
const AppMultipleTap({
super.key,
required this.child,
required this.onMultipleTap,
/// feel free to override these values
this.taps = 3,
this.duration = const Duration(milliseconds: 600),
});
@override
State<AppMultipleTap> createState() => _AppMultipleTapState();
}
class _AppMultipleTapState extends State<AppMultipleTap> {
/// in _count we store current number of taps
int _count = 0;
Timer? _timer;
@override
Widget build(BuildContext context) {
return Listener(
onPointerDown: (_) {
if (_timer == null) _startTimer();
_count++;
},
child: widget.child,
);
}
void _startTimer() {
_timer = Timer(widget.duration, () {
/// you can change this condition to ==, if you need 100% match
if (_count >= widget.taps) {
widget.onMultipleTap.call();
}
_timer = null;
_count = 0;
});
}
}
@override
Widget build(BuildContext context) {
return AppMultipleTap(
onMultipleTap: /// Do some action
int lastTap = DateTime.now().millisecondsSinceEpoch;
int consecutiveTaps = 1;
GestureDetector(
onTap: () {
int now = DateTime.now().millisecondsSinceEpoch;
if (consecutiveTaps == 1) {
print("taps = " + consecutiveTaps.toString());
lastTap = now;
}
if (now - lastTap < 300) {
print("Consecutive tap");
consecutiveTaps++;
print("taps = " + consecutiveTaps.toString());
if (consecutiveTaps == 3) {
print("Consecutive tap 3");
} else if (consecutiveTaps == 2) {
print("Consecutive tap 2");
}
} else {
consecutiveTaps = 1;
}
lastTap = now;
},
child: \\child);
Timer? devPageClickTimer;
num devPageTapped = 0;
final devPageTapGoal = 5;
GestureDetector(
onTap: () {
devPageTapped++;
if (devPageTapped >= devPageTapGoal) {
router.push(const DeveloperRoute());
}
if (devPageClickTimer != null) {
devPageClickTimer!.cancel();
}
devPageClickTimer = Timer(const Duration(milliseconds: 200), () => devPageTapped = 0);
},
我喜欢这种简单的方法,没有那么多嵌套的if块。
// Variables in the state class
var startTap = timeNow;
var consecutiveTaps = 0;
static const int serialTaps = 4;
static const int tapDurationInMs = 1000;
static int get timeNow => DateTime.now().millisecondsSinceEpoch;
// Build method
GestureDetector(
onTap: () {
final now = timeNow;
final userExceededTapDuration = now - startTap > tapDurationInMs;
if (userExceededTapDuration) {
consecutiveTaps = 0;
startTap = now;
}
consecutiveTaps++;
if (consecutiveTaps == serialTaps) {
// widget.onTap();
}
},
);