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
return themeData.fixTextFieldOutlineLabel
? style
.copyWith(height: 1, color: decoration!.enabled ? color : themeData.disabledColor)
.merge(decoration!.labelStyle)
.merge(decoration!.floatingLabelStyle ?? decoration!.labelStyle)
: style
.copyWith(color: decoration!.enabled ? color : themeData.disabledColor)
.merge(decoration!.labelStyle);
.merge(decoration!.floatingLabelStyle ?? decoration!.labelStyle);
}
......@@ -2523,6 +2523,7 @@ class InputDecoration {
this.icon,
this.labelText,
this.labelStyle,
this.floatingLabelStyle,
this.helperText,
this.helperStyle,
this.helperMaxLines,
......@@ -2588,6 +2589,7 @@ class InputDecoration {
icon = null,
labelText = null,
labelStyle = null,
floatingLabelStyle = null,
helperText = null,
helperStyle = null,
helperMaxLines = null,
......@@ -2646,16 +2648,22 @@ class InputDecoration {
/// vertically adjacent to) the input field.
final String? labelText;
/// The style to use for the [labelText] when the label is above (i.e.,
/// vertically adjacent to) the input field.
/// The style to use for the [labelText] when the label is on top of the
/// input field.
///
/// When the [labelText] is on top of the input field, the text uses the
/// [hintStyle] instead.
/// When the [labelText] is above (i.e., vertically adjacent to) the input
/// field, the text uses the [floatingLabelStyle] instead.
///
/// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme].
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
/// as how the value will be used.
///
......@@ -3318,6 +3326,7 @@ class InputDecoration {
Widget? icon,
String? labelText,
TextStyle? labelStyle,
TextStyle? floatingLabelStyle,
String? helperText,
TextStyle? helperStyle,
int? helperMaxLines,
......@@ -3364,6 +3373,7 @@ class InputDecoration {
icon: icon ?? this.icon,
labelText: labelText ?? this.labelText,
labelStyle: labelStyle ?? this.labelStyle,
floatingLabelStyle: floatingLabelStyle ?? this.floatingLabelStyle,
helperText: helperText ?? this.helperText,
helperStyle: helperStyle ?? this.helperStyle,
helperMaxLines : helperMaxLines ?? this.helperMaxLines,
......@@ -3416,6 +3426,7 @@ class InputDecoration {
InputDecoration applyDefaults(InputDecorationTheme theme) {
return copyWith(
labelStyle: labelStyle ?? theme.labelStyle,
floatingLabelStyle: floatingLabelStyle ?? theme.floatingLabelStyle,
helperStyle: helperStyle ?? theme.helperStyle,
helperMaxLines : helperMaxLines ?? theme.helperMaxLines,
hintStyle: hintStyle ?? theme.hintStyle,
......@@ -3453,6 +3464,7 @@ class InputDecoration {
&& other.icon == icon
&& other.labelText == labelText
&& other.labelStyle == labelStyle
&& other.floatingLabelStyle == floatingLabelStyle
&& other.helperText == helperText
&& other.helperStyle == helperStyle
&& other.helperMaxLines == helperMaxLines
......@@ -3501,6 +3513,7 @@ class InputDecoration {
final List<Object?> values = <Object?>[
icon,
labelText,
floatingLabelStyle,
labelStyle,
helperText,
helperStyle,
......@@ -3554,6 +3567,7 @@ class InputDecoration {
final List<String> description = <String>[
if (icon != null) 'icon: $icon',
if (labelText != null) 'labelText: "$labelText"',
if (floatingLabelStyle != null) 'floatingLabelStyle: "$floatingLabelStyle"',
if (helperText != null) 'helperText: "$helperText"',
if (helperMaxLines != null) 'helperMaxLines: "$helperMaxLines"',
if (hintText != null) 'hintText: "$hintText"',
......@@ -3615,6 +3629,7 @@ class InputDecorationTheme with Diagnosticable {
/// not be null.
const InputDecorationTheme({
this.labelStyle,
this.floatingLabelStyle,
this.helperStyle,
this.helperMaxLines,
this.hintStyle,
......@@ -3644,16 +3659,25 @@ class InputDecorationTheme with Diagnosticable {
assert(filled != null),
assert(alignLabelWithHint != null);
/// The style to use for [InputDecoration.labelText] when the label is
/// above (i.e., vertically adjacent to) the input field.
/// The style to use for [InputDecoration.labelText] when the label is on top
/// of the input field.
///
/// When the [InputDecoration.labelText] is on top of the input field, the
/// text uses the [hintStyle] instead.
/// When the [InputDecoration.labelText] is floating above the input field,
/// the text uses the [floatingLabelStyle] instead.
///
/// If null, defaults to a value derived from the base [TextStyle] for the
/// input field and the current [Theme].
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].
final TextStyle? helperStyle;
......@@ -3963,6 +3987,7 @@ class InputDecorationTheme with Diagnosticable {
/// new values.
InputDecorationTheme copyWith({
TextStyle? labelStyle,
TextStyle? floatingLabelStyle,
TextStyle? helperStyle,
int? helperMaxLines,
TextStyle? hintStyle,
......@@ -3990,6 +4015,7 @@ class InputDecorationTheme with Diagnosticable {
}) {
return InputDecorationTheme(
labelStyle: labelStyle ?? this.labelStyle,
floatingLabelStyle: floatingLabelStyle ?? this.floatingLabelStyle,
helperStyle: helperStyle ?? this.helperStyle,
helperMaxLines: helperMaxLines ?? this.helperMaxLines,
hintStyle: hintStyle ?? this.hintStyle,
......@@ -4021,6 +4047,7 @@ class InputDecorationTheme with Diagnosticable {
int get hashCode {
return hashList(<dynamic>[
labelStyle,
floatingLabelStyle,
helperStyle,
helperMaxLines,
hintStyle,
......@@ -4056,6 +4083,7 @@ class InputDecorationTheme with Diagnosticable {
return false;
return other is InputDecorationTheme
&& other.labelStyle == labelStyle
&& other.floatingLabelStyle == floatingLabelStyle
&& other.helperStyle == helperStyle
&& other.helperMaxLines == helperMaxLines
&& other.hintStyle == hintStyle
......@@ -4088,6 +4116,7 @@ class InputDecorationTheme with Diagnosticable {
super.debugFillProperties(properties);
const InputDecorationTheme defaultTheme = InputDecorationTheme();
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(IntProperty('helperMaxLines', helperMaxLines, defaultValue: defaultTheme.helperMaxLines));
properties.add(DiagnosticsProperty<TextStyle>('hintStyle', hintStyle, defaultValue: defaultTheme.hintStyle));
......
......@@ -85,6 +85,15 @@ Rect getLabelRect(WidgetTester tester) {
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) {
if (!tester.any(findBorderPainter()))
return null;
......@@ -3073,20 +3082,79 @@ void main() {
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, 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() {
return tester.firstWidget<AnimatedDefaultTextStyle>(
find.ancestor(
of: find.text('label'),
matching: find.byType(AnimatedDefaultTextStyle),
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
isFocused: true, // Label appears floating above input field.
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;
}
expect(getLabelStyle().color, labelStyle.color);
decoration: const InputDecoration(
labelText: 'label',
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 {
......
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