Skip to content

Commit 0e57147

Browse files
authored
nav drawer (#115668)
1 parent 01c1e8e commit 0e57147

14 files changed

+1711
-34
lines changed

dev/tools/gen_defaults/bin/gen_defaults.dart

+4
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ import 'package:gen_defaults/checkbox_template.dart';
2929
import 'package:gen_defaults/color_scheme_template.dart';
3030
import 'package:gen_defaults/dialog_template.dart';
3131
import 'package:gen_defaults/divider_template.dart';
32+
import 'package:gen_defaults/drawer_template.dart';
3233
import 'package:gen_defaults/fab_template.dart';
3334
import 'package:gen_defaults/filter_chip_template.dart';
3435
import 'package:gen_defaults/icon_button_template.dart';
3536
import 'package:gen_defaults/input_chip_template.dart';
3637
import 'package:gen_defaults/input_decorator_template.dart';
3738
import 'package:gen_defaults/menu_template.dart';
3839
import 'package:gen_defaults/navigation_bar_template.dart';
40+
import 'package:gen_defaults/navigation_drawer_template.dart';
3941
import 'package:gen_defaults/navigation_rail_template.dart';
4042
import 'package:gen_defaults/popup_menu_template.dart';
4143
import 'package:gen_defaults/progress_indicator_template.dart';
@@ -144,6 +146,7 @@ Future<void> main(List<String> args) async {
144146
DialogFullscreenTemplate('DialogFullscreen', '$materialLib/dialog.dart', tokens).updateFile();
145147
DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
146148
DividerTemplate('Divider', '$materialLib/divider.dart', tokens).updateFile();
149+
DrawerTemplate('Drawer', '$materialLib/drawer.dart', tokens).updateFile();
147150
FABTemplate('FAB', '$materialLib/floating_action_button.dart', tokens).updateFile();
148151
FilterChipTemplate('ChoiceChip', '$materialLib/choice_chip.dart', tokens).updateFile();
149152
FilterChipTemplate('FilterChip', '$materialLib/filter_chip.dart', tokens).updateFile();
@@ -152,6 +155,7 @@ Future<void> main(List<String> args) async {
152155
InputDecoratorTemplate('InputDecorator', '$materialLib/input_decorator.dart', tokens).updateFile();
153156
MenuTemplate('Menu', '$materialLib/menu_anchor.dart', tokens).updateFile();
154157
NavigationBarTemplate('NavigationBar', '$materialLib/navigation_bar.dart', tokens).updateFile();
158+
NavigationDrawerTemplate('NavigationDrawer', '$materialLib/navigation_drawer.dart', tokens).updateFile();
155159
NavigationRailTemplate('NavigationRail', '$materialLib/navigation_rail.dart', tokens).updateFile();
156160
PopupMenuTemplate('PopupMenu', '$materialLib/popup_menu.dart', tokens).updateFile();
157161
ProgressIndicatorTemplate('ProgressIndicator', '$materialLib/progress_indicator.dart', tokens).updateFile();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'template.dart';
6+
7+
class DrawerTemplate extends TokenTemplate {
8+
const DrawerTemplate(super.blockName, super.fileName, super.tokens);
9+
10+
@override
11+
String generate() => '''
12+
class _${blockName}DefaultsM3 extends DrawerThemeData {
13+
const _${blockName}DefaultsM3(this.context)
14+
: super(elevation: ${elevation("md.comp.navigation-drawer.modal.container")});
15+
16+
final BuildContext context;
17+
18+
@override
19+
Color? get backgroundColor => ${componentColor("md.comp.navigation-drawer.container")};
20+
21+
@override
22+
Color? get surfaceTintColor => ${colorOrTransparent("md.comp.navigation-drawer.container.surface-tint-layer.color")};
23+
24+
@override
25+
Color? get shadowColor => ${colorOrTransparent("md.comp.navigation-drawer.container.shadow-color")};
26+
27+
// This don't appear to be tokens for this value, but it is
28+
// shown in the spec.
29+
@override
30+
ShapeBorder? get shape => const RoundedRectangleBorder(
31+
borderRadius: BorderRadius.horizontal(right: Radius.circular(16.0)),
32+
);
33+
34+
// This don't appear to be tokens for this value, but it is
35+
// shown in the spec.
36+
@override
37+
ShapeBorder? get endShape => const RoundedRectangleBorder(
38+
borderRadius: BorderRadius.horizontal(left: Radius.circular(16.0)),
39+
);
40+
}
41+
''';
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'template.dart';
6+
7+
class NavigationDrawerTemplate extends TokenTemplate {
8+
const NavigationDrawerTemplate(super.blockName, super.fileName, super.tokens);
9+
10+
@override
11+
String generate() => '''
12+
class _${blockName}DefaultsM3 extends NavigationDrawerThemeData {
13+
const _${blockName}DefaultsM3(this.context)
14+
: super(
15+
elevation: ${elevation("md.comp.navigation-drawer.modal.container")},
16+
tileHeight: ${tokens["md.comp.navigation-drawer.active-indicator.height"]},
17+
indicatorShape: ${shape("md.comp.navigation-drawer.active-indicator")},
18+
indicatorSize: const Size(${tokens["md.comp.navigation-drawer.active-indicator.width"]}, ${tokens["md.comp.navigation-drawer.active-indicator.height"]}),
19+
);
20+
21+
final BuildContext context;
22+
23+
@override
24+
Color? get backgroundColor => ${componentColor("md.comp.navigation-drawer.container")};
25+
26+
@override
27+
Color? get surfaceTintColor => ${colorOrTransparent("md.comp.navigation-drawer.container.surface-tint-layer.color")};
28+
29+
@override
30+
Color? get shadowColor => ${colorOrTransparent("md.comp.navigation-drawer.container.shadow-color")};
31+
32+
@override
33+
Color? get indicatorColor => ${componentColor("md.comp.navigation-drawer.active-indicator")};
34+
35+
@override
36+
MaterialStateProperty<IconThemeData?>? get iconTheme {
37+
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
38+
return IconThemeData(
39+
size: ${tokens["md.comp.navigation-drawer.icon.size"]},
40+
color: states.contains(MaterialState.selected)
41+
? ${componentColor("md.comp.navigation-drawer.active.icon.")}
42+
: ${componentColor("md.comp.navigation-drawer.inactive.icon")},
43+
);
44+
});
45+
}
46+
47+
@override
48+
MaterialStateProperty<TextStyle?>? get labelTextStyle {
49+
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
50+
final TextStyle style = ${textStyle("md.comp.navigation-drawer.label-text")}!;
51+
return style.apply(
52+
color: states.contains(MaterialState.selected)
53+
? ${componentColor("md.comp.navigation-drawer.active.label-text")}
54+
: ${componentColor("md.comp.navigation-drawer.inactive.label-text")},
55+
);
56+
});
57+
}
58+
}
59+
''';
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flutter code sample for [NavigationDrawer] .
6+
7+
// Builds an adaptive navigation widget layout. When the screen width is less than
8+
// 450, A [NavigationBar] will be displayed. Otherwise, a [NavigationRail] will be
9+
// displayed on the left side, and also a button to open the [NavigationDrawer].
10+
// All of these navigation widgets are built from an indentical list of data.
11+
12+
import 'package:flutter/material.dart';
13+
14+
class ExampleDestination {
15+
const ExampleDestination(this.label, this.icon, this.selectedIcon);
16+
17+
final String label;
18+
final Widget icon;
19+
final Widget selectedIcon;
20+
}
21+
22+
const List<ExampleDestination> destinations = <ExampleDestination>[
23+
ExampleDestination('page 0', Icon(Icons.widgets_outlined), Icon(Icons.widgets)),
24+
ExampleDestination('page 1', Icon(Icons.format_paint_outlined), Icon(Icons.format_paint)),
25+
ExampleDestination('page 2', Icon(Icons.text_snippet_outlined), Icon(Icons.text_snippet)),
26+
ExampleDestination('page 3', Icon(Icons.invert_colors_on_outlined), Icon(Icons.opacity)),
27+
];
28+
29+
void main() {
30+
runApp(
31+
MaterialApp(
32+
title: 'NavigationDrawer Example',
33+
debugShowCheckedModeBanner: false,
34+
theme: ThemeData(useMaterial3: true),
35+
home: const NavigationDrawerExample(),
36+
),
37+
);
38+
}
39+
40+
class NavigationDrawerExample extends StatefulWidget {
41+
const NavigationDrawerExample({super.key});
42+
43+
@override
44+
State<NavigationDrawerExample> createState() => _NavigationDrawerExampleState();
45+
}
46+
47+
class _NavigationDrawerExampleState extends State<NavigationDrawerExample> {
48+
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
49+
50+
int screenIndex = 0;
51+
late bool showNavigationDrawer;
52+
53+
void handleScreenChanged(int selectedScreen) {
54+
setState(() {
55+
screenIndex = selectedScreen;
56+
});
57+
}
58+
59+
void openDrawer() {
60+
scaffoldKey.currentState!.openEndDrawer();
61+
}
62+
63+
Widget buildBottomBarScaffold(){
64+
return Scaffold(
65+
body: Center(
66+
child: Column(
67+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
68+
children: <Widget>[
69+
Text('Page Index = $screenIndex'),
70+
],
71+
),
72+
),
73+
bottomNavigationBar: NavigationBar(
74+
selectedIndex: screenIndex,
75+
onDestinationSelected: (int index) {
76+
setState(() {
77+
screenIndex = index;
78+
});
79+
},
80+
destinations: destinations
81+
.map((ExampleDestination destination) {
82+
return NavigationDestination(
83+
label: destination.label,
84+
icon: destination.icon,
85+
selectedIcon: destination.selectedIcon,
86+
tooltip: destination.label,
87+
);
88+
})
89+
.toList(),
90+
),
91+
);
92+
}
93+
94+
Widget buildDrawerScaffold(BuildContext context){
95+
return Scaffold(
96+
key: scaffoldKey,
97+
body: SafeArea(
98+
bottom: false,
99+
top: false,
100+
child: Row(
101+
children: <Widget>[
102+
Padding(
103+
padding: const EdgeInsets.symmetric(horizontal: 5),
104+
child: NavigationRail(
105+
minWidth: 50,
106+
destinations: destinations
107+
.map((ExampleDestination destination) {
108+
return NavigationRailDestination(
109+
label: Text(destination.label),
110+
icon: destination.icon,
111+
selectedIcon: destination.selectedIcon,
112+
);
113+
})
114+
.toList(),
115+
selectedIndex: screenIndex,
116+
useIndicator: true,
117+
onDestinationSelected: (int index) {
118+
setState(() {
119+
screenIndex = index;
120+
});
121+
},
122+
),
123+
),
124+
const VerticalDivider(thickness: 1, width: 1),
125+
Expanded(
126+
child: Column(
127+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
128+
children: <Widget>[
129+
Text('Page Index = $screenIndex'),
130+
ElevatedButton(
131+
onPressed: openDrawer,
132+
child: const Text('Open Drawer'),
133+
),
134+
],
135+
),
136+
),
137+
],
138+
),
139+
),
140+
endDrawer: NavigationDrawer(
141+
onDestinationSelected: handleScreenChanged,
142+
selectedIndex: screenIndex,
143+
children: <Widget>[
144+
Padding(
145+
padding: const EdgeInsets.fromLTRB(28, 16, 16, 10),
146+
child: Text(
147+
'Header',
148+
style: Theme.of(context).textTheme.titleSmall,
149+
),
150+
),
151+
...destinations
152+
.map((ExampleDestination destination) {
153+
return NavigationDrawerDestination(
154+
label: Text(destination.label),
155+
icon: destination.icon,
156+
selectedIcon: destination.selectedIcon,
157+
);
158+
}),
159+
const Padding(
160+
padding: EdgeInsets.fromLTRB(28, 16, 28, 10),
161+
child: Divider(),
162+
),
163+
],
164+
),
165+
);
166+
}
167+
168+
@override
169+
void didChangeDependencies() {
170+
super.didChangeDependencies();
171+
showNavigationDrawer = MediaQuery.of(context).size.width >= 450;
172+
}
173+
174+
@override
175+
Widget build(BuildContext context) {
176+
return showNavigationDrawer ? buildDrawerScaffold(context) : buildBottomBarScaffold();
177+
}
178+
}

packages/flutter/lib/material.dart

+2
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ export 'src/material/menu_theme.dart';
124124
export 'src/material/mergeable_material.dart';
125125
export 'src/material/navigation_bar.dart';
126126
export 'src/material/navigation_bar_theme.dart';
127+
export 'src/material/navigation_drawer.dart';
128+
export 'src/material/navigation_drawer_theme.dart';
127129
export 'src/material/navigation_rail.dart';
128130
export 'src/material/navigation_rail_theme.dart';
129131
export 'src/material/no_splash.dart';

0 commit comments

Comments
 (0)