Skip to content

Commit 32157e3

Browse files
authored
AppBar: Fix nested scroll view doesn't update AppBar elevation for Material 3 (#103899)
1 parent 64cba0e commit 32157e3

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

packages/flutter/lib/src/material/app_bar.dart

+10-1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
162162
this.bottom,
163163
this.elevation,
164164
this.scrolledUnderElevation,
165+
this.notificationPredicate = defaultScrollNotificationPredicate,
165166
this.shadowColor,
166167
this.surfaceTintColor,
167168
this.shape,
@@ -197,6 +198,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
197198
this.systemOverlayStyle,
198199
}) : assert(automaticallyImplyLeading != null),
199200
assert(elevation == null || elevation >= 0.0),
201+
assert(notificationPredicate != null),
200202
assert(primary != null),
201203
assert(toolbarOpacity != null),
202204
assert(bottomOpacity != null),
@@ -421,6 +423,13 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
421423
/// shadow.
422424
final double? scrolledUnderElevation;
423425

426+
/// A check that specifies which child's [ScrollNotification]s should be
427+
/// listened to.
428+
///
429+
/// By default, checks whether `notification.depth == 0`. Set it to something
430+
/// else for more complicated layouts.
431+
final ScrollNotificationPredicate notificationPredicate;
432+
424433
/// {@template flutter.material.appbar.shadowColor}
425434
/// The color of the shadow below the app bar.
426435
///
@@ -809,7 +818,7 @@ class _AppBarState extends State<AppBar> {
809818
}
810819

811820
void _handleScrollNotification(ScrollNotification notification) {
812-
if (notification is ScrollUpdateNotification && notification.depth == 0) {
821+
if (notification is ScrollUpdateNotification && widget.notificationPredicate(notification)) {
813822
final bool oldScrolledUnder = _scrolledUnder;
814823
final ScrollMetrics metrics = notification.metrics;
815824
switch (metrics.axisDirection) {

packages/flutter/test/material/app_bar_test.dart

+47
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,53 @@ void main() {
10111011
expect(getMaterial().elevation, 10);
10121012
});
10131013

1014+
testWidgets('scrolledUnderElevation with nested scroll view', (WidgetTester tester) async {
1015+
Widget buildAppBar({double? scrolledUnderElevation}) {
1016+
return MaterialApp(
1017+
theme: ThemeData(useMaterial3: true),
1018+
home: Scaffold(
1019+
appBar: AppBar(
1020+
title: const Text('Title'),
1021+
scrolledUnderElevation: scrolledUnderElevation,
1022+
notificationPredicate: (ScrollNotification notification) {
1023+
return notification.depth == 1;
1024+
},
1025+
),
1026+
body: ListView.builder(
1027+
scrollDirection: Axis.horizontal,
1028+
itemCount: 4,
1029+
itemBuilder: (BuildContext context, int index) {
1030+
return SizedBox(
1031+
height: 600.0,
1032+
width: 800.0,
1033+
child: ListView.builder(
1034+
itemCount: 100,
1035+
itemBuilder: (BuildContext context, int index) =>
1036+
ListTile(title: Text('Item $index')),
1037+
),
1038+
);
1039+
},
1040+
),
1041+
),
1042+
);
1043+
}
1044+
1045+
Material getMaterial() => tester.widget<Material>(find.descendant(
1046+
of: find.byType(AppBar),
1047+
matching: find.byType(Material),
1048+
));
1049+
1050+
await tester.pumpWidget(buildAppBar(scrolledUnderElevation: 10));
1051+
// Starts with the base elevation.
1052+
expect(getMaterial().elevation, 0.0);
1053+
1054+
await tester.fling(find.text('Item 2'), const Offset(0.0, -600.0), 2000.0);
1055+
await tester.pumpAndSettle();
1056+
1057+
// After scrolling it should be the scrolledUnderElevation.
1058+
expect(getMaterial().elevation, 10);
1059+
});
1060+
10141061
group('SliverAppBar elevation', () {
10151062
Widget buildSliverAppBar(bool forceElevated, {double? elevation, double? themeElevation}) {
10161063
return MaterialApp(

0 commit comments

Comments
 (0)