mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Fix expanded DropdownMenu panel is shorter than text field (#174443)
## Description
This PR fixes DropdownMenu menu panel being shorter than the TextField
when it expands to full-screen width.
## Before
The menu panel is shorter than the text field:
<img width="297" height="130" alt="image"
src="https://github.com/user-attachments/assets/db42bd01-94d8-47fb-9331-ebd78111b931"
/>
## After
The menu panel expands as close as possible to the edge similarly to the
text field.
<img width="297" height="130" alt="image"
src="https://github.com/user-attachments/assets/b7d8f2aa-a668-439a-8195-9b9ce48dba6b"
/>
## Code sample
<details><summary>Code sample for recordings</summary>
```dart
import 'package:flutter/material.dart';
void main() {
runApp(const DropdownMenuExample());
}
class DropdownMenuExample extends StatefulWidget {
const DropdownMenuExample({super.key});
@override
State<DropdownMenuExample> createState() => _DropdownMenuExampleState();
}
class _DropdownMenuExampleState extends State<DropdownMenuExample> {
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: DropdownMenu<int>(
expandedInsets: EdgeInsets.zero,
dropdownMenuEntries: <DropdownMenuEntry<int>>[
DropdownMenuEntry<int>(value: 0, label: 'Flutter'),
],
),
),
);
}
}
```
</details>
## Implementation details
MenuAnchor automatically adds a default padding, see
2c21273bfd/packages/flutter/lib/src/material/menu_anchor.dart (L81-L82)
This PR add a property to menu anchor to expose the view padding.
DropdownMenu sets this padding to EdgeInsets.zero to opt-out from the
default 8 padding.
## Related Issue
Fixes [DropdownMenu children is shorter than the TextField when it
expands to full-screen
width](https://github.com/flutter/flutter/issues/172680)
## Tests
Adds 2 tests.
---------
Co-authored-by: Qun Cheng <36861262+QuncCccccc@users.noreply.github.com>
This commit is contained in:
parent
251f4a8d5b
commit
6e9286d6eb
@ -1110,6 +1110,7 @@ class _DropdownMenuState<T> extends State<DropdownMenu<T>> {
|
||||
Widget menuAnchor = MenuAnchor(
|
||||
style: effectiveMenuStyle,
|
||||
alignmentOffset: widget.alignmentOffset,
|
||||
reservedPadding: EdgeInsets.zero,
|
||||
controller: _controller,
|
||||
menuChildren: menu,
|
||||
crossAxisUnconstrained: false,
|
||||
|
||||
@ -161,6 +161,7 @@ class MenuAnchor extends StatefulWidget {
|
||||
this.childFocusNode,
|
||||
this.style,
|
||||
this.alignmentOffset = Offset.zero,
|
||||
this.reservedPadding,
|
||||
this.layerLink,
|
||||
this.clipBehavior = Clip.hardEdge,
|
||||
@Deprecated(
|
||||
@ -318,6 +319,11 @@ class MenuAnchor extends StatefulWidget {
|
||||
/// to rebuild this child when those change.
|
||||
final Widget? child;
|
||||
|
||||
/// The padding between the edge of the safe area and the menu panel.
|
||||
///
|
||||
/// Defaults to EdgeInsets.all(8).
|
||||
final EdgeInsetsGeometry? reservedPadding;
|
||||
|
||||
@override
|
||||
State<MenuAnchor> createState() => _MenuAnchorState();
|
||||
|
||||
@ -406,6 +412,7 @@ class _MenuAnchorState extends State<MenuAnchor> {
|
||||
menuPosition: position,
|
||||
anchor: this,
|
||||
alignmentOffset: widget.alignmentOffset ?? Offset.zero,
|
||||
reservedPadding: widget.reservedPadding ?? const EdgeInsets.all(_kMenuViewPadding),
|
||||
);
|
||||
}
|
||||
|
||||
@ -2920,6 +2927,7 @@ class _MenuLayout extends SingleChildLayoutDelegate {
|
||||
required this.avoidBounds,
|
||||
required this.orientation,
|
||||
required this.parentOrientation,
|
||||
required this.reservedPadding,
|
||||
});
|
||||
|
||||
// Rectangle of underlying button, relative to the overlay's dimensions.
|
||||
@ -2951,13 +2959,14 @@ class _MenuLayout extends SingleChildLayoutDelegate {
|
||||
// The orientation of this menu's parent.
|
||||
final Axis parentOrientation;
|
||||
|
||||
// How close to the edge of the safe area the menu will be placed.
|
||||
final EdgeInsetsGeometry reservedPadding;
|
||||
|
||||
@override
|
||||
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
|
||||
// The menu can be at most the size of the overlay minus _kMenuViewPadding
|
||||
// pixels in each direction.
|
||||
return BoxConstraints.loose(
|
||||
constraints.biggest,
|
||||
).deflate(const EdgeInsets.all(_kMenuViewPadding));
|
||||
// The menu can be at most the size of the overlay minus the view padding
|
||||
// in each direction.
|
||||
return BoxConstraints.loose(constraints.biggest).deflate(reservedPadding);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -3289,6 +3298,7 @@ class _Submenu extends StatelessWidget {
|
||||
this.crossAxisUnconstrained = true,
|
||||
required this.menuChildren,
|
||||
required this.menuScopeNode,
|
||||
required this.reservedPadding,
|
||||
});
|
||||
|
||||
final FocusScopeNode menuScopeNode;
|
||||
@ -3301,6 +3311,7 @@ class _Submenu extends StatelessWidget {
|
||||
final Clip clipBehavior;
|
||||
final bool crossAxisUnconstrained;
|
||||
final List<Widget> menuChildren;
|
||||
final EdgeInsetsGeometry reservedPadding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -3399,6 +3410,7 @@ class _Submenu extends StatelessWidget {
|
||||
menuPosition: menuPosition.position,
|
||||
orientation: anchor._orientation,
|
||||
parentOrientation: anchor._parent?._orientation ?? Axis.horizontal,
|
||||
reservedPadding: reservedPadding,
|
||||
),
|
||||
child: menuPanel,
|
||||
);
|
||||
|
||||
@ -927,6 +927,31 @@ void main() {
|
||||
expect(buttonSize.width, parentWidth - 35.0 - 20.0);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/172680.
|
||||
testWidgets('Menu panel width can expand to full-screen width', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Scaffold(
|
||||
body: DropdownMenu<int>(
|
||||
expandedInsets: EdgeInsets.zero,
|
||||
dropdownMenuEntries: <DropdownMenuEntry<int>>[
|
||||
DropdownMenuEntry<int>(value: 0, label: 'Flutter'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final double dropdownWidth = tester.getSize(find.byType(DropdownMenu<int>)).width;
|
||||
expect(dropdownWidth, 800);
|
||||
|
||||
await tester.tap(find.byType(DropdownMenu<int>));
|
||||
await tester.pump();
|
||||
|
||||
final double menuWidth = tester.getSize(findMenuItemButton('Flutter')).width;
|
||||
expect(dropdownWidth, menuWidth);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Material2 - The menuHeight property can be used to show a shorter scrollable menu list instead of the complete list',
|
||||
(WidgetTester tester) async {
|
||||
|
||||
@ -5047,6 +5047,55 @@ void main() {
|
||||
expect(find.byIcon(disabledIcon), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('Menu panel default reserved padding', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: MenuAnchor(
|
||||
controller: controller,
|
||||
menuChildren: const <Widget>[SizedBox(width: 800, height: 24)],
|
||||
builder: (BuildContext context, MenuController controller, Widget? child) {
|
||||
return const SizedBox(width: 800, height: 24);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
controller.open();
|
||||
await tester.pump();
|
||||
|
||||
const double defaultReservedPadding = 8.0; // See _kMenuViewPadding.
|
||||
expect(tester.getRect(findMenuPanels()).width, 800.0 - defaultReservedPadding * 2);
|
||||
});
|
||||
|
||||
testWidgets('Menu panel accepts custom reserved padding', (WidgetTester tester) async {
|
||||
const EdgeInsetsGeometry reservedPadding = EdgeInsets.symmetric(horizontal: 13.0);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: MenuAnchor(
|
||||
controller: controller,
|
||||
reservedPadding: reservedPadding,
|
||||
menuChildren: const <Widget>[SizedBox(width: 800, height: 24)],
|
||||
builder: (BuildContext context, MenuController controller, Widget? child) {
|
||||
return const SizedBox(width: 800, height: 24);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
controller.open();
|
||||
await tester.pump();
|
||||
|
||||
expect(tester.getRect(findMenuPanels()).width, 800.0 - reservedPadding.horizontal);
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> createTestMenus({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user