Unverified Commit 7cd6fd43 authored by lirantzairi's avatar lirantzairi Committed by GitHub

TextField - allow to customize cursor color in error state (#136121)

The color of the TextField's cursor in error state is the same as the error text color by default. However we should be allowed to customize it

Fixes #135580
parent c15ff682
...@@ -288,6 +288,7 @@ class TextField extends StatefulWidget { ...@@ -288,6 +288,7 @@ class TextField extends StatefulWidget {
this.cursorRadius, this.cursorRadius,
this.cursorOpacityAnimates, this.cursorOpacityAnimates,
this.cursorColor, this.cursorColor,
this.cursorErrorColor,
this.selectionHeightStyle = ui.BoxHeightStyle.tight, this.selectionHeightStyle = ui.BoxHeightStyle.tight,
this.selectionWidthStyle = ui.BoxWidthStyle.tight, this.selectionWidthStyle = ui.BoxWidthStyle.tight,
this.keyboardAppearance, this.keyboardAppearance,
...@@ -595,6 +596,13 @@ class TextField extends StatefulWidget { ...@@ -595,6 +596,13 @@ class TextField extends StatefulWidget {
/// the value of [ColorScheme.primary] of [ThemeData.colorScheme]. /// the value of [ColorScheme.primary] of [ThemeData.colorScheme].
final Color? cursorColor; final Color? cursorColor;
/// The color of the cursor when the [InputDecorator] is showing an error.
///
/// If this is null it will default to [TextStyle.color] of
/// [InputDecoration.errorStyle]. If that is null, it will use
/// [ColorScheme.error] of [ThemeData.colorScheme].
final Color? cursorErrorColor;
/// Controls how tall the selection highlight boxes are computed to be. /// Controls how tall the selection highlight boxes are computed to be.
/// ///
/// See [ui.BoxHeightStyle] for details on available styles. /// See [ui.BoxHeightStyle] for details on available styles.
...@@ -893,6 +901,7 @@ class TextField extends StatefulWidget { ...@@ -893,6 +901,7 @@ class TextField extends StatefulWidget {
properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null)); properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('cursorOpacityAnimates', cursorOpacityAnimates, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('cursorOpacityAnimates', cursorOpacityAnimates, defaultValue: null));
properties.add(ColorProperty('cursorColor', cursorColor, defaultValue: null)); properties.add(ColorProperty('cursorColor', cursorColor, defaultValue: null));
properties.add(ColorProperty('cursorErrorColor', cursorErrorColor, defaultValue: null));
properties.add(DiagnosticsProperty<Brightness>('keyboardAppearance', keyboardAppearance, defaultValue: null)); properties.add(DiagnosticsProperty<Brightness>('keyboardAppearance', keyboardAppearance, defaultValue: null));
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('scrollPadding', scrollPadding, defaultValue: const EdgeInsets.all(20.0))); properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('scrollPadding', scrollPadding, defaultValue: const EdgeInsets.all(20.0)));
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled')); properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
...@@ -946,7 +955,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements ...@@ -946,7 +955,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
bool get _hasError => widget.decoration?.errorText != null || widget.decoration?.error != 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; Color get _errorColor => widget.cursorErrorColor ?? widget.decoration?.errorStyle?.color ?? Theme.of(context).colorScheme.error;
InputDecoration _getEffectiveDecoration() { InputDecoration _getEffectiveDecoration() {
final MaterialLocalizations localizations = MaterialLocalizations.of(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context);
......
...@@ -144,6 +144,7 @@ class TextFormField extends FormField<String> { ...@@ -144,6 +144,7 @@ class TextFormField extends FormField<String> {
double? cursorHeight, double? cursorHeight,
Radius? cursorRadius, Radius? cursorRadius,
Color? cursorColor, Color? cursorColor,
Color? cursorErrorColor,
Brightness? keyboardAppearance, Brightness? keyboardAppearance,
EdgeInsets scrollPadding = const EdgeInsets.all(20.0), EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
bool? enableInteractiveSelection, bool? enableInteractiveSelection,
...@@ -236,6 +237,7 @@ class TextFormField extends FormField<String> { ...@@ -236,6 +237,7 @@ class TextFormField extends FormField<String> {
cursorHeight: cursorHeight, cursorHeight: cursorHeight,
cursorRadius: cursorRadius, cursorRadius: cursorRadius,
cursorColor: cursorColor, cursorColor: cursorColor,
cursorErrorColor: cursorErrorColor,
scrollPadding: scrollPadding, scrollPadding: scrollPadding,
scrollPhysics: scrollPhysics, scrollPhysics: scrollPhysics,
keyboardAppearance: keyboardAppearance, keyboardAppearance: keyboardAppearance,
......
...@@ -1481,27 +1481,64 @@ void main() { ...@@ -1481,27 +1481,64 @@ void main() {
}); });
testWidgetsWithLeakTracking('Error color for cursor while validating', (WidgetTester tester) async { testWidgetsWithLeakTracking('Error color for cursor while validating', (WidgetTester tester) async {
const Color errorColor = Color(0xff123456); const Color themeErrorColor = Color(0xff111111);
await tester.pumpWidget(MaterialApp( const Color errorStyleColor = Color(0xff777777);
theme: ThemeData( const Color cursorErrorColor = Color(0xffbbbbbb);
colorScheme: const ColorScheme.light(error: errorColor),
), Widget buildWidget({Color? errorStyleColor, Color? cursorErrorColor}) {
home: Material( return MaterialApp(
child: Center( theme: ThemeData(
child: TextFormField( colorScheme: const ColorScheme.light(error: themeErrorColor),
enabled: true, ),
autovalidateMode: AutovalidateMode.always, home: Material(
validator: (String? value) { child: Center(
return 'Please enter value'; child: TextFormField(
}, enabled: true,
autovalidateMode: AutovalidateMode.always,
decoration: InputDecoration(
errorStyle: TextStyle(
color: errorStyleColor,
),
),
cursorErrorColor: cursorErrorColor,
validator: (String? value) {
return 'Please enter value';
},
),
), ),
), ),
);
}
Future<void> runTest(Widget widget, {required Color expectedColor}) async {
await tester.pumpWidget(widget);
await tester.enterText(find.byType(TextField), 'a');
final EditableText textField = tester.widget(
find.byType(EditableText).first,
);
await tester.pump();
expect(textField.cursorColor, expectedColor);
}
await runTest(
buildWidget(),
expectedColor: themeErrorColor,
);
await runTest(
buildWidget(errorStyleColor: errorStyleColor),
expectedColor: errorStyleColor,
);
await runTest(
buildWidget(cursorErrorColor: cursorErrorColor),
expectedColor: cursorErrorColor,
);
await runTest(
buildWidget(
errorStyleColor: errorStyleColor,
cursorErrorColor: cursorErrorColor,
), ),
)); expectedColor: cursorErrorColor,
await tester.enterText(find.byType(TextField), 'a'); );
final EditableText textField = tester.widget(find.byType(EditableText).first);
await tester.pump();
expect(textField.cursorColor, errorColor);
}); });
testWidgetsWithLeakTracking('TextFormField onChanged is called when the form is reset', (WidgetTester tester) async { testWidgetsWithLeakTracking('TextFormField onChanged is called when the form is reset', (WidgetTester tester) async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment