Skip to content

Commit 6ebb7cf

Browse files
authored
Fix InputDecoration.floatingLabelBehavior is not inherited (flutter#170905)
## Description This PR fixes `InputDecoration.floatingLabelBehavior` logic to query ambient InputDecorationTheme.floatingLabelBehavior, previously it was ignored. ## Related Issue Fixes [InputDecorationTheme and IconTheme isn't fully inherited](flutter#71813) Will help to complete flutter#168981 ## Tests Adds 1 test
1 parent 4df47b7 commit 6ebb7cf

File tree

2 files changed

+38
-25
lines changed

2 files changed

+38
-25
lines changed

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

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,12 +1924,8 @@ class InputDecorator extends StatefulWidget {
19241924
/// Whether the label needs to get out of the way of the input, either by
19251925
/// floating or disappearing.
19261926
///
1927-
/// Will withdraw when not empty, when focused while enabled, or when
1928-
/// floating behavior is [FloatingLabelBehavior.always].
1929-
bool get _labelShouldWithdraw =>
1930-
!isEmpty ||
1931-
(isFocused && decoration.enabled) ||
1932-
decoration.floatingLabelBehavior == FloatingLabelBehavior.always;
1927+
/// Will withdraw when not empty or when focused while enabled.
1928+
bool get _labelShouldWithdraw => !isEmpty || (isFocused && decoration.enabled);
19331929

19341930
@override
19351931
State<InputDecorator> createState() => _InputDecoratorState();
@@ -1980,15 +1976,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
19801976
void initState() {
19811977
super.initState();
19821978

1983-
final bool labelIsInitiallyFloating =
1984-
widget.decoration.floatingLabelBehavior != FloatingLabelBehavior.never &&
1985-
widget._labelShouldWithdraw;
1986-
1987-
_floatingLabelController = AnimationController(
1988-
duration: _kTransitionDuration,
1989-
vsync: this,
1990-
value: labelIsInitiallyFloating ? 1.0 : 0.0,
1991-
);
1979+
_floatingLabelController = AnimationController(duration: _kTransitionDuration, vsync: this);
19921980
_floatingLabelController.addListener(_handleChange);
19931981
_floatingLabelAnimation = CurvedAnimation(
19941982
parent: _floatingLabelController,
@@ -2003,6 +1991,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
20031991
void didChangeDependencies() {
20041992
super.didChangeDependencies();
20051993
_effectiveDecoration = null;
1994+
1995+
final bool labelIsInitiallyFloating =
1996+
decoration.floatingLabelBehavior != FloatingLabelBehavior.never && labelShouldWithdraw;
1997+
_floatingLabelController.value = labelIsInitiallyFloating ? 1.0 : 0.0;
20061998
}
20071999

20082000
@override
@@ -2034,6 +2026,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
20342026
return decoration.floatingLabelBehavior != FloatingLabelBehavior.never;
20352027
}
20362028

2029+
bool get labelShouldWithdraw =>
2030+
widget._labelShouldWithdraw ||
2031+
decoration.floatingLabelBehavior == FloatingLabelBehavior.always;
2032+
20372033
@override
20382034
void didUpdateWidget(InputDecorator old) {
20392035
super.didUpdateWidget(old);
@@ -2045,7 +2041,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
20452041
widget.decoration.floatingLabelBehavior != old.decoration.floatingLabelBehavior;
20462042

20472043
if (widget._labelShouldWithdraw != old._labelShouldWithdraw || floatBehaviorChanged) {
2048-
if (_floatingLabelEnabled && widget._labelShouldWithdraw) {
2044+
if (_floatingLabelEnabled && labelShouldWithdraw) {
20492045
_floatingLabelController.forward();
20502046
} else {
20512047
_floatingLabelController.reverse();
@@ -2131,8 +2127,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
21312127
// floatingLabelBehavior isn't set to always, then the label appears where the
21322128
// hint would.
21332129
bool get _hasInlineLabel {
2134-
return !widget._labelShouldWithdraw &&
2135-
(decoration.labelText != null || decoration.label != null);
2130+
return !labelShouldWithdraw && (decoration.labelText != null || decoration.label != null);
21362131
}
21372132

21382133
// If the label is a floating placeholder, it's always shown.
@@ -2357,10 +2352,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
23572352
child: AnimatedDefaultTextStyle(
23582353
duration: _kTransitionDuration,
23592354
curve: _kTransitionCurve,
2360-
style:
2361-
widget._labelShouldWithdraw
2362-
? _getFloatingLabelStyle(themeData, defaults)
2363-
: labelStyle,
2355+
style: labelShouldWithdraw ? _getFloatingLabelStyle(themeData, defaults) : labelStyle,
23642356
child:
23652357
decoration.label ??
23662358
Text(decoration.labelText!, overflow: TextOverflow.ellipsis, textAlign: textAlign),
@@ -2376,13 +2368,13 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
23762368
// If at least two out of the three are visible, it needs semantics sort
23772369
// order.
23782370
final bool needsSemanticsSortOrder =
2379-
widget._labelShouldWithdraw &&
2371+
labelShouldWithdraw &&
23802372
(input != null ? (hasPrefix || hasSuffix) : (hasPrefix && hasSuffix));
23812373

23822374
final Widget? prefix =
23832375
hasPrefix
23842376
? _AffixText(
2385-
labelIsFloating: widget._labelShouldWithdraw,
2377+
labelIsFloating: labelShouldWithdraw,
23862378
text: decoration.prefixText,
23872379
style:
23882380
MaterialStateProperty.resolveAs(decoration.prefixStyle, materialState) ??
@@ -2396,7 +2388,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
23962388
final Widget? suffix =
23972389
hasSuffix
23982390
? _AffixText(
2399-
labelIsFloating: widget._labelShouldWithdraw,
2391+
labelIsFloating: labelShouldWithdraw,
24002392
text: decoration.suffixText,
24012393
style:
24022394
MaterialStateProperty.resolveAs(decoration.suffixStyle, materialState) ??

packages/flutter/test/material/input_decorator_test.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,11 +2944,32 @@ void main() {
29442944
);
29452945

29462946
expect(getDecoratorRect(tester).size, const Size(800.0, 56.0));
2947+
// Label line height is forced to 1.0 and font size is 16.0,
2948+
// the label should be vertically centered (20 pixels above and below).
29472949
expect(getLabelRect(tester).top, 20.0);
29482950
expect(getLabelRect(tester).bottom, 36.0);
29492951
},
29502952
);
29512953

2954+
// Regression test for https://github.com/flutter/flutter/issues/71813.
2955+
testWidgets('Ambient theme floatingLabelBehavior is used', (WidgetTester tester) async {
2956+
const FloatingLabelBehavior floatingLabelBehavior = FloatingLabelBehavior.never;
2957+
await tester.pumpWidget(
2958+
buildInputDecorator(
2959+
inputDecorationTheme: const InputDecorationTheme(
2960+
floatingLabelBehavior: floatingLabelBehavior,
2961+
),
2962+
decoration: const InputDecoration(label: customLabel),
2963+
),
2964+
);
2965+
2966+
expect(getDecoratorRect(tester).size, const Size(800.0, 56.0));
2967+
// Label line height is forced to 1.0 and font size is 16.0,
2968+
// the label should be vertically centered (20 pixels above and below).
2969+
expect(getCustomLabelRect(tester).top, 20.0);
2970+
expect(getCustomLabelRect(tester).bottom, 36.0);
2971+
});
2972+
29522973
testWidgets('Floating label animation duration and curve', (WidgetTester tester) async {
29532974
Future<void> pumpInputDecorator({required bool isFocused}) async {
29542975
return tester.pumpWidget(

0 commit comments

Comments
 (0)