Skip to content

Commit 99876ba

Browse files
authored
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 flutter/flutter#147231 ## Tests Adds 1 test for this particular issue and several missing tests.
1 parent f1037a0 commit 99876ba

File tree

2 files changed

+183
-9
lines changed

2 files changed

+183
-9
lines changed

packages/flutter/lib/src/material/input_decorator.dart

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,8 +1828,11 @@ class InputDecorator extends StatefulWidget {
18281828
/// Whether the label needs to get out of the way of the input, either by
18291829
/// floating or disappearing.
18301830
///
1831-
/// Will withdraw when not empty, or when focused while enabled.
1832-
bool get _labelShouldWithdraw => !isEmpty || (isFocused && decoration.enabled);
1831+
/// Will withdraw when not empty, when focused while enabled, or when
1832+
/// floating behavior is [FloatingLabelBehavior.always].
1833+
bool get _labelShouldWithdraw => !isEmpty
1834+
|| (isFocused && decoration.enabled)
1835+
|| decoration.floatingLabelBehavior == FloatingLabelBehavior.always;
18331836

18341837
@override
18351838
State<InputDecorator> createState() => _InputDecoratorState();
@@ -1872,9 +1875,8 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
18721875
void initState() {
18731876
super.initState();
18741877

1875-
final bool labelIsInitiallyFloating = widget.decoration.floatingLabelBehavior == FloatingLabelBehavior.always
1876-
|| (widget.decoration.floatingLabelBehavior != FloatingLabelBehavior.never &&
1877-
widget._labelShouldWithdraw);
1878+
final bool labelIsInitiallyFloating = widget.decoration.floatingLabelBehavior != FloatingLabelBehavior.never
1879+
&& widget._labelShouldWithdraw;
18781880

18791881
_floatingLabelController = AnimationController(
18801882
duration: _kTransitionDuration,
@@ -1937,8 +1939,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
19371939
final bool floatBehaviorChanged = widget.decoration.floatingLabelBehavior != old.decoration.floatingLabelBehavior;
19381940

19391941
if (widget._labelShouldWithdraw != old._labelShouldWithdraw || floatBehaviorChanged) {
1940-
if (_floatingLabelEnabled
1941-
&& (widget._labelShouldWithdraw || widget.decoration.floatingLabelBehavior == FloatingLabelBehavior.always)) {
1942+
if (_floatingLabelEnabled && widget._labelShouldWithdraw) {
19421943
_floatingLabelController.forward();
19431944
} else {
19441945
_floatingLabelController.reverse();
@@ -2028,8 +2029,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
20282029
// hint would.
20292030
bool get _hasInlineLabel {
20302031
return !widget._labelShouldWithdraw
2031-
&& (decoration.labelText != null || decoration.label != null)
2032-
&& decoration.floatingLabelBehavior != FloatingLabelBehavior.always;
2032+
&& (decoration.labelText != null || decoration.label != null);
20332033
}
20342034

20352035
// If the label is a floating placeholder, it's always shown.

packages/flutter/test/material/input_decorator_test.dart

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,137 @@ void main() {
20322032
});
20332033
});
20342034
});
2035+
2036+
testWidgets('floatingLabelStyle overrides default style', (WidgetTester tester) async {
2037+
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
2038+
2039+
await tester.pumpWidget(
2040+
buildInputDecorator(
2041+
isEmpty: true,
2042+
isFocused: true, // Label appears floating above input field.
2043+
decoration: const InputDecoration(
2044+
labelText: labelText,
2045+
floatingLabelStyle: floatingLabelStyle,
2046+
),
2047+
),
2048+
);
2049+
2050+
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
2051+
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
2052+
});
2053+
2054+
testWidgets('floatingLabelStyle defaults to labelStyle', (WidgetTester tester) async {
2055+
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
2056+
2057+
await tester.pumpWidget(
2058+
buildInputDecorator(
2059+
isEmpty: true,
2060+
isFocused: true, // Label appears floating above input field.
2061+
decoration: const InputDecoration(
2062+
labelText: labelText,
2063+
labelStyle: labelStyle,
2064+
),
2065+
),
2066+
);
2067+
2068+
expect(getLabelStyle(tester).color, labelStyle.color);
2069+
expect(getLabelStyle(tester).fontSize, labelStyle.fontSize);
2070+
});
2071+
2072+
testWidgets('floatingLabelStyle takes precedence over labelStyle', (WidgetTester tester) async {
2073+
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
2074+
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
2075+
2076+
await tester.pumpWidget(
2077+
buildInputDecorator(
2078+
isEmpty: true,
2079+
isFocused: true, // Label appears floating above input field.
2080+
decoration: const InputDecoration(
2081+
labelText: labelText,
2082+
labelStyle: labelStyle,
2083+
floatingLabelStyle: floatingLabelStyle,
2084+
),
2085+
),
2086+
);
2087+
2088+
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
2089+
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
2090+
});
2091+
2092+
testWidgets('InputDecorationTheme labelStyle overrides default style', (WidgetTester tester) async {
2093+
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
2094+
2095+
await tester.pumpWidget(
2096+
buildInputDecorator(
2097+
isEmpty: true, // Label appears inline, on top of the input field.
2098+
inputDecorationTheme: const InputDecorationTheme(
2099+
labelStyle: labelStyle,
2100+
),
2101+
decoration: const InputDecoration(
2102+
labelText: labelText,
2103+
),
2104+
),
2105+
);
2106+
2107+
expect(getLabelStyle(tester).color, labelStyle.color);
2108+
});
2109+
2110+
testWidgets('InputDecorationTheme floatingLabelStyle overrides default style', (WidgetTester tester) async {
2111+
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
2112+
2113+
await tester.pumpWidget(
2114+
buildInputDecorator(
2115+
isEmpty: true,
2116+
isFocused: true, // Label appears floating above input field.
2117+
inputDecorationTheme: const InputDecorationTheme(
2118+
floatingLabelStyle: floatingLabelStyle,
2119+
),
2120+
decoration: const InputDecoration(
2121+
labelText: labelText,
2122+
),
2123+
),
2124+
);
2125+
2126+
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
2127+
});
2128+
2129+
testWidgets('floatingLabelStyle is always used when FloatingLabelBehavior.always', (WidgetTester tester) async {
2130+
// Regression test for https://github.com/flutter/flutter/issues/147231.
2131+
const TextStyle labelStyle = TextStyle(color: Colors.amber, fontSize: 16.0);
2132+
const TextStyle floatingLabelStyle = TextStyle(color: Colors.indigo, fontSize: 16.0);
2133+
2134+
await tester.pumpWidget(
2135+
buildInputDecorator(
2136+
isEmpty: true,
2137+
decoration: const InputDecoration(
2138+
labelText: labelText,
2139+
labelStyle: labelStyle,
2140+
floatingLabelStyle: floatingLabelStyle,
2141+
floatingLabelBehavior: FloatingLabelBehavior.always,
2142+
),
2143+
),
2144+
);
2145+
2146+
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
2147+
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
2148+
2149+
// Focus the input decorator.
2150+
await tester.pumpWidget(
2151+
buildInputDecorator(
2152+
isEmpty: true,
2153+
isFocused: true,
2154+
decoration: const InputDecoration(
2155+
labelText: labelText,
2156+
labelStyle: labelStyle,
2157+
floatingLabelStyle: floatingLabelStyle,
2158+
floatingLabelBehavior: FloatingLabelBehavior.always,
2159+
),
2160+
),
2161+
);
2162+
2163+
expect(getLabelStyle(tester).color, floatingLabelStyle.color);
2164+
expect(getLabelStyle(tester).fontSize, floatingLabelStyle.fontSize);
2165+
});
20352166
});
20362167

