fix a dropdown button menu position bug (#89199)

diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart
index cf05969..265410e 100644
--- a/packages/flutter/lib/src/material/dropdown.dart
+++ b/packages/flutter/lib/src/material/dropdown.dart
@@ -517,7 +517,10 @@
   // 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 @@
       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 @@
     // 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 @@
     // 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 @@
       scrollOffset = math.min(scrollOffset, preferredMenuHeight - menuHeight);
     }
 
+    assert(menuHeight == menuBottom - menuTop);
     return _MenuLimits(menuTop, menuBottom, menuHeight, scrollOffset);
   }
 }
diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart
index 20a9d43..7543ccb 100644
--- a/packages/flutter/test/material/dropdown_test.dart
+++ b/packages/flutter/test/material/dropdown_test.dart
@@ -3127,6 +3127,29 @@
     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