Include forceElevated for scrolledUnder in new SliverAppBar variants (#104536)

This commit is contained in:
Kate Lovett 2022-05-25 16:23:13 -05:00 committed by GitHub
parent 2e7cea6dfe
commit fe04647651
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 169 additions and 2 deletions

View file

@ -1284,7 +1284,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final double extraToolbarHeight = math.max(minExtent - _bottomHeight - topPadding - (toolbarHeight ?? kToolbarHeight), 0.0);
final double visibleToolbarHeight = visibleMainHeight - _bottomHeight - extraToolbarHeight;
final bool isScrolledUnder = overlapsContent || (pinned && shrinkOffset > maxExtent - minExtent);
final bool isScrolledUnder = overlapsContent || forceElevated || (pinned && shrinkOffset > maxExtent - minExtent);
final bool isPinnedWithOpacityFade = pinned && floating && bottom != null && extraToolbarHeight == 0.0;
final double toolbarOpacity = !pinned || isPinnedWithOpacityFade
? clampDouble(visibleToolbarHeight / (toolbarHeight ?? kToolbarHeight), 0.0, 1.0)
@ -1308,7 +1308,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
)
: flexibleSpace,
bottom: bottom,
elevation: forceElevated || isScrolledUnder ? elevation : 0.0,
elevation: isScrolledUnder ? elevation : 0.0,
scrolledUnderElevation: scrolledUnderElevation,
shadowColor: shadowColor,
surfaceTintColor: surfaceTintColor,

View file

@ -2552,8 +2552,175 @@ void main() {
expect(scrollStarted, 2);
expect(scrollEnded, 2);
});
testWidgets('SliverAppBar.medium collapses in NestedScrollView', (WidgetTester tester) async {
final GlobalKey<NestedScrollViewState> nestedScrollView = GlobalKey();
const double collapsedAppBarHeight = 64;
const double expandedAppBarHeight = 112;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: NestedScrollView(
key: nestedScrollView,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar.medium(
title: const Text('AppBar Title'),
),
),
];
},
body: Builder(
builder: (BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => ListTile(title: Text('Item $index')),
childCount: 30,
),
),
],
);
},
),
),
),
));
// There are two widgets for the title.
final Finder expandedTitle = find.text('AppBar Title').last;
final Finder expandedTitleClip = find.ancestor(
of: expandedTitle,
matching: find.byType(ClipRect),
);
// Default, fully expanded app bar.
expect(nestedScrollView.currentState?.outerController.offset, 0);
expect(nestedScrollView.currentState?.innerController.offset, 0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), expandedAppBarHeight);
expect(tester.getSize(expandedTitleClip).height, expandedAppBarHeight - collapsedAppBarHeight);
// Scroll the expanded app bar partially out of view.
final Offset point1 = tester.getCenter(find.text('Item 5'));
await tester.dragFrom(point1, const Offset(0.0, -45.0));
await tester.pump();
expect(nestedScrollView.currentState?.outerController.offset, 45.0);
expect(nestedScrollView.currentState?.innerController.offset, 0.0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), expandedAppBarHeight - 45);
expect(tester.getSize(expandedTitleClip).height, expandedAppBarHeight - collapsedAppBarHeight - 45);
// Scroll so that it is completely collapsed.
await tester.dragFrom(point1, const Offset(0.0, -555.0));
await tester.pump();
expect(nestedScrollView.currentState?.outerController.offset, 48.0);
expect(nestedScrollView.currentState?.innerController.offset, 552.0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), collapsedAppBarHeight);
expect(tester.getSize(expandedTitleClip).height, 0);
// Scroll back to fully expanded.
await tester.dragFrom(point1, const Offset(0.0, 600.0));
await tester.pump();
expect(nestedScrollView.currentState?.outerController.offset, 0);
expect(nestedScrollView.currentState?.innerController.offset, 0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), expandedAppBarHeight);
expect(tester.getSize(expandedTitleClip).height, expandedAppBarHeight - collapsedAppBarHeight);
});
testWidgets('SliverAppBar.large collapses in NestedScrollView', (WidgetTester tester) async {
final GlobalKey<NestedScrollViewState> nestedScrollView = GlobalKey();
const double collapsedAppBarHeight = 64;
const double expandedAppBarHeight = 152;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: NestedScrollView(
key: nestedScrollView,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar.large(
title: const Text('AppBar Title'),
forceElevated: innerBoxIsScrolled,
),
),
];
},
body: Builder(
builder: (BuildContext context) {
return CustomScrollView(
slivers: <Widget>[
SliverOverlapInjector(handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context)),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => ListTile(title: Text('Item $index')),
childCount: 30,
),
),
],
);
},
),
),
),
));
// There are two widgets for the title.
final Finder expandedTitle = find.text('AppBar Title').last;
final Finder expandedTitleClip = find.ancestor(
of: expandedTitle,
matching: find.byType(ClipRect),
);
// Default, fully expanded app bar.
expect(nestedScrollView.currentState?.outerController.offset, 0);
expect(nestedScrollView.currentState?.innerController.offset, 0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), expandedAppBarHeight);
expect(tester.getSize(expandedTitleClip).height, expandedAppBarHeight - collapsedAppBarHeight);
// Scroll the expanded app bar partially out of view.
final Offset point1 = tester.getCenter(find.text('Item 5'));
await tester.dragFrom(point1, const Offset(0.0, -45.0));
await tester.pump();
expect(nestedScrollView.currentState?.outerController.offset, 45.0);
expect(nestedScrollView.currentState?.innerController.offset, 0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), expandedAppBarHeight - 45);
expect(tester.getSize(expandedTitleClip).height, expandedAppBarHeight - collapsedAppBarHeight - 45);
// Scroll so that it is completely collapsed.
await tester.dragFrom(point1, const Offset(0.0, -555.0));
await tester.pump();
expect(nestedScrollView.currentState?.outerController.offset, 88.0);
expect(nestedScrollView.currentState?.innerController.offset, 512.0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), collapsedAppBarHeight);
expect(tester.getSize(expandedTitleClip).height, 0);
// Scroll back to fully expanded.
await tester.dragFrom(point1, const Offset(0.0, 600.0));
await tester.pump();
expect(nestedScrollView.currentState?.outerController.offset, 0);
expect(nestedScrollView.currentState?.innerController.offset, 0);
expect(find.byType(SliverAppBar), findsOneWidget);
expect(appBarHeight(tester), expandedAppBarHeight);
expect(tester.getSize(expandedTitleClip).height, expandedAppBarHeight - collapsedAppBarHeight);
});
}
double appBarHeight(WidgetTester tester) => tester.getSize(find.byType(AppBar, skipOffstage: false)).height;
class TestHeader extends SliverPersistentHeaderDelegate {
const TestHeader({ this.key });
final Key? key;