diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index b56592f7a6..0cbafe1a1d 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -803,6 +803,9 @@ class _RenderDecoration extends RenderBox { + aboveBaseline + belowBaseline + contentPadding.bottom; + containerHeight = math.max( + containerHeight, + math.max(_boxSize(suffixIcon).height, _boxSize(prefixIcon).height)); if (label != null) { // floatingLabelHeight includes the vertical gap between the inline @@ -1688,21 +1691,27 @@ class _InputDecoratorState extends State with TickerProviderStat ); final Widget prefixIcon = decoration.prefixIcon == null ? null : - IconTheme.merge( - data: new IconThemeData( - color: iconColor, - size: iconSize, + new ConstrainedBox( + constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0), + child: IconTheme.merge( + data: new IconThemeData( + color: iconColor, + size: iconSize, + ), + child: decoration.prefixIcon, ), - child: decoration.prefixIcon, ); final Widget suffixIcon = decoration.suffixIcon == null ? null : - IconTheme.merge( - data: new IconThemeData( - color: iconColor, - size: iconSize, + new ConstrainedBox( + constraints: const BoxConstraints(minWidth: 48.0, minHeight: 48.0), + child: IconTheme.merge( + data: new IconThemeData( + color: iconColor, + size: iconSize, + ), + child: decoration.suffixIcon, ), - child: decoration.suffixIcon, ); final Widget helperError = new _HelperError( @@ -1728,6 +1737,8 @@ class _InputDecoratorState extends State with TickerProviderStat EdgeInsets contentPadding; double floatingLabelHeight; + final double leftInset = decoration.prefixIcon == null ? 12.0 : 0.0; + final double rightInset = decoration.suffixIcon == null ? 12.0 : 0.0; if (decoration.isCollapsed) { floatingLabelHeight = 0.0; contentPadding = decorationContentPadding ?? EdgeInsets.zero; @@ -1736,8 +1747,8 @@ class _InputDecoratorState extends State with TickerProviderStat floatingLabelHeight = 4.0 + 0.75 * inlineLabelStyle.fontSize; if (decoration.filled == true) { // filled == null same as filled == false contentPadding = decorationContentPadding ?? (decorationIsDense - ? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0) - : const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0)); + ? new EdgeInsets.fromLTRB(leftInset, 8.0, rightInset, 8.0) + : new EdgeInsets.fromLTRB(leftInset, 12.0, rightInset, 12.0)); } else { // Not left or right padding for underline borders that aren't filled // is a small concession to backwards compatibility. This eliminates @@ -1749,8 +1760,8 @@ class _InputDecoratorState extends State with TickerProviderStat } else { floatingLabelHeight = 0.0; contentPadding = decorationContentPadding ?? (decorationIsDense - ? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0) - : const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0)); + ? new EdgeInsets.fromLTRB(leftInset, 20.0, rightInset, 12.0) + : new EdgeInsets.fromLTRB(leftInset, 24.0, rightInset, 16.0)); } return new _Decorator( @@ -1987,11 +1998,16 @@ class InputDecoration { /// [IconTheme] and therefore does not need to be explicitly given in the /// icon widget. /// - /// The prefix icon is not padded. To pad the trailing edge of the prefix icon: + /// The prefix icon is constrained with a minimum size of 48px by 48px, but + /// can be expanded beyond that. Anything larger than 24px will require + /// additional padding to ensure it matches the material spec of 12px padding + /// between the left edge of the input and leading edge of the prefix icon. + /// To pad the leading edge of the prefix icon: + /// /// ```dart /// prefixIcon: new Padding( - /// padding: const EdgeInsetsDirectional.only(end: 16.0), - /// child: myIcon, + /// padding: const EdgeInsetsDirectional.only(start: 12.0), + /// child: myIcon, // icon is 48px widget. /// ) /// ``` /// @@ -2021,11 +2037,16 @@ class InputDecoration { /// [IconTheme] and therefore does not need to be explicitly given in the /// icon widget. /// - /// The suffix icon is not padded. To pad the leading edge of the suffix icon: + /// The suffix icon is constrained with a minimum size of 48px by 48px, but + /// can be expanded beyond that. Anything larger than 24px will require + /// additional padding to ensure it matches the material spec of 12px padding + /// between the right edge of the input and trailing edge of the prefix icon. + /// To pad the trailing edge of the suffix icon: + /// /// ```dart /// suffixIcon: new Padding( - /// padding: const EdgeInsetsDirectional.only(start: 16.0), - /// child: new Icon(Icons.search), + /// padding: const EdgeInsetsDirectional.only(end: 12.0), + /// child: myIcon, // icon is 48px widget. /// ) /// ``` /// diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 03604a199e..c01f72df15 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -750,6 +750,42 @@ void main() { expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('s')).dx)); }); + testWidgets('InputDecorator prefixIcon/suffixIcon', (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator( + // isEmpty: false (default) + // isFocused: false (default) + decoration: const InputDecoration( + prefixIcon: const Icon(Icons.pages), + suffixIcon: const Icon(Icons.satellite), + filled: true, + ), + ), + ); + + // Overall height for this InputDecorator is 48dps: + // 12 - top padding + // 16 - input text (ahem font size 16dps) + // 12 - bottom padding + // 48 - prefix icon + // 48 - suffix icon + + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); + expect(tester.getSize(find.text('text')).height, 16.0); + expect(tester.getSize(find.byIcon(Icons.pages)).height, 48.0); + expect(tester.getSize(find.byIcon(Icons.satellite)).height, 48.0); + expect(tester.getTopLeft(find.text('text')).dy, 12.0); + expect(tester.getTopLeft(find.byIcon(Icons.pages)).dy, 0.0); + expect(tester.getTopLeft(find.byIcon(Icons.satellite)).dy, 0.0); + expect(tester.getTopRight(find.byIcon(Icons.satellite)).dx, 800.0); + + + // layout is a row: [icon text icon] + expect(tester.getTopLeft(find.byIcon(Icons.pages)).dx, 0.0); + expect(tester.getTopRight(find.byIcon(Icons.pages)).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('text')).dx)); + expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.byIcon(Icons.satellite)).dx)); + }); + testWidgets('InputDecorator error/helper/counter RTL layout', (WidgetTester tester) async { await tester.pumpWidget( buildInputDecorator(