Unverified Commit 834fb7b9 authored by sandrasandeep's avatar sandrasandeep Committed by GitHub

Add cursorWidth and cursorRadius to TextField (Material) cursor (#19317)

* fixed segmented control golden test

* fixed segmented control golden test

* made the comments more explanatory

* changed comment, wrapped tests with if statements so they only run on MacOS

* changed formatting

* added width and radius fields to TextField; to do: tests and Material defaults

* weak warnings

* added tests

* added default cursor width; changed default EditableText width

* only run golden file tests on linux

* changed goldens version

* actually changed goldens.version

* style changes

* small fixes

* added default material cursor color

* changed goldens.version

* changed goldens version again

* changed goldens.version again (3)

* added todo

* deleted whitespace
parent f44f625c
4948d5d7c0e07d092bd38511e5e159007425ec48 6b9d7eb922429f3f86afa4a6ed316e56640ce587
...@@ -117,6 +117,9 @@ class TextField extends StatefulWidget { ...@@ -117,6 +117,9 @@ class TextField extends StatefulWidget {
this.onSubmitted, this.onSubmitted,
this.inputFormatters, this.inputFormatters,
this.enabled, this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.keyboardAppearance, this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
}) : assert(keyboardType != null), }) : assert(keyboardType != null),
...@@ -314,6 +317,18 @@ class TextField extends StatefulWidget { ...@@ -314,6 +317,18 @@ class TextField extends StatefulWidget {
/// [Decoration.enabled] property. /// [Decoration.enabled] property.
final bool enabled; final bool enabled;
/// How thick the cursor will be.
///
/// Defaults to 2.0.
final double cursorWidth;
/// How rounded the corners of the cursor should be.
/// By default, the cursor has a null Radius
final Radius cursorRadius;
/// The color to use when painting the cursor.
final Color cursorColor;
/// The appearance of the keyboard. /// The appearance of the keyboard.
/// ///
/// This setting is only honored on iOS devices. /// This setting is only honored on iOS devices.
...@@ -542,7 +557,6 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -542,7 +557,6 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
obscureText: widget.obscureText, obscureText: widget.obscureText,
autocorrect: widget.autocorrect, autocorrect: widget.autocorrect,
maxLines: widget.maxLines, maxLines: widget.maxLines,
cursorColor: themeData.textSelectionColor,
selectionColor: themeData.textSelectionColor, selectionColor: themeData.textSelectionColor,
selectionControls: themeData.platform == TargetPlatform.iOS selectionControls: themeData.platform == TargetPlatform.iOS
? cupertinoTextSelectionControls ? cupertinoTextSelectionControls
...@@ -553,6 +567,9 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -553,6 +567,9 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
onSelectionChanged: _handleSelectionChanged, onSelectionChanged: _handleSelectionChanged,
inputFormatters: formatters, inputFormatters: formatters,
rendererIgnoresPointer: true, rendererIgnoresPointer: true,
cursorWidth: widget.cursorWidth,
cursorRadius: widget.cursorRadius,
cursorColor: widget.cursorColor ?? Theme.of(context).cursorColor,
scrollPadding: widget.scrollPadding, scrollPadding: widget.scrollPadding,
keyboardAppearance: keyboardAppearance, keyboardAppearance: keyboardAppearance,
), ),
...@@ -595,4 +612,4 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi ...@@ -595,4 +612,4 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
), ),
); );
} }
} }
\ No newline at end of file
...@@ -123,6 +123,7 @@ class ThemeData extends Diagnosticable { ...@@ -123,6 +123,7 @@ class ThemeData extends Diagnosticable {
ButtonThemeData buttonTheme, ButtonThemeData buttonTheme,
Color secondaryHeaderColor, Color secondaryHeaderColor,
Color textSelectionColor, Color textSelectionColor,
Color cursorColor,
Color textSelectionHandleColor, Color textSelectionHandleColor,
Color backgroundColor, Color backgroundColor,
Color dialogBackgroundColor, Color dialogBackgroundColor,
...@@ -172,6 +173,8 @@ class ThemeData extends Diagnosticable { ...@@ -172,6 +173,8 @@ class ThemeData extends Diagnosticable {
// Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess. // Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess.
secondaryHeaderColor ??= isDark ? Colors.grey[700] : primarySwatch[50]; secondaryHeaderColor ??= isDark ? Colors.grey[700] : primarySwatch[50];
textSelectionColor ??= isDark ? accentColor : primarySwatch[200]; textSelectionColor ??= isDark ? accentColor : primarySwatch[200];
// todo (sandrasandeep): change to color provided by Material Design team
cursorColor = const Color.fromRGBO(66, 133, 244, 1.0);
textSelectionHandleColor ??= isDark ? Colors.tealAccent[400] : primarySwatch[300]; textSelectionHandleColor ??= isDark ? Colors.tealAccent[400] : primarySwatch[300];
backgroundColor ??= isDark ? Colors.grey[700] : primarySwatch[200]; backgroundColor ??= isDark ? Colors.grey[700] : primarySwatch[200];
dialogBackgroundColor ??= isDark ? Colors.grey[800] : Colors.white; dialogBackgroundColor ??= isDark ? Colors.grey[800] : Colors.white;
...@@ -230,6 +233,7 @@ class ThemeData extends Diagnosticable { ...@@ -230,6 +233,7 @@ class ThemeData extends Diagnosticable {
buttonTheme: buttonTheme, buttonTheme: buttonTheme,
secondaryHeaderColor: secondaryHeaderColor, secondaryHeaderColor: secondaryHeaderColor,
textSelectionColor: textSelectionColor, textSelectionColor: textSelectionColor,
cursorColor: cursorColor,
textSelectionHandleColor: textSelectionHandleColor, textSelectionHandleColor: textSelectionHandleColor,
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
dialogBackgroundColor: dialogBackgroundColor, dialogBackgroundColor: dialogBackgroundColor,
...@@ -279,6 +283,7 @@ class ThemeData extends Diagnosticable { ...@@ -279,6 +283,7 @@ class ThemeData extends Diagnosticable {
@required this.buttonTheme, @required this.buttonTheme,
@required this.secondaryHeaderColor, @required this.secondaryHeaderColor,
@required this.textSelectionColor, @required this.textSelectionColor,
@required this.cursorColor,
@required this.textSelectionHandleColor, @required this.textSelectionHandleColor,
@required this.backgroundColor, @required this.backgroundColor,
@required this.dialogBackgroundColor, @required this.dialogBackgroundColor,
...@@ -319,6 +324,7 @@ class ThemeData extends Diagnosticable { ...@@ -319,6 +324,7 @@ class ThemeData extends Diagnosticable {
assert(buttonTheme != null), assert(buttonTheme != null),
assert(secondaryHeaderColor != null), assert(secondaryHeaderColor != null),
assert(textSelectionColor != null), assert(textSelectionColor != null),
assert(cursorColor != null),
assert(textSelectionHandleColor != null), assert(textSelectionHandleColor != null),
assert(backgroundColor != null), assert(backgroundColor != null),
assert(dialogBackgroundColor != null), assert(dialogBackgroundColor != null),
...@@ -465,6 +471,9 @@ class ThemeData extends Diagnosticable { ...@@ -465,6 +471,9 @@ class ThemeData extends Diagnosticable {
/// The color of text selections in text fields, such as [TextField]. /// The color of text selections in text fields, such as [TextField].
final Color textSelectionColor; final Color textSelectionColor;
/// The color of cursors in Material-style text fields, such as [TextField].
final Color cursorColor;
/// The color of the handles used to adjust what part of the text is currently selected. /// The color of the handles used to adjust what part of the text is currently selected.
final Color textSelectionHandleColor; final Color textSelectionHandleColor;
...@@ -551,6 +560,7 @@ class ThemeData extends Diagnosticable { ...@@ -551,6 +560,7 @@ class ThemeData extends Diagnosticable {
ButtonThemeData buttonTheme, ButtonThemeData buttonTheme,
Color secondaryHeaderColor, Color secondaryHeaderColor,
Color textSelectionColor, Color textSelectionColor,
Color cursorColor,
Color textSelectionHandleColor, Color textSelectionHandleColor,
Color backgroundColor, Color backgroundColor,
Color dialogBackgroundColor, Color dialogBackgroundColor,
...@@ -593,6 +603,7 @@ class ThemeData extends Diagnosticable { ...@@ -593,6 +603,7 @@ class ThemeData extends Diagnosticable {
buttonTheme: buttonTheme ?? this.buttonTheme, buttonTheme: buttonTheme ?? this.buttonTheme,
secondaryHeaderColor: secondaryHeaderColor ?? this.secondaryHeaderColor, secondaryHeaderColor: secondaryHeaderColor ?? this.secondaryHeaderColor,
textSelectionColor: textSelectionColor ?? this.textSelectionColor, textSelectionColor: textSelectionColor ?? this.textSelectionColor,
cursorColor: cursorColor ?? this.cursorColor,
textSelectionHandleColor: textSelectionHandleColor ?? this.textSelectionHandleColor, textSelectionHandleColor: textSelectionHandleColor ?? this.textSelectionHandleColor,
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor, dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,
...@@ -719,6 +730,7 @@ class ThemeData extends Diagnosticable { ...@@ -719,6 +730,7 @@ class ThemeData extends Diagnosticable {
buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme, buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme,
secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t), secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, t),
textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t), textSelectionColor: Color.lerp(a.textSelectionColor, b.textSelectionColor, t),
cursorColor: Color.lerp(a.cursorColor, b.cursorColor, t),
textSelectionHandleColor: Color.lerp(a.textSelectionHandleColor, b.textSelectionHandleColor, t), textSelectionHandleColor: Color.lerp(a.textSelectionHandleColor, b.textSelectionHandleColor, t),
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t), backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t), dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t),
...@@ -766,6 +778,7 @@ class ThemeData extends Diagnosticable { ...@@ -766,6 +778,7 @@ class ThemeData extends Diagnosticable {
(otherData.buttonTheme == buttonTheme) && (otherData.buttonTheme == buttonTheme) &&
(otherData.secondaryHeaderColor == secondaryHeaderColor) && (otherData.secondaryHeaderColor == secondaryHeaderColor) &&
(otherData.textSelectionColor == textSelectionColor) && (otherData.textSelectionColor == textSelectionColor) &&
(otherData.cursorColor == cursorColor) &&
(otherData.textSelectionHandleColor == textSelectionHandleColor) && (otherData.textSelectionHandleColor == textSelectionHandleColor) &&
(otherData.backgroundColor == backgroundColor) && (otherData.backgroundColor == backgroundColor) &&
(otherData.dialogBackgroundColor == dialogBackgroundColor) && (otherData.dialogBackgroundColor == dialogBackgroundColor) &&
...@@ -828,7 +841,8 @@ class ThemeData extends Diagnosticable { ...@@ -828,7 +841,8 @@ class ThemeData extends Diagnosticable {
sliderTheme, sliderTheme,
chipTheme, chipTheme,
platform, platform,
materialTapTargetSize materialTapTargetSize,
cursorColor
), ),
); );
} }
...@@ -856,6 +870,7 @@ class ThemeData extends Diagnosticable { ...@@ -856,6 +870,7 @@ class ThemeData extends Diagnosticable {
properties.add(new DiagnosticsProperty<Color>('buttonColor', buttonColor, defaultValue: defaultData.buttonColor)); properties.add(new DiagnosticsProperty<Color>('buttonColor', buttonColor, defaultValue: defaultData.buttonColor));
properties.add(new DiagnosticsProperty<Color>('secondaryHeaderColor', secondaryHeaderColor, defaultValue: defaultData.secondaryHeaderColor)); properties.add(new DiagnosticsProperty<Color>('secondaryHeaderColor', secondaryHeaderColor, defaultValue: defaultData.secondaryHeaderColor));
properties.add(new DiagnosticsProperty<Color>('textSelectionColor', textSelectionColor, defaultValue: defaultData.textSelectionColor)); properties.add(new DiagnosticsProperty<Color>('textSelectionColor', textSelectionColor, defaultValue: defaultData.textSelectionColor));
properties.add(new DiagnosticsProperty<Color>('cursorColor', cursorColor, defaultValue: defaultData.cursorColor));
properties.add(new DiagnosticsProperty<Color>('textSelectionHandleColor', textSelectionHandleColor, defaultValue: defaultData.textSelectionHandleColor)); properties.add(new DiagnosticsProperty<Color>('textSelectionHandleColor', textSelectionHandleColor, defaultValue: defaultData.textSelectionHandleColor));
properties.add(new DiagnosticsProperty<Color>('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor)); properties.add(new DiagnosticsProperty<Color>('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor));
properties.add(new DiagnosticsProperty<Color>('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor)); properties.add(new DiagnosticsProperty<Color>('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor));
......
...@@ -209,7 +209,7 @@ class EditableText extends StatefulWidget { ...@@ -209,7 +209,7 @@ class EditableText extends StatefulWidget {
this.onSelectionChanged, this.onSelectionChanged,
List<TextInputFormatter> inputFormatters, List<TextInputFormatter> inputFormatters,
this.rendererIgnoresPointer = false, this.rendererIgnoresPointer = false,
this.cursorWidth = 1.0, this.cursorWidth = 2.0,
this.cursorRadius, this.cursorRadius,
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
this.keyboardAppearance = Brightness.light, this.keyboardAppearance = Brightness.light,
...@@ -374,7 +374,7 @@ class EditableText extends StatefulWidget { ...@@ -374,7 +374,7 @@ class EditableText extends StatefulWidget {
/// How thick the cursor will be. /// How thick the cursor will be.
/// ///
/// Defaults to 1.0 /// Defaults to 2.0
final double cursorWidth; final double cursorWidth;
/// How rounded the corners of the cursor should be. /// How rounded the corners of the cursor should be.
......
...@@ -270,6 +270,72 @@ void main() { ...@@ -270,6 +270,72 @@ void main() {
await checkCursorToggle(); await checkCursorToggle();
}); });
testWidgets('cursor has expected defaults', (WidgetTester tester) async {
await tester.pumpWidget(
overlay(
child: const TextField(
),
)
);
final TextField textField = tester.firstWidget(find.byType(TextField));
expect(textField.cursorWidth, 2.0);
expect(textField.cursorRadius, null);
});
testWidgets('cursor has expected radius value', (WidgetTester tester) async {
await tester.pumpWidget(
overlay(
child: const TextField(
cursorRadius: Radius.circular(3.0),
),
)
);
final TextField textField = tester.firstWidget(find.byType(TextField));
expect(textField.cursorWidth, 2.0);
expect(textField.cursorRadius, const Radius.circular(3.0));
});
testWidgets('cursor layout has correct width', (WidgetTester tester) async {
await tester.pumpWidget(
overlay(
child: const RepaintBoundary(
child: TextField(
cursorWidth: 15.0,
),
),
)
);
await tester.enterText(find.byType(TextField), ' ');
await skipPastScrollingAnimation(tester);
await expectLater(
find.byType(TextField),
matchesGoldenFile('text_field_test.0.0.png'),
);
}, skip: !Platform.isLinux);
testWidgets('cursor layout has correct radius', (WidgetTester tester) async {
await tester.pumpWidget(
overlay(
child: const RepaintBoundary(
child: TextField(
cursorWidth: 15.0,
cursorRadius: Radius.circular(3.0),
),
),
)
);
await tester.enterText(find.byType(TextField), ' ');
await skipPastScrollingAnimation(tester);
await expectLater(
find.byType(TextField),
matchesGoldenFile('text_field_test.1.0.png'),
);
}, skip: !Platform.isLinux);
testWidgets('obscureText control test', (WidgetTester tester) async { testWidgets('obscureText control test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
...@@ -1228,7 +1294,7 @@ void main() { ...@@ -1228,7 +1294,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft,
); );
expect(topLeft.dx, equals(399)); expect(topLeft.dx, equals(398.5));
await tester.enterText(find.byType(TextField), 'abcd'); await tester.enterText(find.byType(TextField), 'abcd');
await tester.pump(); await tester.pump();
...@@ -1237,7 +1303,7 @@ void main() { ...@@ -1237,7 +1303,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft,
); );
expect(topLeft.dx, equals(399)); expect(topLeft.dx, equals(398.5));
}); });
testWidgets('Can align to center within center', (WidgetTester tester) async { testWidgets('Can align to center within center', (WidgetTester tester) async {
...@@ -1260,7 +1326,7 @@ void main() { ...@@ -1260,7 +1326,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft,
); );
expect(topLeft.dx, equals(399)); expect(topLeft.dx, equals(398.5));
await tester.enterText(find.byType(TextField), 'abcd'); await tester.enterText(find.byType(TextField), 'abcd');
await tester.pump(); await tester.pump();
...@@ -1269,7 +1335,7 @@ void main() { ...@@ -1269,7 +1335,7 @@ void main() {
editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft, editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft,
); );
expect(topLeft.dx, equals(399)); expect(topLeft.dx, equals(398.5));
}); });
testWidgets('Controller can update server', (WidgetTester tester) async { testWidgets('Controller can update server', (WidgetTester tester) async {
......
...@@ -79,7 +79,7 @@ void main() { ...@@ -79,7 +79,7 @@ void main() {
expect(editableText.maxLines, equals(1)); expect(editableText.maxLines, equals(1));
expect(editableText.obscureText, isFalse); expect(editableText.obscureText, isFalse);
expect(editableText.autocorrect, isTrue); expect(editableText.autocorrect, isTrue);
expect(editableText.cursorWidth, 1.0); expect(editableText.cursorWidth, 2.0);
}); });
testWidgets('cursor has expected width and radius', testWidgets('cursor has expected width and radius',
......
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