Add PopupMenuTheme to enable theming color, shape, elevation, text style of Menu#36088
Add PopupMenuTheme to enable theming color, shape, elevation, text style of Menu#36088rami-a merged 21 commits intoflutter:masterfrom
Conversation
|
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. ℹ️ Googlers: Go here for more info. |
rami-a
left a comment
There was a problem hiding this comment.
Good stuff, just a handful of minor comments/questions.
| shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(12)), | ||
| elevation: 12.0, | ||
| textStyle: const TextStyle( | ||
| color: Color(0xffffffff), textBaseline: TextBaseline.alphabetic), |
There was a problem hiding this comment.
Specifying the baseline doesn't seem necessary and can be omitted
There was a problem hiding this comment.
textBaseline is null if left unspecified, and basic.dart requires that textBaseline be non-null.
|
|
||
| final AnimatedDefaultTextStyle text = | ||
| tester.widget<AnimatedDefaultTextStyle>( | ||
| find |
There was a problem hiding this comment.
The formatting is a bit weird here, maybe put the find on the same line as descendant here and throughout this file?
| final Material button = tester.widget<Material>( | ||
| find | ||
| .descendant( | ||
| of: find.byKey(popupButtonApp), |
There was a problem hiding this comment.
I don't think I quite follow how this gets the actual menu widget, could you explain?
There was a problem hiding this comment.
The last actual piece of material under the popupButtonApp widget is the menu widget itself. So by finding the last descendent of popupButtonApp that is of type Material, this code gets the actual menu widget.
| return _cache[key] = loader(); | ||
| } | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
You should undo this change
| @override | ||
| void debugFillProperties(DiagnosticPropertiesBuilder properties) { | ||
| super.debugFillProperties(properties); | ||
| properties.add(ColorProperty( |
There was a problem hiding this comment.
You don't have to stick to the 80 char limit so I'd keep everything on the same line so that this is more readable
| this.textStyle, | ||
| }); | ||
|
|
||
| /// Default value for [_PopupMenuRoute.color]. |
There was a problem hiding this comment.
Add an additional line for each property documentation explaining what will be used if this is null. Look at card_theme.dart for examples.
There was a problem hiding this comment.
I think it might be better to describe the property itself. I'm unsure if routing developers to look at source code (since it's a private widget) is ideal. See the properties for ToggleButtonsThemeData for examples.
| import 'package:flutter/foundation.dart'; | ||
| import 'package:flutter/rendering.dart'; | ||
|
|
||
| /// Defines default property values for [PopupMenuEntry]'s [Material]. |
There was a problem hiding this comment.
It's not just for material since the text style is in here too, should be enough to just say `[PopupMenuEntry] widgets
| showMenu<T>( | ||
| context: context, | ||
| elevation: widget.elevation, | ||
| elevation: widget.elevation ?? popupMenuEntryTheme.elevation ?? 8.0, |
There was a problem hiding this comment.
I see this logic (for elevation, shape, and color) repeated in 2 spots, does it have to be?
There was a problem hiding this comment.
Done (The paths are essentially combined into 1 now!)
| }); | ||
|
|
||
| /// Default value for [_PopupMenuRoute.color]. | ||
| final Color surfaceContainerColor; |
There was a problem hiding this comment.
This is a bit of a mouthful, color would be enough (and consistent with other themes) I think
|
|
||
| /// The text style of the entry. | ||
| /// | ||
| /// Defaults to [blackMountainView]. |
There was a problem hiding this comment.
It looks like this doesn't actually default to blackMountainView. You can say something like:
If this is null, then popupMenuTheme.textStyle is used. If that is also null, then theme.textTheme.subhead is used.
shihaohong
left a comment
There was a problem hiding this comment.
Some minor changes to the documentation and removing one line of duplicate logic
|
|
||
| /// If provided, the shape used for the menu. | ||
| /// | ||
| /// If this is null, then popupMenuTheme.shape is used. |
There was a problem hiding this comment.
| /// If this is null, then popupMenuTheme.shape is used. | |
| /// If this is null, then [PopupMenuThemeData.shape] is used. |
| /// The text style of the entry. | ||
| /// | ||
| /// If this is null, then [popupMenuTheme.textStyle] is used. | ||
| /// If that is also null, then [theme.textTheme.subhead] is used. |
There was a problem hiding this comment.
| /// If that is also null, then [theme.textTheme.subhead] is used. | |
| /// If that is also null, then [ThemeData.textTheme.subhead] is used. |
|
|
||
| /// The text style of the entry. | ||
| /// | ||
| /// If this is null, then [popupMenuTheme.textStyle] is used. |
There was a problem hiding this comment.
| /// If this is null, then [popupMenuTheme.textStyle] is used. | |
| /// If this is null, then [PopupMenuThemeData.textStyle] is used. |
|
|
||
| /// If provided, the color used for the menu. | ||
| /// | ||
| /// If this is null, then popupMenuTheme.color is used. |
There was a problem hiding this comment.
| /// If this is null, then popupMenuTheme.color is used. | |
| /// If this is null, then [PopupMenuThemeData.color] is used. |
There was a problem hiding this comment.
Is there a default color for the menu if neither are specified?
There was a problem hiding this comment.
If these are null, then Theme.of(context).cardColor is used. If that is also null, then [ThemeData.cardTheme.color] is used. If that is also null, then [ThemeData.cardColor] is used.
| /// [PopupMenuEntryThemeData.copyWith]. | ||
| /// | ||
| /// Typically a [PopupMenuEntryThemeData] is specified as part of the | ||
| /// overall [Theme] with [ThemeData.popupMenuThemeData]. |
There was a problem hiding this comment.
| /// overall [Theme] with [ThemeData.popupMenuThemeData]. | |
| /// overall [Theme] with [ThemeData.popupMenuTheme]. |
| return typedOther.elevation == elevation | ||
| && typedOther.color == color | ||
| && typedOther.shape == shape | ||
| && typedOther.elevation == elevation |
There was a problem hiding this comment.
remove this line, it's a duplicate check of L99
| final BottomSheetThemeData bottomSheetTheme; | ||
|
|
||
| /// A theme for customizing the color, shape, elevation, and text style of a | ||
| /// menu. |
There was a problem hiding this comment.
| /// menu. | |
| /// popup menus. |
| /// A theme for customizing the color, elevation, and shape of a bottom sheet. | ||
| final BottomSheetThemeData bottomSheetTheme; | ||
|
|
||
| /// A theme for customizing the color, shape, elevation, and text style of a |
There was a problem hiding this comment.
| /// A theme for customizing the color, shape, elevation, and text style of a | |
| /// A theme for customizing the color, shape, elevation, and text style of |
|
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) ℹ️ Googlers: Go here for more info. |
shihaohong
left a comment
There was a problem hiding this comment.
There's a small bug where the widgets do not check for the PopupMenuTheme inherited widget, it would probably be good to add a test for that to validate the right behavior. I also added some comments about docs and formatting.
| @override | ||
| final double height; | ||
|
|
||
| /// The text style of the entry. |
There was a problem hiding this comment.
| /// The text style of the entry. | |
| /// The text style of the popup menu entry. |
There was a problem hiding this comment.
We try to make the first sentence as clear as possible so developers can very quickly understand what the API is for, or be reminded if they forget.
| Widget build(BuildContext context) { | ||
| final ThemeData theme = Theme.of(context); | ||
| TextStyle style = theme.textTheme.subhead; | ||
| final PopupMenuThemeData popupMenuTheme = theme.popupMenuTheme; |
There was a problem hiding this comment.
| final PopupMenuThemeData popupMenuTheme = theme.popupMenuTheme; | |
| final PopupMenuThemeData popupMenuTheme = TooltipTheme.of(context); |
There was a problem hiding this comment.
We want to retrieve the PopupMenuTheme configuration data from the closest ancestor in the widget tree.
There was a problem hiding this comment.
I would maybe also add a test to verify that when
ThemeData.popupThemeis defined, those properties are utilized.- When
ThemeData.popupThemeis defined, but aPopupThemeis also defined as a closer ancestor to any popup menu, properties fromPopupThemeDataare used instead.
| Widget build(BuildContext context) { | ||
| final double unit = 1.0 / (route.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade. | ||
| final List<Widget> children = <Widget>[]; | ||
| final PopupMenuThemeData popupMenuTheme = Theme.of(context).popupMenuTheme; |
There was a problem hiding this comment.
| final PopupMenuThemeData popupMenuTheme = Theme.of(context).popupMenuTheme; | |
| final PopupMenuThemeData popupMenuTheme = TooltipTheme.of(context); |
|
|
||
| /// The text style of the entry. | ||
| /// | ||
| /// If this is null, then [PopupMenuThemeData.textStyle] is used. |
There was a problem hiding this comment.
| /// If this is null, then [PopupMenuThemeData.textStyle] is used. | |
| /// If this property is null, then [PopupMenuThemeData.textStyle] is used. |
There was a problem hiding this comment.
Avoid using 'this' or 'that', since it can be ambiguous
| /// The text style of the entry. | ||
| /// | ||
| /// If this is null, then [PopupMenuThemeData.textStyle] is used. | ||
| /// If that is also null, then [ThemeData.textTheme.subhead] is used. |
There was a problem hiding this comment.
| /// If that is also null, then [ThemeData.textTheme.subhead] is used. | |
| /// If [PopupMenuThemeData.textStyle] is also null, then [ThemeData.textTheme.subhead] is used. |
| expect(button.shape, null); | ||
| expect(button.elevation, 8.0); | ||
|
|
||
| final AnimatedDefaultTextStyle text = |
There was a problem hiding this comment.
Same comment about formatting
| expect(button.shape, popupMenuTheme.shape); | ||
| expect(button.elevation, popupMenuTheme.elevation); | ||
|
|
||
| final AnimatedDefaultTextStyle text = |
| expect(button.shape, shape); | ||
| expect(button.elevation, elevation); | ||
|
|
||
| final AnimatedDefaultTextStyle text = |
| // found in the LICENSE file. | ||
|
|
||
| import 'package:flutter/foundation.dart'; | ||
| import 'package:flutter/material.dart'; |
There was a problem hiding this comment.
I think the analyzer might be complaining about this line being a recursive import. Just import the particular files you need and not the entire material library
|
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. ℹ️ Googlers: Go here for more info. |
| CupertinoThemeData cupertinoOverrideTheme, | ||
| SnackBarThemeData snackBarTheme, | ||
| BottomSheetThemeData bottomSheetTheme, | ||
| PopupMenuTheme popupMenuTheme, |
There was a problem hiding this comment.
This should still be PopupMenuThemeData for the type
| this.semanticLabel, | ||
| this.shape, | ||
| this.color, | ||
| this.popupMenuTheme, |
There was a problem hiding this comment.
I would move this up under theme, so that the list of parameters is more intuitively ordered
| } | ||
|
|
||
| Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel); | ||
| if(popupMenuTheme != null) |
There was a problem hiding this comment.
| if(popupMenuTheme != null) | |
| if (popupMenuTheme != null) |
| barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, | ||
| shape: shape, | ||
| color: color, | ||
| popupMenuTheme: PopupMenuTheme.of(context), |
There was a problem hiding this comment.
Same as prior comment, make this right under theme. In general, we try to order parameters if there is a logical way to do so
|
|
||
| /// If provided, the shape used for the menu. | ||
| /// | ||
| /// If this property is null, then [PopupMenuThemeData.shape] is used. |
There was a problem hiding this comment.
Describe what the shape would be if PopupMenuThemeData.shape was null as well.
|
|
||
| /// If provided, the color used for the menu. | ||
| /// | ||
| /// If this property is null, then [PopupMenuThemeData.color] is used. |
There was a problem hiding this comment.
Describe what the color would be if PopupMenuThemeData.color was null as well.
| ).last, | ||
| ); | ||
| expect(button.color, Colors.pink); | ||
| expect(button.shape, |
There was a problem hiding this comment.
Make this one line, or fix formatting
| }); | ||
| } | ||
|
|
||
| PopupMenuThemeData _popupMenuTheme() { |
There was a problem hiding this comment.
It might be better to put this at the top of the file. I believe most test files in the framework follow that convention
There was a problem hiding this comment.
Done (I was looking at the Bottom Sheet Theme which seems to have this at the bottom of the test file?)
There was a problem hiding this comment.
I see, it must be a preference thing. The files I've worked on had them at the top of the file. Sorry about that!
| home: Material( | ||
| child: Column( | ||
| children: <Widget>[ | ||
| PopupMenuButton<void>( |
There was a problem hiding this comment.
The analyzer says that we can't remove <void> because type annotations must be specified.
| home: Material( | ||
| child: Column( | ||
| children: <Widget>[ | ||
| PopupMenuButton<void>( |
There was a problem hiding this comment.
The analyzer says that we can't remove <void> because type annotations must be specified.
| shape: BeveledRectangleBorder(borderRadius: BorderRadius.circular(10)), | ||
| elevation: 6.0, | ||
| textStyle: const TextStyle(color: Color(0xfffff000), textBaseline: TextBaseline.alphabetic), | ||
| child: PopupMenuButton<void>( |
There was a problem hiding this comment.
The analyzer says that we can't remove <void> because type annotations must be specified.
shihaohong
left a comment
There was a problem hiding this comment.
One tiny nit about the API doc, otherwise LGTM!
You might also have to merge your changes with master. Some of the presubmit tests are complaining that seem unrelated to your changes.
| /// | ||
| /// If this property is null, then [PopupMenuThemeData.color] is used. | ||
| /// If [PopupMenuThemeData.color] is also null, then | ||
| /// [Theme.of(context).cardColor] is used. |
There was a problem hiding this comment.
nit, since this wouldn't create the link you're expecting:
| /// [Theme.of(context).cardColor] is used. | |
| /// Theme.of(context).cardColor is used. |
|
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) ℹ️ Googlers: Go here for more info. |
|
I'm manually updating the CLA since the PR contributor is a Googler |
|
|
||
| /// Defines the visual properties of [PopupMenuEntry] widgets. | ||
| /// | ||
| /// Descendant widgets obtain the current [PopupMenuEntryThemeData] object |
There was a problem hiding this comment.
It looks like PopupMenuEntry* should be PopupMenu* in the next couple of paragraphs.
Also: apps and material widgets should use PopupMenuTheme.of(context) (which defaults Theme.of(context).popupMenuTheme)).
| /// See also: | ||
| /// | ||
| /// * [ThemeData], which describes the overall theme information for the | ||
| /// application. |
There was a problem hiding this comment.
I don't think these links are needed.
|
|
||
| import 'theme.dart'; | ||
|
|
||
| /// Defines the visual properties of [PopupMenuEntry] widgets. |
There was a problem hiding this comment.
I think it would be clearer to say ...properties of the routes used to display popup menus as well as [PopupMenuItem] and [PopupMenuDivider] widgets.
| /// The properties for descendant popup menu widgets. | ||
| final PopupMenuThemeData data; | ||
|
|
||
| /// The closest instance of this class' [data] value that encloses the given |
There was a problem hiding this comment.
class' should be class's. English is strange.
| expect(button.shape, popupMenuTheme.shape); | ||
| expect(button.elevation, popupMenuTheme.elevation); | ||
|
|
||
| final AnimatedDefaultTextStyle text = tester.widget<AnimatedDefaultTextStyle>( |
There was a problem hiding this comment.
Maybe just use DefaultTextStyle here
| find.descendant( | ||
| of: find.byKey(popupButtonApp), | ||
| matching: find.byType(Material), | ||
| ).last, |
There was a problem hiding this comment.
It's not clear what the ".last" material widget is. Maybe add a comment (here and below).
|
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. ℹ️ Googlers: Go here for more info. |
|
A Googler has manually verified that the CLAs look good. (Googler, please make sure the reason for overriding the CLA status is clearly documented in these comments.) ℹ️ Googlers: Go here for more info. |
|
Overriding CLA as @lisa-liao is a googler |
…yle of Menu (flutter#36088) * [Menu] Create Menu theme * [Menu] Create Menu theme * [Menu] Formatting changes for Menu theme * [Menu] Fix spacing difference in theme_data.dart. * [Menu] Fix spacing difference in theme_data.dart. * Specifying types * Formatting changes * Address PR feedback * Formatting changes * Address PR feedback * Add inherited widget * Add inherited widget * Address PR feedback and add inherited widget. * Formatting changes. * Address PR feedback * Address PR feedback * Address PR feedback * Address PR feedback
Description
This change introduces a PopupMenuEntryTheme that allows you to theme the color, shape, elevation, and text style of a menu. This can be done at the theme level and at call time of showMenu. See the following image for an example:
A gist showing how to generate this menu is here.
Related Issues
Closes #36076
Tests
I added the following tests:
*Tests for the theme and modifying the color/shape/elevation/text style of a menu
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?