Custom PopupMenuItem padding (#78523)
This commit is contained in:
parent
c0bcb4fc16
commit
2977a346fe
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user