feat: flutter增加长按功能

This commit is contained in:
2026-07-14 14:25:58 +08:00
parent 2fdf3c3b9e
commit 37fccec6af
3 changed files with 64 additions and 0 deletions

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
@@ -10,6 +11,7 @@ import 'package:flutter/services.dart';
///
/// - 短时间内位移很小的抬起 -> 单击TOUCH
/// - 否则 -> 滑动SWIPE
/// - 按住不动超过阈值 -> 长按LONG_PRESS
/// 同时捕获物理按键事件KEY用于外接键盘 / 遥控器场景。
///
/// 该控件完全基于 Flutter 框架实现,无需任何原生代码,可运行于
@@ -24,12 +26,14 @@ class RemoteTouchView extends StatefulWidget {
int durationMs,
) onSwipe;
final void Function(int keyCode, int action) onKey;
final void Function(double x, double y) onLongPress;
const RemoteTouchView({
super.key,
required this.onTouch,
required this.onSwipe,
required this.onKey,
required this.onLongPress,
});
@override
@@ -38,7 +42,16 @@ class RemoteTouchView extends StatefulWidget {
class _RemoteTouchViewState extends State<RemoteTouchView> {
Offset? _startPosition;
Offset? _currentPosition;
DateTime? _startTime;
bool _isLongPressed = false;
Timer? _longPressTimer;
/// 长按触发阈值,对应 Android GestureDetector 默认的 LONG_PRESS_TIMEOUT400ms
static const _longPressTimeout = Duration(milliseconds: 400);
/// 手指移动超过该像素阈值则视为滚动,取消长按(对应 Android touch slop
static const _touchSlop = 10.0;
double _toRelativeX(Offset local, Size size) =>
(local.dx / size.width).clamp(0.0, 1.0);
@@ -46,6 +59,17 @@ class _RemoteTouchViewState extends State<RemoteTouchView> {
double _toRelativeY(Offset local, Size size) =>
(local.dy / size.height).clamp(0.0, 1.0);
void _cancelLongPress() {
_longPressTimer?.cancel();
_longPressTimer = null;
}
@override
void dispose() {
_cancelLongPress();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Focus(
@@ -64,10 +88,39 @@ class _RemoteTouchViewState extends State<RemoteTouchView> {
child: Listener(
behavior: HitTestBehavior.opaque,
onPointerDown: (event) {
_cancelLongPress();
_isLongPressed = false;
_startPosition = event.localPosition;
_currentPosition = event.localPosition;
_startTime = DateTime.now();
_longPressTimer = Timer(_longPressTimeout, () {
final size = context.size;
if (_currentPosition == null || size == null) return;
_isLongPressed = true;
final x = _toRelativeX(_currentPosition!, size);
final y = _toRelativeY(_currentPosition!, size);
widget.onLongPress(x, y);
});
},
onPointerMove: (event) {
_currentPosition = event.localPosition;
if (_longPressTimer != null && _startPosition != null) {
final dx = (event.localPosition.dx - _startPosition!.dx).abs();
final dy = (event.localPosition.dy - _startPosition!.dy).abs();
if (dx > _touchSlop || dy > _touchSlop) {
_cancelLongPress();
}
}
},
onPointerUp: (event) {
_cancelLongPress();
// 长按已触发则不再派发单击 / 滑动事件(对应 Android 端 isLongPressed 逻辑)。
if (_isLongPressed) {
_startPosition = null;
_currentPosition = null;
_startTime = null;
return;
}
final size = context.size;
if (_startPosition == null || size == null || _startTime == null) {
return;
@@ -87,6 +140,7 @@ class _RemoteTouchViewState extends State<RemoteTouchView> {
widget.onSwipe(startX, startY, endX, endY, max(duration, 1));
}
_startPosition = null;
_currentPosition = null;
_startTime = null;
},
child: const ColoredBox(