sliver_semantics_test.dart 38.2 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 171
                    id: 9,
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
172
                      TestSemantics(
173 174 175 176
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
177
                      TestSemantics(
178 179 180 181
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
182
                      TestSemantics(
183 184 185 186
                        id: 5,
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
187
                      TestSemantics(
188 189 190 191 192
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
193
                      TestSemantics(
194 195 196 197 198 199
                        id: 10,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
200 201 202 203
                  ),
                ],
              ),
            ],
204
          ),
205 206 207 208 209 210 211 212 213 214 215 216
        ],
      ),
      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(
217
      TestSemantics.root(
218
        children: <TestSemantics>[
219
          TestSemantics(
220
            id: 1,
221
            textDirection: TextDirection.ltr,
222
            children: <TestSemantics>[
223
              TestSemantics(
224
                id: 2,
225
                children: <TestSemantics>[
226
                  TestSemantics(
227
                    id: 9,
228 229 230
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
231 232 233 234 235
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
236
                      TestSemantics(
237 238
                        id: 7,
                        children: <TestSemantics>[
239
                          TestSemantics(
240
                            id: 8,
241 242 243 244
                            flags: <SemanticsFlag>[
                              SemanticsFlag.namesRoute,
                              SemanticsFlag.isHeader,
                            ],
245 246 247 248 249
                            label: 'Semantics Test with Slivers',
                            textDirection: TextDirection.ltr,
                          ),
                        ],
                      ),
250
                      TestSemantics(
251 252 253 254
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
255
                      TestSemantics(
256 257 258 259
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
260
                      TestSemantics(
261 262 263 264
                        id: 5,
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
265
                      TestSemantics(
266 267 268 269 270 271
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
272
                  ),
273 274 275
                ],
              ),
            ],
276
          ),
277 278 279 280 281 282 283 284 285
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
    ));

    semantics.dispose();
  });

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

    const double containerHeight = 200.0;

291
    final ScrollController scrollController = ScrollController(
292 293
      initialScrollOffset: containerHeight * 1.5,
    );
294 295 296
    final List<Widget> slivers = List<Widget>.generate(30, (int i) {
      return SliverToBoxAdapter(
        child: Container(
297
          height: containerHeight,
298
          child: Text('Item $i', textDirection: TextDirection.ltr),
299 300 301 302
        ),
      );
    });
    await tester.pumpWidget(
303
      Semantics(
304
        textDirection: TextDirection.ltr,
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
        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,
                ),
320
              ),
321
            ),
322 323 324 325 326 327
          ),
        ),
      ),
    );

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

    semantics.dispose();
  });

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

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

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

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

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

461 462
    final List<Widget> listChildren = List<Widget>.generate(10, (int i) {
      return Container(
463
        height: 200.0,
464
        child: Text('Item $i', textDirection: TextDirection.ltr),
465 466
      );
    });
467 468
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    await tester.pumpWidget(Semantics(
469
      textDirection: TextDirection.ltr,
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
      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,
            ),
493
          ),
494 495
        ),
      ),
496
    ));
497 498

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

    semantics.dispose();
  });

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

571 572 573 574
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    final List<Widget> slivers = List<Widget>.generate(10, (int i) {
      return SliverToBoxAdapter(
        child: Container(
575
          height: 200.0,
576
          child: Text('Item $i', textDirection: TextDirection.ltr),
577 578 579
        ),
      );
    });
580
    await tester.pumpWidget(Semantics(
581
      textDirection: TextDirection.ltr,
582 583 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'),
                ),
              ]..addAll(slivers),
            ),
602
          ),
603 604
        ),
      ),
605
    ));
606 607

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

    semantics.dispose();
  });

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

680 681
    final List<Widget> listChildren = List<Widget>.generate(10, (int i) {
      return Container(
682
        height: 200.0,
683
        child: Text('Item $i', textDirection: TextDirection.ltr),
684 685
      );
    });
686 687
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    await tester.pumpWidget(Semantics(
688
      textDirection: TextDirection.ltr,
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
      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,
            ),
713
          ),
714 715
        ),
      ),
716
    ));
717 718

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

    semantics.dispose();
  });

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

794 795 796 797
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    final List<Widget> slivers = List<Widget>.generate(10, (int i) {
      return SliverToBoxAdapter(
        child: Container(
798
          height: 200.0,
799
          child: Text('Item $i', textDirection: TextDirection.ltr),
800 801 802
        ),
      );
    });
803
    await tester.pumpWidget(Semantics(
804
      textDirection: TextDirection.ltr,
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
      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'),
                ),
              ]..addAll(slivers),
            ),
826
          ),
827 828
        ),
      ),
829
    ));
830 831

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

    semantics.dispose();
  });

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

907 908 909 910
    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(
911
        height: 200.0,
912
        child: Text('Forward Item $i', textDirection: TextDirection.ltr),
913 914
      );
    });
915 916
    final List<Widget> backwardChildren = List<Widget>.generate(10, (int i) {
      return Container(
917
        height: 200.0,
918
        child: Text('Backward Item $i', textDirection: TextDirection.ltr),
919 920
      );
    });
921
    await tester.pumpWidget(Semantics(
922
      textDirection: TextDirection.ltr,
923
      child: Directionality(
924
        textDirection: TextDirection.ltr,
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
        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),
942
                    ),
943 944 945 946 947 948
                    const SliverAppBar(
                      pinned: true,
                      expandedHeight: 100.0,
                      flexibleSpace: FlexibleSpaceBar(
                        title: Text('Backward app bar', textDirection: TextDirection.ltr),
                      ),
949
                    ),
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964
                    SliverAppBar(
                      pinned: true,
                      key: forwardAppBarKey,
                      expandedHeight: 100.0,
                      flexibleSpace: const FlexibleSpaceBar(
                        title: Text('Forward app bar', textDirection: TextDirection.ltr),
                      ),
                    ),
                    SliverList(
                      delegate: SliverChildListDelegate(forwardChildren),
                    ),
                  ],
                );
              },
            ),
965
          ),
966 967
        ),
      ),
968
    ));
969 970

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

    controller.jumpTo(-880.0);
    await tester.pumpAndSettle();
1040 1041 1042

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

    semantics.dispose();
  });

1112
}