Skip to content

Commit

Permalink
Always relies on floatingLabelStyle when FloatingLabelBehavior.always…
Browse files Browse the repository at this point in the history
… (#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 flutter/flutter#147231

## Tests

Adds 1 test for this particular issue and several missing tests.
  • Loading branch information
bleroux authored May 3, 2024
1 parent f1037a0 commit 99876ba
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 9 deletions.
18 changes: 9 additions & 9 deletions packages/flutter/lib/src/material/input_decorator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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.
Expand Down
174 changes: 174 additions & 0 deletions packages/flutter/test/material/input_decorator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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', () {
Expand Down Expand Up @@ -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 Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.';
Expand Down

0 comments on commit 99876ba

Please sign in to comment.