From 3af97d228cd5645eac4569a52df7dfbeadac244d Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Thu, 6 Aug 2020 16:16:15 -0700 Subject: [PATCH] =?UTF-8?q?Fix=20the=20flexible=20space=20bar=20to=20still?= =?UTF-8?q?=20create=20a=20rendering=20object=20even=20if=E2=80=A6=20(#626?= =?UTF-8?q?90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/src/material/flexible_space_bar.dart | 66 ++--- .../material/flexible_space_bar_test.dart | 244 ++++++++++++++++++ 2 files changed, 277 insertions(+), 33 deletions(-) diff --git a/packages/flutter/lib/src/material/flexible_space_bar.dart b/packages/flutter/lib/src/material/flexible_space_bar.dart index 57137c651fb..98da30c2ac5 100644 --- a/packages/flutter/lib/src/material/flexible_space_bar.dart +++ b/packages/flutter/lib/src/material/flexible_space_bar.dart @@ -303,42 +303,42 @@ class _FlexibleSpaceBarState extends State { const double fadeEnd = 1.0; assert(fadeStart <= fadeEnd); final double opacity = 1.0 - Interval(fadeStart, fadeEnd).transform(t); - if (opacity > 0.0) { - double height = settings.maxExtent; + double height = settings.maxExtent; - // StretchMode.zoomBackground - if (widget.stretchModes.contains(StretchMode.zoomBackground) && - constraints.maxHeight > height) { - height = constraints.maxHeight; - } + // StretchMode.zoomBackground + if (widget.stretchModes.contains(StretchMode.zoomBackground) && + constraints.maxHeight > height) { + height = constraints.maxHeight; + } + children.add(Positioned( + top: _getCollapsePadding(t, settings), + left: 0.0, + right: 0.0, + height: height, + child: Opacity( + // IOS is relying on this semantics node to correctly traverse + // through the app bar when it is collapsed. + alwaysIncludeSemantics: true, + opacity: opacity, + child: widget.background, + ), + )); - children.add(Positioned( - top: _getCollapsePadding(t, settings), - left: 0.0, - right: 0.0, - height: height, - child: Opacity( - opacity: opacity, - child: widget.background, - ), - )); - - // StretchMode.blurBackground - if (widget.stretchModes.contains(StretchMode.blurBackground) && - constraints.maxHeight > settings.maxExtent) { - final double blurAmount = (constraints.maxHeight - settings.maxExtent) / 10; - children.add(Positioned.fill( - child: BackdropFilter( - child: Container( - color: Colors.transparent, - ), - filter: ui.ImageFilter.blur( - sigmaX: blurAmount, - sigmaY: blurAmount, - ) + // StretchMode.blurBackground + if (widget.stretchModes.contains(StretchMode.blurBackground) && + constraints.maxHeight > settings.maxExtent) { + final double blurAmount = (constraints.maxHeight - settings.maxExtent) / 10; + children.add(Positioned.fill( + child: BackdropFilter( + child: Container( + color: Colors.transparent, + ), + filter: ui.ImageFilter.blur( + sigmaX: blurAmount, + sigmaY: blurAmount, ) - )); - } + ) + )); } } diff --git a/packages/flutter/test/material/flexible_space_bar_test.dart b/packages/flutter/test/material/flexible_space_bar_test.dart index f8d6c8247d2..bdc77511bb8 100644 --- a/packages/flutter/test/material/flexible_space_bar_test.dart +++ b/packages/flutter/test/material/flexible_space_bar_test.dart @@ -7,6 +7,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../widgets/semantics_tester.dart'; + void main() { testWidgets('FlexibleSpaceBar centers title on iOS', (WidgetTester tester) async { await tester.pumpWidget( @@ -119,6 +121,248 @@ void main() { expect(clipRect.size.height, minExtent); }); + testWidgets('Collpased FlexibleSpaceBar has correct semantics', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + const double expandedHeight = 200; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomScrollView( + slivers: [ + const SliverAppBar( + pinned: true, + expandedHeight: expandedHeight, + title: Text('Title'), + flexibleSpace: FlexibleSpaceBar( + background: Text('Expanded title'), + ), + ), + SliverList( + delegate: SliverChildListDelegate( + [ + for (int i = 0; i < 50; i++) + Container( + height: 200, + child: Center(child: Text('Item $i')), + ), + ], + ), + ), + ], + ), + ), + ), + ); + + TestSemantics expectedSemantics = TestSemantics.root( + children: [ + TestSemantics.rootChild( + id: 1, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 2, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 3, + rect: TestSemantics.fullScreen, + flags: [SemanticsFlag.scopesRoute], + children: [ + TestSemantics( + id: 4, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 9, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, expandedHeight), + children: [ + TestSemantics( + id: 11, + rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 20.0), + flags: [ + SemanticsFlag.isHeader, + SemanticsFlag.namesRoute + ], + label: 'Title', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 10, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, expandedHeight), + label: 'Expanded title', + textDirection: TextDirection.ltr, + ), + ], + ), + TestSemantics( + id: 12, + flags: [SemanticsFlag.hasImplicitScrolling], + rect: TestSemantics.fullScreen, + actions: [SemanticsAction.scrollUp], + children: [ + TestSemantics( + id: 5, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + label: 'Item 0', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 6, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + label: 'Item 1', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 7, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + flags: [SemanticsFlag.isHidden], + label: 'Item 2', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 8, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0), + flags: [SemanticsFlag.isHidden], + label: 'Item 3', + textDirection: TextDirection.ltr, + ), + + ], + ), + ], + ), + ], + ), + ], + ), + ], + ), + ], + ); + + expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true)); + + // We drag up to fully collapse the space bar. + await tester.drag(find.text('Item 1'), const Offset(0, -600.0)); + await tester.pumpAndSettle(); + + expectedSemantics = TestSemantics.root( + children: [ + TestSemantics.rootChild( + id: 1, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 2, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 3, + rect: TestSemantics.fullScreen, + flags: [SemanticsFlag.scopesRoute], + children: [ + TestSemantics( + id: 4, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 9, + // The app bar is collapsed. + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 56.0), + children: [ + TestSemantics( + id: 11, + rect: const Rect.fromLTRB(0.0, 0.0, 100.0, 20.0), + flags: [ + SemanticsFlag.isHeader, + SemanticsFlag.namesRoute + ], + label: 'Title', + textDirection: TextDirection.ltr, + ), + // The flexible space bar still persists in the + // semantic tree even if it is collapsed. + TestSemantics( + id: 10, + rect: const Rect.fromLTRB(0.0, 36.0, 800.0, 92.0), + label: 'Expanded title', + textDirection: TextDirection.ltr, + ), + ], + ), + TestSemantics( + id: 12, + flags: [SemanticsFlag.hasImplicitScrolling], + rect: TestSemantics.fullScreen, + actions: [SemanticsAction.scrollUp, SemanticsAction.scrollDown], + children: [ + TestSemantics( + id: 5, + rect: const Rect.fromLTRB(0.0, 150.0, 800.0, 200.0), + flags: [SemanticsFlag.isHidden], + label: 'Item 0', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 6, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + flags: [SemanticsFlag.isHidden], + label: 'Item 1', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 7, + rect: const Rect.fromLTRB(0.0, 56.0, 800.0, 200.0), + label: 'Item 2', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 8, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + label: 'Item 3', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 13, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + label: 'Item 4', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 14, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), + flags: [SemanticsFlag.isHidden], + label: 'Item 5', + textDirection: TextDirection.ltr, + ), + TestSemantics( + id: 15, + rect: const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0), + flags: [SemanticsFlag.isHidden], + label: 'Item 6', + textDirection: TextDirection.ltr, + ), + + + ], + ), + ], + ), + ], + ), + ], + ), + ], + ), + ], + ); + + expect(semantics, hasSemantics(expectedSemantics, ignoreTransform: true)); + + semantics.dispose(); + }); + // This is a regression test for https://github.com/flutter/flutter/issues/14227 testWidgets('FlexibleSpaceBar sets width constraints for the title', (WidgetTester tester) async { const double titleFontSize = 20.0;