sliver_semantics_test.dart 38.8 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
6 7
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
8 9 10 11

import 'semantics_tester.dart';

void main() {
12 13 14 15 16 17 18 19 20 21
  group('Sliver Semantics', () {
    setUp(() {
      debugResetSemanticsIdCounter();
    });

    _tests();
  });
}

void _tests() {
22
  testWidgets('excludeFromScrollable works correctly', (WidgetTester tester) async {
23
    final SemanticsTester semantics = SemanticsTester(tester);
24 25 26

    const double appBarExpandedHeight = 200.0;

27 28
    final ScrollController scrollController = ScrollController();
    final List<Widget> listChildren = List<Widget>.generate(30, (int i) {
29
      return SizedBox(
30
        height: appBarExpandedHeight,
31
        child: Text('Item $i'),
32 33
      );
    });
Ian Hickson's avatar
Ian Hickson committed
34
    await tester.pumpWidget(
35
      Semantics(
Ian Hickson's avatar
Ian Hickson committed
36
        textDirection: TextDirection.ltr,
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
        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'),
                  ),
55
                  SliverList(
56 57 58 59
                    delegate: SliverChildListDelegate(listChildren),
                  ),
                ],
              ),
60
            ),
Ian Hickson's avatar
Ian Hickson committed
61
          ),
62
        ),
63
      ),
Ian Hickson's avatar
Ian Hickson committed
64
    );
65 66 67

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

    // 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(
138
      TestSemantics.root(
139
        children: <TestSemantics>[
140
          TestSemantics(
141
            id: 1,
142
            textDirection: TextDirection.ltr,
143
            children: <TestSemantics>[
144
              TestSemantics(
145
                id: 2,
146
                children: <TestSemantics>[
147
                  TestSemantics(
148
                    id: 7,
149 150
                    tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
                    children: <TestSemantics>[
151
                      TestSemantics(
152
                        id: 8,
153 154 155 156
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
157 158 159 160 161
                        label: 'Semantics Test with Slivers',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
162
                  TestSemantics(
163 164 165 166 167
                    id: 9,
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
168
                    flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
169
                    children: <TestSemantics>[
170
                      TestSemantics(
171 172 173 174
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
175
                      TestSemantics(
176 177 178 179
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
180
                      TestSemantics(
181 182 183 184
                        id: 5,
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
185
                      TestSemantics(
186 187 188 189 190
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
191
                      TestSemantics(
192 193 194 195 196 197
                        id: 10,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
198 199 200 201
                  ),
                ],
              ),
            ],
202
          ),
203 204 205 206 207 208 209 210 211 212 213 214
        ],
      ),
      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(
215
      TestSemantics.root(
216
        children: <TestSemantics>[
217
          TestSemantics(
218
            id: 1,
219
            textDirection: TextDirection.ltr,
220
            children: <TestSemantics>[
221
              TestSemantics(
222
                id: 2,
223
                children: <TestSemantics>[
224 225 226 227 228 229 230 231 232 233 234 235 236 237
                  TestSemantics(
                    id: 7,
                    children: <TestSemantics>[
                      TestSemantics(
                        id: 8,
                        flags: <SemanticsFlag>[
                          SemanticsFlag.namesRoute,
                          SemanticsFlag.isHeader,
                        ],
                        label: 'Semantics Test with Slivers',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
                  ),
238
                  TestSemantics(
239
                    id: 9,
240 241 242
                    flags: <SemanticsFlag>[
                      SemanticsFlag.hasImplicitScrolling,
                    ],
243 244 245 246 247
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
                    children: <TestSemantics>[
248
                      TestSemantics(
249 250 251 252
                        id: 3,
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
253
                      TestSemantics(
254 255 256 257
                        id: 4,
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
258
                      TestSemantics(
259 260 261 262
                        id: 5,
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
263
                      TestSemantics(
264 265 266 267 268 269
                        id: 6,
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
270
                  ),
271 272 273
                ],
              ),
            ],
274
          ),
275 276 277 278 279 280 281 282 283
        ],
      ),
      ignoreRect: true,
      ignoreTransform: true,
    ));

    semantics.dispose();
  });

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

    const double containerHeight = 200.0;

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

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

    semantics.dispose();
  });

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

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

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

    semantics.dispose();
454
  });
455 456

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

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

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

    semantics.dispose();
  });

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

570 571 572
    final ScrollController controller = ScrollController(initialScrollOffset: 280.0);
    final List<Widget> slivers = List<Widget>.generate(10, (int i) {
      return SliverToBoxAdapter(
573
        child: SizedBox(
574
          height: 200.0,
575
          child: Text('Item $i', textDirection: TextDirection.ltr),
576 577 578
        ),
      );
    });
579
    await tester.pumpWidget(Semantics(
580
      textDirection: TextDirection.ltr,
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598
      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'),
                ),
599 600
                ...slivers,
              ],
601
            ),
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
                    actions: <SemanticsAction>[
                      SemanticsAction.scrollUp,
                      SemanticsAction.scrollDown,
                    ],
633
                    flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
634
                    children: <TestSemantics>[
635
                      TestSemantics(
636 637 638 639
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 0',
                        textDirection: TextDirection.ltr,
                      ),
640
                      TestSemantics(
641 642 643
                        label: 'Item 1',
                        textDirection: TextDirection.ltr,
                      ),
644
                      TestSemantics(
645 646 647
                        label: 'Item 2',
                        textDirection: TextDirection.ltr,
                      ),
648
                      TestSemantics(
649 650 651
                        label: 'Item 3',
                        textDirection: TextDirection.ltr,
                      ),
652
                      TestSemantics(
653 654 655 656
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 4',
                        textDirection: TextDirection.ltr,
                      ),
657
                      TestSemantics(
658 659 660 661 662
                        flags: <SemanticsFlag>[SemanticsFlag.isHidden],
                        label: 'Item 5',
                        textDirection: TextDirection.ltr,
                      ),
                    ],
663 664
                  ),
                ],
665 666
              ),
            ],
667
          ),
668 669
        ],
      ),
670
      ignoreTransform: true,
671 672
      ignoreRect: true,
      ignoreId: true,
673 674 675 676 677 678
    ));

    semantics.dispose();
  });

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

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

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

    semantics.dispose();
  });

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

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

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

    semantics.dispose();
  });

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

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

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

    controller.jumpTo(-880.0);
    await tester.pumpAndSettle();
1048 1049 1050

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

    semantics.dispose();
  });

1125
}