Fix https://github.com/flutter/flutter/issues/153183 Fix https://github.com/flutter/flutter/issues/52008 Pre-launch Checklist
This commit is contained in:
parent
feb9c59284
commit
7f663fc1d8
@ -2163,20 +2163,37 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
|
||||
final TextStyle hintStyle = _getInlineHintStyle(themeData, defaults);
|
||||
final String? hintText = decoration.hintText;
|
||||
final Widget? hint = hintText == null ? null : AnimatedOpacity(
|
||||
opacity: (isEmpty && !_hasInlineLabel) ? 1.0 : 0.0,
|
||||
duration: decoration.hintFadeDuration ?? _kHintFadeTransitionDuration,
|
||||
curve: _kTransitionCurve,
|
||||
child: Text(
|
||||
final bool maintainHintHeight = decoration.maintainHintHeight;
|
||||
Widget? hint;
|
||||
if (hintText != null) {
|
||||
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,
|
||||
) : AnimatedSwitcher(
|
||||
duration: decoration.hintFadeDuration ?? _kHintFadeTransitionDuration,
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return FadeTransition(
|
||||
opacity: CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: _kTransitionCurve,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: showHint ? hintTextWidget : const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
InputBorder? border;
|
||||
if (!decoration.enabled) {
|
||||
border = _hasError ? decoration.errorBorder : decoration.disabledBorder;
|
||||
@ -2574,6 +2591,7 @@ class InputDecoration {
|
||||
this.hintTextDirection,
|
||||
this.hintMaxLines,
|
||||
this.hintFadeDuration,
|
||||
this.maintainHintHeight = true,
|
||||
this.error,
|
||||
this.errorText,
|
||||
this.errorStyle,
|
||||
@ -2643,6 +2661,7 @@ class InputDecoration {
|
||||
this.hintTextDirection,
|
||||
this.hintMaxLines,
|
||||
this.hintFadeDuration,
|
||||
this.maintainHintHeight = true,
|
||||
this.filled = false,
|
||||
this.fillColor,
|
||||
this.focusColor,
|
||||
@ -2904,6 +2923,15 @@ class InputDecoration {
|
||||
/// If [InputDecorationTheme.hintFadeDuration] is null defaults to 20ms.
|
||||
final Duration? hintFadeDuration;
|
||||
|
||||
/// Whether the input field's height should always be greater than or equal to
|
||||
/// the height of the [hintText], even if the [hintText] is not visible.
|
||||
///
|
||||
/// The [InputDecorator] widget ignores [hintText] during layout when
|
||||
/// it's not visible, if this flag is set to false.
|
||||
///
|
||||
/// Defaults to true.
|
||||
final bool maintainHintHeight;
|
||||
|
||||
/// Optional widget that appears below the [InputDecorator.child] and the border.
|
||||
///
|
||||
/// If non-null, the border's color animates to red and the [helperText] is not shown.
|
||||
@ -3578,6 +3606,7 @@ class InputDecoration {
|
||||
TextDirection? hintTextDirection,
|
||||
Duration? hintFadeDuration,
|
||||
int? hintMaxLines,
|
||||
bool? maintainHintHeight,
|
||||
Widget? error,
|
||||
String? errorText,
|
||||
TextStyle? errorStyle,
|
||||
@ -3633,6 +3662,7 @@ class InputDecoration {
|
||||
hintTextDirection: hintTextDirection ?? this.hintTextDirection,
|
||||
hintMaxLines: hintMaxLines ?? this.hintMaxLines,
|
||||
hintFadeDuration: hintFadeDuration ?? this.hintFadeDuration,
|
||||
maintainHintHeight: maintainHintHeight ?? this.maintainHintHeight,
|
||||
error: error ?? this.error,
|
||||
errorText: errorText ?? this.errorText,
|
||||
errorStyle: errorStyle ?? this.errorStyle,
|
||||
@ -3741,6 +3771,7 @@ class InputDecoration {
|
||||
&& other.hintTextDirection == hintTextDirection
|
||||
&& other.hintMaxLines == hintMaxLines
|
||||
&& other.hintFadeDuration == hintFadeDuration
|
||||
&& other.maintainHintHeight == maintainHintHeight
|
||||
&& other.error == error
|
||||
&& other.errorText == errorText
|
||||
&& other.errorStyle == errorStyle
|
||||
@ -3799,6 +3830,7 @@ class InputDecoration {
|
||||
hintTextDirection,
|
||||
hintMaxLines,
|
||||
hintFadeDuration,
|
||||
maintainHintHeight,
|
||||
error,
|
||||
errorText,
|
||||
errorStyle,
|
||||
@ -3855,6 +3887,7 @@ class InputDecoration {
|
||||
if (hintText != null) 'hintText: "$hintText"',
|
||||
if (hintMaxLines != null) 'hintMaxLines: "$hintMaxLines"',
|
||||
if (hintFadeDuration != null) 'hintFadeDuration: "$hintFadeDuration"',
|
||||
if (!maintainHintHeight) 'maintainHintHeight: false',
|
||||
if (error != null) 'error: "$error"',
|
||||
if (errorText != null) 'errorText: "$errorText"',
|
||||
if (errorStyle != null) 'errorStyle: "$errorStyle"',
|
||||
|
@ -4505,6 +4505,120 @@ void main() {
|
||||
final Text hintTextWidget = tester.widget(hintTextFinder);
|
||||
expect(hintTextWidget.style!.overflow, decoration.hintStyle!.overflow);
|
||||
});
|
||||
|
||||
testWidgets('Widget height collapses from hint height when maintainHintHeight is false', (WidgetTester tester) async {
|
||||
final String hintText = 'hint' * 20;
|
||||
final InputDecoration decoration = InputDecoration(
|
||||
hintText: hintText,
|
||||
hintMaxLines: 3,
|
||||
maintainHintHeight: false,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: decoration,
|
||||
),
|
||||
);
|
||||
expect(tester.getSize(find.byType(InputDecorator)).height, 48.0);
|
||||
});
|
||||
|
||||
testWidgets('Widget height stays at hint height by default', (WidgetTester tester) async {
|
||||
final String hintText = 'hint' * 20;
|
||||
final InputDecoration decoration = InputDecoration(
|
||||
hintMaxLines: 3,
|
||||
hintText: hintText,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: decoration,
|
||||
),
|
||||
);
|
||||
final double hintHeight = tester.getSize(find.text(hintText)).height;
|
||||
final double inputHeight = tester.getSize(find.byType(InputDecorator)).height;
|
||||
expect(inputHeight, hintHeight + 16.0);
|
||||
});
|
||||
|
||||
testWidgets('hintFadeDuration applies to hint fade-in when maintainHintHeight is false', (WidgetTester tester) async {
|
||||
const InputDecoration decoration = InputDecoration(
|
||||
hintText: hintText,
|
||||
hintMaxLines: 3,
|
||||
hintFadeDuration: Duration(milliseconds: 120),
|
||||
maintainHintHeight: false,
|
||||
);
|
||||
|
||||
// Build once with empty content.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: decoration,
|
||||
),
|
||||
);
|
||||
|
||||
// Hint is not exist.
|
||||
expect(find.text(hintText), findsNothing);
|
||||
|
||||
// Rebuild with empty content.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
isEmpty: true,
|
||||
decoration: decoration,
|
||||
),
|
||||
);
|
||||
|
||||
// The hint's opacity animates from 0.0 to 1.0.
|
||||
// The animation's default duration is 20ms.
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
final double hintOpacity50ms = getHintOpacity(tester);
|
||||
expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0));
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
final double hintOpacity100ms = getHintOpacity(tester);
|
||||
expect(hintOpacity100ms, inExclusiveRange(hintOpacity50ms, 1.0));
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
final double hintOpacity120ms = getHintOpacity(tester);
|
||||
expect(hintOpacity120ms, 1.0);
|
||||
});
|
||||
|
||||
testWidgets('hintFadeDuration applies to hint fade-out when maintainHintHeight is false', (WidgetTester tester) async {
|
||||
const InputDecoration decoration = InputDecoration(
|
||||
hintText: hintText,
|
||||
hintMaxLines: 3,
|
||||
hintFadeDuration: Duration(milliseconds: 120),
|
||||
maintainHintHeight: false,
|
||||
);
|
||||
|
||||
// Build once with empty content.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
isEmpty: true,
|
||||
decoration: decoration,
|
||||
),
|
||||
);
|
||||
|
||||
// Hint is visible (opacity 1.0).
|
||||
expect(getHintOpacity(tester), 1.0);
|
||||
|
||||
// Rebuild with non-empty content.
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
decoration: decoration,
|
||||
),
|
||||
);
|
||||
|
||||
// The hint's opacity animates from 1.0 to 0.0.
|
||||
// The animation's default duration is 20ms.
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
final double hintOpacity50ms = getHintOpacity(tester);
|
||||
expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0));
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
final double hintOpacity100ms = getHintOpacity(tester);
|
||||
expect(hintOpacity100ms, inExclusiveRange(0.0, hintOpacity50ms));
|
||||
await tester.pump(const Duration(milliseconds: 20));
|
||||
final double hintOpacity120ms = getHintOpacity(tester);
|
||||
expect(hintOpacity120ms, 0);
|
||||
await tester.pump(const Duration(milliseconds: 1));
|
||||
// The hintText replaced with SizeBox.
|
||||
expect(find.text(hintText), findsNothing);
|
||||
});
|
||||
});
|
||||
|
||||
group('Material3 - InputDecoration helper/counter/error', () {
|
||||
|
Loading…
x
Reference in New Issue
Block a user