Skip to content

Commit 44ff2f5

Browse files
authored
Fix DropdownMenu isCollapsed decoration does not Reduce height (flutter#161427)
## Description This PR fixes DropdownMenu arrow icon position when `InputDecoration.isCollapsed` is set to true and `InputDecoration.suffixConstraints` is set to a smaller value than the min interactive height. It makes it possible to use collapsed `DropdownMenu` such as: ![image](https://github.com/user-attachments/assets/145c2a0b-a638-4226-ae29-0e21bb9d4bb0) _____ Before this PR and flutter#153089, `InputDecoration.isCollapsed` had no impact on the `DropdownMenu` height and there was no solution to reduce the height because its minimum height is enforced by the `IconButton` (arrow down or up) which is part of the `DropdownMenu`. Since flutter#153089, the height can be reduce with `InputDecoration.suffixIconConstraints` but it results in the icon being misaligned: ![image](https://github.com/user-attachments/assets/2a4d99bc-c26d-454b-8e62-dd8828789ae5) After this PR: When `InputDecoration.suffixIconConstraints` is used the icon is correctly aligned: ![image](https://github.com/user-attachments/assets/145c2a0b-a638-4226-ae29-0e21bb9d4bb0) <details><summary>Code sample</summary> ```dart import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @OverRide Widget build(BuildContext context) { return const MaterialApp( title: 'Flutter Demo', home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @OverRide State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @OverRide Widget build(BuildContext context) { return const Scaffold( body: Center( child: Center( child: DropdownMenu<String>( dropdownMenuEntries: [ DropdownMenuEntry(label: 'Item 1', value: '1'), DropdownMenuEntry(label: 'Item 2', value: '2'), ], inputDecorationTheme: InputDecorationTheme( contentPadding: EdgeInsets.fromLTRB(5, 0, 5, 0), isCollapsed: true, // Usable since flutter#153089. suffixIconConstraints: BoxConstraints(minHeight: 24, maxHeight: 24), filled: true, ), ), ), ), ); } } ``` </details> ## Related Issue Fixes [DropdownMenu inputDecoration isCollapsed property does not reduce vertical spacing](flutter#138691) ## Tests Adds 2 tests.
1 parent 1e79b65 commit 44ff2f5

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,10 +965,13 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
965965
crossAxisUnconstrained: false,
966966
builder: (BuildContext context, MenuController controller, Widget? child) {
967967
assert(_initialMenu != null);
968+
final bool isCollapsed = widget.inputDecorationTheme?.isCollapsed ?? false;
968969
final Widget trailingButton = Padding(
969-
padding: const EdgeInsets.all(4.0),
970+
padding: isCollapsed ? EdgeInsets.zero : const EdgeInsets.all(4.0),
970971
child: IconButton(
971972
isSelected: controller.isOpen,
973+
constraints: widget.inputDecorationTheme?.suffixIconConstraints,
974+
padding: isCollapsed ? EdgeInsets.zero : null,
972975
icon: widget.trailingIcon ?? const Icon(Icons.arrow_drop_down),
973976
selectedIcon: widget.selectedTrailingIcon ?? const Icon(Icons.arrow_drop_up),
974977
onPressed:

packages/flutter/test/material/dropdown_menu_test.dart

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void main() {
4646
double? menuHeight,
4747
Widget? leadingIcon,
4848
Widget? label,
49+
InputDecorationTheme? decorationTheme,
4950
}) {
5051
return MaterialApp(
5152
theme: themeData,
@@ -56,6 +57,7 @@ void main() {
5657
width: width,
5758
menuHeight: menuHeight,
5859
dropdownMenuEntries: entries,
60+
inputDecorationTheme: decorationTheme,
5961
),
6062
),
6163
);
@@ -1172,6 +1174,71 @@ void main() {
11721174
expect(iconButton, findsOneWidget);
11731175
});
11741176

1177+
testWidgets('Trailing IconButton height respects InputDecorationTheme.suffixIconConstraints', (
1178+
WidgetTester tester,
1179+
) async {
1180+
final ThemeData themeData = ThemeData();
1181+
1182+
// Default suffix icon constraints.
1183+
await tester.pumpWidget(buildTest(themeData, menuChildren));
1184+
await tester.pump();
1185+
1186+
final Finder iconButton = find.widgetWithIcon(IconButton, Icons.arrow_drop_down).first;
1187+
expect(tester.getSize(iconButton), const Size(48, 48));
1188+
1189+
// Custom suffix icon constraints.
1190+
await tester.pumpWidget(
1191+
buildTest(
1192+
themeData,
1193+
menuChildren,
1194+
decorationTheme: const InputDecorationTheme(
1195+
suffixIconConstraints: BoxConstraints(minWidth: 66, minHeight: 62),
1196+
),
1197+
),
1198+
);
1199+
await tester.pump();
1200+
1201+
expect(tester.getSize(iconButton), const Size(66, 62));
1202+
});
1203+
1204+
testWidgets('InputDecorationTheme.isCollapsed reduces height', (WidgetTester tester) async {
1205+
final ThemeData themeData = ThemeData();
1206+
1207+
// Default height.
1208+
await tester.pumpWidget(buildTest(themeData, menuChildren));
1209+
await tester.pump();
1210+
1211+
final Finder textField = find.byType(TextField).first;
1212+
expect(tester.getSize(textField).height, 56);
1213+
1214+
// Collapsed height.
1215+
await tester.pumpWidget(
1216+
buildTest(
1217+
themeData,
1218+
menuChildren,
1219+
decorationTheme: const InputDecorationTheme(isCollapsed: true),
1220+
),
1221+
);
1222+
await tester.pump();
1223+
1224+
expect(tester.getSize(textField).height, 48); // IconButton min height.
1225+
1226+
// Collapsed height with custom suffix icon constraints.
1227+
await tester.pumpWidget(
1228+
buildTest(
1229+
themeData,
1230+
menuChildren,
1231+
decorationTheme: const InputDecorationTheme(
1232+
isCollapsed: true,
1233+
suffixIconConstraints: BoxConstraints(maxWidth: 24, maxHeight: 24),
1234+
),
1235+
),
1236+
);
1237+
await tester.pump();
1238+
1239+
expect(tester.getSize(textField).height, 24);
1240+
});
1241+
11751242
testWidgets('Do not crash when resize window during menu opening', (WidgetTester tester) async {
11761243
addTearDown(tester.view.reset);
11771244
final ThemeData themeData = ThemeData();

0 commit comments

Comments
 (0)