Repository navigation
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work lista: desktopRunning on desktopRunning on desktopengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.platform-windowsBuilding on or for Windows specificallyBuilding on or for Windows specificallyteam-windowsOwned by the Windows platform teamOwned by the Windows platform teamtriaged-windowsTriaged by the Windows platform teamTriaged by the Windows platform team
Description
Steps to reproduce
Create a gesture detector and add a callback on ScaleUpdate to inspect the focal point of the scale gesture and the number of pointer that it has.
GestureDetector(
onScaleUpdate: (d) {
print("Focal Point: ${d.focalPoint} Scale: ${d.scale}, Pointer Count: ${d.pointerCount}");
},
child: Container(
color: Colors.red,
child: Center(),
),
);
Then try to pinch and zoom first with a trackpad and then with a touch device.
Expected results
When Using The Touch device, the focalPoint stays correctly around the center of the gesture (give or take a take a few pixel due to human error) and the pointerCount is 2 as expected.
Actual results
When performing the same gesture on the trackpad (I am using the same device, a windows laptop with a touch screen), the focal point changes a lot (it appears to coincide with the location of one pointer) and the pointerCount is 1 despite using two fingers to perform the action.
Code sample
Code sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
onScaleUpdate: (d) {
print("Focal Point: ${d.focalPoint} Scale: ${d.scale}, Pointer Count: ${d.pointerCount}");
},
child: Container(
color: Colors.red,
child: const Center(),
),
);
}
}Flutter Doctor output
Doctor output
[√] Flutter (Channel stable, 3.13.6, on Microsoft Windows [Version 10.0.22621.2283], locale en-US)
[√] Windows Version (Installed version of Windows is version 10 or higher)
[X] Android toolchain - develop for Android devices
X cmdline-tools component is missing
Run `path/to/sdkmanager --install "cmdline-tools;latest"`
See https://developer.android.com/studio/command-line for more details.
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.4.0)
[!] Android Studio (not installed)
[√] VS Code (version 1.83.0)
[√] Connected device (3 available)
[√] Network resources
! Doctor found issues in 2 categories.Reacted by KevinBrendel and Luka Stillingfleet
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work lista: desktopRunning on desktopRunning on desktopengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.platform-windowsBuilding on or for Windows specificallyBuilding on or for Windows specificallyteam-windowsOwned by the Windows platform teamOwned by the Windows platform teamtriaged-windowsTriaged by the Windows platform teamTriaged by the Windows platform team
Activity
moffatman commentedon Oct 5, 2023
Windows doesn't say how many pointers, I set it to 1. Maybe a mistake, it should be 2? Also the focal point is just passed on from Windows. Depending on your driver, it may not move at all. But I didn't hear of it moving strangely.
FabrizioG202 commentedon Oct 5, 2023
The focal point on my machine appears to be one of the fingers, instead of somewhere in the middle of them. When I try the same gesture on other applications, like figma, the zoom seems to work correctly (as it does using touch in flutter). Is this normal behaviour?
Basically, doing the same gesture (on the trackpad and touch screen) has two different effects due to the difference in focal point between the two.
moffatman commentedon Oct 5, 2023
I don't know what's normal here, in Windows, the driver has a lot to do with it. We don't receive the exact finger positions from Windows, it figures out the zoom and focal point for us. And to be honest, there are many drivers that do different things depending on the program, treating Google Chrome better for example. So if I can rephrase it, when you're zooming with trackpad in flutter, moving finger A will change both the scale and focalPoint, but moving finger B will just change the focal point? What's the device mdoel?
FabrizioG202 commentedon Oct 5, 2023
I tested it with the following code:
And I got this result:
https://github.com/flutter/flutter/assets/54724353/b864c53c-588c-494a-9fcd-9ec921f9a428
In the first part I am doing a pan-and-scale gesture using the touchscreen and it works as espected. While in the second part, the focalPoint moves on a straight line, which is unrelated to the position of the pointers or the movement.
It does not coincide with the position of one finger, so I was wrong, but it seems to stay on the topLeft to bottomRight line.
I am using a Microsoft Surface 7 pro.
moffatman commentedon Oct 5, 2023
OK, so this is a bug in my file here https://github.com/flutter/engine/blob/main/shell/platform/windows/direct_manipulation.cc within
ConvertToGestureDatafunction.transform[4]andtransform[5]will change as you zoom in, even if focal point doesn't change, because they represent the top left of the viewport. Instead, thepan_xandpan_yshould be calculated based on the center ofviewportrectangle. I don't think it is a very big change, but I don't have any time or equipment to do this myself any time soon.[-]Scale Gesture is not recognized correctly using trackpad.[/-][+]Windows trackpad pinch gesture has wrong focal point[/+]gspencergoog commentedon Oct 12, 2023
cc @loic-sharma
3 remaining items
flutter-triage-bot commentedon Jun 6, 2024
The
triaged-desktoplabel is irrelevant if there is noteam-desktoplabel orfyi-desktoplabel.espresso3389 commentedon May 14, 2025
@Dusk-afk your link is dead now. Could you explain what did you do if possible?
Dusk-afk commentedon May 14, 2025
@espresso3389 this is the commit I had linked earlier.
JaffaKetchup commentedon Aug 30, 2025
Hey @moffatman, apologies for the ping :D
I'd love to investigate fixing this at the source in the code you mentioned, as it's a source of issue for flutter_map.
From what I understand,
ConvertToGestureDatawould need to accept another argument with theIDirectManipulationViewport* viewport, then calculate its center, potentially viaGetViewportRect(as appears on these lines)?Could you expand on what you mean by...
Is this just as simple as
viewport.width / 2&height / 2?I don't have any experience with working on the engine, or indeed with C/C++, but I do have a Windows laptop with trackpad demonstrating the issue, and would like to have a go. Any help would be hugely appriciated!
moffatman commentedon Aug 30, 2025
I think you might have to scale the rect as scale changes. But this is rather forgotten stuff now for me. Also could check the other values in the transform matrix. It might be useful.
JaffaKetchup commentedon Aug 31, 2025
Hey thanks @moffatman. Just trying to get my head around this now. Wouldn't it be necessary to know the point at which the gesture starts in screen space, not the viewport center? I'm really struggling to understand how to resolve the problem, but I understand what the problem is I think.
The gesture starts at (0, 0) in gesture space. The viewport origin (0, 0) is at the top left. As the scale changes, the pan_x and pan_y are scaled based on that same scale, but to the wrong origin?
moffatman commentedon Aug 31, 2025
The windows direct manipulation thing does not really give you gesture values. You have to fake a viewport, see what it is after the gesture is applied, and reverse out what the gesture translations were.
JaffaKetchup commentedon Sep 1, 2025
I'm looking at Chromium's Direct Manipulation API code at https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/direct_manipulation_event_handler_win.cc.
A comment in the code says:
This sounds like the problem we're experiencing. Does this suggest it's not possible to resolve this issue? It looks like Chromium also takes the route of only allowing one to occur in any one gesture:
moffatman commentedon Sep 1, 2025
No it is possible to resolve it. we just need to calculate the center of the transformed rectangle as the focalPoint.
They are talking about gesture disambiguation, which for flutter is done at the framework level, we need to keep all the raw data present in the engine.
jacksonrl commentedon Sep 28, 2025
Not a fix, but if you are using InteractiveViewer or similar you can just change it to use the initial focal point from onScaleStart on trackpads, which has an accurate focal point. This means you can't pan and scale with a trackpad at the same time but I don't think many apps use that feature anyway.
Here's how
note: This may also have to be special-cased to Windows trackpads specifically, not sure. It relies on the fact that pans are given scale = 1.0 exactly on Windows trackpads. You can get it to work with any pointerDevice but it would need more work.
add these new members to
_InteractiveViewerState:add to
onScaleStart:in onScaleUpdate, in
case _GestureType.scale:JaffaKetchup commentedon Dec 21, 2025
Unfortunately the time I had available to fix this was short and I didn't have enough experience to follow Callum's clues (thanks anyway :)). I'm open to share what I tried if anyone else wants to try to work on this, but it won't be helpful.
Unfortunately any sort of workaround where the result is only being able to use pan or scale (not both) isn't the best for something like a map.