diff --git a/packages/flutter/lib/src/material/navigation_drawer.dart b/packages/flutter/lib/src/material/navigation_drawer.dart index eb3f5ec2edd..99932ba48bf 100644 --- a/packages/flutter/lib/src/material/navigation_drawer.dart +++ b/packages/flutter/lib/src/material/navigation_drawer.dart @@ -57,6 +57,8 @@ class NavigationDrawer extends StatelessWidget { const NavigationDrawer({ super.key, required this.children, + this.header, + this.footer, this.backgroundColor, this.shadowColor, this.surfaceTintColor, @@ -120,6 +122,16 @@ class NavigationDrawer extends StatelessWidget { /// widgets like headlines and dividers. final List children; + /// A widget to display at the top of the layout. + /// + /// Typically used for titles, navigation bars, or other header content. + final Widget? header; + + /// A widget to display at the bottom of the layout. + /// + /// Typically used for actions, navigation controls, or other footer content. + final Widget? footer; + /// The index into destinations for the current selected /// [NavigationDrawerDestination] or null if no destination is selected. /// @@ -173,7 +185,16 @@ class NavigationDrawer extends StatelessWidget { shadowColor: shadowColor ?? navigationDrawerTheme.shadowColor, surfaceTintColor: surfaceTintColor ?? navigationDrawerTheme.surfaceTintColor, elevation: elevation ?? navigationDrawerTheme.elevation, - child: SafeArea(bottom: false, child: ListView(children: wrappedChildren)), + child: SafeArea( + bottom: false, + child: Column( + children: [ + if (header != null) header!, + Expanded(child: ListView(children: wrappedChildren)), + if (footer != null) footer!, + ], + ), + ), ); } } diff --git a/packages/flutter/test/material/navigation_drawer_test.dart b/packages/flutter/test/material/navigation_drawer_test.dart index fde25e676a6..06f217f94a2 100644 --- a/packages/flutter/test/material/navigation_drawer_test.dart +++ b/packages/flutter/test/material/navigation_drawer_test.dart @@ -488,6 +488,46 @@ void main() { await tester.pumpAndSettle(); }); + + testWidgets('NavigationDrawer can display header and footer', (WidgetTester tester) async { + final GlobalKey scaffoldKey = GlobalKey(); + widgetSetup(tester, 3000, viewHeight: 3000); + final Widget widget = _buildWidget( + scaffoldKey, + NavigationDrawer( + header: const DrawerHeader( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 8, + children: [FlutterLogo(), Text('Header')], + ), + ), + ), + footer: ListTile( + leading: const FlutterLogo(), + title: const Text('Footer'), + trailing: const Icon(Icons.settings), + onTap: () {}, + ), + children: [ + for (int i = 0; i < 10; i++) + NavigationDrawerDestination(icon: const Icon(Icons.home), label: Text('Item $i')), + ], + ), + ); + + await tester.pumpWidget(widget); + scaffoldKey.currentState!.openDrawer(); + await tester.pump(const Duration(seconds: 1)); + + expect(find.byType(DrawerHeader), findsOneWidget); + expect(find.text('Header'), findsOneWidget); + expect(find.byType(FlutterLogo), findsNWidgets(2)); + expect(find.byType(ListTile), findsOneWidget); + expect(find.text('Footer'), findsOneWidget); + expect(find.byIcon(Icons.settings), findsOneWidget); + }); } Widget _buildWidget(GlobalKey scaffoldKey, Widget child, {bool? useMaterial3}) {