mirror of
https://github.com/flutter/flutter
synced 2024-08-27 12:00:58 +00:00
feat: added custom padding in PopupMenuButton (#96657)
This commit is contained in:
parent
017ed1792b
commit
49c5871844
|
@ -528,10 +528,12 @@ class _PopupMenu<T> extends StatelessWidget {
|
|||
Key? key,
|
||||
required this.route,
|
||||
required this.semanticLabel,
|
||||
this.padding,
|
||||
}) : super(key: key);
|
||||
|
||||
final _PopupMenuRoute<T> route;
|
||||
final String? semanticLabel;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -583,9 +585,7 @@ class _PopupMenu<T> extends StatelessWidget {
|
|||
explicitChildNodes: true,
|
||||
label: semanticLabel,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: _kMenuVerticalPadding,
|
||||
),
|
||||
padding: padding,
|
||||
child: ListBody(children: children),
|
||||
),
|
||||
),
|
||||
|
@ -727,6 +727,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
|||
_PopupMenuRoute({
|
||||
required this.position,
|
||||
required this.items,
|
||||
this.padding,
|
||||
this.initialValue,
|
||||
this.elevation,
|
||||
required this.barrierLabel,
|
||||
|
@ -740,6 +741,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
|||
final List<PopupMenuEntry<T>> items;
|
||||
final List<Size?> itemSizes;
|
||||
final T? initialValue;
|
||||
final EdgeInsetsGeometry? padding;
|
||||
final double? elevation;
|
||||
final String? semanticLabel;
|
||||
final ShapeBorder? shape;
|
||||
|
@ -778,7 +780,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
|||
}
|
||||
}
|
||||
|
||||
final Widget menu = _PopupMenu<T>(route: this, semanticLabel: semanticLabel);
|
||||
final Widget menu = _PopupMenu<T>(padding: padding, route: this, semanticLabel: semanticLabel);
|
||||
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
return MediaQuery.removePadding(
|
||||
context: context,
|
||||
|
@ -850,6 +852,11 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
|||
/// label is not provided, it will default to
|
||||
/// [MaterialLocalizations.popupMenuLabel].
|
||||
///
|
||||
/// The `padding` argument is used to add empty space around the outside
|
||||
/// of the popup menu. If this property is not provided, then [PopupMenuThemeData.padding]
|
||||
/// is used. If [PopupMenuThemeData.padding] is also null, then
|
||||
/// EdgeInsets.symmetric(vertical: 8.0) is used.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [PopupMenuItem], a popup menu entry for a single value.
|
||||
|
@ -864,6 +871,7 @@ Future<T?> showMenu<T>({
|
|||
required RelativeRect position,
|
||||
required List<PopupMenuEntry<T>> items,
|
||||
T? initialValue,
|
||||
EdgeInsetsGeometry? padding,
|
||||
double? elevation,
|
||||
String? semanticLabel,
|
||||
ShapeBorder? shape,
|
||||
|
@ -892,6 +900,7 @@ Future<T?> showMenu<T>({
|
|||
position: position,
|
||||
items: items,
|
||||
initialValue: initialValue,
|
||||
padding: padding,
|
||||
elevation: elevation,
|
||||
semanticLabel: semanticLabel,
|
||||
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
|
@ -984,6 +993,7 @@ class PopupMenuButton<T> extends StatefulWidget {
|
|||
this.tooltip,
|
||||
this.elevation,
|
||||
this.padding = const EdgeInsets.all(8.0),
|
||||
this.menuPadding,
|
||||
this.child,
|
||||
this.icon,
|
||||
this.iconSize,
|
||||
|
@ -1078,6 +1088,14 @@ class PopupMenuButton<T> extends StatefulWidget {
|
|||
/// Theme.of(context).cardColor is used.
|
||||
final Color? color;
|
||||
|
||||
/// If provided, menu padding is used for empty space around the outside
|
||||
/// of the popup menu.
|
||||
///
|
||||
/// If this property is null, then [PopupMenuThemeData.padding] is used.
|
||||
/// If [PopupMenuThemeData.padding] is also null, then
|
||||
/// EdgeInsets.symmetric(vertical: 8.0) is used.
|
||||
final EdgeInsetsGeometry? menuPadding;
|
||||
|
||||
/// Whether detected gestures should provide acoustic and/or haptic feedback.
|
||||
///
|
||||
/// For example, on Android a tap will produce a clicking sound and a
|
||||
|
@ -1121,6 +1139,11 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
|
|||
),
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
|
||||
final EdgeInsetsGeometry menuPadding = widget.menuPadding ??
|
||||
popupMenuTheme.padding ??
|
||||
const EdgeInsets.symmetric(vertical: _kMenuVerticalPadding);
|
||||
|
||||
final List<PopupMenuEntry<T>> items = widget.itemBuilder(context);
|
||||
// Only show the menu if there is something to show
|
||||
if (items.isNotEmpty) {
|
||||
|
@ -1129,6 +1152,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
|
|||
elevation: widget.elevation ?? popupMenuTheme.elevation,
|
||||
items: items,
|
||||
initialValue: widget.initialValue,
|
||||
padding: menuPadding,
|
||||
position: position,
|
||||
shape: widget.shape ?? popupMenuTheme.shape,
|
||||
color: widget.color ?? popupMenuTheme.color,
|
||||
|
|
|
@ -40,6 +40,7 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
this.textStyle,
|
||||
this.enableFeedback,
|
||||
this.mouseCursor,
|
||||
this.padding,
|
||||
});
|
||||
|
||||
/// The background color of the popup menu.
|
||||
|
@ -64,6 +65,11 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
/// If specified, overrides the default value of [PopupMenuItem.mouseCursor].
|
||||
final MaterialStateProperty<MouseCursor?>? mouseCursor;
|
||||
|
||||
/// If specified, defines the padding for the popup menu of [PopupMenuButton].
|
||||
///
|
||||
/// If [PopupMenuButton.menuPadding] is provided, [padding] is ignored.
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
/// Creates a copy of this object with the given fields replaced with the
|
||||
/// new values.
|
||||
PopupMenuThemeData copyWith({
|
||||
|
@ -73,6 +79,7 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
TextStyle? textStyle,
|
||||
bool? enableFeedback,
|
||||
MaterialStateProperty<MouseCursor?>? mouseCursor,
|
||||
EdgeInsets? padding,
|
||||
}) {
|
||||
return PopupMenuThemeData(
|
||||
color: color ?? this.color,
|
||||
|
@ -81,6 +88,7 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
textStyle: textStyle ?? this.textStyle,
|
||||
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||
mouseCursor: mouseCursor ?? this.mouseCursor,
|
||||
padding: padding ?? this.padding,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -100,6 +108,7 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
textStyle: TextStyle.lerp(a?.textStyle, b?.textStyle, t),
|
||||
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
|
||||
mouseCursor: t < 0.5 ? a?.mouseCursor : b?.mouseCursor,
|
||||
padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -111,7 +120,8 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
elevation,
|
||||
textStyle,
|
||||
enableFeedback,
|
||||
mouseCursor
|
||||
mouseCursor,
|
||||
padding,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -127,7 +137,8 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
&& other.shape == shape
|
||||
&& other.textStyle == textStyle
|
||||
&& other.enableFeedback == enableFeedback
|
||||
&& other.mouseCursor == mouseCursor;
|
||||
&& other.mouseCursor == mouseCursor
|
||||
&& other.padding == padding;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -139,6 +150,7 @@ class PopupMenuThemeData with Diagnosticable {
|
|||
properties.add(DiagnosticsProperty<TextStyle>('text style', textStyle, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<MaterialStateProperty<MouseCursor?>>('mouseCursor', mouseCursor, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('padding', padding, defaultValue: null));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1471,6 +1471,72 @@ void main() {
|
|||
expect(tester.widget<Container>(find.widgetWithText(Container, 'Item 3')).padding, const EdgeInsets.all(20));
|
||||
});
|
||||
|
||||
testWidgets('PopupMenu custom padding', (WidgetTester tester) async {
|
||||
final Key popupMenuButtonWithDefaultPaddingKey = UniqueKey();
|
||||
final Key popupMenuButtonWithOverriddenPaddingKey = UniqueKey();
|
||||
|
||||
const EdgeInsets padding = EdgeInsets.zero;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Column(
|
||||
children: <PopupMenuButton<String>>[
|
||||
PopupMenuButton<String>(
|
||||
key: popupMenuButtonWithDefaultPaddingKey,
|
||||
child: const Text('button'),
|
||||
onSelected: (String result) {},
|
||||
itemBuilder: (BuildContext context) {
|
||||
return <PopupMenuEntry<String>>[
|
||||
const PopupMenuItem<String>(
|
||||
value: '0',
|
||||
child: Text('Item 0'),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
menuPadding: padding,
|
||||
key: popupMenuButtonWithOverriddenPaddingKey,
|
||||
child: const Text('button'),
|
||||
onSelected: (String result) {},
|
||||
itemBuilder: (BuildContext context) {
|
||||
return <PopupMenuEntry<String>>[
|
||||
const PopupMenuItem<String>(
|
||||
value: '0',
|
||||
child: Text('Item 0'),
|
||||
),
|
||||
];
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final Finder popupFinder = find.byType(SingleChildScrollView);
|
||||
// Show the menu
|
||||
await tester.tap(find.byKey(popupMenuButtonWithDefaultPaddingKey));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.widget<SingleChildScrollView>(popupFinder).padding,
|
||||
const EdgeInsets.symmetric(vertical: 8),
|
||||
reason: 'The popup without explicit paddings should utilise the default ones.',);
|
||||
|
||||
// Close previous menu
|
||||
await tester.tap(find.byKey(popupMenuButtonWithDefaultPaddingKey), warnIfMissed: false);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Show the menu
|
||||
await tester.tap(find.byKey(popupMenuButtonWithOverriddenPaddingKey));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.widget<SingleChildScrollView>(popupFinder).padding,
|
||||
padding,
|
||||
reason: 'The popup should utilise explicitly set paddings.',);
|
||||
});
|
||||
|
||||
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;
|
||||
|
|
|
@ -13,6 +13,7 @@ PopupMenuThemeData _popupMenuTheme() {
|
|||
shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))),
|
||||
elevation: 12.0,
|
||||
textStyle: TextStyle(color: Color(0xffffffff), textBaseline: TextBaseline.alphabetic),
|
||||
padding: EdgeInsets.symmetric(vertical: 6, horizontal: 6),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -195,6 +196,7 @@ void main() {
|
|||
);
|
||||
const double elevation = 7.0;
|
||||
const TextStyle textStyle = TextStyle(color: Color(0x00000000), textBaseline: TextBaseline.alphabetic);
|
||||
const EdgeInsets menuPadding = EdgeInsets.zero;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
theme: ThemeData(popupMenuTheme: popupMenuTheme),
|
||||
|
@ -207,6 +209,7 @@ void main() {
|
|||
elevation: elevation,
|
||||
color: color,
|
||||
shape: shape,
|
||||
menuPadding: menuPadding,
|
||||
itemBuilder: (BuildContext context) {
|
||||
return <PopupMenuEntry<void>>[
|
||||
PopupMenuItem<void>(
|
||||
|
@ -250,6 +253,12 @@ void main() {
|
|||
).last,
|
||||
);
|
||||
expect(text.style, textStyle);
|
||||
|
||||
/// PopupMenu widget is private so in order to test padding the widget
|
||||
/// with the popup padding is extracted.
|
||||
final SingleChildScrollView popupMenu = tester.widget<SingleChildScrollView>
|
||||
(find.byType(SingleChildScrollView));
|
||||
expect(popupMenu.padding, menuPadding);
|
||||
});
|
||||
|
||||
testWidgets('ThemeData.popupMenuTheme properties are utilized', (WidgetTester tester) async {
|
||||
|
@ -258,6 +267,8 @@ void main() {
|
|||
final Key enabledPopupItemKey = UniqueKey();
|
||||
final Key disabledPopupItemKey = UniqueKey();
|
||||
|
||||
const EdgeInsets themePadding = EdgeInsets.zero;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
key: popupButtonApp,
|
||||
home: Material(
|
||||
|
@ -275,6 +286,7 @@ void main() {
|
|||
}
|
||||
return SystemMouseCursors.alias;
|
||||
}),
|
||||
padding: themePadding,
|
||||
),
|
||||
child: PopupMenuButton<void>(
|
||||
key: popupButtonKey,
|
||||
|
@ -333,5 +345,11 @@ void main() {
|
|||
await gesture.down(tester.getCenter(find.byKey(enabledPopupItemKey)));
|
||||
await tester.pumpAndSettle();
|
||||
expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.alias);
|
||||
|
||||
/// PopupMenu widget is private so in order to test padding we extract
|
||||
/// the widget which holds the padding.
|
||||
final SingleChildScrollView popupMenu = tester.widget<SingleChildScrollView>
|
||||
(find.byType(SingleChildScrollView));
|
||||
expect(popupMenu.padding, themePadding);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue