Unverified Commit 2977a346 authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

Custom PopupMenuItem padding (#78523)

parent c0bcb4fc
......@@ -221,6 +221,7 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
this.value,
this.enabled = true,
this.height = kMinInteractiveDimension,
this.padding,
this.textStyle,
this.mouseCursor,
required this.child,
......@@ -243,6 +244,15 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
@override
final double height;
/// The padding of the menu item.
///
/// Note that [height] may interact with the applied padding. For example,
/// If a [height] greater than the height of the sum of the padding and [child]
/// is provided, then the padding's effect will not be visible.
///
/// When null, the horizontal padding defaults to 16.0 on both sides.
final EdgeInsets? padding;
/// The text style of the popup menu item.
///
/// If this property is null, then [PopupMenuThemeData.textStyle] is used.
......@@ -327,7 +337,7 @@ class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
child: Container(
alignment: AlignmentDirectional.centerStart,
constraints: BoxConstraints(minHeight: widget.height),
padding: const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding),
padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding),
child: buildChild(),
),
);
......@@ -435,14 +445,18 @@ class CheckedPopupMenuItem<T> extends PopupMenuItem<T> {
T? value,
this.checked = false,
bool enabled = true,
EdgeInsets? padding,
double height = kMinInteractiveDimension,
Widget? child,
}) : assert(checked != null),
super(
key: key,
value: value,
enabled: enabled,
child: child,
);
key: key,
value: value,
enabled: enabled,
padding: padding,
height: height,
child: child,
);
/// Whether to display a checkmark next to the menu item.
///
......
......@@ -1278,6 +1278,219 @@ void main() {
);
});
testWidgets('PopupMenuItem custom padding', (WidgetTester tester) async {
final Key popupMenuButtonKey = UniqueKey();
final Type menuItemType = const PopupMenuItem<String>(child: Text('item')).runtimeType;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: PopupMenuButton<String>(
key: popupMenuButtonKey,
child: const Text('button'),
onSelected: (String result) { },
itemBuilder: (BuildContext context) {
return <PopupMenuEntry<String>>[
const PopupMenuItem<String>(
padding: EdgeInsets.zero,
value: '0',
child: Text('Item 0'),
),
const PopupMenuItem<String>(
padding: EdgeInsets.zero,
height: 0,
value: '0',
child: Text('Item 1'),
),
const PopupMenuItem<String>(
padding: EdgeInsets.all(20),
value: '0',
child: Text('Item 2'),
),
const PopupMenuItem<String>(
padding: EdgeInsets.all(20),
height: 100,
value: '0',
child: Text('Item 3'),
),
];
},
),
),
),
),
);
// Show the menu
await tester.tap(find.byKey(popupMenuButtonKey));
await tester.pumpAndSettle();
// The menu items and their InkWells should have the expected vertical size
// given the interactions between heights and padding.
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 0')).height, 48); // Minimum interactive height (48)
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 1')).height, 16); // Height of text (16)
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 2')).height, 56); // Padding (20.0 + 20.0) + Height of text (16) = 56
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 3')).height, 100); // Height value of 100, since child (16) + padding (40) < 100
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 0')).padding, EdgeInsets.zero);
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 1')).padding, EdgeInsets.zero);
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 2')).padding, const EdgeInsets.all(20));
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 3')).padding, const EdgeInsets.all(20));
});
testWidgets('CheckedPopupMenuItem child height is a minimum, child is vertically centered', (WidgetTester tester) async {
final Key popupMenuButtonKey = UniqueKey();
final Type menuItemType = const CheckedPopupMenuItem<String>(child: Text('item')).runtimeType;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: PopupMenuButton<String>(
key: popupMenuButtonKey,
child: const Text('button'),
onSelected: (String result) { },
itemBuilder: (BuildContext context) {
return <PopupMenuEntry<String>>[
// This menu item's height will be 56.0 because the default minimum height
// is 48, but the contents of PopupMenuItem are 56.0 tall.
const CheckedPopupMenuItem<String>(
checked: true,
value: '0',
child: Text('Item 0'),
),
// This menu item's height parameter specifies its minimum height. The
// overall height of the menu item will be 60 because the child's
// height 56, is less than 60.
const CheckedPopupMenuItem<String>(
checked: true,
height: 60,
value: '1',
child: SizedBox(
height: 40,
child: Text('Item 1'),
),
),
// This menu item's height parameter specifies its minimum height, so the
// overall height of the menu item will be 75.
const CheckedPopupMenuItem<String>(
checked: true,
height: 75,
value: '2',
child: SizedBox(
child: Text('Item 2'),
),
),
// This menu item's height will be 100.
const CheckedPopupMenuItem<String>(
checked: true,
height: 100,
value: '3',
child: SizedBox(
child: Text('Item 3'),
),
),
];
},
),
),
),
),
);
// Show the menu
await tester.tap(find.byKey(popupMenuButtonKey));
await tester.pumpAndSettle();
// The menu items and their InkWells should have the expected vertical size
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 0')).height, 56);
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 1')).height, 60);
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 2')).height, 75);
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 3')).height, 100);
// We evaluate the InkWell at the first index because that is the ListTile's
// InkWell, which wins in the gesture arena over the child's InkWell and
// is the one of interest.
expect(tester.getSize(find.widgetWithText(InkWell, 'Item 0').at(1)).height, 56);
expect(tester.getSize(find.widgetWithText(InkWell, 'Item 1').at(1)).height, 60);
expect(tester.getSize(find.widgetWithText(InkWell, 'Item 2').at(1)).height, 75);
expect(tester.getSize(find.widgetWithText(InkWell, 'Item 3').at(1)).height, 100);
// Menu item children which whose height is less than the PopupMenuItem
// are vertically centered.
expect(
tester.getRect(find.widgetWithText(menuItemType, 'Item 0')).center.dy,
tester.getRect(find.text('Item 0')).center.dy,
);
expect(
tester.getRect(find.widgetWithText(menuItemType, 'Item 2')).center.dy,
tester.getRect(find.text('Item 2')).center.dy,
);
});
testWidgets('CheckedPopupMenuItem custom padding', (WidgetTester tester) async {
final Key popupMenuButtonKey = UniqueKey();
final Type menuItemType = const CheckedPopupMenuItem<String>(child: Text('item')).runtimeType;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Center(
child: PopupMenuButton<String>(
key: popupMenuButtonKey,
child: const Text('button'),
onSelected: (String result) { },
itemBuilder: (BuildContext context) {
return <PopupMenuEntry<String>>[
const CheckedPopupMenuItem<String>(
padding: EdgeInsets.zero,
value: '0',
child: Text('Item 0'),
),
const CheckedPopupMenuItem<String>(
padding: EdgeInsets.zero,
height: 0,
value: '0',
child: Text('Item 1'),
),
const CheckedPopupMenuItem<String>(
padding: EdgeInsets.all(20),
value: '0',
child: Text('Item 2'),
),
const CheckedPopupMenuItem<String>(
padding: EdgeInsets.all(20),
height: 100,
value: '0',
child: Text('Item 3'),
),
];
},
),
),
),
),
);
// Show the menu
await tester.tap(find.byKey(popupMenuButtonKey));
await tester.pumpAndSettle();
// The menu items and their InkWells should have the expected vertical size
// given the interactions between heights and padding.
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 0')).height, 56); // Minimum ListTile height (56)
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 1')).height, 56); // Minimum ListTile height (56)
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 2')).height, 96); // Padding (20.0 + 20.0) + Height of ListTile (56) = 96
expect(tester.getSize(find.widgetWithText(menuItemType, 'Item 3')).height, 100); // Height value of 100, since child (56) + padding (40) < 100
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 0')).padding, EdgeInsets.zero);
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 1')).padding, EdgeInsets.zero);
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 2')).padding, const EdgeInsets.all(20));
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 3')).padding, const EdgeInsets.all(20));
});
testWidgets('Update PopupMenuItem layout while the menu is visible', (WidgetTester tester) async {
final Key popupMenuButtonKey = UniqueKey();
final Type menuItemType = const PopupMenuItem<String>(child: Text('item')).runtimeType;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment