From e00b1531af2aeca01a8c15effec6992cc0e6ce97 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 18 Sep 2020 15:02:04 -0700 Subject: [PATCH] Reland "TextField constrained layout bug (#65966)" (#66055) --- .../lib/src/material/input_decorator.dart | 13 ++-- .../test/material/input_decorator_test.dart | 76 +++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 4a726d5be38..26ae53ac59f 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -1032,11 +1032,12 @@ class _RenderDecoration extends RenderBox { counterHeight, helperErrorHeight, ); + final Offset densityOffset = decoration.visualDensity.baseSizeAdjustment; boxToBaseline[input] = _layoutLineBox( input, boxConstraints.deflate(EdgeInsets.only( - top: contentPadding.top + topHeight, - bottom: contentPadding.bottom + bottomHeight, + top: contentPadding.top + topHeight + densityOffset.dy / 2, + bottom: contentPadding.bottom + bottomHeight + densityOffset.dy / 2, )).copyWith( minWidth: inputWidth, maxWidth: inputWidth, @@ -1065,13 +1066,15 @@ class _RenderDecoration extends RenderBox { prefixHeight - boxToBaseline[prefix], suffixHeight - boxToBaseline[suffix], ); + // TODO(justinmc): fixBelowInput should have no effect when there is no + // prefix/suffix below the input. + // https://github.com/flutter/flutter/issues/66050 final double fixBelowInput = math.max( 0, fixBelowBaseline - (inputHeight - inputInternalBaseline), ); // Calculate the height of the input text container. - final Offset densityOffset = decoration.visualDensity.baseSizeAdjustment; final double prefixIconHeight = prefixIcon == null ? 0 : prefixIcon.size.height; final double suffixIconHeight = suffixIcon == null ? 0 : suffixIcon.size.height; final double fixIconHeight = math.max(prefixIconHeight, suffixIconHeight); @@ -1087,8 +1090,8 @@ class _RenderDecoration extends RenderBox { ); final double minContainerHeight = decoration.isDense || decoration.isCollapsed || expands ? 0.0 - : kMinInteractiveDimension + densityOffset.dy; - final double maxContainerHeight = boxConstraints.maxHeight - bottomHeight + densityOffset.dy; + : kMinInteractiveDimension; + final double maxContainerHeight = boxConstraints.maxHeight - bottomHeight; final double containerHeight = expands ? maxContainerHeight : math.min(math.max(contentHeight, minContainerHeight), maxContainerHeight); diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 61cf8c9fa19..625389df838 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -4235,4 +4235,80 @@ void main() { expect(tester.getSize(find.text(labelText)).width, labelWidth); expect(getOpacity(tester, prefixText), 1.0); }); + + testWidgets('given enough space, constrained and unconstrained heights result in the same size widget', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/65572 + final UniqueKey keyUnconstrained = UniqueKey(); + final UniqueKey keyConstrained = UniqueKey(); + + Widget getInputDecorator(VisualDensity visualDensity) { + return MaterialApp( + home: Material( + child: Builder( + builder: (BuildContext context) { + return Theme( + data: Theme.of(context).copyWith(visualDensity: visualDensity), + child: Center( + child: Row( + children: [ + SizedBox( + width: 35.0, + child: TextField( + key: keyUnconstrained, + ), + ), + SizedBox( + width: 35.0, + // 48 is the height that this TextField would take when + // laid out with no constraints. + height: 48.0, + child: TextField( + key: keyConstrained, + ), + ), + ], + ), + ), + ); + }, + ), + ), + ); + } + + await tester.pumpWidget(getInputDecorator(VisualDensity.standard)); + final double constrainedHeight = tester.getSize(find.byKey(keyConstrained)).height; + final double unConstrainedHeight = tester.getSize(find.byKey(keyUnconstrained)).height; + expect(constrainedHeight, equals(unConstrainedHeight)); + + await tester.pumpWidget(getInputDecorator(VisualDensity.compact)); + final double constrainedHeightCompact = tester.getSize(find.byKey(keyConstrained)).height; + final double unConstrainedHeightCompact = tester.getSize(find.byKey(keyUnconstrained)).height; + expect(constrainedHeightCompact, equals(unConstrainedHeightCompact)); + }); + + testWidgets('A vertically constrained TextField still positions its text inside of itself', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Material( + child: Center( + child: Container( + width: 200, + height: 28, + child: TextField( + controller: TextEditingController(text: 'A'), + ), + ), + ), + ), + )); + + final double textFieldTop = tester.getTopLeft(find.byType(TextField)).dy; + final double textFieldBottom = tester.getBottomLeft(find.byType(TextField)).dy; + final double textTop = tester.getTopLeft(find.text('A')).dy; + + // The text is inside the field. + expect(tester.getSize(find.text('A')).height, lessThan(textFieldBottom - textFieldTop)); + expect(textTop, greaterThan(textFieldTop)); + expect(textTop, lessThan(textFieldBottom)); + }); }