build: 控制端功能移植到flutter版本
This commit is contained in:
99
webrtc_controller_flutter/lib/widgets/remote_touch_view.dart
Normal file
99
webrtc_controller_flutter/lib/widgets/remote_touch_view.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// 远端触摸控制层(对应 Android 端 RemoteTouchView)。
|
||||
///
|
||||
/// 捕获指针(触摸/鼠标)事件,将其转换为相对于控件区域的百分比坐标
|
||||
/// (0.0 ~ 1.0),以便在不同分辨率的设备间换算。
|
||||
///
|
||||
/// - 短时间内位移很小的抬起 -> 单击(TOUCH)
|
||||
/// - 否则 -> 滑动(SWIPE)
|
||||
/// 同时捕获物理按键事件(KEY),用于外接键盘 / 遥控器场景。
|
||||
///
|
||||
/// 该控件完全基于 Flutter 框架实现,无需任何原生代码,可运行于
|
||||
/// Android、iOS 等平台。
|
||||
class RemoteTouchView extends StatefulWidget {
|
||||
final void Function(double x, double y) onTouch;
|
||||
final void Function(
|
||||
double x1,
|
||||
double y1,
|
||||
double x2,
|
||||
double y2,
|
||||
int durationMs,
|
||||
) onSwipe;
|
||||
final void Function(int keyCode, int action) onKey;
|
||||
|
||||
const RemoteTouchView({
|
||||
super.key,
|
||||
required this.onTouch,
|
||||
required this.onSwipe,
|
||||
required this.onKey,
|
||||
});
|
||||
|
||||
@override
|
||||
State<RemoteTouchView> createState() => _RemoteTouchViewState();
|
||||
}
|
||||
|
||||
class _RemoteTouchViewState extends State<RemoteTouchView> {
|
||||
Offset? _startPosition;
|
||||
DateTime? _startTime;
|
||||
|
||||
double _toRelativeX(Offset local, Size size) =>
|
||||
(local.dx / size.width).clamp(0.0, 1.0);
|
||||
|
||||
double _toRelativeY(Offset local, Size size) =>
|
||||
(local.dy / size.height).clamp(0.0, 1.0);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Focus(
|
||||
autofocus: true,
|
||||
// 捕获物理按键(外接键盘 / 遥控器等)。
|
||||
onKeyEvent: (node, event) {
|
||||
if (event is KeyDownEvent) {
|
||||
widget.onKey(event.logicalKey.keyId, 0);
|
||||
return KeyEventResult.handled;
|
||||
} else if (event is KeyUpEvent) {
|
||||
widget.onKey(event.logicalKey.keyId, 1);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
return KeyEventResult.ignored;
|
||||
},
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onPointerDown: (event) {
|
||||
_startPosition = event.localPosition;
|
||||
_startTime = DateTime.now();
|
||||
},
|
||||
onPointerUp: (event) {
|
||||
final size = context.size;
|
||||
if (_startPosition == null || size == null || _startTime == null) {
|
||||
return;
|
||||
}
|
||||
final startX = _toRelativeX(_startPosition!, size);
|
||||
final startY = _toRelativeY(_startPosition!, size);
|
||||
final endX = _toRelativeX(event.localPosition, size);
|
||||
final endY = _toRelativeY(event.localPosition, size);
|
||||
final duration = DateTime.now().difference(_startTime!).inMilliseconds;
|
||||
|
||||
final dx = (endX - startX).abs();
|
||||
final dy = (endY - startY).abs();
|
||||
|
||||
if (duration < 200 && dx < 0.02 && dy < 0.02) {
|
||||
widget.onTouch(startX, startY);
|
||||
} else {
|
||||
widget.onSwipe(startX, startY, endX, endY, max(duration, 1));
|
||||
}
|
||||
_startPosition = null;
|
||||
_startTime = null;
|
||||
},
|
||||
child: const ColoredBox(
|
||||
color: Colors.transparent,
|
||||
child: SizedBox.expand(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user