Skip to content

implicit a11y scrolling broken with pinned & floating AppBar #35488

Description

@goderbauer
  1. Run the following code:
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);


  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            title: Text('Hello'),
            pinned: true,
            floating: true,
            expandedHeight: 200.0,
          ),
          SliverFixedExtentList(
            itemExtent: 100.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext c, int index) {
                return Container(
                  height: 100.0,
                  color: index % 2 == 0 ? Colors.red : Colors.yellow,
                  child: Text('Tile $index'),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
  1. Scroll down a bit
  2. turn on talkback
  3. focus on an item in the list
  4. swipe left a couple of times

Expected behavior:
When the a11y focus reaches an item that's partially visible, it should be brought fully on screen. When moving a11y focus to the next off-screen item, the list should scroll and a11y focus should move to the next item

Observed behavior:
Partially visible items are not brought on screen. When moving focus to an offscreen item it moves to the AppBar and the list scrolls all the way to the top.

It looks like the semantics tree is already incorrect:

I/flutter ( 8037): SemanticsNode#0
I/flutter ( 8037):  │ Rect.fromLTRB(0.0, 0.0, 1440.0, 2392.0)
I/flutter ( 8037):  │
I/flutter ( 8037):  └─SemanticsNode#1
I/flutter ( 8037):    │ Rect.fromLTRB(0.0, 0.0, 411.4, 683.4) scaled by 3.5x
I/flutter ( 8037):    │ textDirection: ltr
I/flutter ( 8037):    │
I/flutter ( 8037):    └─SemanticsNode#2
I/flutter ( 8037):      │ Rect.fromLTRB(0.0, 0.0, 411.4, 683.4)
I/flutter ( 8037):      │ flags: scopesRoute
I/flutter ( 8037):      │
I/flutter ( 8037):      ├─SemanticsNode#3
I/flutter ( 8037):      │ │ Rect.fromLTRB(0.0, 0.0, 411.4, 683.4)
I/flutter ( 8037):      │ │
I/flutter ( 8037):      │ └─SemanticsNode#14
I/flutter ( 8037):      │   │ Rect.fromLTRB(0.0, 0.0, 411.4, 683.4)
I/flutter ( 8037):      │   │ actions: scrollDown, scrollUp
I/flutter ( 8037):      │   │ flags: hasImplicitScrolling
I/flutter ( 8037):      │   │ scrollIndex: 23
I/flutter ( 8037):      │   │ scrollExtentMin: 0.0
I/flutter ( 8037):      │   │ scrollPosition: 2500.0
I/flutter ( 8037):      │   │ scrollExtentMax: Infinity
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#137
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, -250.0, 411.4, -176.0)
I/flutter ( 8037):      │   │   flags: isHidden
I/flutter ( 8037):      │   │   HIDDEN
I/flutter ( 8037):      │   │   label: "Tile 20"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#138
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, -176.0, 411.4, -76.0)
I/flutter ( 8037):      │   │   flags: isHidden
I/flutter ( 8037):      │   │   HIDDEN
I/flutter ( 8037):      │   │   label: "Tile 21"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#139
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, -76.0, 411.4, 24.0)
I/flutter ( 8037):      │   │   flags: isHidden
I/flutter ( 8037):      │   │   HIDDEN
I/flutter ( 8037):      │   │   label: "Tile 22"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#12
I/flutter ( 8037):      │   │ │ Rect.fromLTRB(0.0, 0.0, 411.4, 80.0)
I/flutter ( 8037):      │   │ │
I/flutter ( 8037):      │   │ └─SemanticsNode#13
I/flutter ( 8037):      │   │     Rect.fromLTRB(16.0, 40.5, 63.0, 63.5)
I/flutter ( 8037):      │   │     flags: isHeader, namesRoute
I/flutter ( 8037):      │   │     label: "Hello"
I/flutter ( 8037):      │   │     textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#140
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 80.0, 411.4, 124.0)
I/flutter ( 8037):      │   │   label: "Tile 23"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#141
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 124.0, 411.4, 224.0)
I/flutter ( 8037):      │   │   label: "Tile 24"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#142
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 224.0, 411.4, 324.0)
I/flutter ( 8037):      │   │   label: "Tile 25"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#143
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 324.0, 411.4, 424.0)
I/flutter ( 8037):      │   │   label: "Tile 26"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#144
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 424.0, 411.4, 524.0)
I/flutter ( 8037):      │   │   label: "Tile 27"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#145
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 524.0, 411.4, 624.0)
I/flutter ( 8037):      │   │   label: "Tile 28"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#146
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 624.0, 411.4, 683.4)
I/flutter ( 8037):      │   │   label: "Tile 29"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#147
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 724.0, 411.4, 824.0)
I/flutter ( 8037):      │   │   flags: isHidden
I/flutter ( 8037):      │   │   HIDDEN
I/flutter ( 8037):      │   │   label: "Tile 30"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   ├─SemanticsNode#148
I/flutter ( 8037):      │   │   Rect.fromLTRB(0.0, 824.0, 411.4, 924.0)
I/flutter ( 8037):      │   │   flags: isHidden
I/flutter ( 8037):      │   │   HIDDEN
I/flutter ( 8037):      │   │   label: "Tile 31"
I/flutter ( 8037):      │   │   textDirection: ltr
I/flutter ( 8037):      │   │
I/flutter ( 8037):      │   └─SemanticsNode#149
I/flutter ( 8037):      │       Rect.fromLTRB(0.0, 924.0, 411.4, 933.4)
I/flutter ( 8037):      │       flags: isHidden
I/flutter ( 8037):      │       HIDDEN
I/flutter ( 8037):      │       label: "Tile 32"
I/flutter ( 8037):      │       textDirection: ltr
I/flutter ( 8037):      │
I/flutter ( 8037):      └─SemanticsNode#15
I/flutter ( 8037):        │ merge boundary ⛔️
I/flutter ( 8037):        │ Rect.fromLTRB(0.0, 0.0, 56.0, 56.0) with transform
I/flutter ( 8037):        │ [1.0,2.4492935982947064e-16,0.0,339.42857142857144;
I/flutter ( 8037):        │ -2.4492935982947064e-16,1.0,0.0,611.4285714285714;
I/flutter ( 8037):        │ 0.0,0.0,1.0,0.0; 0.0,0.0,0.0,1.0]
I/flutter ( 8037):        │ label: "Increment"
I/flutter ( 8037):        │ textDirection: ltr
I/flutter ( 8037):        │
I/flutter ( 8037):        └─SemanticsNode#16
I/flutter ( 8037):            merged up ⬆️
I/flutter ( 8037):            Rect.fromLTRB(0.0, 0.0, 56.0, 56.0)
I/flutter ( 8037):            actions: tap
I/flutter ( 8037):            flags: isButton, hasEnabledState, isEnabled
I/flutter ( 8037):            thickness: 6.0
I/flutter ( 8037): 

