From fe7d01ffab1cac2b97aa07e66b0c31a2bf851efa Mon Sep 17 00:00:00 2001 From: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:35:20 -0700 Subject: [PATCH] Prevent `InputDecorator` from supplying its descendants with non-normalized constraints (#130460) Fixes https://github.com/flutter/flutter/issues/129611 --- .../lib/src/material/input_decorator.dart | 4 +- .../test/material/input_decorator_test.dart | 56 ++++++++++++++----- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 0a9374c0bd..ecfd98ab85 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -964,7 +964,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin boxToBaseline[prefixIcon] = _layoutLineBox(prefixIcon, containerConstraints); boxToBaseline[suffixIcon] = _layoutLineBox(suffixIcon, containerConstraints); final BoxConstraints contentConstraints = containerConstraints.copyWith( - maxWidth: containerConstraints.maxWidth - contentPadding.horizontal, + maxWidth: math.max(0.0, containerConstraints.maxWidth - contentPadding.horizontal), ); boxToBaseline[prefix] = _layoutLineBox(prefix, contentConstraints); boxToBaseline[suffix] = _layoutLineBox(suffix, contentConstraints); @@ -1093,7 +1093,7 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin final double minContainerHeight = decoration.isDense! || decoration.isCollapsed || expands ? 0.0 : kMinInteractiveDimension; - final double maxContainerHeight = boxConstraints.maxHeight - bottomHeight; + final double maxContainerHeight = math.max(0.0, 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 d7b8641797..088cd67a5d 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -158,7 +158,11 @@ TextStyle? getIconStyle(WidgetTester tester, IconData icon) { } void main() { - for (final bool useMaterial3 in [true, false]){ + runAllTests(useMaterial3: true); + runAllTests(useMaterial3: false); +} + +void runAllTests({ required bool useMaterial3 }) { testWidgets('InputDecorator input/label text layout', (WidgetTester tester) async { // The label appears above the input text await tester.pumpWidget( @@ -1796,7 +1800,7 @@ void main() { ), ); await tester.pumpWidget( - MaterialApp( + MaterialApp( theme: theme, home: Material( child: TextField( @@ -2011,9 +2015,9 @@ void main() { // Overall height for this InputDecorator is 48dps because the prefix icon's minimum size // is 48x48 and the rest of the elements only require 40dps: - // 12 - top padding - // 16 - input text (font size 16dps) - // 12 - bottom padding + // 12 - top padding + // 16 - input text (font size 16dps) + // 12 - bottom padding expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); expect(tester.getSize(find.text('text')).height, 16.0); @@ -2085,10 +2089,10 @@ void main() { ); // Overall height for this InputDecorator is 48dps because the prefix icon's minimum size - // is 48x48 and the rest of the elements only require 40dps: - // 12 - top padding - // 16 - input text (font size 16dps) - // 12 - bottom padding + // is 48x48 and the rest of the elements only require 40dps: + // 12 - top padding + // 16 - input text (font size 16dps) + // 12 - bottom padding expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); expect(tester.getSize(find.byKey(prefixKey)).height, 16.0); @@ -2303,9 +2307,9 @@ void main() { // Overall height for this InputDecorator is 100dps because the prefix icon's size // is 100x100 and the rest of the elements only require 40dps: - // 12 - top padding - // 16 - input text (font size 16dps) - // 12 - bottom padding + // 12 - top padding + // 16 - input text (font size 16dps) + // 12 - bottom padding expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0)); expect(tester.getSize(find.byKey(prefixKey)).height, 100.0); @@ -5487,7 +5491,7 @@ void main() { ); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/55317 - testWidgets('OutlineInputBorder with BorderRadius.zero should draw a rectangular border', (WidgetTester tester) async { +testWidgets('OutlineInputBorder with BorderRadius.zero should draw a rectangular border', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/78855 const String labelText = 'Flutter'; @@ -6546,5 +6550,29 @@ void main() { // The prefix is inside the decorator. expect(decoratorRight, lessThanOrEqualTo(prefixRight)); }); -} + + testWidgets('InputDecorator with counter does not crash when given a 0 size', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/129611 + const InputDecoration decoration = InputDecoration( + contentPadding: EdgeInsetsDirectional.all(99), + prefixIcon: Focus(child: Icon(Icons.search)), + counter: Text('COUNTER'), + ); + + await tester.pumpWidget( + Center( + child: SizedBox.square( + dimension: 0.0, + child: buildInputDecorator( + useMaterial3: useMaterial3, + decoration: decoration, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(InputDecorator), findsOneWidget); + expect(tester.renderObject(find.text('COUNTER')).size, Size.zero); + }); }