Unverified Commit a2c2d1ad authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

[PopupMenu]: Add menu `ClipBehavior` (#107466)

parent 329afbe9
...@@ -534,11 +534,13 @@ class _PopupMenu<T> extends StatelessWidget { ...@@ -534,11 +534,13 @@ class _PopupMenu<T> extends StatelessWidget {
required this.route, required this.route,
required this.semanticLabel, required this.semanticLabel,
this.constraints, this.constraints,
required this.clipBehavior,
}); });
final _PopupMenuRoute<T> route; final _PopupMenuRoute<T> route;
final String? semanticLabel; final String? semanticLabel;
final BoxConstraints? constraints; final BoxConstraints? constraints;
final Clip clipBehavior;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -607,6 +609,7 @@ class _PopupMenu<T> extends StatelessWidget { ...@@ -607,6 +609,7 @@ class _PopupMenu<T> extends StatelessWidget {
child: Material( child: Material(
shape: route.shape ?? popupMenuTheme.shape, shape: route.shape ?? popupMenuTheme.shape,
color: route.color ?? popupMenuTheme.color, color: route.color ?? popupMenuTheme.color,
clipBehavior: clipBehavior,
type: MaterialType.card, type: MaterialType.card,
elevation: route.elevation ?? popupMenuTheme.elevation ?? 8.0, elevation: route.elevation ?? popupMenuTheme.elevation ?? 8.0,
child: Align( child: Align(
...@@ -769,6 +772,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> { ...@@ -769,6 +772,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
this.color, this.color,
required this.capturedThemes, required this.capturedThemes,
this.constraints, this.constraints,
required this.clipBehavior,
}) : itemSizes = List<Size?>.filled(items.length, null); }) : itemSizes = List<Size?>.filled(items.length, null);
final RelativeRect position; final RelativeRect position;
...@@ -781,6 +785,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> { ...@@ -781,6 +785,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final Color? color; final Color? color;
final CapturedThemes capturedThemes; final CapturedThemes capturedThemes;
final BoxConstraints? constraints; final BoxConstraints? constraints;
final Clip clipBehavior;
@override @override
Animation<double> createAnimation() { Animation<double> createAnimation() {
...@@ -819,6 +824,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> { ...@@ -819,6 +824,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
route: this, route: this,
semanticLabel: semanticLabel, semanticLabel: semanticLabel,
constraints: constraints, constraints: constraints,
clipBehavior: clipBehavior,
); );
final MediaQueryData mediaQuery = MediaQuery.of(context); final MediaQueryData mediaQuery = MediaQuery.of(context);
return MediaQuery.removePadding( return MediaQuery.removePadding(
...@@ -896,6 +902,9 @@ class _PopupMenuRoute<T> extends PopupRoute<T> { ...@@ -896,6 +902,9 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
/// label is not provided, it will default to /// label is not provided, it will default to
/// [MaterialLocalizations.popupMenuLabel]. /// [MaterialLocalizations.popupMenuLabel].
/// ///
/// The `clipBehavior` argument is used to clip the shape of the menu. Defaults to
/// [Clip.none].
///
/// See also: /// See also:
/// ///
/// * [PopupMenuItem], a popup menu entry for a single value. /// * [PopupMenuItem], a popup menu entry for a single value.
...@@ -916,6 +925,7 @@ Future<T?> showMenu<T>({ ...@@ -916,6 +925,7 @@ Future<T?> showMenu<T>({
Color? color, Color? color,
bool useRootNavigator = false, bool useRootNavigator = false,
BoxConstraints? constraints, BoxConstraints? constraints,
Clip clipBehavior = Clip.none,
}) { }) {
assert(context != null); assert(context != null);
assert(position != null); assert(position != null);
...@@ -946,6 +956,7 @@ Future<T?> showMenu<T>({ ...@@ -946,6 +956,7 @@ Future<T?> showMenu<T>({
color: color, color: color,
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context), capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
constraints: constraints, constraints: constraints,
clipBehavior: clipBehavior,
)); ));
} }
...@@ -1015,6 +1026,7 @@ class PopupMenuButton<T> extends StatefulWidget { ...@@ -1015,6 +1026,7 @@ class PopupMenuButton<T> extends StatefulWidget {
this.enableFeedback, this.enableFeedback,
this.constraints, this.constraints,
this.position = PopupMenuPosition.over, this.position = PopupMenuPosition.over,
this.clipBehavior = Clip.none,
}) : assert(itemBuilder != null), }) : assert(itemBuilder != null),
assert(enabled != null), assert(enabled != null),
assert( assert(
...@@ -1146,6 +1158,13 @@ class PopupMenuButton<T> extends StatefulWidget { ...@@ -1146,6 +1158,13 @@ class PopupMenuButton<T> extends StatefulWidget {
/// popup menu appear directly over the button that was used to create it. /// popup menu appear directly over the button that was used to create it.
final PopupMenuPosition position; final PopupMenuPosition position;
/// {@macro flutter.material.Material.clipBehavior}
///
/// The [clipBehavior] argument is used the clip shape of the menu.
///
/// Defaults to [Clip.none], and must not be null.
final Clip clipBehavior;
@override @override
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>(); PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
} }
...@@ -1195,6 +1214,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> { ...@@ -1195,6 +1214,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
shape: widget.shape ?? popupMenuTheme.shape, shape: widget.shape ?? popupMenuTheme.shape,
color: widget.color ?? popupMenuTheme.color, color: widget.color ?? popupMenuTheme.color,
constraints: widget.constraints, constraints: widget.constraints,
clipBehavior: widget.clipBehavior,
) )
.then<void>((T? newValue) { .then<void>((T? newValue) {
if (!mounted) { if (!mounted) {
......
...@@ -2885,6 +2885,58 @@ void main() { ...@@ -2885,6 +2885,58 @@ void main() {
// PopupMenuButton icon size overrides IconTheme's size. // PopupMenuButton icon size overrides IconTheme's size.
expect(iconButton.iconSize, 50.0); expect(iconButton.iconSize, 50.0);
}); });
testWidgets('Popup menu clip behavior', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/107215
final Key popupButtonKey = UniqueKey();
const double radius = 20.0;
Widget buildPopupMenu({required Clip clipBehavior}) {
return MaterialApp(
home: Scaffold(
body: Center(
child: PopupMenuButton<String>(
key: popupButtonKey,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(radius)),
),
clipBehavior: clipBehavior,
itemBuilder: (_) => <PopupMenuEntry<String>>[
const PopupMenuItem<String>(
value: 'value',
child: Text('Item 0'),
),
],
),
),
),
);
}
// Popup menu with default ClipBehavior.
await tester.pumpWidget(buildPopupMenu(clipBehavior: Clip.none));
// Open the popup to build and show the menu contents.
await tester.tap(find.byKey(popupButtonKey));
await tester.pumpAndSettle();
Material material = tester.widget<Material>(find.byType(Material).last);
expect(material.clipBehavior, Clip.none);
// Close the popup menu.
await tester.tapAt(Offset.zero);
await tester.pumpAndSettle();
// Popup menu with custom ClipBehavior.
await tester.pumpWidget(buildPopupMenu(clipBehavior: Clip.hardEdge));
// Open the popup to build and show the menu contents.
await tester.tap(find.byKey(popupButtonKey));
await tester.pumpAndSettle();
material = tester.widget<Material>(find.byType(Material).last);
expect(material.clipBehavior, Clip.hardEdge);
});
} }
class TestApp extends StatefulWidget { class TestApp extends StatefulWidget {
......
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