The app bar nod (label: "Hallo") shouldn't be in the middle of all the tiles within the scrollable node.

This reproduces on Android, still investigating if iOS is also affected.

Activity

  1. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)
    f: scrollingViewports, list views, slivers, etc.
    on Jul 2, 2019
  2. goderbauer commented on Jul 2, 2019

    @goderbauer
    MemberAuthor

    iOS is also affected. It is probably the incorrectly generated a11y tree that's causing this.

  3. goderbauer commented on Jul 2, 2019

    @goderbauer
    MemberAuthor
  4. goderbauer commented on Jul 2, 2019

    @goderbauer
    MemberAuthor

    Little more debugging: This line sets excludeFromScrolling to false even though it should be true for everything to work correctly:

    excludeFromSemanticsScrolling = overlapsContent;

  5. dnfield commented on Jul 4, 2019

    @dnfield
    Contributor

    So at the commit where you added that, things worked. Unfortunately it's a little tricky to get things to build at that commit now.

    A big difference that i'm seeing is that at that commit, for a floating app bar to be pinned, it had to have a bottom set. That seems to no longer be the case.

  6. dnfield commented on Jul 4, 2019

    @dnfield
    Contributor

    Actually that makes sense - the problem now is that overlapsContent ends up being false all the time. I think that's our bug.

  7. dnfield commented on Jul 4, 2019

    @dnfield
    Contributor

    Archaeology: 76baa5e removed the assert.

    Once you get rid of a bottom perferred size widget, the overlaps content logic doesn't work anymore.

  8. modified the milestones: April 2020, July 2019 on Jul 4, 2019
  9. self-assigned this
    on Jul 4, 2019
  10. dnfield commented on Jul 16, 2019

    @dnfield
    Contributor

    Reopening since this is being reverted

  11. github-actions commented on Aug 29, 2021

    @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.

  12. locked as resolved and limited conversation to collaborators on Aug 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

a: accessibilityAccessibility, e.g. VoiceOver or TalkBack. (aka a11y)f: scrollingViewports, list views, slivers, etc.frameworkflutter/packages/flutter repository. See also f: labels.

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions