diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version index cf8c5a576c..a1f2caf9fe 100644 --- a/bin/internal/goldens.version +++ b/bin/internal/goldens.version @@ -1 +1,2 @@ -4948d5d7c0e07d092bd38511e5e159007425ec48 +6b9d7eb922429f3f86afa4a6ed316e56640ce587 + diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 9edfced28c..656ba44fbc 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -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 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 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 with AutomaticKeepAliveClientMixi ), ); } -} +} \ No newline at end of file diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 6f4fce0184..1c9160167d 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -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('buttonColor', buttonColor, defaultValue: defaultData.buttonColor)); properties.add(new DiagnosticsProperty('secondaryHeaderColor', secondaryHeaderColor, defaultValue: defaultData.secondaryHeaderColor)); properties.add(new DiagnosticsProperty('textSelectionColor', textSelectionColor, defaultValue: defaultData.textSelectionColor)); + properties.add(new DiagnosticsProperty('cursorColor', cursorColor, defaultValue: defaultData.cursorColor)); properties.add(new DiagnosticsProperty('textSelectionHandleColor', textSelectionHandleColor, defaultValue: defaultData.textSelectionHandleColor)); properties.add(new DiagnosticsProperty('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor)); properties.add(new DiagnosticsProperty('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor)); diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 9f65aeeb8a..ada90e8e0b 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -209,7 +209,7 @@ class EditableText extends StatefulWidget { this.onSelectionChanged, List 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. diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 051501179a..c97207647c 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -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 { diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 3f9126ff7e..ac241bd0c5 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -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',