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
This commit is contained in:
parent
f44f625c06
commit
834fb7b9a8
@ -1 +1,2 @@
|
|||||||
4948d5d7c0e07d092bd38511e5e159007425ec48
|
6b9d7eb922429f3f86afa4a6ed316e56640ce587
|
||||||
|
|
||||||
|
@ -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 {
|
|||||||
/// [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
|
|||||||
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
|
|||||||
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,
|
||||||
),
|
),
|
||||||
|
@ -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 {
|
|||||||
// 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 {
|
|||||||
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 {
|
|||||||
@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 {
|
|||||||
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 {
|
|||||||
/// 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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
(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 {
|
|||||||
sliderTheme,
|
sliderTheme,
|
||||||
chipTheme,
|
chipTheme,
|
||||||
platform,
|
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>('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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
/// 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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
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',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user