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 {
this.onSubmitted,
this.inputFormatters,
this.enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
}) : assert(keyboardType != null),
......@@ -314,6 +317,18 @@ class TextField extends StatefulWidget {
/// [Decoration.enabled] property.
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.
///
/// This setting is only honored on iOS devices.
......@@ -542,7 +557,6 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
obscureText: widget.obscureText,
autocorrect: widget.autocorrect,
maxLines: widget.maxLines,
cursorColor: themeData.textSelectionColor,
selectionColor: themeData.textSelectionColor,
selectionControls: themeData.platform == TargetPlatform.iOS
? cupertinoTextSelectionControls
......@@ -553,6 +567,9 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
onSelectionChanged: _handleSelectionChanged,
inputFormatters: formatters,
rendererIgnoresPointer: true,
cursorWidth: widget.cursorWidth,
cursorRadius: widget.cursorRadius,
cursorColor: widget.cursorColor ?? Theme.of(context).cursorColor,
scrollPadding: widget.scrollPadding,
keyboardAppearance: keyboardAppearance,
),
......@@ -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 {
ButtonThemeData buttonTheme,
Color secondaryHeaderColor,
Color textSelectionColor,
Color cursorColor,
Color textSelectionHandleColor,
Color backgroundColor,
Color dialogBackgroundColor,
......@@ -172,6 +173,8 @@ class ThemeData extends Diagnosticable {
// Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess.
secondaryHeaderColor ??= isDark ? Colors.grey[700] : primarySwatch[50];
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];
backgroundColor ??= isDark ? Colors.grey[700] : primarySwatch[200];
dialogBackgroundColor ??= isDark ? Colors.grey[800] : Colors.white;
......@@ -230,6 +233,7 @@ class ThemeData extends Diagnosticable {
buttonTheme: buttonTheme,
secondaryHeaderColor: secondaryHeaderColor,
textSelectionColor: textSelectionColor,
cursorColor: cursorColor,
textSelectionHandleColor: textSelectionHandleColor,
backgroundColor: backgroundColor,
dialogBackgroundColor: dialogBackgroundColor,
......@@ -279,6 +283,7 @@ class ThemeData extends Diagnosticable {
@required this.buttonTheme,
@required this.secondaryHeaderColor,
@required this.textSelectionColor,
@required this.cursorColor,
@required this.textSelectionHandleColor,
@required this.backgroundColor,
@required this.dialogBackgroundColor,
......@@ -319,6 +324,7 @@ class ThemeData extends Diagnosticable {
assert(buttonTheme != null),
assert(secondaryHeaderColor != null),
assert(textSelectionColor != null),
assert(cursorColor != null),
assert(textSelectionHandleColor != null),
assert(backgroundColor != null),
assert(dialogBackgroundColor != null),
......@@ -465,6 +471,9 @@ class ThemeData extends Diagnosticable {
/// The color of text selections in text fields, such as [TextField].
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.
final Color textSelectionHandleColor;
......@@ -551,6 +560,7 @@ class ThemeData extends Diagnosticable {
ButtonThemeData buttonTheme,
Color secondaryHeaderColor,
Color textSelectionColor,
Color cursorColor,
Color textSelectionHandleColor,
Color backgroundColor,
Color dialogBackgroundColor,
......@@ -593,6 +603,7 @@ class ThemeData extends Diagnosticable {
buttonTheme: buttonTheme ?? this.buttonTheme,
secondaryHeaderColor: secondaryHeaderColor ?? this.secondaryHeaderColor,
textSelectionColor: textSelectionColor ?? this.textSelectionColor,
cursorColor: cursorColor ?? this.cursorColor,
textSelectionHandleColor: textSelectionHandleColor ?? this.textSelectionHandleColor,
backgroundColor: backgroundColor ?? this.backgroundColor,
dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor,
......@@ -719,6 +730,7 @@ class ThemeData extends Diagnosticable {
buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme,
secondaryHeaderColor: Color.lerp(a.secondaryHeaderColor, b.secondaryHeaderColor, 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),
backgroundColor: Color.lerp(a.backgroundColor, b.backgroundColor, t),
dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t),
......@@ -766,6 +778,7 @@ class ThemeData extends Diagnosticable {
(otherData.buttonTheme == buttonTheme) &&
(otherData.secondaryHeaderColor == secondaryHeaderColor) &&
(otherData.textSelectionColor == textSelectionColor) &&
(otherData.cursorColor == cursorColor) &&
(otherData.textSelectionHandleColor == textSelectionHandleColor) &&
(otherData.backgroundColor == backgroundColor) &&
(otherData.dialogBackgroundColor == dialogBackgroundColor) &&
......@@ -828,7 +841,8 @@ class ThemeData extends Diagnosticable {
sliderTheme,
chipTheme,
platform,
materialTapTargetSize
materialTapTargetSize,
cursorColor
),
);
}
......@@ -856,6 +870,7 @@ class ThemeData extends Diagnosticable {
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>('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>('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor));
properties.add(new DiagnosticsProperty<Color>('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor));
......
......@@ -209,7 +209,7 @@ class EditableText extends StatefulWidget {
this.onSelectionChanged,
List<TextInputFormatter> inputFormatters,
this.rendererIgnoresPointer = false,
this.cursorWidth = 1.0,
this.cursorWidth = 2.0,
this.cursorRadius,
this.scrollPadding = const EdgeInsets.all(20.0),
this.keyboardAppearance = Brightness.light,
......@@ -374,7 +374,7 @@ class EditableText extends StatefulWidget {
/// How thick the cursor will be.
///
/// Defaults to 1.0
/// Defaults to 2.0
final double cursorWidth;
/// How rounded the corners of the cursor should be.
......
......@@ -270,6 +270,72 @@ void main() {
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 {
await tester.pumpWidget(
overlay(
......@@ -1228,7 +1294,7 @@ void main() {
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.pump();
......@@ -1237,7 +1303,7 @@ void main() {
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 {
......@@ -1260,7 +1326,7 @@ void main() {
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.pump();
......@@ -1269,7 +1335,7 @@ void main() {
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 {
......
......@@ -79,7 +79,7 @@ void main() {
expect(editableText.maxLines, equals(1));
expect(editableText.obscureText, isFalse);
expect(editableText.autocorrect, isTrue);
expect(editableText.cursorWidth, 1.0);
expect(editableText.cursorWidth, 2.0);
});
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