Skip to content

[web] WebKit (iOS and macOS Safari) shifts the whole semantics layer off-screen when semantics is enabled, breaking VoiceOver and TextField input #190483

Closed

Description

@flutter-zl

Steps to reproduce

  1. Create a Flutter web app that calls SemanticsBinding.instance.ensureSemantics(). A real screen reader enables semantics on its own; this forces it for a deterministic repro.
  2. Add a few controls: two buttons, a checkbox, and a TextField (see the code sample).
  3. flutter build web, deploy, and open in a WebKit browser: iOS Safari, or macOS/iPadOS Safari. Turn on VoiceOver.

Demo app:
https://flutter-demo-57-before.web.app/

Expected results

  • VoiceOver focus lands on each control at its visible position; the user can navigate and activate the buttons, the checkbox, and the field.
  • Tapping the TextField opens the on-screen keyboard.

Actual results

Every interactive semantic element is placed about one viewport to the right, off-screen.

  • VoiceOver is broken for the whole app. The DOM node for each control is off the right edge, so VoiceOver focus rectangles land off-screen. A screen-reader user cannot reliably focus or activate any control.
  • The TextField cannot be typed into. The visible field is painted correctly by the canvas, but the invisible <input> is off-screen, so a tap misses it and the keyboard never opens. This is the symptom reported in [Web] Unable to type in TextField when ensureSemantics is enabled for accessibility #129324.

Confirmed cross-browser

The same deployed build, opened in different browsers. The browser engine is the only variable:

Browser Engine Interactive semantic nodes off-screen
iOS Safari WebKit 4 to 5 of 5
macOS Safari WebKit 4 of 5
macOS Chrome Blink 0 of 5
desktop Chrome Blink 0 of 5
Android Chrome / Firefox Blink / Gecko 0

Per-node measurement on an iPhone at 430px width and devicePixelRatio 3: the text field <input> reports getBoundingClientRect().left = 446, off the 430-wide screen; the same build in Chrome reports 16. The offset is about one viewport width.

Root cause

<flt-semantics-host> is position: absolute with no left/top, so it falls back to its CSS static position. WebKit resolves that static position by flowing the host past its in-flow sibling <flt-glass-pane>, which is about one viewport wide, shifting the entire semantics subtree right by about one viewport. Blink and Gecko resolve the same static position to 0, so they are unaffected.

Suggested fix: anchor the host explicitly by setting left: 0; top: 0 in StyleManager.styleSemanticsHost (lib/web_ui/lib/src/engine/view_embedder/style_manager.dart). Blink and Gecko already resolve to 0, so it is a no-op there.

Related issues

Code sample

main.dart
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(const MyApp());
  // The bug requires semantics to be enabled. A real screen reader turns this
  // on automatically; ensureSemantics() forces it for a deterministic repro.
  SemanticsBinding.instance.ensureSemantics();
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: HomePage());
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _checked = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            ElevatedButton(onPressed: () {}, child: const Text('Alpha')),
            ElevatedButton(onPressed: () {}, child: const Text('Beta')),
            Row(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                Checkbox(
                  value: _checked,
                  onChanged: (bool? v) => setState(() => _checked = v ?? false),
                ),
                const Text('Check me'),
              ],
            ),
            const Padding(
              padding: EdgeInsets.all(16),
              child: TextField(),
            ),
          ],
        ),
      ),
    );
  }
}

Environment

flutter --version
Flutter 3.47.0-1.0.pre-134 • channel [user-branch] • [email protected]:flutter/flutter.git
Framework • revision 891cee207b (2026-07-20)
Engine • hash 82480b3ba2ddf43762fb9d24757bba63db165a03 (revision 9b105336e8) (2026-07-20)
Tools • Dart 3.14.0 (build 3.14.0-41.0.dev) • DevTools 2.60.0

cc @flutter-zl

Activity

  1. added
    a: text inputEntering text in a text field or keyboard related problems
    platform-iosiOS applications specifically
    engineflutter/engine related. See also e: labels.
    a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)
    platform-webWeb applications specifically
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    on Aug 3, 2026
  2. self-assigned this
    on Aug 3, 2026
  3. added
    P1High-priority issues at the top of the work list
    triaged-webTriaged by Web platform team
    on Aug 3, 2026
  4. flutter-triage-bot commented on Aug 4, 2026

    @flutter-triage-bot

    The triaged-web label is irrelevant if there is no team-web label or fyi-web label.

  5. changed the title [-][web] iOS Safari (WebKit) shifts the whole semantics layer off-screen when semantics is enabled, breaking VoiceOver and TextField input[/-] [+][web] WebKit (iOS and macOS Safari) shifts the whole semantics layer off-screen when semantics is enabled, breaking VoiceOver and TextField input[/+] on Aug 4, 2026
  6. added a commit that references this issue on Sep 1, 2026
    32e7fff
  7. github-actions commented on Sep 15, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  8. locked as resolved and limited conversation to collaborators on Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)a: text inputEntering text in a text field or keyboard related problemsbrowser: chrome-iosonly manifests in Chrome on iOSbrowser: safari-iosonly manifests in Safari on iOSengineflutter/engine related. See also e: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-iosiOS applications specificallyplatform-webWeb applications specificallyteam-webOwned by Web platform teamtriaged-webTriaged by Web platform team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions