InputDecoration.error should activate error state (#134001)
When passed an `error` widget, `InputDecoration` should activate its error state. Before this change the `errorBorder` would only activate if an `errorText` was provided. This change solves this issue by accounting for a provided `error` widget.
This commit is contained in:
@@ -1971,6 +1971,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
|
||||
TextAlign? get textAlign => widget.textAlign;
|
||||
bool get isFocused => widget.isFocused;
|
||||
bool get _hasError => decoration.errorText != null || decoration.error != null;
|
||||
bool get isHovering => widget.isHovering && decoration.enabled;
|
||||
bool get isEmpty => widget.isEmpty;
|
||||
bool get _floatingLabelEnabled {
|
||||
@@ -2011,7 +2012,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
? Colors.transparent
|
||||
: themeData.disabledColor;
|
||||
}
|
||||
if (decoration.errorText != null) {
|
||||
if (_hasError) {
|
||||
return themeData.colorScheme.error;
|
||||
}
|
||||
if (isFocused) {
|
||||
@@ -2107,7 +2108,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
|
||||
TextStyle _getFloatingLabelStyle(ThemeData themeData, InputDecorationTheme defaults) {
|
||||
TextStyle defaultTextStyle = MaterialStateProperty.resolveAs(defaults.floatingLabelStyle!, materialState);
|
||||
if (decoration.errorText != null && decoration.errorStyle?.color != null) {
|
||||
if (_hasError && decoration.errorStyle?.color != null) {
|
||||
defaultTextStyle = defaultTextStyle.copyWith(color: decoration.errorStyle?.color);
|
||||
}
|
||||
defaultTextStyle = defaultTextStyle.merge(decoration.floatingLabelStyle ?? decoration.labelStyle);
|
||||
@@ -2137,7 +2138,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
if (!decoration.enabled) MaterialState.disabled,
|
||||
if (isFocused) MaterialState.focused,
|
||||
if (isHovering) MaterialState.hovered,
|
||||
if (decoration.errorText != null) MaterialState.error,
|
||||
if (_hasError) MaterialState.error,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2205,14 +2206,13 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
),
|
||||
);
|
||||
|
||||
final bool isError = decoration.errorText != null;
|
||||
InputBorder? border;
|
||||
if (!decoration.enabled) {
|
||||
border = isError ? decoration.errorBorder : decoration.disabledBorder;
|
||||
border = _hasError ? decoration.errorBorder : decoration.disabledBorder;
|
||||
} else if (isFocused) {
|
||||
border = isError ? decoration.focusedErrorBorder : decoration.focusedBorder;
|
||||
border = _hasError ? decoration.focusedErrorBorder : decoration.focusedBorder;
|
||||
} else {
|
||||
border = isError ? decoration.errorBorder : decoration.enabledBorder;
|
||||
border = _hasError ? decoration.errorBorder : decoration.enabledBorder;
|
||||
}
|
||||
border ??= _getDefaultBorder(themeData, defaults);
|
||||
|
||||
|
||||
@@ -945,7 +945,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
|
||||
|
||||
bool get _hasIntrinsicError => widget.maxLength != null && widget.maxLength! > 0 && _effectiveController.value.text.characters.length > widget.maxLength!;
|
||||
|
||||
bool get _hasError => widget.decoration?.errorText != null || _hasIntrinsicError;
|
||||
bool get _hasError => widget.decoration?.errorText != null || widget.decoration?.error != null || _hasIntrinsicError;
|
||||
|
||||
Color get _errorColor => widget.decoration?.errorStyle?.color ?? Theme.of(context).colorScheme.error;
|
||||
|
||||
|
||||
@@ -1668,6 +1668,105 @@ void runAllTests({ required bool useMaterial3 }) {
|
||||
expect(find.text('errorText'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('InputDecoration shows error border for errorText and error widget', (WidgetTester tester) async {
|
||||
const InputBorder errorBorder = OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.red, width: 1.5),
|
||||
);
|
||||
const InputBorder focusedErrorBorder = OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.teal, width: 5.0),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
useMaterial3: useMaterial3,
|
||||
isFocused: true,
|
||||
decoration: const InputDecoration(
|
||||
errorText: 'error',
|
||||
// enabled: true (default)
|
||||
errorBorder: errorBorder,
|
||||
focusedErrorBorder: focusedErrorBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(); // Border changes are animated.
|
||||
expect(getBorder(tester), focusedErrorBorder);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
useMaterial3: useMaterial3,
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
errorText: 'error',
|
||||
// enabled: true (default)
|
||||
errorBorder: errorBorder,
|
||||
focusedErrorBorder: focusedErrorBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(); // Border changes are animated.
|
||||
expect(getBorder(tester), errorBorder);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
useMaterial3: useMaterial3,
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
errorText: 'error',
|
||||
enabled: false,
|
||||
errorBorder: errorBorder,
|
||||
focusedErrorBorder: focusedErrorBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(); // Border changes are animated.
|
||||
expect(getBorder(tester), errorBorder);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
useMaterial3: useMaterial3,
|
||||
isFocused: true,
|
||||
decoration: const InputDecoration(
|
||||
error: Text('error'),
|
||||
// enabled: true (default)
|
||||
errorBorder: errorBorder,
|
||||
focusedErrorBorder: focusedErrorBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(); // Border changes are animated.
|
||||
expect(getBorder(tester), focusedErrorBorder);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
useMaterial3: useMaterial3,
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
error: Text('error'),
|
||||
// enabled: true (default)
|
||||
errorBorder: errorBorder,
|
||||
focusedErrorBorder: focusedErrorBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(); // Border changes are animated.
|
||||
expect(getBorder(tester), errorBorder);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
useMaterial3: useMaterial3,
|
||||
// isFocused: false (default)
|
||||
decoration: const InputDecoration(
|
||||
error: Text('error'),
|
||||
enabled: false,
|
||||
errorBorder: errorBorder,
|
||||
focusedErrorBorder: focusedErrorBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(); // Border changes are animated.
|
||||
expect(getBorder(tester), errorBorder);
|
||||
});
|
||||
|
||||
testWidgetsWithLeakTracking('InputDecorator shows error widget', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
buildInputDecorator(
|
||||
|
||||
@@ -787,6 +787,42 @@ void main() {
|
||||
expect(state.widget.cursorColor, cursorColor);
|
||||
});
|
||||
|
||||
testWidgets('Use error cursor color when an InputDecoration with an errorText or error widget is provided', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Material(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
error: Text('error'),
|
||||
errorStyle: TextStyle(color: Colors.teal),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
expect(state.widget.cursorColor, Colors.teal);
|
||||
|
||||
await tester.pumpWidget(
|
||||
const MaterialApp(
|
||||
home: Material(
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
errorText: 'error',
|
||||
errorStyle: TextStyle(color: Colors.teal),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
expect(state.widget.cursorColor, Colors.teal);
|
||||
});
|
||||
|
||||
testWidgetsWithLeakTracking('sets cursorOpacityAnimates on EditableText correctly', (WidgetTester tester) async {
|
||||
|
||||
// True
|
||||
|
||||
Reference in New Issue
Block a user