11 KiB
Menus
Menus display a list of choices on temporary surfaces.
Contents
Using menus
A menu displays a list of choices on a temporary surface. They appear when users interact with a button, action, or other control.
Making menus accessible
Flutter's list component APIs support labeling for accessibility. For more guidance on writing labels, go to our page on how to write a good accessibility label.
Types
Menus allow users to make a selection from multiple options. They are less prominent and take up less space than selection controls, such as a set of radio buttons.
There are two types of menus: 1. dropdown menus, 2. exposed dropdown menus.
Anatomy
The following are menu anatomy diagrams showing all possible elements:
- Container
- Leading icon (optional)
- Text
- Divider (optional)
- Command (optional)
- Cascading menu indicator (optional)
- Selection state (optional)
Container attributes
| Property | |
|---|---|
| Color | Use the PopupMenuButton widget. Within this widget use the Colors property |
| Height | Use the PopupMenuButton widget. Within this widget use the padding property. |
Leading icon attributes
| Property | |
|---|---|
| Adding Icon | Use the PopupMenuButton widget. Within this widget use itemBuilder. Within the list of itemBuilder add a PopupMenuItem. Within the child parameter add a ListTile widget and set the leading parameter to an Icon. |
Text attributes
| Property | |
|---|---|
| Text label | Use the PopupMenuButton widget. Within this widget use itemBuilder. Within the list of itemBuilder add a PopupMenuItem. Within the child parameter add a ListTile widget and set the title parameter to a Text widget. |
| Color | Follow the steps above and then within the Text widget fill out the style parameter. |
| Typography | Follow the steps above and then within the Text widget fill out the style parameter. |
Divider attributes
| Property | |
|---|---|
| Add Divider | Use the PopupMenuButton widget. Within this widget use itemBuilder. Within the list of itemBuilder add a PopupMenuDivider. |
Command attributes
| Property | |
|---|---|
| Adding Icon | Use the PopupMenuButton widget. Within this widget use itemBuilder. Within the list of itemBuilder add a PopupMenuItem. Within the child parameter add a ListTile widget and set the trailing parameter to an Icon. |
Cascading menu indicator attributes
| Property | |
|---|---|
| Adding cascading menu | Not currently supported. |
Selection state attributes
| Property | |
|---|---|
| Adding selection state attributes | Use the PopupMenuButton widget. Within this widget use itemBuilder. Within the list of itemBuilder add a CheckedPopupMenuItem. |
Dropdown menus
Dropdown menus display a list of options, triggered by an icon, button, or action. Their placement varies based on the element that opens them.
Dropdown menu example
The following is an example of a dropdown menu:
PopupMenuButton(
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
const PopupMenuItem(
child: ListTile(
leading: Icon(Icons.add),
title: Text('Item 1'),
),
),
const PopupMenuItem(
child: ListTile(
leading: Icon(Icons.anchor),
title: Text('Item 2'),
),
),
const PopupMenuItem(
child: ListTile(
leading: Icon(Icons.article),
title: Text('Item 3'),
),
),
const PopupMenuDivider(),
const PopupMenuItem(child: Text('Item A')),
const PopupMenuItem(child: Text('Item B')),
],
),
Exposed dropdown menus
Exposed dropdown menus display the currently selected menu item above the list of options. Some variations can accept user-entered input.
Exposed dropdown menu example
The following is an example of a dropdown menu:
DropdownButton(
value: dropdownValue,
items: <DropdownMenuItem>[
DropdownMenuItem(
value: 'Option 1',
child: Text('Option 1'),
),
DropdownMenuItem(
value: 'Option 2',
child: Text('Option 2'),
),
DropdownMenuItem(
value: 'Option 3',
child: Text('Option 3'),
),
DropdownMenuItem(
value: 'Option 4',
child: Text('Option 4'),
),
DropdownMenuItem(
value: 'Option 5',
child: Text('Option 5'),
),
DropdownMenuItem(
value: 'Option 6',
child: Text('Option 6'),
),
],
onChanged: (value) {
setState(() {
dropdownValue = value;
});
},
),
Theming
Menus support Material Theming and can be customized in terms of color, typography and shape.
Theming example
The following is an example of a dropdown menu:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dropdown Menu Themed Demo',
debugShowCheckedModeBanner: false,
home: DropdownMenuDemo(),
theme: _buildShrineTheme(),
);
}
}
class DropdownMenuDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Dropdown Menu Themed Demo'),
actions: [
PopupMenuButton(
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => <PopupMenuEntry>[
const PopupMenuItem(
child: ListTile(
leading: Icon(Icons.add),
title: Text('Item 1'),
),
),
const PopupMenuItem(
child: ListTile(
leading: Icon(Icons.anchor),
title: Text('Item 2'),
),
),
const PopupMenuItem(
child: ListTile(
leading: Icon(Icons.article),
title: Text('Item 3'),
),
),
const PopupMenuDivider(),
const PopupMenuItem(child: Text('Item A')),
const PopupMenuItem(child: Text('Item B')),
],
),
],
),
body: Center(),
);
}
}
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: _shrineColorScheme,
accentColor: shrineBrown900,
primaryColor: shrinePink100,
buttonColor: shrinePink100,
scaffoldBackgroundColor: shrineBackgroundWhite,
cardColor: shrineBackgroundWhite,
textSelectionTheme: TextSelectionThemeData(selectionColor: shrinePink100),
errorColor: shrineErrorRed,
buttonTheme: const ButtonThemeData(
colorScheme: _shrineColorScheme,
textTheme: ButtonTextTheme.normal,
),
primaryIconTheme: _customIconTheme(base.iconTheme),
textTheme: _buildShrineTextTheme(base.textTheme),
primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme),
accentTextTheme: _buildShrineTextTheme(base.accentTextTheme),
iconTheme: _customIconTheme(base.iconTheme),
);
}
IconThemeData _customIconTheme(IconThemeData original) {
return original.copyWith(color: shrineBrown900);
}
TextTheme _buildShrineTextTheme(TextTheme base) {
return base
.copyWith(
caption: base.caption.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
letterSpacing: defaultLetterSpacing,
),
button: base.button.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14,
letterSpacing: defaultLetterSpacing,
),
)
.apply(
fontFamily: 'Rubik',
displayColor: shrineBrown900,
bodyColor: shrineBrown900,
);
}
const ColorScheme _shrineColorScheme = ColorScheme(
primary: shrinePink100,
primaryVariant: shrineBrown900,
secondary: shrinePink50,
secondaryVariant: shrineBrown900,
surface: shrineSurfaceWhite,
background: shrineBackgroundWhite,
error: shrineErrorRed,
onPrimary: shrineBrown900,
onSecondary: shrineBrown900,
onSurface: shrineBrown900,
onBackground: shrineBrown900,
onError: shrineSurfaceWhite,
brightness: Brightness.light,
);
const Color shrinePink50 = Color(0xFFFEEAE6);
const Color shrinePink100 = Color(0xFFFEDBD0);
const Color shrinePink300 = Color(0xFFFBB8AC);
const Color shrinePink400 = Color(0xFFEAA4A4);
const Color shrineBrown900 = Color(0xFF442B2D);
const Color shrineBrown600 = Color(0xFF7D4F52);
const Color shrineErrorRed = Color(0xFFC5032B);
const Color shrineSurfaceWhite = Color(0xFFFFFBFA);
const Color shrineBackgroundWhite = Colors.white;
const defaultLetterSpacing = 0.03;





