Pierre-Louis 61362ec84c
Replace some deprecated properties (#105)
* 1

* Update pubspec.yaml
2021-05-17 15:50:09 +02:00

11 KiB

Menus

Menus display a list of choices on temporary surfaces.

Menu hero example showing two cascading menus

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.

Composite image of example dropdown and exposed dropdown menu types

Anatomy

The following are menu anatomy diagrams showing all possible elements:

Menus anatomy diagrams

  1. Container
  2. Leading icon (optional)
  3. Text
  4. Divider (optional)
  5. Command (optional)
  6. Cascading menu indicator (optional)
  7. 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:

Dropdown menu example

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:

Exposed Dropdown menu example listing 6 options

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:

Dropdown menu with dark brown text and grey icons

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;