Skip to content

Commit 7cd6fd4

Browse files
authored
TextField - allow to customize cursor color in error state (flutter#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 flutter#135580
1 parent c15ff68 commit 7cd6fd4

File tree

3 files changed

+67
-19
lines changed

3 files changed

+67
-19
lines changed

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ class TextField extends StatefulWidget {
288288
this.cursorRadius,
289289
this.cursorOpacityAnimates,
290290
this.cursorColor,
291+
this.cursorErrorColor,
291292
this.selectionHeightStyle = ui.BoxHeightStyle.tight,
292293
this.selectionWidthStyle = ui.BoxWidthStyle.tight,
293294
this.keyboardAppearance,
@@ -595,6 +596,13 @@ class TextField extends StatefulWidget {
595596
/// the value of [ColorScheme.primary] of [ThemeData.colorScheme].
596597
final Color? cursorColor;
597598

599+
/// The color of the cursor when the [InputDecorator] is showing an error.
600+
///
601+
/// If this is null it will default to [TextStyle.color] of
602+
/// [InputDecoration.errorStyle]. If that is null, it will use
603+
/// [ColorScheme.error] of [ThemeData.colorScheme].
604+
final Color? cursorErrorColor;
605+
598606
/// Controls how tall the selection highlight boxes are computed to be.
599607
///
600608
/// See [ui.BoxHeightStyle] for details on available styles.
@@ -893,6 +901,7 @@ class TextField extends StatefulWidget {
893901
properties.add(DiagnosticsProperty<Radius>('cursorRadius', cursorRadius, defaultValue: null));
894902
properties.add(DiagnosticsProperty<bool>('cursorOpacityAnimates', cursorOpacityAnimates, defaultValue: null));
895903
properties.add(ColorProperty('cursorColor', cursorColor, defaultValue: null));
904+
properties.add(ColorProperty('cursorErrorColor', cursorErrorColor, defaultValue: null));
896905
properties.add(DiagnosticsProperty<Brightness>('keyboardAppearance', keyboardAppearance, defaultValue: null));
897906
properties.add(DiagnosticsProperty<EdgeInsetsGeometry>('scrollPadding', scrollPadding, defaultValue: const EdgeInsets.all(20.0)));
898907
properties.add(FlagProperty('selectionEnabled', value: selectionEnabled, defaultValue: true, ifFalse: 'selection disabled'));
@@ -946,7 +955,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
946955

947956
bool get _hasError => widget.decoration?.errorText != null || widget.decoration?.error != null || _hasIntrinsicError;
948957

949-
Color get _errorColor => widget.decoration?.errorStyle?.color ?? Theme.of(context).colorScheme.error;
958+
Color get _errorColor => widget.cursorErrorColor ?? widget.decoration?.errorStyle?.color ?? Theme.of(context).colorScheme.error;
950959

951960
InputDecoration _getEffectiveDecoration() {
952961
final MaterialLocalizations localizations = MaterialLocalizations.of(context);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class TextFormField extends FormField<String> {
144144
double? cursorHeight,
145145
Radius? cursorRadius,
146146
Color? cursorColor,
147+
Color? cursorErrorColor,
147148
Brightness? keyboardAppearance,
148149
EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
149150
bool? enableInteractiveSelection,
@@ -236,6 +237,7 @@ class TextFormField extends FormField<String> {
236237
cursorHeight: cursorHeight,
237238
cursorRadius: cursorRadius,
238239
cursorColor: cursorColor,
240+
cursorErrorColor: cursorErrorColor,
239241
scrollPadding: scrollPadding,
240242
scrollPhysics: scrollPhysics,
241243
keyboardAppearance: keyboardAppearance,

packages/flutter/test/material/text_form_field_test.dart

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,27 +1481,64 @@ void main() {
14811481
});
14821482

14831483
testWidgetsWithLeakTracking('Error color for cursor while validating', (WidgetTester tester) async {
1484-
const Color errorColor = Color(0xff123456);
1485-
await tester.pumpWidget(MaterialApp(
1486-
theme: ThemeData(
1487-
colorScheme: const ColorScheme.light(error: errorColor),
1488-
),
1489-
home: Material(
1490-
child: Center(
1491-
child: TextFormField(
1492-
enabled: true,
1493-
autovalidateMode: AutovalidateMode.always,
1494-
validator: (String? value) {
1495-
return 'Please enter value';
1496-
},
1484+
const Color themeErrorColor = Color(0xff111111);
1485+
const Color errorStyleColor = Color(0xff777777);
1486+
const Color cursorErrorColor = Color(0xffbbbbbb);
1487+
1488+
Widget buildWidget({Color? errorStyleColor, Color? cursorErrorColor}) {
1489+
return MaterialApp(
1490+
theme: ThemeData(
1491+
colorScheme: const ColorScheme.light(error: themeErrorColor),
1492+
),
1493+
home: Material(
1494+
child: Center(
1495+
child: TextFormField(
1496+
enabled: true,
1497+
autovalidateMode: AutovalidateMode.always,
1498+
decoration: InputDecoration(
1499+
errorStyle: TextStyle(
1500+
color: errorStyleColor,
1501+
),
1502+
),
1503+
cursorErrorColor: cursorErrorColor,
1504+
validator: (String? value) {
1505+
return 'Please enter value';
1506+
},
1507+
),
14971508
),
14981509
),
1510+
);
1511+
}
1512+
1513+
Future<void> runTest(Widget widget, {required Color expectedColor}) async {
1514+
await tester.pumpWidget(widget);
1515+
await tester.enterText(find.byType(TextField), 'a');
1516+
final EditableText textField = tester.widget(
1517+
find.byType(EditableText).first,
1518+
);
1519+
await tester.pump();
1520+
expect(textField.cursorColor, expectedColor);
1521+
}
1522+
1523+
await runTest(
1524+
buildWidget(),
1525+
expectedColor: themeErrorColor,
1526+
);
1527+
await runTest(
1528+
buildWidget(errorStyleColor: errorStyleColor),
1529+
expectedColor: errorStyleColor,
1530+
);
1531+
await runTest(
1532+
buildWidget(cursorErrorColor: cursorErrorColor),
1533+
expectedColor: cursorErrorColor,
1534+
);
1535+
await runTest(
1536+
buildWidget(
1537+
errorStyleColor: errorStyleColor,
1538+
cursorErrorColor: cursorErrorColor,
14991539
),
1500-
));
1501-
await tester.enterText(find.byType(TextField), 'a');
1502-
final EditableText textField = tester.widget(find.byType(EditableText).first);
1503-
await tester.pump();
1504-
expect(textField.cursorColor, errorColor);
1540+
expectedColor: cursorErrorColor,
1541+
);
15051542
});
15061543

15071544
testWidgetsWithLeakTracking('TextFormField onChanged is called when the form is reset', (WidgetTester tester) async {

0 commit comments

Comments
 (0)