Fix DropdownButtonFormField clips text when large text scale is used (#159975)

## Description

This PR fixes `DropdownButtonFormField` text being clipped when using a
large text scale.

Before:


![image](https://github.com/user-attachments/assets/6c4deed2-eb61-4c0a-912e-dba364013038)

After:


![image](https://github.com/user-attachments/assets/1dee5cda-9885-47c1-92a6-afbbc2312266)


This extend the fix from https://github.com/flutter/flutter/pull/107201
which does not work properly with Material 3 (because of
TextStyle.height being set for M3 default text styles).

## Related Issue

Fixes [DropdownButtonFormField clips text when large text scale is used
and useMaterial3 is
true](https://github.com/flutter/flutter/issues/159971)

## Tests

Adds 1 test.
Updates 1 test.
This commit is contained in:
Bruno Leroux 2025-01-23 06:38:20 +01:00 committed by GitHub
parent 9c960ff7da
commit 97ca57cf08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 66 additions and 10 deletions

View File

@ -1452,7 +1452,9 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
double get _denseButtonHeight {
final double fontSize =
_textStyle!.fontSize ?? Theme.of(context).textTheme.titleMedium!.fontSize!;
final double scaledFontSize = MediaQuery.textScalerOf(context).scale(fontSize);
final double lineHeight =
_textStyle!.height ?? Theme.of(context).textTheme.titleMedium!.height ?? 1.0;
final double scaledFontSize = MediaQuery.textScalerOf(context).scale(fontSize * lineHeight);
return math.max(scaledFontSize, math.max(widget.iconSize, _kDenseButtonHeight));
}

View File

@ -488,11 +488,65 @@ void main() {
}
});
testWidgets('DropdownButtonFormField with isDense:true does not clip large scale text', (
// Regression test for https://github.com/flutter/flutter/issues/159971.
testWidgets('DropdownButtonFormField does not clip large scale text', (
WidgetTester tester,
) async {
final Key buttonKey = UniqueKey();
const String value = 'two';
const double scaleFactor = 3.0;
final List<DropdownMenuItem<String>> dropdownItems =
menuItems.map<DropdownMenuItem<String>>((String item) {
return DropdownMenuItem<String>(
key: ValueKey<String>(item),
value: item,
child: Text(item, key: ValueKey<String>('${item}Text')),
);
}).toList();
await tester.pumpWidget(
TestApp(
textDirection: TextDirection.ltr,
child: Builder(
builder: (BuildContext context) {
return MediaQuery.withClampedTextScaling(
minScaleFactor: scaleFactor,
maxScaleFactor: scaleFactor,
child: Material(
child: Center(
child: DropdownButtonFormField<String>(
key: buttonKey,
value: value,
onChanged: onChanged,
items: dropdownItems,
),
),
),
);
},
),
),
);
final BuildContext context = tester.element(find.byType(DropdownButton<String>));
final TextStyle style = Theme.of(context).textTheme.titleMedium!;
final double lineHeight = style.fontSize! * style.height!; // 16 * 1.5 = 24
final double labelHeight = lineHeight * scaleFactor; // 24 * 3.0 = 72
const double decorationVerticalPadding = 16.0;
final RenderBox box = tester.renderObject<RenderBox>(find.byType(DropdownButton<String>));
expect(box.size.height, labelHeight + decorationVerticalPadding);
});
// Regression test for https://github.com/flutter/flutter/issues/159971.
testWidgets('DropdownButtonFormField with custom text style does not clip large scale text', (
WidgetTester tester,
) async {
final Key buttonKey = UniqueKey();
const String value = 'two';
const double scaleFactor = 3.0;
const double fontSize = 22;
const double fontHeight = 1.5;
await tester.pumpWidget(
TestApp(
@ -500,24 +554,21 @@ void main() {
child: Builder(
builder:
(BuildContext context) => MediaQuery.withClampedTextScaling(
minScaleFactor: 3.0,
maxScaleFactor: 3.0,
minScaleFactor: scaleFactor,
maxScaleFactor: scaleFactor,
child: Material(
child: Center(
child: DropdownButtonFormField<String>(
key: buttonKey,
value: value,
onChanged: onChanged,
style: const TextStyle(fontSize: fontSize, height: fontHeight),
items:
menuItems.map<DropdownMenuItem<String>>((String item) {
return DropdownMenuItem<String>(
key: ValueKey<String>(item),
value: item,
child: Text(
item,
key: ValueKey<String>('${item}Text'),
style: const TextStyle(fontSize: 20.0),
),
child: Text(item, key: ValueKey<String>('${item}Text')),
);
}).toList(),
),
@ -528,8 +579,11 @@ void main() {
),
);
const double lineHeight = fontSize * fontHeight; // 22 * 1.5 = 33
const double labelHeight = lineHeight * scaleFactor; // 33 * 3.0 = 99
const double decorationVerticalPadding = 16.0;
final RenderBox box = tester.renderObject<RenderBox>(find.byType(DropdownButton<String>));
expect(box.size.height, 64.0);
expect(box.size.height, labelHeight + decorationVerticalPadding);
});
testWidgets('DropdownButtonFormField.isDense is true by default', (WidgetTester tester) async {