From 0ddcd70ca1854caeb15107eeb4526124e05d0015 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 8 Oct 2019 19:34:20 +0200 Subject: [PATCH] Add helperMaxLines to InputDecoration and InputDecorationTheme (#39433) Similar to errorMaxLines --- .../lib/src/material/input_decorator.dart | 52 +++++++++ .../test/material/input_decorator_test.dart | 102 ++++++++++++++++++ 2 files changed, 154 insertions(+) diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index bd351d6ffe7..840f490dd1b 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -283,6 +283,7 @@ class _HelperError extends StatefulWidget { this.textAlign, this.helperText, this.helperStyle, + this.helperMaxLines, this.errorText, this.errorStyle, this.errorMaxLines, @@ -291,6 +292,7 @@ class _HelperError extends StatefulWidget { final TextAlign textAlign; final String helperText; final TextStyle helperStyle; + final int helperMaxLines; final String errorText; final TextStyle errorStyle; final int errorMaxLines; @@ -372,6 +374,7 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta style: widget.helperStyle, textAlign: widget.textAlign, overflow: TextOverflow.ellipsis, + maxLines: widget.helperMaxLines, ), ), ); @@ -2184,6 +2187,7 @@ class _InputDecoratorState extends State with TickerProviderStat textAlign: textAlign, helperText: decoration.helperText, helperStyle: _getHelperStyle(themeData), + helperMaxLines: decoration.helperMaxLines, errorText: decoration.errorText, errorStyle: _getErrorStyle(themeData), errorMaxLines: decoration.errorMaxLines, @@ -2304,6 +2308,7 @@ class InputDecoration { this.labelStyle, this.helperText, this.helperStyle, + this.helperMaxLines, this.hintText, this.hintStyle, this.hintMaxLines, @@ -2363,6 +2368,7 @@ class InputDecoration { labelStyle = null, helperText = null, helperStyle = null, + helperMaxLines = null, hintMaxLines = null, errorText = null, errorStyle = null, @@ -2436,6 +2442,19 @@ class InputDecoration { /// The style to use for the [helperText]. final TextStyle helperStyle; + /// The maximum number of lines the [helperText] can occupy. + /// + /// Defaults to null, which means that the [helperText] will be limited + /// to a single line with [TextOverflow.ellipsis]. + /// + /// This value is passed along to the [Text.maxLines] attribute + /// of the [Text] widget used to display the helper. + /// + /// See also: + /// + /// * [errorMaxLines], the equivalent but for the [errorText]. + final int helperMaxLines; + /// Text that suggests what sort of input the field accepts. /// /// Displayed on top of the input [child] (i.e., at the same location on the @@ -2485,6 +2504,10 @@ class InputDecoration { /// /// This value is passed along to the [Text.maxLines] attribute /// of the [Text] widget used to display the error. + /// + /// See also: + /// + /// * [helperMaxLines], the equivalent but for the [helperText]. final int errorMaxLines; /// Whether the label floats on focus. @@ -2939,6 +2962,7 @@ class InputDecoration { TextStyle labelStyle, String helperText, TextStyle helperStyle, + int helperMaxLines, String hintText, TextStyle hintStyle, int hintMaxLines, @@ -2979,6 +3003,7 @@ class InputDecoration { labelStyle: labelStyle ?? this.labelStyle, helperText: helperText ?? this.helperText, helperStyle: helperStyle ?? this.helperStyle, + helperMaxLines : helperMaxLines ?? this.helperMaxLines, hintText: hintText ?? this.hintText, hintStyle: hintStyle ?? this.hintStyle, hintMaxLines: hintMaxLines ?? this.hintMaxLines, @@ -3024,6 +3049,7 @@ class InputDecoration { return copyWith( labelStyle: labelStyle ?? theme.labelStyle, helperStyle: helperStyle ?? theme.helperStyle, + helperMaxLines : helperMaxLines ?? theme.helperMaxLines, hintStyle: hintStyle ?? theme.hintStyle, errorStyle: errorStyle ?? theme.errorStyle, errorMaxLines: errorMaxLines ?? theme.errorMaxLines, @@ -3059,6 +3085,7 @@ class InputDecoration { && typedOther.labelStyle == labelStyle && typedOther.helperText == helperText && typedOther.helperStyle == helperStyle + && typedOther.helperMaxLines == helperMaxLines && typedOther.hintText == hintText && typedOther.hintStyle == hintStyle && typedOther.hintMaxLines == hintMaxLines @@ -3103,6 +3130,7 @@ class InputDecoration { labelStyle, helperText, helperStyle, + helperMaxLines, hintText, hintStyle, hintMaxLines, @@ -3149,6 +3177,7 @@ class InputDecoration { if (icon != null) 'icon: $icon', if (labelText != null) 'labelText: "$labelText"', if (helperText != null) 'helperText: "$helperText"', + if (helperMaxLines != null) 'helperMaxLines: "$helperMaxLines"', if (hintText != null) 'hintText: "$hintText"', if (hintMaxLines != null) 'hintMaxLines: "$hintMaxLines"', if (errorText != null) 'errorText: "$errorText"', @@ -3206,6 +3235,7 @@ class InputDecorationTheme extends Diagnosticable { const InputDecorationTheme({ this.labelStyle, this.helperStyle, + this.helperMaxLines, this.hintStyle, this.errorStyle, this.errorMaxLines, @@ -3245,6 +3275,19 @@ class InputDecorationTheme extends Diagnosticable { /// The style to use for [InputDecoration.helperText]. final TextStyle helperStyle; + /// The maximum number of lines the [helperText] can occupy. + /// + /// Defaults to null, which means that the [helperText] will be limited + /// to a single line with [TextOverflow.ellipsis]. + /// + /// This value is passed along to the [Text.maxLines] attribute + /// of the [Text] widget used to display the helper. + /// + /// See also: + /// + /// * [errorMaxLines], the equivalent but for the [errorText]. + final int helperMaxLines; + /// The style to use for the [InputDecoration.hintText]. /// /// Also used for the [labelText] when the [labelText] is displayed on @@ -3268,6 +3311,10 @@ class InputDecorationTheme extends Diagnosticable { /// /// This value is passed along to the [Text.maxLines] attribute /// of the [Text] widget used to display the error. + /// + /// See also: + /// + /// * [helperMaxLines], the equivalent but for the [helperText]. final int errorMaxLines; /// Whether the placeholder text floats to become a label on focus. @@ -3522,6 +3569,7 @@ class InputDecorationTheme extends Diagnosticable { InputDecorationTheme copyWith({ TextStyle labelStyle, TextStyle helperStyle, + int helperMaxLines, TextStyle hintStyle, TextStyle errorStyle, int errorMaxLines, @@ -3547,6 +3595,7 @@ class InputDecorationTheme extends Diagnosticable { return InputDecorationTheme( labelStyle: labelStyle ?? this.labelStyle, helperStyle: helperStyle ?? this.helperStyle, + helperMaxLines: helperMaxLines ?? this.helperMaxLines, hintStyle: hintStyle ?? this.hintStyle, errorStyle: errorStyle ?? this.errorStyle, errorMaxLines: errorMaxLines ?? this.errorMaxLines, @@ -3576,6 +3625,7 @@ class InputDecorationTheme extends Diagnosticable { return hashList([ labelStyle, helperStyle, + helperMaxLines, hintStyle, errorStyle, errorMaxLines, @@ -3609,6 +3659,7 @@ class InputDecorationTheme extends Diagnosticable { final InputDecorationTheme typedOther = other; return typedOther.labelStyle == labelStyle && typedOther.helperStyle == helperStyle + && typedOther.helperMaxLines == helperMaxLines && typedOther.hintStyle == hintStyle && typedOther.errorStyle == errorStyle && typedOther.errorMaxLines == errorMaxLines @@ -3638,6 +3689,7 @@ class InputDecorationTheme extends Diagnosticable { const InputDecorationTheme defaultTheme = InputDecorationTheme(); properties.add(DiagnosticsProperty('labelStyle', labelStyle, defaultValue: defaultTheme.labelStyle)); properties.add(DiagnosticsProperty('helperStyle', helperStyle, defaultValue: defaultTheme.helperStyle)); + properties.add(IntProperty('helperMaxLines', helperMaxLines, defaultValue: defaultTheme.helperMaxLines)); properties.add(DiagnosticsProperty('hintStyle', hintStyle, defaultValue: defaultTheme.hintStyle)); properties.add(DiagnosticsProperty('errorStyle', errorStyle, defaultValue: defaultTheme.errorStyle)); properties.add(IntProperty('errorMaxLines', errorMaxLines, defaultValue: defaultTheme.errorMaxLines)); diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index f74699915cf..bd1398206e0 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -1026,6 +1026,103 @@ void main() { expect(tester.getBottomLeft(find.text(kError1)), const Offset(12.0, 76.0)); }); + testWidgets('InputDecoration helperMaxLines', (WidgetTester tester) async { + const String kHelper1 = 'e0'; + const String kHelper2 = 'e0\ne1'; + const String kHelper3 = 'e0\ne1\ne2'; + + await tester.pumpWidget( + buildInputDecorator( + isEmpty: true, + // isFocused: false (default) + decoration: const InputDecoration( + labelText: 'label', + helperText: kHelper3, + helperMaxLines: 3, + errorText: null, + filled: true, + ), + ), + ); + + // Overall height for this InputDecorator is 100dps: + // + // 12 - top padding + // 12 - floating label (ahem font size 16dps * 0.75 = 12) + // 4 - floating label / input text gap + // 16 - input text (ahem font size 16dps) + // 12 - bottom padding + // 8 - below the border padding + // 36 - helper text (3 lines, ahem font size 12dps) + + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0)); + expect(tester.getTopLeft(find.text(kHelper3)), const Offset(12.0, 64.0)); + expect(tester.getBottomLeft(find.text(kHelper3)), const Offset(12.0, 100.0)); + + // Overall height for this InputDecorator is 12 less than the first + // one, 88dps, because helperText only occupies two lines. + + await tester.pumpWidget( + buildInputDecorator( + isEmpty: true, + // isFocused: false (default) + decoration: const InputDecoration( + labelText: 'label', + helperText: kHelper3, + helperMaxLines: 2, + errorText: null, + filled: true, + ), + ), + ); + + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 88.0)); + expect(tester.getTopLeft(find.text(kHelper3)), const Offset(12.0, 64.0)); + expect(tester.getBottomLeft(find.text(kHelper3)), const Offset(12.0, 88.0)); + + // Overall height for this InputDecorator is 12 less than the first + // one, 88dps, because helperText only occupies two lines. + + await tester.pumpWidget( + buildInputDecorator( + isEmpty: true, + // isFocused: false (default) + decoration: const InputDecoration( + labelText: 'label', + helperText: kHelper2, + helperMaxLines: 3, + errorText: null, + filled: true, + ), + ), + ); + + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 88.0)); + expect(tester.getTopLeft(find.text(kHelper2)), const Offset(12.0, 64.0)); + expect(tester.getBottomLeft(find.text(kHelper2)), const Offset(12.0, 88.0)); + + // Overall height for this InputDecorator is 24 less than the first + // one, 88dps, because helperText only occupies one line. + + await tester.pumpWidget( + buildInputDecorator( + isEmpty: true, + // isFocused: false (default) + decoration: const InputDecoration( + labelText: 'label', + helperText: kHelper1, + helperMaxLines: 3, + errorText: null, + filled: true, + ), + ), + ); + + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0)); + expect(tester.getTopLeft(find.text(kHelper1)), const Offset(12.0, 64.0)); + expect(tester.getBottomLeft(find.text(kHelper1)), const Offset(12.0, 76.0)); + }); + testWidgets('InputDecorator prefix/suffix texts', (WidgetTester tester) async { await tester.pumpWidget( buildInputDecorator( @@ -2703,6 +2800,7 @@ void main() { const InputDecorationTheme( labelStyle: themeStyle, helperStyle: themeStyle, + helperMaxLines: 5, hintStyle: themeStyle, errorStyle: themeStyle, errorMaxLines: 4, @@ -2721,6 +2819,7 @@ void main() { expect(decoration.labelStyle, decorationStyle); expect(decoration.helperStyle, decorationStyle); + expect(decoration.helperMaxLines, 5); expect(decoration.hintStyle, decorationStyle); expect(decoration.errorStyle, decorationStyle); expect(decoration.errorMaxLines, 4); @@ -3024,6 +3123,7 @@ void main() { final String debugString = const InputDecorationTheme( labelStyle: TextStyle(height: 1.0), helperStyle: TextStyle(height: 2.0), + helperMaxLines: 5, hintStyle: TextStyle(height: 3.0), errorStyle: TextStyle(height: 4.0), errorMaxLines: 5, @@ -3412,6 +3512,7 @@ void main() { const InputDecorationTheme( labelStyle: TextStyle(), helperStyle: TextStyle(), + helperMaxLines: 6, hintStyle: TextStyle(), errorMaxLines: 5, hasFloatingPlaceholder: false, @@ -3436,6 +3537,7 @@ void main() { expect(description, [ 'labelStyle: TextStyle()', 'helperStyle: TextStyle()', + 'helperMaxLines: 6', 'hintStyle: TextStyle()', 'errorMaxLines: 5', 'hasFloatingPlaceholder: false',