diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 7cdf684673..a63463b35d 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -2249,31 +2249,35 @@ class _InputDecoratorState extends State with TickerProviderStat final String? hintText = decoration.hintText; final bool maintainHintHeight = decoration.maintainHintHeight; Widget? hint; - if (hintText != null) { + if (decoration.hint != null || hintText != null) { + final Widget hintWidget = + decoration.hint ?? + Text( + hintText!, + style: hintStyle, + textDirection: decoration.hintTextDirection, + overflow: + hintStyle.overflow ?? + (decoration.hintMaxLines == null ? null : TextOverflow.ellipsis), + textAlign: textAlign, + maxLines: decoration.hintMaxLines, + ); final bool showHint = isEmpty && !_hasInlineLabel; - final Text hintTextWidget = Text( - hintText, - style: hintStyle, - textDirection: decoration.hintTextDirection, - overflow: - hintStyle.overflow ?? (decoration.hintMaxLines == null ? null : TextOverflow.ellipsis), - textAlign: textAlign, - maxLines: decoration.hintMaxLines, - ); hint = maintainHintHeight ? AnimatedOpacity( opacity: showHint ? 1.0 : 0.0, duration: decoration.hintFadeDuration ?? _kHintFadeTransitionDuration, curve: _kTransitionCurve, - child: hintTextWidget, + child: hintWidget, ) : AnimatedSwitcher( duration: decoration.hintFadeDuration ?? _kHintFadeTransitionDuration, transitionBuilder: _buildTransition, - child: showHint ? hintTextWidget : const SizedBox.shrink(), + child: showHint ? hintWidget : const SizedBox.shrink(), ); } + InputBorder? border; if (!decoration.enabled) { border = _hasError ? decoration.errorBorder : decoration.disabledBorder; @@ -2695,6 +2699,7 @@ class InputDecoration { this.helperStyle, this.helperMaxLines, this.hintText, + this.hint, this.hintStyle, this.hintTextDirection, this.hintMaxLines, @@ -2742,6 +2747,10 @@ class InputDecoration { !(label != null && labelText != null), 'Declaring both label and labelText is not supported.', ), + assert( + hint == null || hintText == null, + 'Declaring both hint and hintText is not supported.', + ), assert( !(helper != null && helperText != null), 'Declaring both helper and helperText is not supported.', @@ -2781,6 +2790,7 @@ class InputDecoration { ) FloatingLabelAlignment? floatingLabelAlignment, this.hintStyle, + this.hint, this.hintTextDirection, this.hintMaxLines, this.hintFadeDuration, @@ -3019,6 +3029,11 @@ class InputDecoration { /// or (b) the input has the focus. final String? hintText; + /// The widget to use in place of the [hintText]. + /// + /// Either [hintText] or [hint] can be specified, but not both. + final Widget? hint; + /// The style to use for the [hintText]. /// /// If [hintStyle] is a [WidgetStateTextStyle], then the effective @@ -3743,6 +3758,7 @@ class InputDecoration { TextStyle? helperStyle, int? helperMaxLines, String? hintText, + Widget? hint, TextStyle? hintStyle, TextDirection? hintTextDirection, Duration? hintFadeDuration, @@ -3799,6 +3815,7 @@ class InputDecoration { helperStyle: helperStyle ?? this.helperStyle, helperMaxLines: helperMaxLines ?? this.helperMaxLines, hintText: hintText ?? this.hintText, + hint: hint ?? this.hint, hintStyle: hintStyle ?? this.hintStyle, hintTextDirection: hintTextDirection ?? this.hintTextDirection, hintMaxLines: hintMaxLines ?? this.hintMaxLines, @@ -3908,6 +3925,7 @@ class InputDecoration { other.helperStyle == helperStyle && other.helperMaxLines == helperMaxLines && other.hintText == hintText && + other.hint == hint && other.hintStyle == hintStyle && other.hintTextDirection == hintTextDirection && other.hintMaxLines == hintMaxLines && @@ -3967,6 +3985,7 @@ class InputDecoration { helperStyle, helperMaxLines, hintText, + hint, hintStyle, hintTextDirection, hintMaxLines, @@ -4026,6 +4045,7 @@ class InputDecoration { if (helperText != null) 'helperText: "$helperText"', if (helperMaxLines != null) 'helperMaxLines: "$helperMaxLines"', if (hintText != null) 'hintText: "$hintText"', + if (hint != null) 'hint: $hint', if (hintMaxLines != null) 'hintMaxLines: "$hintMaxLines"', if (hintFadeDuration != null) 'hintFadeDuration: "$hintFadeDuration"', if (!maintainHintHeight) 'maintainHintHeight: false', diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 15d7b683db..d77d99001f 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -4684,6 +4684,26 @@ void main() { }); }); + testWidgets('InputDecorator throws Assertion Error when hint and hintText are provided', ( + WidgetTester tester, + ) async { + expect(() { + buildInputDecorator( + decoration: InputDecoration( + hintText: 'Enter text here', + hint: const Text('Enter text here', style: TextStyle(fontSize: 20.0)), + ), + ); + }, throwsAssertionError); + }); + + testWidgets('InputDecorator shows hint widget', (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator(decoration: const InputDecoration(hint: Text('hint'))), + ); + expect(find.text('hint'), findsOneWidget); + }); + testWidgets('hint style overflow works', (WidgetTester tester) async { final String hintText = 'hint text' * 20; const TextStyle hintStyle = TextStyle(fontSize: 14.0, overflow: TextOverflow.fade);