Skip to content

Commit

Permalink
Include forceElevated for scrolledUnder in new SliverAppBar variants …
Browse files Browse the repository at this point in the history
…(#104536)
  • Loading branch information
Piinks authored May 25, 2022
1 parent 2e7cea6 commit fe04647
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/flutter/lib/src/material/app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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,
Expand Down
167 changes: 167 additions & 0 deletions packages/flutter/test/widgets/nested_scroll_view_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit fe04647

Please sign in to comment.