Skip to content

Commit 6e04bdd

Browse files
authored
Fix scrolling in the Drawer and NavigationDrawer triggers AppBar's scrolledUnderElevation (#122600)
Fix scrolling in the `Drawer` and `NavigationDrawer` triggers AppBar's scrolledUnderElevation
1 parent 32b75f0 commit 6e04bdd

File tree

4 files changed

+100
-1
lines changed

4 files changed

+100
-1
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,8 @@ class _AppBarState extends State<AppBar> {
763763
}
764764

765765
void _handleScrollNotification(ScrollNotification notification) {
766-
if (notification is ScrollUpdateNotification && widget.notificationPredicate(notification)) {
766+
if (notification is ScrollUpdateNotification && widget.notificationPredicate(notification)
767+
&& Scaffold.isBodyDescendant(notification.context!)) {
767768
final bool oldScrolledUnder = _scrolledUnder;
768769
final ScrollMetrics metrics = notification.metrics;
769770
switch (metrics.axisDirection) {

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,6 +1994,17 @@ class Scaffold extends StatefulWidget {
19941994
}
19951995
}
19961996

1997+
/// Whether the given context is descendant of the [Scaffold.body].
1998+
///
1999+
/// This is used by [AppBar] to determine if the [AppBar] should listen to
2000+
/// [ScrollNotification] from the [Scrollable]s in the [Scaffold.body].
2001+
///
2002+
/// It returns true if the given context is descendant of the [Scaffold.body].
2003+
static bool isBodyDescendant(BuildContext context) {
2004+
final _BodyBuilder? body = context.findAncestorWidgetOfExactType<_BodyBuilder>();
2005+
return body != null;
2006+
}
2007+
19972008
@override
19982009
ScaffoldState createState() => ScaffoldState();
19992010
}

packages/flutter/test/material/app_bar_test.dart

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4501,4 +4501,75 @@ void main() {
45014501
expect(buttonWasPressed, isFalse);
45024502
});
45034503
});
4504+
4505+
testWidgets("scrolledUnderElevation only listens to scroll notifications from Scaffold's body", (WidgetTester tester) async {
4506+
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
4507+
Widget buildWidget() {
4508+
return MaterialApp(
4509+
theme: ThemeData(useMaterial3: true),
4510+
home: Scaffold(
4511+
key: scaffoldKey,
4512+
appBar: AppBar(),
4513+
drawer: Drawer(
4514+
child: ListView.builder(
4515+
itemCount: 10,
4516+
itemBuilder: (BuildContext context, int index) {
4517+
return ListTile(
4518+
title: Text('$index'),
4519+
);
4520+
}
4521+
),
4522+
),
4523+
body: ListView.builder(
4524+
itemCount: 10,
4525+
itemBuilder: (BuildContext context, int index) {
4526+
return ListTile(
4527+
title: Text('$index'),
4528+
);
4529+
}
4530+
),
4531+
),
4532+
);
4533+
}
4534+
4535+
await tester.pumpWidget(buildWidget());
4536+
4537+
// The first Material is the AppBar's Material.
4538+
Material getMaterial() => tester.widget<Material>(find.descendant(
4539+
of: find.byType(AppBar),
4540+
matching: find.byType(Material),
4541+
).first);
4542+
4543+
expect(getMaterial().elevation, 0.0);
4544+
4545+
// Scroll down the list view in the body.
4546+
await tester.drag(find.byType(ListView), const Offset(0.0, -300.0));
4547+
await tester.pumpAndSettle();
4548+
4549+
// AppBar should be elevated.
4550+
expect(getMaterial().elevation, 3.0);
4551+
4552+
// Scroll up the list view in the body.
4553+
await tester.drag(find.byType(ListView), const Offset(0.0, 300.0));
4554+
await tester.pumpAndSettle();
4555+
4556+
// AppBar should not be elevated.
4557+
expect(getMaterial().elevation, 0.0);
4558+
4559+
final ScaffoldState state = tester.firstState(find.byType(Scaffold));
4560+
4561+
// Open the drawer.
4562+
state.openDrawer();
4563+
await tester.pump();
4564+
await tester.pump(const Duration(seconds: 1));
4565+
4566+
expect(scaffoldKey.currentState!.isDrawerOpen, isTrue);
4567+
4568+
// Scroll down the list view in the drawer.
4569+
await tester.drag(find.byType(ListView).last, const Offset(0.0, -250.0));
4570+
await tester.pump();
4571+
4572+
// AppBar should still not be elevated.
4573+
expect(getMaterial().elevation, 0.0);
4574+
});
45044575
}

packages/flutter/test/material/scaffold_test.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2698,6 +2698,22 @@ void main() {
26982698

26992699
expect(tester.takeException(), isNull);
27002700
});
2701+
2702+
testWidgets('Scaffold.isBodyDescendant checks if given context is descendant of the body', (WidgetTester tester) async {
2703+
final GlobalKey scaffoldKey = GlobalKey();
2704+
final GlobalKey bodyChildKey = GlobalKey();
2705+
await tester.pumpWidget(
2706+
MaterialApp(
2707+
home: Scaffold(
2708+
key: scaffoldKey,
2709+
body: SizedBox(key: bodyChildKey),
2710+
),
2711+
),
2712+
);
2713+
2714+
expect(Scaffold.isBodyDescendant(scaffoldKey.currentContext!), false);
2715+
expect(Scaffold.isBodyDescendant(bodyChildKey.currentContext!), true);
2716+
});
27012717
}
27022718

27032719
class _GeometryListener extends StatefulWidget {

0 commit comments

Comments
 (0)