Skip to content

Commit a2c2d1a

Browse files
authored
[PopupMenu]: Add menu ClipBehavior (flutter#107466)
1 parent 329afbe commit a2c2d1a

File tree

2 files changed

+72
-0
lines changed

2 files changed

+72
-0
lines changed

packages/flutter/lib/src/material/popup_menu.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,11 +534,13 @@ class _PopupMenu<T> extends StatelessWidget {
534534
required this.route,
535535
required this.semanticLabel,
536536
this.constraints,
537+
required this.clipBehavior,
537538
});
538539

539540
final _PopupMenuRoute<T> route;
540541
final String? semanticLabel;
541542
final BoxConstraints? constraints;
543+
final Clip clipBehavior;
542544

543545
@override
544546
Widget build(BuildContext context) {
@@ -607,6 +609,7 @@ class _PopupMenu<T> extends StatelessWidget {
607609
child: Material(
608610
shape: route.shape ?? popupMenuTheme.shape,
609611
color: route.color ?? popupMenuTheme.color,
612+
clipBehavior: clipBehavior,
610613
type: MaterialType.card,
611614
elevation: route.elevation ?? popupMenuTheme.elevation ?? 8.0,
612615
child: Align(
@@ -769,6 +772,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
769772
this.color,
770773
required this.capturedThemes,
771774
this.constraints,
775+
required this.clipBehavior,
772776
}) : itemSizes = List<Size?>.filled(items.length, null);
773777

774778
final RelativeRect position;
@@ -781,6 +785,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
781785
final Color? color;
782786
final CapturedThemes capturedThemes;
783787
final BoxConstraints? constraints;
788+
final Clip clipBehavior;
784789

785790
@override
786791
Animation<double> createAnimation() {
@@ -819,6 +824,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
819824
route: this,
820825
semanticLabel: semanticLabel,
821826
constraints: constraints,
827+
clipBehavior: clipBehavior,
822828
);
823829
final MediaQueryData mediaQuery = MediaQuery.of(context);
824830
return MediaQuery.removePadding(
@@ -896,6 +902,9 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
896902
/// label is not provided, it will default to
897903
/// [MaterialLocalizations.popupMenuLabel].
898904
///
905+
/// The `clipBehavior` argument is used to clip the shape of the menu. Defaults to
906+
/// [Clip.none].
907+
///
899908
/// See also:
900909
///
901910
/// * [PopupMenuItem], a popup menu entry for a single value.
@@ -916,6 +925,7 @@ Future<T?> showMenu<T>({
916925
Color? color,
917926
bool useRootNavigator = false,
918927
BoxConstraints? constraints,
928+
Clip clipBehavior = Clip.none,
919929
}) {
920930
assert(context != null);
921931
assert(position != null);
@@ -946,6 +956,7 @@ Future<T?> showMenu<T>({
946956
color: color,
947957
capturedThemes: InheritedTheme.capture(from: context, to: navigator.context),
948958
constraints: constraints,
959+
clipBehavior: clipBehavior,
949960
));
950961
}
951962

@@ -1015,6 +1026,7 @@ class PopupMenuButton<T> extends StatefulWidget {
10151026
this.enableFeedback,
10161027
this.constraints,
10171028
this.position = PopupMenuPosition.over,
1029+
this.clipBehavior = Clip.none,
10181030
}) : assert(itemBuilder != null),
10191031
assert(enabled != null),
10201032
assert(
@@ -1146,6 +1158,13 @@ class PopupMenuButton<T> extends StatefulWidget {
11461158
/// popup menu appear directly over the button that was used to create it.
11471159
final PopupMenuPosition position;
11481160

1161+
/// {@macro flutter.material.Material.clipBehavior}
1162+
///
1163+
/// The [clipBehavior] argument is used the clip shape of the menu.
1164+
///
1165+
/// Defaults to [Clip.none], and must not be null.
1166+
final Clip clipBehavior;
1167+
11491168
@override
11501169
PopupMenuButtonState<T> createState() => PopupMenuButtonState<T>();
11511170
}
@@ -1195,6 +1214,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
11951214
shape: widget.shape ?? popupMenuTheme.shape,
11961215
color: widget.color ?? popupMenuTheme.color,
11971216
constraints: widget.constraints,
1217+
clipBehavior: widget.clipBehavior,
11981218
)
11991219
.then<void>((T? newValue) {
12001220
if (!mounted) {

packages/flutter/test/material/popup_menu_test.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2885,6 +2885,58 @@ void main() {
28852885
// PopupMenuButton icon size overrides IconTheme's size.
28862886
expect(iconButton.iconSize, 50.0);
28872887
});
2888+
2889+
testWidgets('Popup menu clip behavior', (WidgetTester tester) async {
2890+
// Regression test for https://github.com/flutter/flutter/issues/107215
2891+
final Key popupButtonKey = UniqueKey();
2892+
const double radius = 20.0;
2893+
2894+
Widget buildPopupMenu({required Clip clipBehavior}) {
2895+
return MaterialApp(
2896+
home: Scaffold(
2897+
body: Center(
2898+
child: PopupMenuButton<String>(
2899+
key: popupButtonKey,
2900+
shape: const RoundedRectangleBorder(
2901+
borderRadius: BorderRadius.all(Radius.circular(radius)),
2902+
),
2903+
clipBehavior: clipBehavior,
2904+
itemBuilder: (_) => <PopupMenuEntry<String>>[
2905+
const PopupMenuItem<String>(
2906+
value: 'value',
2907+
child: Text('Item 0'),
2908+
),
2909+
],
2910+
),
2911+
),
2912+
),
2913+
);
2914+
}
2915+
2916+
// Popup menu with default ClipBehavior.
2917+
await tester.pumpWidget(buildPopupMenu(clipBehavior: Clip.none));
2918+
2919+
// Open the popup to build and show the menu contents.
2920+
await tester.tap(find.byKey(popupButtonKey));
2921+
await tester.pumpAndSettle();
2922+
2923+
Material material = tester.widget<Material>(find.byType(Material).last);
2924+
expect(material.clipBehavior, Clip.none);
2925+
2926+
// Close the popup menu.
2927+
await tester.tapAt(Offset.zero);
2928+
await tester.pumpAndSettle();
2929+
2930+
// Popup menu with custom ClipBehavior.
2931+
await tester.pumpWidget(buildPopupMenu(clipBehavior: Clip.hardEdge));
2932+
2933+
// Open the popup to build and show the menu contents.
2934+
await tester.tap(find.byKey(popupButtonKey));
2935+
await tester.pumpAndSettle();
2936+
2937+
material = tester.widget<Material>(find.byType(Material).last);
2938+
expect(material.clipBehavior, Clip.hardEdge);
2939+
});
28882940
}
28892941

28902942
class TestApp extends StatefulWidget {

0 commit comments

Comments
 (0)