feat: flutter增加长按功能
This commit is contained in:
@@ -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_TIMEOUT(400ms)。
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user