diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index cf5170711f..1788a7b555 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -65,12 +65,12 @@ class _DropdownMenuPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final double selectedItemOffset = getSelectedItemOffset(); final Tween top = Tween( - begin: selectedItemOffset.clamp(0.0, size.height - _kMenuItemHeight), + begin: selectedItemOffset.clamp(0.0, math.max(size.height - _kMenuItemHeight, 0.0)), end: 0.0, ); final Tween bottom = Tween( - begin: (top.begin! + _kMenuItemHeight).clamp(_kMenuItemHeight, size.height), + begin: (top.begin! + _kMenuItemHeight).clamp(math.min(_kMenuItemHeight, size.height), size.height), end: size.height, ); diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart index 61ee8934dc..0670bef9fd 100644 --- a/packages/flutter/test/material/dropdown_test.dart +++ b/packages/flutter/test/material/dropdown_test.dart @@ -3008,4 +3008,61 @@ void main() { expect(scrollController.position.maxScrollExtent > 0, isTrue); expect(find.byType(Scrollbar), paints..rect()); }); + + // 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 + // the menu requires empty space surrounding the menu. Finally, the constraint height + // is 47.0 pixels for the menu rendering. + tester.binding.window.physicalSizeTestValue = const Size(800.0, 48.0 * 3 - 1.0); + tester.binding.window.devicePixelRatioTestValue = 1; + addTearDown(tester.binding.window.clearPhysicalSizeTestValue); + addTearDown(tester.binding.window.clearDevicePixelRatioTestValue); + + const String value = 'foo'; + final UniqueKey itemKey = UniqueKey(); + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: DropdownButton( + value: value, + items: >[ + DropdownMenuItem( + key: itemKey, + value: value, + child: const Text(value), + ), + ], + onChanged: (_) { }, + ), + ), + ), + ), + ); + + await tester.tap(find.text(value)); + await tester.pumpAndSettle(); + + final List itemBoxes = tester.renderObjectList(find.byKey(itemKey)).toList(); + expect(itemBoxes[0].localToGlobal(Offset.zero).dx, 364.0); + expect(itemBoxes[0].localToGlobal(Offset.zero).dy, 47.5); + + expect(itemBoxes[1].localToGlobal(Offset.zero).dx, 364.0); + expect(itemBoxes[1].localToGlobal(Offset.zero).dy, 47.5); + + expect( + find.ancestor( + of: find.text(value).last, + matching: find.byType(CustomPaint), + ).at(2), + paints + ..save() + ..rrect() + ..rrect() + ..rrect() + // The height of menu is 47.0. + ..rrect(rrect: const RRect.fromLTRBXY(0.0, 0.0, 112.0, 47.0, 2.0, 2.0), color: Colors.grey[50], hasMaskFilter: false) + ); + }); }