20372168
group('Material3 - InputDecoration labelText layout', () {
@@ -4915,6 +5046,49 @@ void main() {
49155046
expect(getOpacity(tester, prefixText), 1.0);
49165047
});
49175048

5049+
testWidgets('Prefix and suffix are not visible when decorator is empty', (WidgetTester tester) async {
5050+
const String prefixText = 'Prefix';
5051+
const String suffixText = 'Suffix';
5052+
5053+
await tester.pumpWidget(
5054+
buildInputDecorator(
5055+
isEmpty: true,
5056+
decoration: const InputDecoration(
5057+
filled: true,
5058+
labelText: labelText,
5059+
prefixText: prefixText,
5060+
suffixText: suffixText,
5061+
),
5062+
),
5063+
);
5064+
5065+
// Prefix and suffix are hidden.
5066+
expect(getOpacity(tester, prefixText), 0.0);
5067+
expect(getOpacity(tester, suffixText), 0.0);
5068+
});
5069+
5070+
testWidgets('Prefix and suffix are visible when decorator is empty and floating behavior is FloatingBehavior.always', (WidgetTester tester) async {
5071+
const String prefixText = 'Prefix';
5072+
const String suffixText = 'Suffix';
5073+
5074+
await tester.pumpWidget(
5075+
buildInputDecorator(
5076+
isEmpty: true,
5077+
decoration: const InputDecoration(
5078+
filled: true,
5079+
labelText: labelText,
5080+
prefixText: prefixText,
5081+
suffixText: suffixText,
5082+
floatingLabelBehavior: FloatingLabelBehavior.always,
5083+
),
5084+
),
5085+
);
5086+
5087+
// Prefix and suffix are visible.
5088+
expect(getOpacity(tester, prefixText), 1.0);
5089+
expect(getOpacity(tester, suffixText), 1.0);
5090+
});
5091+
49185092
testWidgets('OutlineInputBorder and InputDecorator long labels and in Floating, the width should ignore the icon width', (WidgetTester tester) async {
49195093
// Regression test for https://github.com/flutter/flutter/issues/64427.
49205094
const String labelText = 'Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.';

0 commit comments

Comments
 (0)