mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
fix a dropdown button menu position bug (#89199)
This commit is contained in:
parent
3a6085474c
commit
b2550fe5fa
@ -517,7 +517,10 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
|
||||
// selected item is aligned with the button's vertical center, as far as
|
||||
// that's possible given availableHeight.
|
||||
_MenuLimits getMenuLimits(Rect buttonRect, double availableHeight, int index) {
|
||||
final double maxMenuHeight = availableHeight - 2.0 * _kMenuItemHeight;
|
||||
double computedMaxHeight = availableHeight - 2.0 * _kMenuItemHeight;
|
||||
if (menuMaxHeight != null) {
|
||||
computedMaxHeight = math.min(computedMaxHeight, menuMaxHeight!);
|
||||
}
|
||||
final double buttonTop = buttonRect.top;
|
||||
final double buttonBottom = math.min(buttonRect.bottom, availableHeight);
|
||||
final double selectedItemOffset = getItemOffset(index);
|
||||
@ -535,8 +538,8 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
|
||||
preferredMenuHeight += itemHeights.reduce((double total, double height) => total + height);
|
||||
|
||||
// If there are too many elements in the menu, we need to shrink it down
|
||||
// so it is at most the maxMenuHeight.
|
||||
final double menuHeight = math.min(maxMenuHeight, preferredMenuHeight);
|
||||
// so it is at most the computedMaxHeight.
|
||||
final double menuHeight = math.min(computedMaxHeight, preferredMenuHeight);
|
||||
double menuBottom = menuTop + menuHeight;
|
||||
|
||||
// If the computed top or bottom of the menu are outside of the range
|
||||
@ -544,14 +547,21 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
|
||||
// than the button height and the button is at the very bottom or top of the
|
||||
// screen, the menu will be aligned with the bottom or top of the button
|
||||
// respectively.
|
||||
if (menuTop < topLimit)
|
||||
if (menuTop < topLimit) {
|
||||
menuTop = math.min(buttonTop, topLimit);
|
||||
menuBottom = menuTop + menuHeight;
|
||||
}
|
||||
|
||||
if (menuBottom > bottomLimit) {
|
||||
menuBottom = math.max(buttonBottom, bottomLimit);
|
||||
menuTop = menuBottom - menuHeight;
|
||||
}
|
||||
|
||||
if (menuBottom - itemHeights[selectedIndex] / 2.0 < buttonBottom - buttonRect.height / 2.0) {
|
||||
menuBottom = buttonBottom - buttonRect.height / 2.0 + itemHeights[selectedIndex] / 2.0;
|
||||
menuTop = menuBottom - menuHeight;
|
||||
}
|
||||
|
||||
double scrollOffset = 0;
|
||||
// If all of the menu items will not fit within availableHeight then
|
||||
// compute the scroll offset that will line the selected menu item up
|
||||
@ -559,7 +569,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
|
||||
// shown - subsequently we leave the scroll offset where the user left
|
||||
// it. This scroll offset is only accurate for fixed height menu items
|
||||
// (the default).
|
||||
if (preferredMenuHeight > maxMenuHeight) {
|
||||
if (preferredMenuHeight > computedMaxHeight) {
|
||||
// The offset should be zero if the selected item is in view at the beginning
|
||||
// of the menu. Otherwise, the scroll offset should center the item if possible.
|
||||
scrollOffset = math.max(0.0, selectedItemOffset - (buttonTop - menuTop));
|
||||
@ -568,6 +578,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
|
||||
scrollOffset = math.min(scrollOffset, preferredMenuHeight - menuHeight);
|
||||
}
|
||||
|
||||
assert(menuHeight == menuBottom - menuTop);
|
||||
return _MenuLimits(menuTop, menuBottom, menuHeight, scrollOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3127,6 +3127,29 @@ void main() {
|
||||
expect(menuHeight, defaultMenuHeight);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/89029
|
||||
testWidgets('menu position test with `menuMaxHeight`', (WidgetTester tester) async {
|
||||
final Key buttonKey = UniqueKey();
|
||||
await tester.pumpWidget(buildFrame(
|
||||
buttonKey: buttonKey,
|
||||
value: '6',
|
||||
items: List<String>.generate(/*length=*/64, (int index) => index.toString()),
|
||||
onChanged: onChanged,
|
||||
menuMaxHeight: 2 * kMinInteractiveDimension,
|
||||
));
|
||||
|
||||
await tester.tap(find.text('6'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final RenderBox menuBox = tester.renderObject(find.byType(ListView));
|
||||
final RenderBox buttonBox = tester.renderObject(find.byKey(buttonKey));
|
||||
// The menu's bottom should align with the drop-button's bottom.
|
||||
expect(
|
||||
menuBox.localToGlobal(menuBox.paintBounds.bottomCenter).dy,
|
||||
buttonBox.localToGlobal(buttonBox.paintBounds.bottomCenter).dy,
|
||||
);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/76614
|
||||
testWidgets('Do not crash if used in very short screen', (WidgetTester tester) async {
|
||||
// The default item height is 48.0 pixels and needs two items padding since
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user