| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:ui'; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import 'semantics_tester.dart'; |
| |
| void main() { |
| group('Sliver Semantics', () { |
| setUp(() { |
| debugResetSemanticsIdCounter(); |
| }); |
| |
| _tests(); |
| }); |
| } |
| |
| void _tests() { |
| testWidgets('excludeFromScrollable works correctly', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| const double appBarExpandedHeight = 200.0; |
| |
| final ScrollController scrollController = ScrollController(); |
| final List<Widget> listChildren = List<Widget>.generate(30, (int i) { |
| return SizedBox( |
| height: appBarExpandedHeight, |
| child: Text('Item $i'), |
| ); |
| }); |
| await tester.pumpWidget( |
| Semantics( |
| textDirection: TextDirection.ltr, |
| 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), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // AppBar is child of node with semantic scroll actions. |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 1, |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 2, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 7, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 8, |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'Semantics Test with Slivers', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| id: 9, |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| actions: <SemanticsAction>[SemanticsAction.scrollUp], |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 3, |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 4, |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 5, |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 6, |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreRect: true, |
| ignoreTransform: true, |
| )); |
| |
| // 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( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 1, |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 2, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 7, |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 8, |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'Semantics Test with Slivers', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| id: 9, |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 3, |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 4, |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 5, |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 6, |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 10, |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| 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( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 1, |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 2, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 7, |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 8, |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'Semantics Test with Slivers', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| id: 9, |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| id: 3, |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 4, |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 5, |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| id: 6, |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreRect: true, |
| ignoreTransform: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Offscreen sliver are hidden in semantics tree', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| const double containerHeight = 200.0; |
| |
| final ScrollController scrollController = ScrollController( |
| initialScrollOffset: containerHeight * 1.5, |
| ); |
| final List<Widget> slivers = List<Widget>.generate(30, (int i) { |
| return SliverToBoxAdapter( |
| child: SizedBox( |
| height: containerHeight, |
| child: Text('Item $i', textDirection: TextDirection.ltr), |
| ), |
| ); |
| }); |
| await tester.pumpWidget( |
| Semantics( |
| textDirection: TextDirection.ltr, |
| 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, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreRect: true, |
| ignoreTransform: true, |
| ignoreId: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('SemanticsNodes of Slivers are in paint order', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| final List<Widget> slivers = List<Widget>.generate(5, (int i) { |
| return SliverToBoxAdapter( |
| child: SizedBox( |
| height: 20.0, |
| child: Text('Item $i'), |
| ), |
| ); |
| }); |
| await tester.pumpWidget( |
| Semantics( |
| textDirection: TextDirection.ltr, |
| child: Localizations( |
| locale: const Locale('en', 'us'), |
| delegates: const <LocalizationsDelegate<dynamic>>[ |
| DefaultWidgetsLocalizations.delegate, |
| DefaultMaterialLocalizations.delegate, |
| ], |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: CustomScrollView( |
| slivers: slivers, |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| label: 'Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreRect: true, |
| ignoreTransform: true, |
| ignoreId: true, |
| childOrder: DebugSemanticsDumpOrder.inverseHitTest, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('SemanticsNodes of a sliver fully covered by another overlapping sliver are excluded', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| final List<Widget> listChildren = List<Widget>.generate(10, (int i) { |
| return SizedBox( |
| height: 200.0, |
| child: Text('Item $i', textDirection: TextDirection.ltr), |
| ); |
| }); |
| final ScrollController controller = ScrollController(initialScrollOffset: 280.0); |
| await tester.pumpWidget(Semantics( |
| textDirection: TextDirection.ltr, |
| 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, |
| ), |
| ), |
| ), |
| ), |
| )); |
| |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'AppBar', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 5', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreTransform: true, |
| ignoreId: true, |
| ignoreRect: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Slivers fully covered by another overlapping sliver are hidden', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| final ScrollController controller = ScrollController(initialScrollOffset: 280.0); |
| final List<Widget> slivers = List<Widget>.generate(10, (int i) { |
| return SliverToBoxAdapter( |
| child: SizedBox( |
| height: 200.0, |
| child: Text('Item $i', textDirection: TextDirection.ltr), |
| ), |
| ); |
| }); |
| await tester.pumpWidget(Semantics( |
| textDirection: TextDirection.ltr, |
| 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'), |
| ), |
| ...slivers, |
| ], |
| ), |
| ), |
| ), |
| ), |
| )); |
| |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'AppBar', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 5', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreTransform: true, |
| ignoreRect: true, |
| ignoreId: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('SemanticsNodes of a sliver fully covered by another overlapping sliver are excluded (reverse)', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| final List<Widget> listChildren = List<Widget>.generate(10, (int i) { |
| return SizedBox( |
| height: 200.0, |
| child: Text('Item $i', textDirection: TextDirection.ltr), |
| ); |
| }); |
| final ScrollController controller = ScrollController(initialScrollOffset: 280.0); |
| await tester.pumpWidget(Semantics( |
| textDirection: TextDirection.ltr, |
| 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, |
| ), |
| ), |
| ), |
| ), |
| )); |
| |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 5', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'AppBar', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreTransform: true, |
| ignoreId: true, |
| ignoreRect: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Slivers fully covered by another overlapping sliver are hidden (reverse)', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| final ScrollController controller = ScrollController(initialScrollOffset: 280.0); |
| final List<Widget> slivers = List<Widget>.generate(10, (int i) { |
| return SliverToBoxAdapter( |
| child: SizedBox( |
| height: 200.0, |
| child: Text('Item $i', textDirection: TextDirection.ltr), |
| ), |
| ); |
| }); |
| await tester.pumpWidget(Semantics( |
| textDirection: TextDirection.ltr, |
| 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'), |
| ), |
| ...slivers, |
| ], |
| ), |
| ), |
| ), |
| ), |
| )); |
| |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 5', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'AppBar', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreTransform: true, |
| ignoreId: true, |
| ignoreRect: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| testWidgets('Slivers fully covered by another overlapping sliver are hidden (with center sliver)', (WidgetTester tester) async { |
| final SemanticsTester semantics = SemanticsTester(tester); |
| |
| 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 SizedBox( |
| height: 200.0, |
| child: Text('Forward Item $i', textDirection: TextDirection.ltr), |
| ); |
| }); |
| final List<Widget> backwardChildren = List<Widget>.generate(10, (int i) { |
| return SizedBox( |
| height: 200.0, |
| child: Text('Backward Item $i', textDirection: TextDirection.ltr), |
| ); |
| }); |
| await tester.pumpWidget(Semantics( |
| textDirection: TextDirection.ltr, |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| 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), |
| ), |
| const SliverAppBar( |
| pinned: true, |
| expandedHeight: 100.0, |
| flexibleSpace: FlexibleSpaceBar( |
| title: Text('Backward app bar', textDirection: TextDirection.ltr), |
| ), |
| ), |
| SliverAppBar( |
| pinned: true, |
| key: forwardAppBarKey, |
| expandedHeight: 100.0, |
| flexibleSpace: const FlexibleSpaceBar( |
| title: Text('Forward app bar', textDirection: TextDirection.ltr), |
| ), |
| ), |
| SliverList( |
| delegate: SliverChildListDelegate(forwardChildren), |
| ), |
| ], |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| )); |
| |
| // 'Forward Item 0' is covered by app bar. |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics(), |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'Forward app bar', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| TestSemantics( |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Forward Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Forward Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Forward Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Forward Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Forward Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Forward Item 5', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ignoreTransform: true, |
| ignoreRect: true, |
| ignoreId: true, |
| )); |
| |
| controller.jumpTo(-880.0); |
| await tester.pumpAndSettle(); |
| |
| // 'Backward Item 0' is covered by app bar. |
| expect(semantics, hasSemantics( |
| TestSemantics.root( |
| children: <TestSemantics>[ |
| TestSemantics( |
| textDirection: TextDirection.ltr, |
| children: <TestSemantics>[ |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.hasImplicitScrolling, |
| ], |
| actions: <SemanticsAction>[ |
| SemanticsAction.scrollUp, |
| SemanticsAction.scrollDown, |
| ], |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Backward Item 5', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Backward Item 4', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Backward Item 3', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Backward Item 2', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| label: 'Backward Item 1', |
| textDirection: TextDirection.ltr, |
| ), |
| TestSemantics( |
| flags: <SemanticsFlag>[SemanticsFlag.isHidden], |
| label: 'Backward Item 0', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| TestSemantics( |
| tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], |
| children: <TestSemantics>[ |
| TestSemantics(), |
| TestSemantics( |
| children: <TestSemantics>[ |
| TestSemantics( |
| flags: <SemanticsFlag>[ |
| SemanticsFlag.namesRoute, |
| SemanticsFlag.isHeader, |
| ], |
| label: 'Backward app bar', |
| textDirection: TextDirection.ltr, |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), |
| ], |
| ), ignoreTransform: true, ignoreRect: true, ignoreId: true, |
| )); |
| |
| semantics.dispose(); |
| }); |
| |
| } |