Skip to content

Make popup menu position configurable#98979

Merged
fluttergithubbot merged 6 commits intoflutter:masterfrom
NevercodeHQ:popup_menu_position
Mar 5, 2022
Merged

Make popup menu position configurable#98979
fluttergithubbot merged 6 commits intoflutter:masterfrom
NevercodeHQ:popup_menu_position

Conversation

@TahaTesser
Copy link
Contributor

@TahaTesser TahaTesser commented Feb 23, 2022

fixes #82067

Description

Currently, PopupMenuButton provides a default position for the popup menu. According to the issue and material guidelines, it should be able to position below the button, you can achieve this by providing an offset parameter however, this is roughly calculated.

According to guidelines, a popup menu should be positioned below and if there is no room, it can be positioned over the button. Flutter automatically does this calculation.

However, the default offset is still Offset.zero so a roughly calculated offset parameter must be provided to achieve the same design as material guidelines.

Here I decided to add an enum that provides control over the position of the popup menu which calculates the height of the button itself minus vertical padding and position the menu correctly without setting rough offset every time.

This way popup menu can be positioned over or under the button without rough offset and offset can be provided if enum positions are insufficient.

Code sample

minimal code sample
import 'package:flutter/material.dart';

enum Fruits { mango, apple, banana, straberry }

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key, this.dark = true}) : super(key: key);

  final bool dark;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Material App',
      theme: dark ? ThemeData.dark() : ThemeData.light(),
      home: const PopupMenuButtonSample(),
    );
  }
}

class PopupMenuButtonSample extends StatelessWidget {
  const PopupMenuButtonSample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(' PopupMenuButton Sample'),
        actions: <Widget>[
          PopupMenuButton<Fruits>(
            position: MenuPosition.under,
            shape: RoundedRectangleBorder(
              borderRadius: BorderRadius.circular(8),
            ),
            onSelected: (Fruits result) {
              print('selected fruit: ${result.name}');
            },
            onCanceled: () {
              print('onCanceled');
            },
            color: Colors.red[700],
            itemBuilder: (BuildContext context) => <PopupMenuEntry<Fruits>>[
              const PopupMenuItem<Fruits>(
                value: Fruits.mango,
                child: Text('Mango'),
              ),
              const PopupMenuItem<Fruits>(
                value: Fruits.apple,
                child: Text('Apple'),
              ),
              const PopupMenuItem<Fruits>(
                value: Fruits.banana,
                child: Text('Banana'),
              ),
              const PopupMenuItem<Fruits>(
                value: Fruits.straberry,
                child: Text('Strawberry'),
              ),
            ],
          )
        ],
      ),
      body: Center(
        child: Stack(
          children: [
            Positioned(
              bottom: 25,
              right: 10,
              child: PopupMenuButton<Fruits>(
                position: MenuPosition.under,
                icon: const Icon(Icons.apple),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
                onSelected: (Fruits result) {
                  print('selected fruit: ${result.name}');
                },
                onCanceled: () {
                  print('onCanceled');
                },
                color: Colors.red[700],
                itemBuilder: (BuildContext context) => <PopupMenuEntry<Fruits>>[
                  const PopupMenuItem<Fruits>(
                    value: Fruits.mango,
                    child: Text('Mango'),
                  ),
                  const PopupMenuItem<Fruits>(
                    value: Fruits.apple,
                    child: Text('Apple'),
                  ),
                  const PopupMenuItem<Fruits>(
                    value: Fruits.banana,
                    child: Text('Banana'),
                  ),
                  const PopupMenuItem<Fruits>(
                    value: Fruits.straberry,
                    child: Text('Strawberry'),
                  ),
                ],
              ),
            ),
            Positioned.fill(
              child: Center(
                child: PopupMenuButton<Fruits>(
                  position: MenuPosition.under,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(8),
                  ),
                  onSelected: (Fruits result) {
                    print('selected fruit: ${result.name}');
                  },
                  onCanceled: () {
                    print('onCanceled');
                  },
                  color: Colors.red[700],
                  itemBuilder: (BuildContext context) =>
                      <PopupMenuEntry<Fruits>>[
                    const PopupMenuItem<Fruits>(
                      value: Fruits.mango,
                      child: Text('Mango'),
                    ),
                    const PopupMenuItem<Fruits>(
                      value: Fruits.apple,
                      child: Text('Apple'),
                    ),
                    const PopupMenuItem<Fruits>(
                      value: Fruits.banana,
                      child: Text('Banana'),
                    ),
                    const PopupMenuItem<Fruits>(
                      value: Fruits.straberry,
                      child: Text('Strawberry'),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Preview

Screen.Recording.2022-02-23.at.13.49.30.mov

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I signed the CLA.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@flutter-dashboard flutter-dashboard bot added f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels. labels Feb 23, 2022
Copy link
Contributor

@gspencergoog gspencergoog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry! I wrote this feedback a few days ago, and just forgot to submit.

@TahaTesser TahaTesser requested a review from gspencergoog March 4, 2022 22:33
Copy link
Contributor

@gspencergoog gspencergoog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

32384589-a60f0e74-c078-11e7-9bc1-e5b5287aea9d

Just the one rename.

@TahaTesser TahaTesser force-pushed the popup_menu_position branch from bf34306 to 383581f Compare March 4, 2022 23:29
@fluttergithubbot fluttergithubbot merged commit f1d8858 into flutter:master Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 7, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
clocksmith pushed a commit to clocksmith/flutter that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PopupMenuButton: Menu is positioned directly over the button

3 participants