sliver_semantics_test.dart 38.5 KB
Newer Older
1 2 3 4
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:ui';

7 8 9 10 11 12 13 14
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';

import 'semantics_tester.dart';

void main() {
15 16 17 18 19 20 21 22 23 24
  group('Sliver Semantics', () {
    setUp(() {
      debugResetSemanticsIdCounter();
    });

    _tests();
  });
}

void _tests() {
25
  testWidgets('excludeFromScrollable works correctly', (WidgetTester tester) async {
26
    final SemanticsTester semantics = SemanticsTester(tester);
27 28 29

    const double appBarExpandedHeight = 200.0;

30 31 32
    final ScrollController scrollController = ScrollController();
    final List<Widget> listChildren = List<Widget>.generate(30, (int i) {
      return Container(
33
        height: appBarExpandedHeight,
34
        child: Text('Item $i'),
35 36
      );
    });
Ian Hickson's avatar
Ian Hickson committed
37
    await tester.pumpWidget(
38
      Semantics(
Ian Hickson's avatar
Ian Hickson committed
39
        textDirection: TextDirection.ltr,
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
        child: Localizations(
          locale: const Locale('en', 'us'),
          delegates: const <LocalizationsDelegate<dynamic>>[
            DefaultWidgetsLocalizations.delegate,
            DefaultMaterialLocalizations.delegate,
          ],
          child: Directionality(
            textDirection: TextDirection.ltr,
            child: MediaQuery(
              data: const MediaQueryData(),
              child: CustomScrollView(
                controller: scrollController,
                slivers: <Widget>[
                  const SliverAppBar(
                    pinned: true,
                    expandedHeight: appBarExpandedHeight,
                    title: Text('Semantics Test with Slivers'),
                  ),
                   SliverList(
                    delegate: SliverChildListDelegate(listChildren),
                  ),
                ],
              ),
63
            ),
Ian Hickson's avatar
Ian Hickson committed
64
          ),
65
        ),
66
      ),
Ian Hickson's avatar
Ian Hickson committed
67
    );
68 69 70

    // AppBar is child of node with semantic scroll actions.
    expect(semantics, hasSemantics(
71
      TestSemantics.root(
Ian Hickson's avatar
Ian Hickson committed
72
        children: <TestSemantics>[
73
          TestSemantics(
74
            id: 1,
75
            textDirection: TextDirection.ltr,
Ian Hickson's avatar
Ian Hickson committed
76
            children: <TestSemantics>[
77
              TestSemantics(
78
                id: 2,
Ian Hickson's avatar
Ian Hickson committed
79
                children: <TestSemantics>[
80
                  TestSemantics(
81
                    id: 9,
82 83 84
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
85 86
                    actions: <SemanticsAction>[SemanticsAction.scrollUp],
                    children: <TestSemantics>[
87
                      TestSemantics(
88 89
                        id: 7,
                        children: <TestSemantics>[
90
                          TestSemantics(
91
                            id: 8,
92 93 94 95
                            flags: <SemanticsFlag>[
                              SemanticsFlag.namesRoute,
                              SemanticsFlag.isHeader,
                            ],
96 97 98 99 100
                            label: 'Semantics Test with Slivers',
                            textDirection: TextDirection.ltr,
                          ),
                        ],
                      ),
101
                      TestSemantics(
102 103 104 105
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
106
                      TestSemantics(
107 108 109 110
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
111
                      TestSemantics(
112 113 114 115 116
                        id: 5,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
117
                      TestSemantics(
118 119 120 121 122 123
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
Ian Hickson's avatar
Ian Hickson committed
124 125 126 127
                  ),
                ],
              ),
            ],
128
          ),
Ian Hickson's avatar
Ian Hickson committed
129 130 131 132
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
133 134 135 136 137 138 139 140
    ));

    // Scroll down far enough to reach the pinned state of the app bar.
    scrollController.jumpTo(appBarExpandedHeight);
    await tester.pump();

    // App bar is NOT a child of node with semantic scroll actions.
    expect(semantics, hasSemantics(
141
      TestSemantics.root(
142
        children: <TestSemantics>[
143
          TestSemantics(
144
            id: 1,
145
            textDirection: TextDirection.ltr,
146
            children: <TestSemantics>[
147
              TestSemantics(
148
                id: 2,
149
                children: <TestSemantics>[
150
                  TestSemantics(
151
                    id: 7,
152 153
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
154
                      TestSemantics(
155
                        id: 8,
156 157 158 159
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
160 161 162 163 164
                        label: 'Semantics Test with Slivers',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
165
                  TestSemantics(
166 167 168 169 170
                    id: 9,
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
171
                    flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
172
                    children: <TestSemantics>[
173
                      TestSemantics(
174 175 176 177
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
178
                      TestSemantics(
179 180 181 182
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
183
                      TestSemantics(
184 185 186 187
                        id: 5,
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
188
                      TestSemantics(
189 190 191 192 193
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
194
                      TestSemantics(
195 196 197 198 199 200
                        id: 10,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
201 202 203 204
                  ),
                ],
              ),
            ],
205
          ),
206 207 208 209 210 211 212 213 214 215 216 217
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
    ));

    // Scroll halfway back to the top, app bar is no longer in pinned state.
    scrollController.jumpTo(appBarExpandedHeight / 2);
    await tester.pump();

    // AppBar is child of node with semantic scroll actions.
    expect(semantics, hasSemantics(
218
      TestSemantics.root(
219
        children: <TestSemantics>[
220
          TestSemantics(
221
            id: 1,
222
            textDirection: TextDirection.ltr,
223
            children: <TestSemantics>[
224
              TestSemantics(
225
                id: 2,
226
                children: <TestSemantics>[
227
                  TestSemantics(
228
                    id: 9,
229 230 231
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
232 233 234 235 236
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
237
                      TestSemantics(
238 239
                        id: 7,
                        children: <TestSemantics>[
240
                          TestSemantics(
241
                            id: 8,
242 243 244 245
                            flags: <SemanticsFlag>[
                              SemanticsFlag.namesRoute,
                              SemanticsFlag.isHeader,
                            ],
246 247 248 249 250
                            label: 'Semantics Test with Slivers',
                            textDirection: TextDirection.ltr,
                          ),
                        ],
                      ),
251
                      TestSemantics(
252 253 254 255
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
256
                      TestSemantics(
257 258 259 260
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
261
                      TestSemantics(
262 263 264 265
                        id: 5,
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
266
                      TestSemantics(
267 268 269 270 271 272
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
273
                  ),
274 275 276
                ],
              ),
            ],
277
          ),
278 279 280 281 282 283 284 285 286
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
    ));

    semantics.dispose();
  });

287
  testWidgets('Offscreen sliver are hidden in semantics tree', (WidgetTester tester) async {
288
    final SemanticsTester semantics = SemanticsTester(tester);
289 290 291

    const double containerHeight = 200.0;

292
    final ScrollController scrollController = ScrollController(
293 294
      initialScrollOffset: containerHeight * 1.5,
    );
295 296 297
    final List<Widget> slivers = List<Widget>.generate(30, (int i) {
      return SliverToBoxAdapter(
        child: Container(
298
          height: containerHeight,
299
          child: Text('Item $i', textDirection: TextDirection.ltr),
300 301 302 303
        ),
      );
    });
    await tester.pumpWidget(
304
      Semantics(
305
        textDirection: TextDirection.ltr,
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
        child: Localizations(
          locale: const Locale('en', 'us'),
          delegates: const <LocalizationsDelegate<dynamic>>[
            DefaultWidgetsLocalizations.delegate,
            DefaultMaterialLocalizations.delegate,
          ],
          child: Directionality(
            textDirection: TextDirection.ltr,
            child: Center(
              child: SizedBox(
                height: containerHeight,
                child: CustomScrollView(
                  controller: scrollController,
                  slivers: slivers,
                ),
321
              ),
322
            ),
323 324 325 326 327 328
          ),
        ),
      ),
    );

    expect(semantics, hasSemantics(
329
      TestSemantics.root(
330
        children: <TestSemantics>[
331
          TestSemantics(
332
            textDirection: TextDirection.ltr,
333
            children: <TestSemantics>[
334
              TestSemantics(
335
                children: <TestSemantics>[
336
                  TestSemantics(
337 338 339
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
340 341 342 343 344
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
345
                      TestSemantics(
346 347 348 349
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
350
                      TestSemantics(
351 352 353
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
354
                      TestSemantics(
355 356 357
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
358
                      TestSemantics(
359 360 361 362 363
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
364
                  ),
365 366 367
                ],
              ),
            ],
368
          ),
369 370 371 372
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
373
      ignoreId: true,
374 375 376 377 378 379
    ));

    semantics.dispose();
  });

  testWidgets('SemanticsNodes of Slivers are in paint order', (WidgetTester tester) async {
380
    final SemanticsTester semantics = SemanticsTester(tester);
381

382 383 384
    final List<Widget> slivers = List<Widget>.generate(5, (int i) {
      return SliverToBoxAdapter(
        child: Container(
385
          height: 20.0,
386
          child: Text('Item $i'),
387 388 389 390
        ),
      );
    });
    await tester.pumpWidget(
391
      Semantics(
392
        textDirection: TextDirection.ltr,
393 394 395 396 397 398 399 400 401 402 403
        child: Localizations(
          locale: const Locale('en', 'us'),
          delegates: const <LocalizationsDelegate<dynamic>>[
            DefaultWidgetsLocalizations.delegate,
            DefaultMaterialLocalizations.delegate,
          ],
          child: Directionality(
            textDirection: TextDirection.ltr,
            child: CustomScrollView(
              slivers: slivers,
            ),
404
          ),
405
        ),
406 407
      ),
    );
408

409
    expect(semantics, hasSemantics(
410
      TestSemantics.root(
411
        children: <TestSemantics>[
412
          TestSemantics(
413
            textDirection: TextDirection.ltr,
414
            children: <TestSemantics>[
415
              TestSemantics(
416
                children: <TestSemantics>[
417
                  TestSemantics(
418 419 420
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
421
                    children: <TestSemantics>[
422
                      TestSemantics(
423 424 425
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
426
                      TestSemantics(
427 428 429
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
430
                      TestSemantics(
431 432 433
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
434
                      TestSemantics(
435 436 437
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
438
                      TestSemantics(
439 440 441 442
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
443
                  ),
444 445 446
                ],
              ),
            ],
447
          ),
448 449 450 451
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
452 453
      ignoreId: true,
      childOrder: DebugSemanticsDumpOrder.inverseHitTest,
454
    ));
455 456

    semantics.dispose();
457
  });
458 459

  testWidgets('SemanticsNodes of a sliver fully covered by another overlapping sliver are excluded', (WidgetTester tester) async {
460
    final SemanticsTester semantics = SemanticsTester(tester);
461

462 463
    final List<Widget> listChildren = List<Widget>.generate(10, (int i) {
      return Container(
464
        height: 200.0,
465
        child: Text('Item $i', textDirection: TextDirection.ltr),
466 467
      );
    });
468 469
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    await tester.pumpWidget(Semantics(
470
      textDirection: TextDirection.ltr,
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
      child: Localizations(
        locale: const Locale('en', 'us'),
        delegates: const <LocalizationsDelegate<dynamic>>[
          DefaultWidgetsLocalizations.delegate,
          DefaultMaterialLocalizations.delegate,
        ],
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: MediaQuery(
            data: const MediaQueryData(),
            child: CustomScrollView(
              slivers: <Widget>[
                const SliverAppBar(
                  pinned: true,
                  expandedHeight: 100.0,
                  title: Text('AppBar'),
                ),
                SliverList(
                  delegate: SliverChildListDelegate(listChildren),
                ),
              ],
              controller: controller,
            ),
494
          ),
495 496
        ),
      ),
497
    ));
498 499

    expect(semantics, hasSemantics(
500
      TestSemantics.root(
501
        children: <TestSemantics>[
502
          TestSemantics(
503
            textDirection: TextDirection.ltr,
504
            children: <TestSemantics>[
505
              TestSemantics(
506
                children: <TestSemantics>[
507
                  TestSemantics(
508 509
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
510
                      TestSemantics(
511 512 513 514
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
515 516 517 518 519
                        label: 'AppBar',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
520
                  TestSemantics(
521 522 523 524
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
525
                    flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
526
                    children: <TestSemantics>[
527
                      TestSemantics(
528 529 530 531
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
532
                      TestSemantics(
533 534 535
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
536
                      TestSemantics(
537 538 539
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
540
                      TestSemantics(
541 542 543
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
544
                      TestSemantics(
545 546 547 548
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
549
                      TestSemantics(
550 551 552 553 554
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 5',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
555 556
                  ),
                ],
557 558
              ),
            ],
559
          ),
560 561
        ],
      ),
562
      ignoreTransform: true,
563 564
      ignoreId: true,
      ignoreRect: true,
565 566 567 568 569
    ));

    semantics.dispose();
  });

570
  testWidgets('Slivers fully covered by another overlapping sliver are hidden', (WidgetTester tester) async {
571
    final SemanticsTester semantics = SemanticsTester(tester);
572

573 574 575 576
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    final List<Widget> slivers = List<Widget>.generate(10, (int i) {
      return SliverToBoxAdapter(
        child: Container(
577
          height: 200.0,
578
          child: Text('Item $i', textDirection: TextDirection.ltr),
579 580 581
        ),
      );
    });
582
    await tester.pumpWidget(Semantics(
583
      textDirection: TextDirection.ltr,
584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
      child: Localizations(
        locale: const Locale('en', 'us'),
        delegates: const <LocalizationsDelegate<dynamic>>[
          DefaultWidgetsLocalizations.delegate,
          DefaultMaterialLocalizations.delegate,
        ],
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: MediaQuery(
            data: const MediaQueryData(),
            child: CustomScrollView(
              controller: controller,
              slivers: <Widget>[
                const SliverAppBar(
                  pinned: true,
                  expandedHeight: 100.0,
                  title: Text('AppBar'),
                ),
602 603
                ...slivers,
              ],
604
            ),
605
          ),
606 607
        ),
      ),
608
    ));
609 610

    expect(semantics, hasSemantics(
611
      TestSemantics.root(
612
        children: <TestSemantics>[
613
          TestSemantics(
614
            textDirection: TextDirection.ltr,
615
            children: <TestSemantics>[
616
              TestSemantics(
617
                children: <TestSemantics>[
618
                  TestSemantics(
619 620
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
621
                      TestSemantics(
622 623 624 625
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
626 627 628 629 630
                        label: 'AppBar',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
631
                  TestSemantics(
632 633 634 635
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
636
                    flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
637
                    children: <TestSemantics>[
638
                      TestSemantics(
639 640 641 642
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
643
                      TestSemantics(
644 645 646
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
647
                      TestSemantics(
648 649 650
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
651
                      TestSemantics(
652 653 654
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
655
                      TestSemantics(
656 657 658 659
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
660
                      TestSemantics(
661 662 663 664 665
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 5',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
666 667
                  ),
                ],
668 669
              ),
            ],
670
          ),
671 672
        ],
      ),
673
      ignoreTransform: true,
674 675
      ignoreRect: true,
      ignoreId: true,
676 677 678 679 680 681
    ));

    semantics.dispose();
  });

  testWidgets('SemanticsNodes of a sliver fully covered by another overlapping sliver are excluded (reverse)', (WidgetTester tester) async {
682
    final SemanticsTester semantics = SemanticsTester(tester);
683

684 685
    final List<Widget> listChildren = List<Widget>.generate(10, (int i) {
      return Container(
686
        height: 200.0,
687
        child: Text('Item $i', textDirection: TextDirection.ltr),
688 689
      );
    });
690 691
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    await tester.pumpWidget(Semantics(
692
      textDirection: TextDirection.ltr,
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
      child: Localizations(
        locale: const Locale('en', 'us'),
        delegates: const <LocalizationsDelegate<dynamic>>[
          DefaultWidgetsLocalizations.delegate,
          DefaultMaterialLocalizations.delegate,
        ],
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: MediaQuery(
            data: const MediaQueryData(),
            child: CustomScrollView(
              reverse: true, // This is the important setting for this test.
              slivers: <Widget>[
                const SliverAppBar(
                  pinned: true,
                  expandedHeight: 100.0,
                  title: Text('AppBar'),
                ),
                SliverList(
                  delegate: SliverChildListDelegate(listChildren),
                ),
              ],
              controller: controller,
            ),
717
          ),
718 719
        ),
      ),
720
    ));
721 722

    expect(semantics, hasSemantics(
723
      TestSemantics.root(
724
        children: <TestSemantics>[
725
          TestSemantics(
726
            textDirection: TextDirection.ltr,
727
            children: <TestSemantics>[
728
              TestSemantics(
729
                children: <TestSemantics>[
730
                  TestSemantics(
731 732 733
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
734 735 736 737 738
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
739
                      TestSemantics(
740 741 742 743
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 5',
                        textDirection: TextDirection.ltr,
                      ),
744
                      TestSemantics(
745 746 747 748
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
749
                      TestSemantics(
750 751 752
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
753
                      TestSemantics(
754 755 756
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
757
                      TestSemantics(
758 759 760
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
761
                      TestSemantics(
762 763 764 765 766 767
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
768
                  TestSemantics(
769 770
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
771
                      TestSemantics(
772 773 774 775
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
776 777 778 779
                        label: 'AppBar',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
780 781
                  ),
                ],
782 783
              ),
            ],
784
          ),
785 786
        ],
      ),
787
      ignoreTransform: true,
788 789
      ignoreId: true,
      ignoreRect: true,
790 791 792 793 794
    ));

    semantics.dispose();
  });

795
  testWidgets('Slivers fully covered by another overlapping sliver are hidden (reverse)', (WidgetTester tester) async {
796
    final SemanticsTester semantics = SemanticsTester(tester);
797

798 799 800 801
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    final List<Widget> slivers = List<Widget>.generate(10, (int i) {
      return SliverToBoxAdapter(
        child: Container(
802
          height: 200.0,
803
          child: Text('Item $i', textDirection: TextDirection.ltr),
804 805 806
        ),
      );
    });
807
    await tester.pumpWidget(Semantics(
808
      textDirection: TextDirection.ltr,
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
      child: Localizations(
        locale: const Locale('en', 'us'),
        delegates: const <LocalizationsDelegate<dynamic>>[
          DefaultWidgetsLocalizations.delegate,
          DefaultMaterialLocalizations.delegate,
        ],
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: MediaQuery(
            data: const MediaQueryData(),
            child: CustomScrollView(
              reverse: true, // This is the important setting for this test.
              controller: controller,
              slivers: <Widget>[
                const SliverAppBar(
                  pinned: true,
                  expandedHeight: 100.0,
                  title: Text('AppBar'),
                ),
828 829
                ...slivers,
              ],
830
            ),
831
          ),
832 833
        ),
      ),
834
    ));
835 836

    expect(semantics, hasSemantics(
837
      TestSemantics.root(
838
        children: <TestSemantics>[
839
          TestSemantics(
840
            textDirection: TextDirection.ltr,
841
            children: <TestSemantics>[
842
              TestSemantics(
843
                children: <TestSemantics>[
844
                  TestSemantics(
845 846 847 848 849 850 851
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
852
                    children: <TestSemantics>[
853
                      TestSemantics(
854 855 856 857
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 5',
                        textDirection: TextDirection.ltr,
                      ),
858
                      TestSemantics(
859 860 861 862
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
863
                      TestSemantics(
864 865 866
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
867
                      TestSemantics(
868 869 870
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
871
                      TestSemantics(
872 873 874
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
875
                      TestSemantics(
876 877 878 879 880 881
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
882
                  TestSemantics(
883 884
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
885
                      TestSemantics(
886 887 888 889
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
890 891 892 893
                        label: 'AppBar',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
894 895
                  ),
                ],
896 897
              ),
            ],
898
          ),
899 900
        ],
      ),
901
      ignoreTransform: true,
902 903
      ignoreId: true,
      ignoreRect: true,
904 905 906 907 908
    ));

    semantics.dispose();
  });

909
  testWidgets('Slivers fully covered by another overlapping sliver are hidden (with center sliver)', (WidgetTester tester) async {
910
    final SemanticsTester semantics = SemanticsTester(tester);
911

912 913 914 915
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    final GlobalKey forwardAppBarKey = GlobalKey(debugLabel: 'forward app bar');
    final List<Widget> forwardChildren = List<Widget>.generate(10, (int i) {
      return Container(
916
        height: 200.0,
917
        child: Text('Forward Item $i', textDirection: TextDirection.ltr),
918 919
      );
    });
920 921
    final List<Widget> backwardChildren = List<Widget>.generate(10, (int i) {
      return Container(
922
        height: 200.0,
923
        child: Text('Backward Item $i', textDirection: TextDirection.ltr),
924 925
      );
    });
926
    await tester.pumpWidget(Semantics(
927
      textDirection: TextDirection.ltr,
928
      child: Directionality(
929
        textDirection: TextDirection.ltr,
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
        child: Localizations(
          locale: const Locale('en', 'us'),
          delegates: const <LocalizationsDelegate<dynamic>>[
            DefaultWidgetsLocalizations.delegate,
            DefaultMaterialLocalizations.delegate,
          ],
          child: MediaQuery(
            data: const MediaQueryData(),
            child: Scrollable(
              controller: controller,
              viewportBuilder: (BuildContext context, ViewportOffset offset) {
                return Viewport(
                  offset: offset,
                  center: forwardAppBarKey,
                  slivers: <Widget>[
                    SliverList(
                      delegate: SliverChildListDelegate(backwardChildren),
947
                    ),
948 949 950 951 952 953
                    const SliverAppBar(
                      pinned: true,
                      expandedHeight: 100.0,
                      flexibleSpace: FlexibleSpaceBar(
                        title: Text('Backward app bar', textDirection: TextDirection.ltr),
                      ),
954
                    ),
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
                    SliverAppBar(
                      pinned: true,
                      key: forwardAppBarKey,
                      expandedHeight: 100.0,
                      flexibleSpace: const FlexibleSpaceBar(
                        title: Text('Forward app bar', textDirection: TextDirection.ltr),
                      ),
                    ),
                    SliverList(
                      delegate: SliverChildListDelegate(forwardChildren),
                    ),
                  ],
                );
              },
            ),
970
          ),
971 972
        ),
      ),
973
    ));
974 975

    // 'Forward Item 0' is covered by app bar.
976
    expect(semantics, hasSemantics(
977
      TestSemantics.root(
978
        children: <TestSemantics>[
979
          TestSemantics(
980 981
            textDirection: TextDirection.ltr,
            children: <TestSemantics>[
982
              TestSemantics(
983
                children: <TestSemantics>[
984
                  TestSemantics(
985 986
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
987
                      TestSemantics(
988 989 990 991
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
992 993 994 995 996
                        label: 'Forward app bar',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
997
                  TestSemantics(
998 999 1000 1001
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
1002
                    flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
1003
                    children: <TestSemantics>[
1004
                      TestSemantics(
1005 1006 1007 1008
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Forward Item 0',
                        textDirection: TextDirection.ltr,
                      ),
1009
                      TestSemantics(
1010 1011 1012
                        label: 'Forward Item 1',
                        textDirection: TextDirection.ltr,
                      ),
1013
                      TestSemantics(
1014 1015 1016
                        label: 'Forward Item 2',
                        textDirection: TextDirection.ltr,
                      ),
1017
                      TestSemantics(
1018 1019 1020
                        label: 'Forward Item 3',
                        textDirection: TextDirection.ltr,
                      ),
1021
                      TestSemantics(
1022 1023 1024 1025
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Forward Item 4',
                        textDirection: TextDirection.ltr,
                      ),
1026
                      TestSemantics(
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Forward Item 5',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
                ],
              ),
            ],
          ),
        ],
      ),
      ignoreTransform: true,
      ignoreRect: true,
      ignoreId: true,
    ));
1043 1044 1045

    controller.jumpTo(-880.0);
    await tester.pumpAndSettle();
1046 1047 1048

    // 'Backward Item 0' is covered by app bar.
    expect(semantics, hasSemantics(
1049
      TestSemantics.root(
1050
        children: <TestSemantics>[
1051
          TestSemantics(
1052 1053
            textDirection: TextDirection.ltr,
            children: <TestSemantics>[
1054
              TestSemantics(
1055
                children: <TestSemantics>[
1056
                  TestSemantics(
1057 1058 1059
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
1060 1061 1062 1063 1064
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
1065
                      TestSemantics(
1066 1067 1068 1069
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Backward Item 5',
                        textDirection: TextDirection.ltr,
                      ),
1070
                      TestSemantics(
1071 1072 1073 1074
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Backward Item 4',
                        textDirection: TextDirection.ltr,
                      ),
1075
                      TestSemantics(
1076 1077 1078
                        label: 'Backward Item 3',
                        textDirection: TextDirection.ltr,
                      ),
1079
                      TestSemantics(
1080 1081 1082
                        label: 'Backward Item 2',
                        textDirection: TextDirection.ltr,
                      ),
1083
                      TestSemantics(
1084 1085 1086
                        label: 'Backward Item 1',
                        textDirection: TextDirection.ltr,
                      ),
1087
                      TestSemantics(
1088 1089 1090 1091 1092 1093
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Backward Item 0',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
1094
                  TestSemantics(
1095 1096
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
1097
                      TestSemantics(
1098 1099 1100 1101
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113
                        label: 'Backward app bar',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
                ],
              ),
            ],
          ),
        ],
      ), ignoreTransform: true, ignoreRect: true, ignoreId: true,
    ));
1114 1115 1116 1117

    semantics.dispose();
  });

1118
}