Skip to content

[web] Let the browser handle Control+wheel zoom outside of macOS - #193362

Open
diegolopezrm wants to merge 6 commits into
flutter:masterfrom
diegolopezrm:web-ctrl-wheel-zoom
Open

diegolopezrm wants to merge 6 commits into
flutter:masterfrom
diegolopezrm:web-ctrl-wheel-zoom

Conversation

@diegolopezrm

Copy link
Copy Markdown
Contributor

The problem

On Windows and Linux, holding Control and turning the mouse wheel zooms the page in every browser. In a Flutter web app it does nothing: the page does not zoom and the app does not react either. Control+Plus and Control+Minus still work, as the reporter noted, which makes the wheel feel broken rather than deliberately disabled.

Issue: #129933

Root cause

Browsers report a trackpad pinch as a wheel event with ctrlKey set. Since flutter-team-archive/engine#36348 (Flutter 3.10) the web engine turns every wheel event with ctrlKey into a PointerSignalKind.scale and calls preventDefault() on it, so that InteractiveViewer and friends can zoom on pinch. That is right for a pinch. But a user holding the Control key and turning the wheel produces exactly the same event, and gets swallowed with it.

Before that change _handleWheelEvent had this:

if (event.getModifierState('Control') &&
    operatingSystem != OperatingSystem.macOs &&
    operatingSystem != OperatingSystem.iOs) {
  // Ignore Control+wheel events since the default handler
  // will change browser zoom level instead of scrolling.
  return;
}

The macOS path already knows how to tell the two apart: it asks the keyboard converter whether a physical Control key is held, because a pinch never involves a key press. Windows and Linux never got that check, which is how they lost browser zoom.

The fix

_isBrowserZoomShortcut applies the same physical-key check on every platform except macOS and iOS. When the user is holding Control and turning the wheel, _handleWheelEvent returns before forwarding the event to the framework and without cancelling it, so the browser zooms the page the way it does on any other site. A pinch still reaches the framework as a scale signal and is still cancelled, so nothing changes for InteractiveViewer.

macOS keeps its current behaviour on purpose: Control+wheel does not zoom the browser there, and the existing code already reports it as a plain scroll.

Platform Event Before After
Windows, Linux Trackpad pinch (ctrlKey, no key held) scale, cancelled scale, cancelled
Windows, Linux Control held + mouse wheel scale, cancelled, no zoom left to the browser, page zooms
macOS Control held + wheel scroll scroll
macOS Trackpad pinch scale, cancelled scale, cancelled

The one behavioural change is for an app that relied on getting a PointerScaleEvent from a mouse wheel with Control held on Windows or Linux. That never worked on macOS, did not work before 3.10, and it competes with the browser's own shortcut, so restoring the pre-3.10 behaviour looks like the right call to me. If the team would rather keep delivering the event and only skip preventDefault(), I am happy to change it.

Testing

  • New test leaves Control+wheel to the browser outside of macOS in pointer_binding_test.dart covers Windows and Linux: a pinch is delivered as a scale and cancelled, a wheel with Control physically held is neither delivered nor cancelled, and releasing the key brings scrolling back. felt test --gcs-prod --compiler dart2js test/engine/pointer_binding_test.dart passes (73 tests), and I checked that the new test fails without the fix.
  • The existing does choose scroll vs scale based on ctrlKey test still passes, so the macOS path is unchanged.
  • I do not have a Windows or Linux machine at hand, so I have not exercised this in a real browser yet. The change is small and the unit test pins the exact event flow, but if a reviewer can give it a spin with a mouse on Windows Chrome or Edge, that would be a welcome sanity check.

Pre-launch Checklist

Notes on the checklist: the only behaviour that changes is described in the table above, and it restores what the engine did before 3.10. There is no API change, so no Data Driven Fixes apply.

Browsers report trackpad pinch gestures as wheel events with ctrlKey set,
and since flutter-team-archive/engine#36348 every such event is turned into a scale
signal and cancelled. That also swallows the user holding the Control key
and turning the mouse wheel, which is the browser's zoom shortcut on
Windows, Linux and ChromeOS, so those pages could no longer be zoomed.

Tell the two apart the same way the macOS code path already does: a pinch
never involves a physical Control key press. When the key is held, leave
the event to the browser instead of forwarding or cancelling it.

Fixes flutter#129933
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team labels Sep 25, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the web engine's pointer binding to identify browser zoom shortcuts (Control+wheel) on platforms outside of macOS and iOS, allowing the browser to handle the zoom instead of forwarding the event to the framework. A test is added to verify this behavior. The review feedback suggests resetting ui.PlatformDispatcher.instance.onPointerDataPacket to null at the end of the new test to prevent test pollution.

@flutter-zl
flutter-zl requested a review from mdebbar October 7, 2026 18:25
@flutter-zl
flutter-zl self-requested a review October 21, 2026 18:11
@flutter-zl flutter-zl added the CICD Run CI/CD label Oct 23, 2026
flutter-zl
flutter-zl previously approved these changes Oct 23, 2026

@flutter-zl flutter-zl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified on Linux Chrome with before/after builds:
Before: https://flutter-demo-81-before.web.app (master engine): Control+wheel does not zoom, Flutter receives PointerScaleEvents.
After: https://flutter-demo-81-after.web.app (this PR's engine): Control+wheel zooms the page, Flutter receives nothing. Plain wheel still scrolls.
LGTM.

Optional nit: isMacOrIOS from browser_detection.dart could replace the two enum comparisons in _isBrowserZoomShortcut.

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 23, 2026
@mdebbar mdebbar added the CICD Run CI/CD label Oct 28, 2026
mdebbar
mdebbar previously approved these changes Oct 28, 2026
@mdebbar mdebbar added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 28, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 28, 2026
@auto-submit

auto-submit Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193362, because - The status or check suite ci.yaml validation has failed. Please fix the issues identified (or deflake) before re-applying this label.

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 28, 2026
@flutter-zl flutter-zl added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 28, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 28, 2026
@auto-submit

auto-submit Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193362, because - The status or check suite Google testing has failed. Please fix the issues identified (or deflake) before re-applying this label.

@flutter-zl flutter-zl added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels Oct 28, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 28, 2026
@auto-submit

auto-submit Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193362, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@flutter-zl flutter-zl added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 29, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 29, 2026
@auto-submit

auto-submit Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193362, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 29, 2026
@flutter-zl flutter-zl added CICD Run CI/CD autosubmit Merge PR when tree becomes green via auto submit App labels Oct 29, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 29, 2026
@auto-submit

auto-submit Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193362, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@flutter-zl

Copy link
Copy Markdown
Contributor

Hi @diegolopezrm , there is a test failure:

Analyzing flutter...

info - lib/web_ui/test/engine/pointer_binding_test.dart:1369:16 - Omit the type annotation on a local variable when the type is obvious. Try removing the type annotation. - omit_obvious_local_variable_types

1 issue found.

please check: https://logs.chromium.org/logs/flutter/buildbucket/cr-buildbucket/8666584305085662913/+/u/test:_analyze_dart_ui/stdout

@flutter-zl flutter-zl added the waiting for response The Flutter team cannot make further progress on this issue until the original reporter responds label Oct 29, 2026
@diegolopezrm
diegolopezrm dismissed stale reviews from mdebbar and flutter-zl via 0e270cc November 10, 2026 00:50
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Nov 10, 2026
@github-actions github-actions Bot removed the waiting for response The Flutter team cannot make further progress on this issue until the original reporter responds label Nov 10, 2026
@diegolopezrm

Copy link
Copy Markdown
Contributor Author

Thanks @flutter-zl, fixed in 0e270cc: the loop variable no longer repeats its type. Could you add CICD and autosubmit back when you get a chance?

@flutter-zl flutter-zl added the CICD Run CI/CD label Nov 10, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Nov 10, 2026
@flutter-zl flutter-zl added the CICD Run CI/CD label Nov 10, 2026
@flutter-zl
flutter-zl self-requested a review November 10, 2026 18:15
@flutter-zl flutter-zl added the autosubmit Merge PR when tree becomes green via auto submit App label Nov 10, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Nov 10, 2026
@auto-submit

auto-submit Bot commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/193362, because This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.

  • Merge guidelines: A PR needs at least one approved review if the author is already part of flutter-hackers or two member reviews if the author is not a member of flutter-hackers before re-applying the autosubmit label. Reviewers: If you left a comment approving, please use the "approve" review action instead.

@diegolopezrm

Copy link
Copy Markdown
Contributor Author

Thanks @flutter-zl! The bot says it needs a second approval since I'm not in flutter-hackers. @mdebbar, would you have a minute to take a look?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants