Skip to content

[web:a11y] DOM focus shift causes unexpected scroll  #130950

@yjbanov

Description

@yjbanov

Is there an existing issue for this?

Steps to reproduce

  1. flutter run the repro example below.
  2. Enable the screen reader (e.g. VoiceOver or TalkBack).
  3. Move the a11y focus onto the checkbox and double-tap to move the input focus on it as well.
  4. Traverse down the list using the screen reader until it attempts to scroll.

Expected results

The list should scroll.

Actual results

The list fails to scroll.

Analysis: when the list scrolls down the checkbox remains focused, and the framework sends an update with SemanticsFlag.isFocused set on the checkbox. The web engine calls element.focus() on the checkbox and the browser (trying to be nice no doubt) scrolls back to the checkbox, which is not what user wanted.

The first step in the fix is to stop repeatedly calling focus() on the same element. However, that alone does not fix the issue. A follow-up issue is that as you keep scrolling the checkbox will be removed from the DOM (due to lazy-rendering). On Android + TalkBack removing a currently focused DOM node causes TalkBack to focus on the <body> element since nothing else is focusable on the page.

The second step to fix this is: when a focused element is removed by the framework, the engine does not immediately remove it from the DOM. Instead, it leaves a "tombstone" for that element and keeps it focused until one of:

Code sample

Code sample
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(const MyApp());
  if (kIsWeb) {
    SemanticsBinding.instance.ensureSemantics();
  }
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Accessibility tester app',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Accessibility tester app'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: CheckboxTest(),
    );
  }
}

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

  @override
  State<CheckboxTest> createState() => _CheckboxTestState();
}

class _CheckboxTestState extends State<CheckboxTest> {
  bool _value1 = false;

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget>[
        Row(
          children: [
            Checkbox(
              value: _value1,
              onChanged: (bool? value) {
                setState(() {
                  _value1 = value!;
                });
              },
            ),
            const Text('A checkbox'),
          ],
        ),
        ...List<Widget>.generate(100, (int i) {
          return Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text('$i', style: const TextStyle(fontSize: 24)),
          );
        }),
      ],
    );
  }
}

Screenshots or Video

No response

Logs

No response

Flutter Doctor output

Doctor output
[!] Flutter (Channel main, 3.13.0-3.0.pre.19, on macOS 13.4.1 22F82 darwin-arm64, locale en)
    • Flutter version 3.13.0-3.0.pre.19 on channel main at /Users/yjbanov/code/flutter/flutter
    ! Upstream repository [email protected]:yjbanov/flutter.git is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to [email protected]:yjbanov/flutter.git to dismiss this error.
    • Framework revision dd0b6e35f3 (6 days ago), 2023-07-12 15:27:37 -0700
    • Engine revision 16e2ab7e98
    • Dart version 3.1.0 (build 3.1.0-300.0.dev)
    • DevTools version 2.25.0
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to
      perform update checks and upgrades.

[!] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    • Android SDK at /Users/yjbanov/Library/Android/sdk
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.

[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14C18
    • CocoaPods version 1.12.0

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2022.2)
    • Android Studio at /Applications/Android Studio with Blaze.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)

[✓] VS Code (version 1.79.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.68.0

[✓] Connected device (3 available)
    • Pixel 6 Pro (mobile) • 1A051FDEE00ADZ • android-arm64  • Android 13 (API 33)
    • macOS (desktop)      • macos          • darwin-arm64   • macOS 13.4.1 22F82 darwin-arm64
    • Chrome (web)         • chrome         • web-javascript • Google Chrome 114.0.5735.198

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 2 categories.

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work lista: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)engineflutter/engine related. See also e: labels.platform-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