diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 427072677..6fdffd796 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -1495,6 +1495,16 @@ class InputModel { return false; } + /// iOS may emit a synthesized touch event after a real mouse click. + /// This helper ignores touch-down events that arrive shortly after a mouse down, + /// even when the position is far (e.g., near the top edge). + bool _shouldIgnoreTouchAfterMouse(int nowMs) { + if (!isIOS) return false; + const int kTouchAfterMouseWindowMs = 700; + final dt = nowMs - _lastMouseDownTimeMs; + return dt >= 0 && dt < kTouchAfterMouseWindowMs; + } + void onPointDownImage(PointerDownEvent e) { debugPrint("onPointDownImage ${e.kind}"); _stopFling = true; @@ -1507,6 +1517,9 @@ class InputModel { // Track mouse down events for duplicate detection on iOS. final nowMs = DateTime.now().millisecondsSinceEpoch; if (e.kind == ui.PointerDeviceKind.mouse) { + if (!isPhysicalMouse.value) { + isPhysicalMouse.value = true; + } _lastMouseDownTimeMs = nowMs; _lastMouseDownPos = e.position; } @@ -1516,6 +1529,10 @@ class InputModel { } if (e.kind != ui.PointerDeviceKind.mouse) { + // Ignore duplicate touch events that follow a recent mouse click (iOS Magic Mouse issue). + if (isPhysicalMouse.value && _shouldIgnoreTouchAfterMouse(nowMs)) { + return; + } if (isPhysicalMouse.value) { isPhysicalMouse.value = false; }