Skip to content

[iOS] [a11y] Unable to fully navigate page using talkback when using nested scroll views #113615

@ChristianEdwardPadilla

Description

@ChristianEdwardPadilla

Internal: b/211049563

Steps to Reproduce

  1. Execute flutter run on the code sample
  2. Enable iOS VoiceOver
  3. Use VoiceOver to change focus from the top app bar to the scroll view elements.
  4. Use VoiceOver to focus successive elements until the view port moves down the scroll view.
  5. Use VoiceOver to focus previous elements, causing the view port to scroll back up.
  6. Observe that there are elements at the top of the page that cannot be focused.

Expected:
VoiceOver should be able to access all elements on the screen. It should be able to tab through to the bottom and then tab back up to the top of the scroll view.

Actual:
VoiceOver is unable to return to elements at the initial top of the scroll view.

Code Sample:

import 'package:mobile.flutter.google_material.material2/google_material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: LongColumnContainer(),
    );
  }
}

class LongColumnContainer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('A11y Repro App'),
      ),
      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            SizedBox(
              height: 16,
            ),
            Icon(
              Icons.favorite,
              size: 14,
            ),
            SizedBox(
              height: 16,
            ),
            Text(
              'Page title',
              textAlign: TextAlign.center,
            ),
            SizedBox(
              height: 16,
            ),
            Text(
              'Page subtitle',
              textAlign: TextAlign.center,
            ),
            SizedBox(
              height: 20,
            ),
            Text(
              'Title1',
            ),
            cardCarousel(),
            SizedBox(
              height: 20,
            ),
            Text(
              'Title2',
            ),
            SizedBox(height: 16),
            Text(
              'subtitle2',
            ),
            SizedBox(height: 16),
            cardCarousel(),
            SizedBox(
              height: 20,
            ),
            Icon(
              Icons.favorite,
              size: 100,
            ),
            SizedBox(
              height: 20,
            ),
            Text(
              'end of carousel',
              textAlign: TextAlign.center,
            ),
            Icon(
              Icons.favorite,
              size: 100,
            ),
            SizedBox(
              height: 20,
            ),
            Text(
              'end of page',
              textAlign: TextAlign.center,
            ),
            SizedBox(
              height: 20,
            ),
          ],
        ),
      ),
      drawer: Drawer(child: Text('drawer tile')),
    );
  }
}

Widget cardCarousel() {
  return SingleChildScrollView(
    scrollDirection: Axis.horizontal,
    child: IntrinsicHeight(
      child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 14),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            card(Icons.favorite, 'calendar', 'some small text'),
            SizedBox(width: 10),
          ],
        ),
      ),
    ),
  );
}

Widget card(IconData iconData, String title, String body) {
  return Semantics(
    container: true,
    explicitChildNodes: true,
    child: Container(
      width: 304,
      foregroundDecoration: BoxDecoration(
          borderRadius: BorderRadius.circular(8),
          border: Border.all(color: Colors.red)),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(8),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            SizedBox(
              height: 210,
              child: Icon(
                iconData,
                size: 200,
              ),
            ),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Text(
                    title,
                  ),
                  SizedBox(
                    height: 8,
                  ),
                  Text(
                    body,
                  ),
                ],
              ),
            ),
            Container(
              padding: const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 24),
              alignment: Alignment.centerLeft,
              child: Text('View details'),
            ),
          ],
        ),
      ),
    ),
  );
}

flutter doctor:

[✓] Flutter (Channel google3, on Debian GNU/Linux rodete 5.18.16-1rodete1-amd64, locale
    en_US.UTF-8)
    • Framework revision fe9b598c12 (9 days ago), 2022-10-09T00:00:00.000
    • Engine revision ac1fec6367
    • Dart version 2f0c186d3b

[✓] Android toolchain - develop for Android devices (Android SDK version Stable)
    • Android SDK at google3
    • Platform Stable, build-tools Stable
    • Java binary at: /opt/android-studio-with-blaze-2021.3/jre/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] Android Studio (version 4.2)
    • Android Studio at /opt/android-studio-with-blaze-4.2
    • Flutter plugin version 58.0.1
    • Dart plugin version 202.8443
    • Java version OpenJDK Runtime Environment (build 11.0.8+0-b944-P17168821)

[✓] Android Studio (version 2020.3)
    • Android Studio at /opt/android-studio-with-blaze-2020.3
    • Flutter plugin version 61.2.2
    • Dart plugin version 203.8292
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7249189)

[✓] Android Studio (version 2021.3)
    • Android Studio at /opt/android-studio-with-blaze-2021.3
    • Flutter plugin version 69.0.3
    • Dart plugin version 213.7371
    • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)

[✓] IntelliJ IDEA Community Edition (version 2019.2)
    • IntelliJ at /opt/intellij-ce-stable

[✓] VS Code (version 1.69.1)
    • VS Code at /usr/share/code
    • Flutter extension version 3.15.1

[✓] Connected device (1 available)
    • Linux (desktop) • linux • linux-x64 • Debian GNU/Linux rodete 5.18.16-1rodete1-amd64

[✓] Google3 (on linux)
    • KVM enabled

• No issues found!

repro video attached:

a11y_smaller_prototype_backward_sequence_issue.mp4

Metadata

Metadata

Assignees

Labels

P0Critical issues such as a build break or regressiona: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)customer: money (g3)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions