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.value,
|
||||||
this.enabled = true,
|
this.enabled = true,
|
||||||
this.height = kMinInteractiveDimension,
|
this.height = kMinInteractiveDimension,
|
||||||
|
this.padding,
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.mouseCursor,
|
this.mouseCursor,
|
||||||
required this.child,
|
required this.child,
|
||||||
@ -243,6 +244,15 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
|
|||||||
@override
|
@override
|
||||||
final double height;
|
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.
|
/// The text style of the popup menu item.
|
||||||
///
|
///
|
||||||
/// If this property is null, then [PopupMenuThemeData.textStyle] is used.
|
/// 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(
|
child: Container(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
constraints: BoxConstraints(minHeight: widget.height),
|
constraints: BoxConstraints(minHeight: widget.height),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding),
|
padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: _kMenuHorizontalPadding),
|
||||||
child: buildChild(),
|
child: buildChild(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -435,12 +445,16 @@ class CheckedPopupMenuItem<T> extends PopupMenuItem<T> {
|
|||||||
T? value,
|
T? value,
|
||||||
this.checked = false,
|
this.checked = false,
|
||||||
bool enabled = true,
|
bool enabled = true,
|
||||||
|
EdgeInsets? padding,
|
||||||
|
double height = kMinInteractiveDimension,
|
||||||
Widget? child,
|
Widget? child,
|
||||||
}) : assert(checked != null),
|
}) : assert(checked != null),
|
||||||
super(
|
super(
|
||||||
key: key,
|
key: key,
|
||||||
value: value,
|
value: value,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
|
padding: padding,
|
||||||
|
height: height,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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 {
|
testWidgets('Update PopupMenuItem layout while the menu is visible', (WidgetTester tester) async {
|
||||||
final Key popupMenuButtonKey = UniqueKey();
|
final Key popupMenuButtonKey = UniqueKey();
|
||||||
final Type menuItemType = const PopupMenuItem<String>(child: Text('item')).runtimeType;
|
final Type menuItemType = const PopupMenuItem<String>(child: Text('item')).runtimeType;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user