Unverified Commit ae7d5f53 authored by Renzo Olivares's avatar Renzo Olivares Committed by GitHub

Add floatingLabelStyle parameter to InputDecoration (#86576)

parent b14fce0a
...@@ -2115,10 +2115,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat ...@@ -2115,10 +2115,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
return themeData.fixTextFieldOutlineLabel return themeData.fixTextFieldOutlineLabel
? style ? style
.copyWith(height: 1, color: decoration!.enabled ? color : themeData.disabledColor) .copyWith(height: 1, color: decoration!.enabled ? color : themeData.disabledColor)
.merge(decoration!.labelStyle) .merge(decoration!.floatingLabelStyle ?? decoration!.labelStyle)
: style : style
.copyWith(color: decoration!.enabled ? color : themeData.disabledColor) .copyWith(color: decoration!.enabled ? color : themeData.disabledColor)
.merge(decoration!.labelStyle); .merge(decoration!.floatingLabelStyle ?? decoration!.labelStyle);
} }
...@@ -2523,6 +2523,7 @@ class InputDecoration { ...@@ -2523,6 +2523,7 @@ class InputDecoration {
this.icon, this.icon,
this.labelText, this.labelText,
this.labelStyle, this.labelStyle,
this.floatingLabelStyle,
this.helperText, this.helperText,
this.helperStyle, this.helperStyle,
this.helperMaxLines, this.helperMaxLines,
...@@ -2588,6 +2589,7 @@ class InputDecoration { ...@@ -2588,6 +2589,7 @@ class InputDecoration {
icon = null, icon = null,
labelText = null, labelText = null,
labelStyle = null, labelStyle = null,
floatingLabelStyle = null,
helperText = null, helperText = null,
helperStyle = null, helperStyle = null,
helperMaxLines = null, helperMaxLines = null,
...@@ -2646,16 +2648,22 @@ class InputDecoration { ...@@ -2646,16 +2648,22 @@ class InputDecoration {
/// vertically adjacent to) the input field. /// vertically adjacent to) the input field.
final String? labelText; final String? labelText;
/// The style to use for the [labelText] when the label is above (i.e., /// The style to use for the [labelText] when the label is on top of the
/// vertically adjacent to) the input field. /// input field.
/// ///
/// When the [labelText] is on top of the input field, the text uses the /// When the [labelText] is above (i.e., vertically adjacent to) the input
/// [hintStyle] instead. /// field, the text uses the [floatingLabelStyle] instead.
/// ///
/// If null, defaults to a value derived from the base [TextStyle] for the /// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme]. /// input field and the current [Theme].
final TextStyle? labelStyle; final TextStyle? labelStyle;
/// The style to use for the [labelText] when the label is above (i.e.,
/// vertically adjacent to) the input field.
///
/// If null, defaults to [labelStyle].
final TextStyle? floatingLabelStyle;
/// Text that provides context about the [InputDecorator.child]'s value, such /// Text that provides context about the [InputDecorator.child]'s value, such
/// as how the value will be used. /// as how the value will be used.
/// ///
...@@ -3318,6 +3326,7 @@ class InputDecoration { ...@@ -3318,6 +3326,7 @@ class InputDecoration {
Widget? icon, Widget? icon,
String? labelText, String? labelText,
TextStyle? labelStyle, TextStyle? labelStyle,
TextStyle? floatingLabelStyle,
String? helperText, String? helperText,
TextStyle? helperStyle, TextStyle? helperStyle,
int? helperMaxLines, int? helperMaxLines,
...@@ -3364,6 +3373,7 @@ class InputDecoration { ...@@ -3364,6 +3373,7 @@ class InputDecoration {
icon: icon ?? this.icon, icon: icon ?? this.icon,
labelText: labelText ?? this.labelText, labelText: labelText ?? this.labelText,
labelStyle: labelStyle ?? this.labelStyle, labelStyle: labelStyle ?? this.labelStyle,
floatingLabelStyle: floatingLabelStyle ?? this.floatingLabelStyle,
helperText: helperText ?? this.helperText, helperText: helperText ?? this.helperText,
helperStyle: helperStyle ?? this.helperStyle, helperStyle: helperStyle ?? this.helperStyle,
helperMaxLines : helperMaxLines ?? this.helperMaxLines, helperMaxLines : helperMaxLines ?? this.helperMaxLines,
...@@ -3416,6 +3426,7 @@ class InputDecoration { ...@@ -3416,6 +3426,7 @@ class InputDecoration {
InputDecoration applyDefaults(InputDecorationTheme theme) { InputDecoration applyDefaults(InputDecorationTheme theme) {
return copyWith( return copyWith(
labelStyle: labelStyle ?? theme.labelStyle, labelStyle: labelStyle ?? theme.labelStyle,
floatingLabelStyle: floatingLabelStyle ?? theme.floatingLabelStyle,
helperStyle: helperStyle ?? theme.helperStyle, helperStyle: helperStyle ?? theme.helperStyle,
helperMaxLines : helperMaxLines ?? theme.helperMaxLines, helperMaxLines : helperMaxLines ?? theme.helperMaxLines,
hintStyle: hintStyle ?? theme.hintStyle, hintStyle: hintStyle ?? theme.hintStyle,
...@@ -3453,6 +3464,7 @@ class InputDecoration { ...@@ -3453,6 +3464,7 @@ class InputDecoration {
&& other.icon == icon && other.icon == icon
&& other.labelText == labelText && other.labelText == labelText
&& other.labelStyle == labelStyle && other.labelStyle == labelStyle
&& other.floatingLabelStyle == floatingLabelStyle
&& other.helperText == helperText && other.helperText == helperText
&& other.helperStyle == helperStyle && other.helperStyle == helperStyle
&& other.helperMaxLines == helperMaxLines && other.helperMaxLines == helperMaxLines
...@@ -3501,6 +3513,7 @@ class InputDecoration { ...@@ -3501,6 +3513,7 @@ class InputDecoration {
final List<Object?> values = <Object?>[ final List<Object?> values = <Object?>[
icon, icon,
labelText, labelText,
floatingLabelStyle,
labelStyle, labelStyle,
helperText, helperText,
helperStyle, helperStyle,
...@@ -3554,6 +3567,7 @@ class InputDecoration { ...@@ -3554,6 +3567,7 @@ class InputDecoration {
final List<String> description = <String>[ final List<String> description = <String>[
if (icon != null) 'icon: $icon', if (icon != null) 'icon: $icon',
if (labelText != null) 'labelText: "$labelText"', if (labelText != null) 'labelText: "$labelText"',
if (floatingLabelStyle != null) 'floatingLabelStyle: "$floatingLabelStyle"',
if (helperText != null) 'helperText: "$helperText"', if (helperText != null) 'helperText: "$helperText"',
if (helperMaxLines != null) 'helperMaxLines: "$helperMaxLines"', if (helperMaxLines != null) 'helperMaxLines: "$helperMaxLines"',
if (hintText != null) 'hintText: "$hintText"', if (hintText != null) 'hintText: "$hintText"',
...@@ -3615,6 +3629,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -3615,6 +3629,7 @@ class InputDecorationTheme with Diagnosticable {
/// not be null. /// not be null.
const InputDecorationTheme({ const InputDecorationTheme({
this.labelStyle, this.labelStyle,
this.floatingLabelStyle,
this.helperStyle, this.helperStyle,
this.helperMaxLines, this.helperMaxLines,
this.hintStyle, this.hintStyle,
...@@ -3644,16 +3659,25 @@ class InputDecorationTheme with Diagnosticable { ...@@ -3644,16 +3659,25 @@ class InputDecorationTheme with Diagnosticable {
assert(filled != null), assert(filled != null),
assert(alignLabelWithHint != null); assert(alignLabelWithHint != null);
/// The style to use for [InputDecoration.labelText] when the label is /// The style to use for [InputDecoration.labelText] when the label is on top
/// above (i.e., vertically adjacent to) the input field. /// of the input field.
/// ///
/// When the [InputDecoration.labelText] is on top of the input field, the /// When the [InputDecoration.labelText] is floating above the input field,
/// text uses the [hintStyle] instead. /// the text uses the [floatingLabelStyle] instead.
/// ///
/// If null, defaults to a value derived from the base [TextStyle] for the /// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme]. /// input field and the current [Theme].
final TextStyle? labelStyle; final TextStyle? labelStyle;
/// The style to use for [InputDecoration.labelText] when the label is
/// above (i.e., vertically adjacent to) the input field.
///
/// When the [InputDecoration.labelText] is on top of the input field, the
/// text uses the [labelStyle] instead.
///
/// If null, defaults to [labelStyle].
final TextStyle? floatingLabelStyle;
/// The style to use for [InputDecoration.helperText]. /// The style to use for [InputDecoration.helperText].
final TextStyle? helperStyle; final TextStyle? helperStyle;
...@@ -3963,6 +3987,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -3963,6 +3987,7 @@ class InputDecorationTheme with Diagnosticable {
/// new values. /// new values.
InputDecorationTheme copyWith({ InputDecorationTheme copyWith({
TextStyle? labelStyle, TextStyle? labelStyle,
TextStyle? floatingLabelStyle,
TextStyle? helperStyle, TextStyle? helperStyle,
int? helperMaxLines, int? helperMaxLines,
TextStyle? hintStyle, TextStyle? hintStyle,
...@@ -3990,6 +4015,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -3990,6 +4015,7 @@ class InputDecorationTheme with Diagnosticable {
}) { }) {
return InputDecorationTheme( return InputDecorationTheme(
labelStyle: labelStyle ?? this.labelStyle, labelStyle: labelStyle ?? this.labelStyle,
floatingLabelStyle: floatingLabelStyle ?? this.floatingLabelStyle,
helperStyle: helperStyle ?? this.helperStyle, helperStyle: helperStyle ?? this.helperStyle,
helperMaxLines: helperMaxLines ?? this.helperMaxLines, helperMaxLines: helperMaxLines ?? this.helperMaxLines,
hintStyle: hintStyle ?? this.hintStyle, hintStyle: hintStyle ?? this.hintStyle,
...@@ -4021,6 +4047,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4021,6 +4047,7 @@ class InputDecorationTheme with Diagnosticable {
int get hashCode { int get hashCode {
return hashList(<dynamic>[ return hashList(<dynamic>[
labelStyle, labelStyle,
floatingLabelStyle,
helperStyle, helperStyle,
helperMaxLines, helperMaxLines,
hintStyle, hintStyle,
...@@ -4056,6 +4083,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4056,6 +4083,7 @@ class InputDecorationTheme with Diagnosticable {
return false; return false;
return other is InputDecorationTheme return other is InputDecorationTheme
&& other.labelStyle == labelStyle && other.labelStyle == labelStyle
&& other.floatingLabelStyle == floatingLabelStyle
&& other.helperStyle == helperStyle && other.helperStyle == helperStyle
&& other.helperMaxLines == helperMaxLines && other.helperMaxLines == helperMaxLines
&& other.hintStyle == hintStyle && other.hintStyle == hintStyle
...@@ -4088,6 +4116,7 @@ class InputDecorationTheme with Diagnosticable { ...@@ -4088,6 +4116,7 @@ class InputDecorationTheme with Diagnosticable {
super.debugFillProperties(properties); super.debugFillProperties(properties);
const InputDecorationTheme defaultTheme = InputDecorationTheme(); const InputDecorationTheme defaultTheme = InputDecorationTheme();
properties.add(DiagnosticsProperty<TextStyle>('labelStyle', labelStyle, defaultValue: defaultTheme.labelStyle)); properties.add(DiagnosticsProperty<TextStyle>('labelStyle', labelStyle, defaultValue: defaultTheme.labelStyle));
properties.add(DiagnosticsProperty<TextStyle>('floatingLabelStyle', floatingLabelStyle, defaultValue: defaultTheme.floatingLabelStyle));
properties.add(DiagnosticsProperty<TextStyle>('helperStyle', helperStyle, defaultValue: defaultTheme.helperStyle)); properties.add(DiagnosticsProperty<TextStyle>('helperStyle', helperStyle, defaultValue: defaultTheme.helperStyle));
properties.add(IntProperty('helperMaxLines', helperMaxLines, defaultValue: defaultTheme.helperMaxLines)); properties.add(IntProperty('helperMaxLines', helperMaxLines, defaultValue: defaultTheme.helperMaxLines));
properties.add(DiagnosticsProperty<TextStyle>('hintStyle', hintStyle, defaultValue: defaultTheme.hintStyle)); properties.add(DiagnosticsProperty<TextStyle>('hintStyle', hintStyle, defaultValue: defaultTheme.hintStyle));
......
...@@ -85,6 +85,15 @@ Rect getLabelRect(WidgetTester tester) { ...@@ -85,6 +85,15 @@ Rect getLabelRect(WidgetTester tester) {
return tester.getRect(findLabel()); return tester.getRect(findLabel());
} }
TextStyle getLabelStyle(WidgetTester tester) {
return tester.firstWidget<AnimatedDefaultTextStyle>(
find.ancestor(
of: find.text('label'),
matching: find.byType(AnimatedDefaultTextStyle),
),
).style;
}
InputBorder? getBorder(WidgetTester tester) { InputBorder? getBorder(WidgetTester tester) {
if (!tester.any(findBorderPainter())) if (!tester.any(findBorderPainter()))
return null; return null;
...@@ -3073,20 +3082,79 @@ void main() { ...@@ -3073,20 +3082,79 @@ void main() {
expect(tester.getTopRight(find.text('counter')), const Offset(800.0, 64.0)); expect(tester.getTopRight(find.text('counter')), const Offset(800.0, 64.0));
// Verify that the styles were passed along // Verify that the styles were passed along
expect(tester.widget<Text>(find.text('hint')).style!.color, hintStyle.color);
expect(tester.widget<Text>(find.text('prefix')).style!.color, prefixStyle.color); expect(tester.widget<Text>(find.text('prefix')).style!.color, prefixStyle.color);
expect(tester.widget<Text>(find.text('suffix')).style!.color, suffixStyle.color); expect(tester.widget<Text>(find.text('suffix')).style!.color, suffixStyle.color);
expect(tester.widget<Text>(find.text('helper')).style!.color, helperStyle.color); expect(tester.widget<Text>(find.text('helper')).style!.color, helperStyle.color);
expect(tester.widget<Text>(find.text('counter')).style!.color, counterStyle.color); expect(tester.widget<Text>(find.text('counter')).style!.color, counterStyle.color);
expect(getLabelStyle(tester).color, labelStyle.color);
});
testWidgets('InputDecorationTheme style overrides (focused)', (WidgetTester tester) async {
const TextStyle style16 = TextStyle(fontFamily: 'Ahem', fontSize: 16.0);
final TextStyle labelStyle = style16.merge(const TextStyle(color: Colors.red));
final TextStyle floatingLabelStyle = style16.merge(const TextStyle(color: Colors.indigo));
final TextStyle hintStyle = style16.merge(const TextStyle(color: Colors.green));
final TextStyle prefixStyle = style16.merge(const TextStyle(color: Colors.blue));
final TextStyle suffixStyle = style16.merge(const TextStyle(color: Colors.purple));
const TextStyle style12 = TextStyle(fontFamily: 'Ahem', fontSize: 12.0);
final TextStyle helperStyle = style12.merge(const TextStyle(color: Colors.orange));
final TextStyle counterStyle = style12.merge(const TextStyle(color: Colors.orange));
// This test also verifies that the default InputDecorator provides a
// "small concession to backwards compatibility" by not padding on
// the left and right. If filled is true or an outline border is
// provided then the horizontal padding is included.
TextStyle getLabelStyle() { await tester.pumpWidget(
return tester.firstWidget<AnimatedDefaultTextStyle>( buildInputDecorator(
find.ancestor( isEmpty: true,
of: find.text('label'), isFocused: true, // Label appears floating above input field.
matching: find.byType(AnimatedDefaultTextStyle), inputDecorationTheme: InputDecorationTheme(
labelStyle: labelStyle,
floatingLabelStyle: floatingLabelStyle,
hintStyle: hintStyle,
prefixStyle: prefixStyle,
suffixStyle: suffixStyle,
helperStyle: helperStyle,
counterStyle: counterStyle,
// filled: false (default) - don't pad by left/right 12dps
), ),
).style; decoration: const InputDecoration(
} labelText: 'label',
expect(getLabelStyle().color, labelStyle.color); hintText: 'hint',
prefixText: 'prefix',
suffixText: 'suffix',
helperText: 'helper',
counterText: 'counter',
),
),
);
// Overall height for this InputDecorator is 76dps. Layout is:
// 12 - top padding
// 12 - floating label (ahem font size 16dps * 0.75 = 12)
// 4 - floating label / input text gap
// 16 - prefix/hint/input/suffix text (ahem font size 16dps)
// 12 - bottom padding
// 8 - below the border padding
// 12 - help/error/counter text (ahem font size 12dps)
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0));
expect(tester.getTopLeft(find.text('label')).dy, 12.0);
expect(tester.getBottomLeft(find.text('label')).dy, 24.0);
expect(getBorderBottom(tester), 56.0);
expect(getBorderWeight(tester), 2.0);
expect(tester.getTopLeft(find.text('helper')), const Offset(0.0, 64.0));
expect(tester.getTopRight(find.text('counter')), const Offset(800.0, 64.0));
// Verify that the styles were passed along
expect(tester.widget<Text>(find.text('hint')).style!.color, hintStyle.color);
expect(tester.widget<Text>(find.text('prefix')).style!.color, prefixStyle.color);
expect(tester.widget<Text>(find.text('suffix')).style!.color, suffixStyle.color);
expect(tester.widget<Text>(find.text('helper')).style!.color, helperStyle.color);
expect(tester.widget<Text>(find.text('counter')).style!.color, counterStyle.color);
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
}); });
testWidgets('InputDecorator.toString()', (WidgetTester tester) async { testWidgets('InputDecorator.toString()', (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment