Always relies on floatingLabelStyle when FloatingLabelBehavior.always (#147374)

## Description

With this PR, when `InputDecorator.floatingLabelBehavior` is set to `FloatingLabelBehavior.always` the label style is always set to `InputDecorator.floatingLabelStyle`, previously `InputDecorator.labelStyle` was used when the field was not focused or was empty.  

## Related Issue

Fixes https://github.com/flutter/flutter/issues/147231

## Tests

Adds 1 test for this particular issue and several missing tests.
This commit is contained in:
Bruno Leroux 2024-05-03 17:16:05 +02:00 committed by GitHub
parent f1037a01b7
commit 99876ba2ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 183 additions and 9 deletions

View File

@ -1828,8 +1828,11 @@ class InputDecorator extends StatefulWidget {
/// Whether the label needs to get out of the way of the input, either by
/// floating or disappearing.
///
/// Will withdraw when not empty, or when focused while enabled.
bool get _labelShouldWithdraw => !isEmpty || (isFocused && decoration.enabled);
/// Will withdraw when not empty, when focused while enabled, or when
/// floating behavior is [FloatingLabelBehavior.always].
bool get _labelShouldWithdraw => !isEmpty
|| (isFocused && decoration.enabled)
|| decoration.floatingLabelBehavior == FloatingLabelBehavior.always;
@override
State<InputDecorator> createState() => _InputDecoratorState();
@ -1872,9 +1875,8 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
void initState() {
super.initState();
final bool labelIsInitiallyFloating = widget.decoration.floatingLabelBehavior == FloatingLabelBehavior.always
|| (widget.decoration.floatingLabelBehavior != FloatingLabelBehavior.never &&
widget._labelShouldWithdraw);
final bool labelIsInitiallyFloating = widget.decoration.floatingLabelBehavior != FloatingLabelBehavior.never
&& widget._labelShouldWithdraw;
_floatingLabelController = AnimationController(
duration: _kTransitionDuration,
@ -1937,8 +1939,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
final bool floatBehaviorChanged = widget.decoration.floatingLabelBehavior != old.decoration.floatingLabelBehavior;
if (widget._labelShouldWithdraw != old._labelShouldWithdraw || floatBehaviorChanged) {
if (_floatingLabelEnabled
&& (widget._labelShouldWithdraw || widget.decoration.floatingLabelBehavior == FloatingLabelBehavior.always)) {
if (_floatingLabelEnabled && widget._labelShouldWithdraw) {
_floatingLabelController.forward();
} else {
_floatingLabelController.reverse();
@ -2028,8 +2029,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
// hint would.
bool get _hasInlineLabel {
return !widget._labelShouldWithdraw
&& (decoration.labelText != null || decoration.label != null)
&& decoration.floatingLabelBehavior != FloatingLabelBehavior.always;
&& (decoration.labelText != null || decoration.label != null);
}
// If the label is a floating placeholder, it's always shown.

View File

@ -2032,6 +2032,137 @@ void main() {
});
});
});
testWidgets('floatingLabelStyle overrides default style', (WidgetTester tester) async {
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
isFocused: true, // Label appears floating above input field.
decoration: const InputDecoration(
labelText: labelText,
floatingLabelStyle: floatingLabelStyle,
),
),
);
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
});
testWidgets('floatingLabelStyle defaults to labelStyle', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
isFocused: true, // Label appears floating above input field.
decoration: const InputDecoration(
labelText: labelText,
labelStyle: labelStyle,
),
),
);
expect(getLabelStyle(tester).color, labelStyle.color);
expect(getLabelStyle(tester).fontSize, labelStyle.fontSize);
});
testWidgets('floatingLabelStyle takes precedence over labelStyle', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
isFocused: true, // Label appears floating above input field.
decoration: const InputDecoration(
labelText: labelText,
labelStyle: labelStyle,
floatingLabelStyle: floatingLabelStyle,
),
),
);
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
});
testWidgets('InputDecorationTheme labelStyle overrides default style', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true, // Label appears inline, on top of the input field.
inputDecorationTheme: const InputDecorationTheme(
labelStyle: labelStyle,
),
decoration: const InputDecoration(
labelText: labelText,
),
),
);
expect(getLabelStyle(tester).color, labelStyle.color);
});
testWidgets('InputDecorationTheme floatingLabelStyle overrides default style', (WidgetTester tester) async {
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
isFocused: true, // Label appears floating above input field.
inputDecorationTheme: const InputDecorationTheme(
floatingLabelStyle: floatingLabelStyle,
),
decoration: const InputDecoration(
labelText: labelText,
),
),
);
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
});
testWidgets('floatingLabelStyle is always used when FloatingLabelBehavior.always', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/147231.
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
decoration: const InputDecoration(
labelText: labelText,
labelStyle: labelStyle,
floatingLabelStyle: floatingLabelStyle,
floatingLabelBehavior: FloatingLabelBehavior.always,
),
),
);
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
// Focus the input decorator.
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
isFocused: true,
decoration: const InputDecoration(
labelText: labelText,
labelStyle: labelStyle,
floatingLabelStyle: floatingLabelStyle,
floatingLabelBehavior: FloatingLabelBehavior.always,
),
),
);
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
});
});
group('Material3 - InputDecoration labelText layout', () {
@ -4915,6 +5046,49 @@ void main() {
expect(getOpacity(tester, prefixText), 1.0);
});
testWidgets('Prefix and suffix are not visible when decorator is empty', (WidgetTester tester) async {
const String prefixText = 'Prefix';
const String suffixText = 'Suffix';
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
decoration: const InputDecoration(
filled: true,
labelText: labelText,
prefixText: prefixText,
suffixText: suffixText,
),
),
);
// Prefix and suffix are hidden.
expect(getOpacity(tester, prefixText), 0.0);
expect(getOpacity(tester, suffixText), 0.0);
});
testWidgets('Prefix and suffix are visible when decorator is empty and floating behavior is FloatingBehavior.always', (WidgetTester tester) async {
const String prefixText = 'Prefix';
const String suffixText = 'Suffix';
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
decoration: const InputDecoration(
filled: true,
labelText: labelText,
prefixText: prefixText,
suffixText: suffixText,
floatingLabelBehavior: FloatingLabelBehavior.always,
),
),
);
// Prefix and suffix are visible.
expect(getOpacity(tester, prefixText), 1.0);
expect(getOpacity(tester, suffixText), 1.0);
});
testWidgets('OutlineInputBorder and InputDecorator long labels and in Floating, the width should ignore the icon width', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/64427.
const String labelText = 'Flutter is Googles UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.';