From 3d478c49353d81596c2be97c5f617cb1d231520c Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 19 Jun 2026 20:50:56 +0800 Subject: [PATCH] fix(ios): mouse mismatch (#15339) Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 39 ++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index fc3b8903c..a701e6e53 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -1307,7 +1307,8 @@ class InputModel { } if (isPhysicalMouse.value) { if (!_relativeMouse.handleRelativeMouseMove(e.localPosition)) { - handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position, + final canvasPosition = _pointerPositionForRemoteCanvas(e); + handleMouse(_getMouseEvent(e, _kMouseEventMove), canvasPosition, edgeScroll: useEdgeScroll); } } @@ -1548,7 +1549,8 @@ class InputModel { _relativeMouse .sendRelativeMouseButton(_getMouseEvent(e, _kMouseEventDown)); } else { - handleMouse(_getMouseEvent(e, _kMouseEventDown), e.position); + final canvasPosition = _pointerPositionForRemoteCanvas(e); + handleMouse(_getMouseEvent(e, _kMouseEventDown), canvasPosition); } } } @@ -1570,7 +1572,8 @@ class InputModel { _relativeMouse .sendRelativeMouseButton(_getMouseEvent(e, _kMouseEventUp)); } else { - handleMouse(_getMouseEvent(e, _kMouseEventUp), e.position); + final canvasPosition = _pointerPositionForRemoteCanvas(e); + handleMouse(_getMouseEvent(e, _kMouseEventUp), canvasPosition); } } } @@ -1592,12 +1595,40 @@ class InputModel { } if (isPhysicalMouse.value) { if (!_relativeMouse.handleRelativeMouseMove(e.localPosition)) { - handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position, + final canvasPosition = _pointerPositionForRemoteCanvas(e); + handleMouse(_getMouseEvent(e, _kMouseEventMove), canvasPosition, edgeScroll: useEdgeScroll); } } } + /// Convert pointer coordinates into the visible remote canvas space. + /// + /// On mobile, the remote page body is wrapped in `SafeArea`, but the pointer + /// listener that feeds these events sits outside that subtree. As a result, + /// `event.localPosition` still includes the top/left safe-area inset. + /// + /// When the keyboard-visible path shows `KeyHelpTools`, the remote canvas is + /// also shifted downward by `CanvasModel.getAdjustY()`. The downstream mouse + /// mapping logic expects coordinates relative to the visible canvas area, so + /// we subtract both the mobile safe-area padding and the current canvas + /// adjustment before passing the position into mouse mapping. + /// + /// Desktop and web desktop continue to use the global position directly + /// because their pointer mapping is window-based. + Offset _pointerPositionForRemoteCanvas(PointerEvent event) { + if (isDesktop || isWebDesktop) { + return event.position; + } + final mediaData = MediaQueryData.fromView( + WidgetsBinding.instance.platformDispatcher.views.first); + final adjustY = parent.target?.canvasModel.getAdjustY() ?? 0.0; + return Offset( + event.localPosition.dx - mediaData.padding.left, + event.localPosition.dy - mediaData.padding.top - adjustY, + ); + } + static Future fillRemoteCoordsAndGetCurFrame( List remoteWindowCoords) async { final coords =