diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index 40903409c2..4c3f4d9f54 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -16,7 +16,6 @@ import 'viewport_offset.dart'; const double _kCaretGap = 1.0; // pixels const double _kCaretHeightOffset = 2.0; // pixels -const double _kCaretWidth = 1.0; // pixels /// Signature for the callback that reports when the user changes the selection /// (including the cursor location). @@ -134,6 +133,8 @@ class RenderEditable extends RenderBox { this.ignorePointer = false, bool obscureText = false, Locale locale, + double cursorWidth = 1.0, + Radius cursorRadius, }) : assert(textAlign != null), assert(textDirection != null, 'RenderEditable created without a textDirection.'), assert(maxLines == null || maxLines > 0), @@ -155,6 +156,8 @@ class RenderEditable extends RenderBox { _selectionColor = selectionColor, _selection = selection, _offset = offset, + _cursorWidth = cursorWidth, + _cursorRadius = cursorRadius, _obscureText = obscureText { assert(_showCursor != null); assert(!_showCursor.value || cursorColor != null); @@ -382,6 +385,26 @@ class RenderEditable extends RenderBox { markNeedsLayout(); } + /// How thick the cursor will be. + double get cursorWidth => _cursorWidth; + double _cursorWidth = 1.0; + set cursorWidth(double value) { + if (_cursorWidth == value) + return; + _cursorWidth = value; + markNeedsLayout(); + } + + /// How rounded the corners of the cursor should be. + Radius get cursorRadius => _cursorRadius; + Radius _cursorRadius; + set cursorRadius(Radius value) { + if (_cursorRadius == value) + return; + _cursorRadius = value; + markNeedsPaint(); + } + @override void describeSemanticsConfiguration(SemanticsConfiguration config) { super.describeSemanticsConfiguration(config); @@ -546,7 +569,7 @@ class RenderEditable extends RenderBox { _layoutText(constraints.maxWidth); final Offset caretOffset = _textPainter.getOffsetForCaret(caretPosition, _caretPrototype); // This rect is the same as _caretPrototype but without the vertical padding. - return new Rect.fromLTWH(0.0, 0.0, _kCaretWidth, preferredLineHeight).shift(caretOffset + _paintOffset); + return new Rect.fromLTWH(0.0, 0.0, cursorWidth, preferredLineHeight).shift(caretOffset + _paintOffset); } @override @@ -683,7 +706,7 @@ class RenderEditable extends RenderBox { assert(constraintWidth != null); if (_textLayoutLastWidth == constraintWidth) return; - const double caretMargin = _kCaretGap + _kCaretWidth; + final double caretMargin = _kCaretGap + cursorWidth; final double availableWidth = math.max(0.0, constraintWidth - caretMargin); final double maxWidth = _isMultiline ? availableWidth : double.infinity; _textPainter.layout(minWidth: availableWidth, maxWidth: maxWidth); @@ -693,7 +716,7 @@ class RenderEditable extends RenderBox { @override void performLayout() { _layoutText(constraints.maxWidth); - _caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, _kCaretWidth, preferredLineHeight - 2.0 * _kCaretHeightOffset); + _caretPrototype = new Rect.fromLTWH(0.0, _kCaretHeightOffset, cursorWidth, preferredLineHeight - 2.0 * _kCaretHeightOffset); _selectionRects = null; // We grab _textPainter.size here because assigning to `size` on the next // line will trigger us to validate our intrinsic sizes, which will change @@ -705,7 +728,7 @@ class RenderEditable extends RenderBox { // See also RenderParagraph which has a similar issue. final Size textPainterSize = _textPainter.size; size = new Size(constraints.maxWidth, constraints.constrainHeight(_preferredHeight(constraints.maxWidth))); - final Size contentSize = new Size(textPainterSize.width + _kCaretGap + _kCaretWidth, textPainterSize.height); + final Size contentSize = new Size(textPainterSize.width + _kCaretGap + cursorWidth, textPainterSize.height); final double _maxScrollExtent = _getMaxScrollExtent(contentSize); _hasVisualOverflow = _maxScrollExtent > 0.0; offset.applyViewportDimension(_viewportExtent); @@ -715,9 +738,18 @@ class RenderEditable extends RenderBox { void _paintCaret(Canvas canvas, Offset effectiveOffset) { assert(_textLayoutLastWidth == constraints.maxWidth); final Offset caretOffset = _textPainter.getOffsetForCaret(_selection.extent, _caretPrototype); - final Paint paint = new Paint()..color = _cursorColor; + final Paint paint = new Paint() + ..color = _cursorColor; + final Rect caretRect = _caretPrototype.shift(caretOffset + effectiveOffset); - canvas.drawRect(caretRect, paint); + + if (cursorRadius == null) { + canvas.drawRect(caretRect, paint); + } else { + final RRect caretRRect = RRect.fromRectAndRadius(caretRect, cursorRadius); + canvas.drawRRect(caretRRect, paint); + } + if (caretRect != _lastCaretRect) { _lastCaretRect = caretRect; if (onCaretChanged != null) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 2b484578f4..1c821ae9d5 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -208,6 +208,8 @@ class EditableText extends StatefulWidget { this.onSelectionChanged, List inputFormatters, this.rendererIgnoresPointer = false, + this.cursorWidth = 1.0, + this.cursorRadius, }) : assert(controller != null), assert(focusNode != null), assert(obscureText != null), @@ -353,6 +355,16 @@ class EditableText extends StatefulWidget { /// This property is false by default. final bool rendererIgnoresPointer; + /// How thick the cursor will be. + /// + /// Defaults to 1.0 + final double cursorWidth; + + /// How rounded the corners of the cursor should be. + /// + /// By default, the cursor has a Radius of zero. + final Radius cursorRadius; + @override EditableTextState createState() => new EditableTextState(); @@ -810,6 +822,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien FocusScope.of(context).reparentIfNeeded(widget.focusNode); super.build(context); // See AutomaticKeepAliveClientMixin. final TextSelectionControls controls = widget.selectionControls; + return new Scrollable( excludeFromSemantics: true, axisDirection: _isMultiline ? AxisDirection.down : AxisDirection.right, @@ -841,6 +854,8 @@ class EditableTextState extends State with AutomaticKeepAliveClien onSelectionChanged: _handleSelectionChanged, onCaretChanged: _handleCaretChanged, rendererIgnoresPointer: widget.rendererIgnoresPointer, + cursorWidth: widget.cursorWidth, + cursorRadius: widget.cursorRadius, ), ), ); @@ -902,6 +917,8 @@ class _Editable extends LeafRenderObjectWidget { this.onSelectionChanged, this.onCaretChanged, this.rendererIgnoresPointer = false, + this.cursorWidth, + this.cursorRadius, }) : assert(textDirection != null), assert(rendererIgnoresPointer != null), super(key: key); @@ -923,6 +940,8 @@ class _Editable extends LeafRenderObjectWidget { final SelectionChangedHandler onSelectionChanged; final CaretChangedHandler onCaretChanged; final bool rendererIgnoresPointer; + final double cursorWidth; + final Radius cursorRadius; @override RenderEditable createRenderObject(BuildContext context) { @@ -943,6 +962,8 @@ class _Editable extends LeafRenderObjectWidget { onCaretChanged: onCaretChanged, ignorePointer: rendererIgnoresPointer, obscureText: obscureText, + cursorWidth: cursorWidth, + cursorRadius: cursorRadius, ); } @@ -964,6 +985,8 @@ class _Editable extends LeafRenderObjectWidget { ..onSelectionChanged = onSelectionChanged ..onCaretChanged = onCaretChanged ..ignorePointer = rendererIgnoresPointer - ..obscureText = obscureText; + ..obscureText = obscureText + ..cursorWidth = cursorWidth + ..cursorRadius = cursorRadius; } } diff --git a/packages/flutter/test/cupertino/segmented_control_test.dart b/packages/flutter/test/cupertino/segmented_control_test.dart index 06cec88225..b77ac70491 100644 --- a/packages/flutter/test/cupertino/segmented_control_test.dart +++ b/packages/flutter/test/cupertino/segmented_control_test.dart @@ -967,6 +967,7 @@ void main() { final Offset center = tester.getCenter(find.text('B')); await tester.startGesture(center); await tester.pumpAndSettle(); + await expectLater( find.byType(RepaintBoundary), matchesGoldenFile('segmented_control_test.1.0.png'), diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index bb2b0aab20..6702312f7d 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -1208,7 +1208,7 @@ void main() { editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft, ); - expect(topLeft.dx, equals(399.0)); + expect(topLeft.dx, equals(399)); await tester.enterText(find.byType(TextField), 'abcd'); await tester.pump(); @@ -1217,7 +1217,7 @@ void main() { editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft, ); - expect(topLeft.dx, equals(399.0)); + expect(topLeft.dx, equals(399)); }); testWidgets('Can align to center within center', (WidgetTester tester) async { @@ -1240,7 +1240,7 @@ void main() { editable.getLocalRectForCaret(const TextPosition(offset: 0)).topLeft, ); - expect(topLeft.dx, equals(399.0)); + expect(topLeft.dx, equals(399)); await tester.enterText(find.byType(TextField), 'abcd'); await tester.pump(); @@ -1249,7 +1249,7 @@ void main() { editable.getLocalRectForCaret(const TextPosition(offset: 2)).topLeft, ); - expect(topLeft.dx, equals(399.0)); + expect(topLeft.dx, equals(399)); }); 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 2373041f54..df795f5fdc 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -56,7 +57,8 @@ void main() { controller.text = 'test'; await tester.idle(); expect(tester.testTextInput.editingState['text'], equals('test')); - expect(tester.testTextInput.setClientArgs['inputAction'], equals(serializedActionName)); + expect(tester.testTextInput.setClientArgs['inputAction'], + equals(serializedActionName)); } testWidgets('has expected defaults', (WidgetTester tester) async { @@ -77,6 +79,26 @@ void main() { expect(editableText.maxLines, equals(1)); expect(editableText.obscureText, isFalse); expect(editableText.autocorrect, isTrue); + expect(editableText.cursorWidth, 1.0); + }); + + testWidgets('cursor has expected width and radius', + (WidgetTester tester) async { + await tester.pumpWidget(new Directionality( + textDirection: TextDirection.ltr, + child: new EditableText( + controller: controller, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + cursorWidth: 10.0, + cursorRadius: const Radius.circular(2.0), + ))); + + final EditableText editableText = + tester.firstWidget(find.byType(EditableText)); + expect(editableText.cursorWidth, 10.0); + expect(editableText.cursorRadius.x, 2.0); }); testWidgets('text keyboard is requested when maxLines is default', @@ -104,13 +126,15 @@ void main() { tester.firstWidget(find.byType(EditableText)); expect(editableText.maxLines, equals(1)); expect(tester.testTextInput.editingState['text'], equals('test')); - expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.text')); + expect(tester.testTextInput.setClientArgs['inputType']['name'], + equals('TextInputType.text')); expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done')); }); - testWidgets('Keyboard is configured for "unspecified" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "unspecified" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.unspecified, @@ -118,8 +142,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "none" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "none" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.none, @@ -127,8 +152,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "done" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "done" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.done, @@ -136,8 +162,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "send" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "send" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.send, @@ -145,8 +172,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "go" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "go" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.go, @@ -154,8 +182,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "search" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "search" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.search, @@ -163,8 +192,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "send" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "send" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.send, @@ -172,8 +202,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "next" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "next" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.next, @@ -181,8 +212,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "previous" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "previous" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.previous, @@ -190,8 +222,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "continue" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "continue" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.continueAction, @@ -199,8 +232,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "join" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "join" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.join, @@ -208,8 +242,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "route" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "route" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.route, @@ -217,8 +252,9 @@ void main() { ); }); - testWidgets('Keyboard is configured for "emergencyCall" action when explicitly requested', - (WidgetTester tester) async { + testWidgets( + 'Keyboard is configured for "emergencyCall" action when explicitly requested', + (WidgetTester tester) async { await _desiredKeyboardActionIsRequested( tester: tester, action: TextInputAction.emergencyCall, @@ -250,11 +286,14 @@ void main() { controller.text = 'test'; await tester.idle(); expect(tester.testTextInput.editingState['text'], equals('test')); - expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.multiline')); - expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.newline')); + expect(tester.testTextInput.setClientArgs['inputType']['name'], + equals('TextInputType.multiline')); + expect(tester.testTextInput.setClientArgs['inputAction'], + equals('TextInputAction.newline')); }); - testWidgets('Correct keyboard is requested when set explicitly and maxLines > 1', + testWidgets( + 'Correct keyboard is requested when set explicitly and maxLines > 1', (WidgetTester tester) async { await tester.pumpWidget( new Directionality( @@ -279,8 +318,10 @@ void main() { controller.text = 'test'; await tester.idle(); expect(tester.testTextInput.editingState['text'], equals('test')); - expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.phone')); - expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done')); + expect(tester.testTextInput.setClientArgs['inputType']['name'], + equals('TextInputType.phone')); + expect(tester.testTextInput.setClientArgs['inputAction'], + equals('TextInputAction.done')); }); testWidgets('multiline keyboard is requested when set implicitly', @@ -307,8 +348,10 @@ void main() { controller.text = 'test'; await tester.idle(); expect(tester.testTextInput.editingState['text'], equals('test')); - expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.multiline')); - expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.newline')); + expect(tester.testTextInput.setClientArgs['inputType']['name'], + equals('TextInputType.multiline')); + expect(tester.testTextInput.setClientArgs['inputAction'], + equals('TextInputAction.newline')); }); testWidgets('single line inputs have correct default keyboard', @@ -335,12 +378,16 @@ void main() { controller.text = 'test'; await tester.idle(); expect(tester.testTextInput.editingState['text'], equals('test')); - expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.text')); - expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done')); + expect(tester.testTextInput.setClientArgs['inputType']['name'], + equals('TextInputType.text')); + expect(tester.testTextInput.setClientArgs['inputAction'], + equals('TextInputAction.done')); }); - testWidgets('Fires onChanged when text changes via TextSelectionOverlay', (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); + testWidgets('Fires onChanged when text changes via TextSelectionOverlay', + (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); String changedValue; final Widget widget = new MaterialApp( @@ -361,9 +408,10 @@ void main() { // Populate a fake clipboard. const String clipboardContent = 'Dobunezumi mitai ni utsukushiku naritai'; - SystemChannels.platform.setMockMethodCallHandler((MethodCall methodCall) async { + SystemChannels.platform + .setMockMethodCallHandler((MethodCall methodCall) async { if (methodCall.method == 'Clipboard.getData') - return const { 'text': clipboardContent }; + return const {'text': clipboardContent}; return null; }); @@ -378,39 +426,111 @@ void main() { expect(changedValue, clipboardContent); }); - testWidgets('Loses focus by default when "done" action is pressed', (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); - final FocusNode focusNode = new FocusNode(); + testWidgets('cursor layout has correct width', (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); + String changedValue; final Widget widget = new MaterialApp( - home: new EditableText( - key: editableTextKey, - controller: new TextEditingController(), - focusNode: focusNode, - style: new Typography(platform: TargetPlatform.android).black.subhead, - cursorColor: Colors.blue, - selectionControls: materialTextSelectionControls, - keyboardType: TextInputType.text, + home: new RepaintBoundary( + key: const ValueKey(1), + child: new EditableText( + key: editableTextKey, + controller: new TextEditingController(), + focusNode: new FocusNode(), + style: new Typography(platform: TargetPlatform.android).black.subhead, + cursorColor: Colors.blue, + selectionControls: materialTextSelectionControls, + keyboardType: TextInputType.text, + onChanged: (String value) { + changedValue = value; + }, + cursorWidth: 15.0, + ), ), ); await tester.pumpWidget(widget); - // Select EditableText to give it focus. + // Populate a fake clipboard. + const String clipboardContent = ' '; + SystemChannels.platform + .setMockMethodCallHandler((MethodCall methodCall) async { + if (methodCall.method == 'Clipboard.getData') + return const {'text': clipboardContent}; + return null; + }); + + // Long-press to bring up the text editing controls. final Finder textFinder = find.byKey(editableTextKey); - await tester.tap(textFinder); + await tester.longPress(textFinder); await tester.pump(); - assert(focusNode.hasFocus); - - await tester.testTextInput.receiveAction(TextInputAction.done); + await tester.tap(find.text('PASTE')); await tester.pump(); - // Lost focus because "done" was pressed. - expect(focusNode.hasFocus, false); - }); + expect(changedValue, clipboardContent); - testWidgets('Does not lose focus by default when "next" action is pressed', (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); + await expectLater( + find.byKey(const ValueKey(1)), + matchesGoldenFile('editable_text_test.0.0.png'), + ); + }, skip: !Platform.isLinux); + + testWidgets('cursor layout has correct radius', (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); + + String changedValue; + final Widget widget = new MaterialApp( + home: new RepaintBoundary( + key: const ValueKey(1), + child: new EditableText( + key: editableTextKey, + controller: new TextEditingController(), + focusNode: new FocusNode(), + style: new Typography(platform: TargetPlatform.android).black.subhead, + cursorColor: Colors.blue, + selectionControls: materialTextSelectionControls, + keyboardType: TextInputType.text, + onChanged: (String value) { + changedValue = value; + }, + cursorWidth: 15.0, + cursorRadius: const Radius.circular(3.0), + ), + ), + ); + await tester.pumpWidget(widget); + + // Populate a fake clipboard. + const String clipboardContent = ' '; + SystemChannels.platform + .setMockMethodCallHandler((MethodCall methodCall) async { + if (methodCall.method == 'Clipboard.getData') + return const {'text': clipboardContent}; + return null; + }); + + // Long-press to bring up the text editing controls. + final Finder textFinder = find.byKey(editableTextKey); + await tester.longPress(textFinder); + await tester.pump(); + + await tester.tap(find.text('PASTE')); + await tester.pump(); + + expect(changedValue, clipboardContent); + + await expectLater( + find.byKey(const ValueKey(1)), + matchesGoldenFile('editable_text_test.1.0.png'), + ); + }, skip: !Platform.isLinux); + + testWidgets('Does not lose focus by default when "next" action is pressed', + (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); final FocusNode focusNode = new FocusNode(); final Widget widget = new MaterialApp( @@ -440,9 +560,11 @@ void main() { expect(focusNode.hasFocus, true); }); - testWidgets('Does not lose focus by default when "done" action is pressed and onEditingComplete is provided', - (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); + testWidgets( + 'Does not lose focus by default when "done" action is pressed and onEditingComplete is provided', + (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); final FocusNode focusNode = new FocusNode(); final Widget widget = new MaterialApp( @@ -476,9 +598,11 @@ void main() { expect(focusNode.hasFocus, true); }); - testWidgets('When "done" is pressed callbacks are invoked: onEditingComplete > onSubmitted', - (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); + testWidgets( + 'When "done" is pressed callbacks are invoked: onEditingComplete > onSubmitted', + (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); final FocusNode focusNode = new FocusNode(); bool onEditingCompleteCalled = false; @@ -518,9 +642,11 @@ void main() { // and onSubmission callbacks. }); - testWidgets('When "next" is pressed callbacks are invoked: onEditingComplete > onSubmitted', - (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); + testWidgets( + 'When "next" is pressed callbacks are invoked: onEditingComplete > onSubmitted', + (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); final FocusNode focusNode = new FocusNode(); bool onEditingCompleteCalled = false; @@ -560,10 +686,14 @@ void main() { // and onSubmission callbacks. }); - testWidgets('Changing controller updates EditableText', (WidgetTester tester) async { - final GlobalKey editableTextKey = new GlobalKey(); - final TextEditingController controller1 = new TextEditingController(text: 'Wibble'); - final TextEditingController controller2 = new TextEditingController(text: 'Wobble'); + testWidgets('Changing controller updates EditableText', + (WidgetTester tester) async { + final GlobalKey editableTextKey = + new GlobalKey(); + final TextEditingController controller1 = + new TextEditingController(text: 'Wibble'); + final TextEditingController controller2 = + new TextEditingController(text: 'Wobble'); TextEditingController currentController = controller1; StateSetter setState; @@ -579,11 +709,13 @@ void main() { key: editableTextKey, controller: currentController, focusNode: new FocusNode(), - style: new Typography(platform: TargetPlatform.android).black.subhead, + style: new Typography(platform: TargetPlatform.android) + .black + .subhead, cursorColor: Colors.blue, selectionControls: materialTextSelectionControls, keyboardType: TextInputType.text, - onChanged: (String value) { }, + onChanged: (String value) {}, ), ), ), @@ -591,6 +723,7 @@ void main() { }, ); } + await tester.pumpWidget(builder()); await tester.showKeyboard(find.byType(EditableText)); @@ -605,7 +738,9 @@ void main() { await tester.pump(); expect(log, hasLength(1)); - expect(log.single, isMethodCall( + expect( + log.single, + isMethodCall( 'TextInput.setEditingState', arguments: const { 'text': 'Wobble', @@ -616,10 +751,11 @@ void main() { 'composingBase': -1, 'composingExtent': -1, }, - )); + )); }); - testWidgets('EditableText identifies as text field (w/ focus) in semantics', (WidgetTester tester) async { + testWidgets('EditableText identifies as text field (w/ focus) in semantics', + (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); await tester.pumpWidget( @@ -638,18 +774,25 @@ void main() { ), ); - expect(semantics, includesNodeWith(flags: [SemanticsFlag.isTextField])); + expect(semantics, + includesNodeWith(flags: [SemanticsFlag.isTextField])); await tester.tap(find.byType(EditableText)); await tester.idle(); await tester.pump(); - expect(semantics, includesNodeWith(flags: [SemanticsFlag.isTextField, SemanticsFlag.isFocused])); + expect( + semantics, + includesNodeWith(flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.isFocused + ])); semantics.dispose(); }); - testWidgets('EditableText includes text as value in semantics', (WidgetTester tester) async { + testWidgets('EditableText includes text as value in semantics', + (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); const String value1 = 'EditableText content'; @@ -671,58 +814,73 @@ void main() { ), ); - expect(semantics, includesNodeWith( - flags: [SemanticsFlag.isTextField], - value: value1, - )); + expect( + semantics, + includesNodeWith( + flags: [SemanticsFlag.isTextField], + value: value1, + )); const String value2 = 'Changed the EditableText content'; controller.text = value2; await tester.idle(); await tester.pump(); - expect(semantics, includesNodeWith( - flags: [SemanticsFlag.isTextField], - value: value2, - )); + expect( + semantics, + includesNodeWith( + flags: [SemanticsFlag.isTextField], + value: value2, + )); semantics.dispose(); }); - testWidgets('changing selection with keyboard does not show handles', (WidgetTester tester) async { + testWidgets('changing selection with keyboard does not show handles', + (WidgetTester tester) async { const String value1 = 'Hello World'; controller.text = value1; - await tester.pumpWidget(new MaterialApp( - home: new EditableText( - controller: controller, - selectionControls: materialTextSelectionControls, - focusNode: focusNode, - style: textStyle, - cursorColor: cursorColor, + await tester.pumpWidget( + new MaterialApp( + home: new EditableText( + controller: controller, + selectionControls: materialTextSelectionControls, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + ), ), - )); + ); // Simulate selection change via tap to show handles. - final RenderEditable render = tester.allRenderObjects.firstWhere((RenderObject o) => o.runtimeType == RenderEditable); - render.onSelectionChanged(const TextSelection.collapsed(offset: 4), render, SelectionChangedCause.tap); + final RenderEditable render = tester.allRenderObjects + .firstWhere((RenderObject o) => o.runtimeType == RenderEditable); + render.onSelectionChanged(const TextSelection.collapsed(offset: 4), render, + SelectionChangedCause.tap); await tester.pumpAndSettle(); final EditableTextState textState = tester.state(find.byType(EditableText)); expect(textState.selectionOverlay.handlesAreVisible, isTrue); - expect(textState.selectionOverlay.selectionDelegate.textEditingValue.selection, const TextSelection.collapsed(offset: 4)); + expect( + textState.selectionOverlay.selectionDelegate.textEditingValue.selection, + const TextSelection.collapsed(offset: 4)); // Simulate selection change via keyboard and expect handles to disappear. - render.onSelectionChanged(const TextSelection.collapsed(offset: 10), render, SelectionChangedCause.keyboard); + render.onSelectionChanged(const TextSelection.collapsed(offset: 10), render, + SelectionChangedCause.keyboard); await tester.pumpAndSettle(); expect(textState.selectionOverlay.handlesAreVisible, isFalse); - expect(textState.selectionOverlay.selectionDelegate.textEditingValue.selection, const TextSelection.collapsed(offset: 10)); + expect( + textState.selectionOverlay.selectionDelegate.textEditingValue.selection, + const TextSelection.collapsed(offset: 10)); }); - testWidgets('exposes correct cursor movement semantics', (WidgetTester tester) async { + testWidgets('exposes correct cursor movement semantics', + (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); controller.text = 'test'; @@ -736,46 +894,56 @@ void main() { ), )); - expect(semantics, includesNodeWith( - value: 'test', - )); + expect( + semantics, + includesNodeWith( + value: 'test', + )); - controller.selection = new TextSelection.collapsed(offset: controller.text.length); + controller.selection = + new TextSelection.collapsed(offset: controller.text.length); await tester.pumpAndSettle(); // At end, can only go backwards. - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + ], + )); - controller.selection = new TextSelection.collapsed(offset: controller.text.length - 2); + controller.selection = + new TextSelection.collapsed(offset: controller.text.length - 2); await tester.pumpAndSettle(); // Somewhere in the middle, can go in both directions. - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.moveCursorForwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.moveCursorForwardByCharacter, + SemanticsAction.setSelection, + ], + )); controller.selection = const TextSelection.collapsed(offset: 0); await tester.pumpAndSettle(); // At beginning, can only go forward. - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorForwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorForwardByCharacter, + SemanticsAction.setSelection, + ], + )); semantics.dispose(); }); @@ -785,7 +953,8 @@ void main() { const bool doNotExtendSelection = false; controller.text = 'test'; - controller.selection = new TextSelection.collapsed(offset: controller.text.length); + controller.selection = + new TextSelection.collapsed(offset: controller.text.length); await tester.pumpWidget(new MaterialApp( home: new EditableText( @@ -796,54 +965,66 @@ void main() { ), )); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + ], + )); - final RenderEditable render = tester.allRenderObjects.firstWhere((RenderObject o) => o.runtimeType == RenderEditable); + final RenderEditable render = tester.allRenderObjects + .firstWhere((RenderObject o) => o.runtimeType == RenderEditable); final int semanticsId = render.debugSemantics.id; expect(controller.selection.baseOffset, 4); expect(controller.selection.extentOffset, 4); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 3); expect(controller.selection.extentOffset, 3); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.moveCursorForwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.moveCursorForwardByCharacter, + SemanticsAction.setSelection, + ], + )); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); await tester.pumpAndSettle(); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); await tester.pumpAndSettle(); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 0); expect(controller.selection.extentOffset, 0); await tester.pumpAndSettle(); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorForwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorForwardByCharacter, + SemanticsAction.setSelection, + ], + )); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorForwardByCharacter, doNotExtendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorForwardByCharacter, doNotExtendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 1); @@ -852,13 +1033,15 @@ void main() { semantics.dispose(); }); - testWidgets('can extend selection with a11y means', (WidgetTester tester) async { + testWidgets('can extend selection with a11y means', + (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); const bool extendSelection = true; const bool doNotExtendSelection = false; controller.text = 'test'; - controller.selection = new TextSelection.collapsed(offset: controller.text.length); + controller.selection = + new TextSelection.collapsed(offset: controller.text.length); await tester.pumpWidget(new MaterialApp( home: new EditableText( @@ -869,60 +1052,73 @@ void main() { ), )); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + ], + )); - final RenderEditable render = tester.allRenderObjects.firstWhere((RenderObject o) => o.runtimeType == RenderEditable); + final RenderEditable render = tester.allRenderObjects + .firstWhere((RenderObject o) => o.runtimeType == RenderEditable); final int semanticsId = render.debugSemantics.id; expect(controller.selection.baseOffset, 4); expect(controller.selection.extentOffset, 4); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, extendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 4); expect(controller.selection.extentOffset, 3); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.moveCursorForwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.moveCursorForwardByCharacter, + SemanticsAction.setSelection, + ], + )); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, extendSelection); await tester.pumpAndSettle(); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, extendSelection); await tester.pumpAndSettle(); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorBackwardByCharacter, extendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 4); expect(controller.selection.extentOffset, 0); await tester.pumpAndSettle(); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorForwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorForwardByCharacter, + SemanticsAction.setSelection, + ], + )); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorForwardByCharacter, doNotExtendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorForwardByCharacter, doNotExtendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 1); expect(controller.selection.extentOffset, 1); - tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorForwardByCharacter, extendSelection); + tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, + SemanticsAction.moveCursorForwardByCharacter, extendSelection); await tester.pumpAndSettle(); expect(controller.selection.baseOffset, 1); @@ -931,7 +1127,8 @@ void main() { semantics.dispose(); }); - testWidgets('password fields have correct semantics', (WidgetTester tester) async { + testWidgets('password fields have correct semantics', + (WidgetTester tester) async { final SemanticsTester semantics = new SemanticsTester(tester); controller.text = 'super-secret-password!!1'; @@ -948,30 +1145,40 @@ void main() { final String expectedValue = '•' * controller.text.length; - expect(semantics, hasSemantics(new TestSemantics( - children: [ - new TestSemantics.rootChild( - children: [ + expect( + semantics, + hasSemantics( new TestSemantics( - flags: [SemanticsFlag.scopesRoute], - children: [ - new TestSemantics( - flags: [SemanticsFlag.isTextField, SemanticsFlag.isObscured], - value: expectedValue, - textDirection: TextDirection.ltr, + children: [ + new TestSemantics.rootChild( + children: [ + new TestSemantics( + flags: [SemanticsFlag.scopesRoute], + children: [ + new TestSemantics( + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.isObscured + ], + value: expectedValue, + textDirection: TextDirection.ltr, + ), + ], + ), + ], ), ], ), - ], - ), - ], - ), ignoreTransform: true, ignoreRect: true, ignoreId: true)); + ignoreTransform: true, + ignoreRect: true, + ignoreId: true)); semantics.dispose(); }); group('a11y copy/cut/paste', () { - Future _buildApp(MockTextSelectionControls controls, WidgetTester tester) { + Future _buildApp( + MockTextSelectionControls controls, WidgetTester tester) { return tester.pumpWidget(new MaterialApp( home: new EditableText( controller: controller, @@ -987,11 +1194,13 @@ void main() { setUp(() { controller.text = 'test'; - controller.selection = new TextSelection.collapsed(offset: controller.text.length); + controller.selection = + new TextSelection.collapsed(offset: controller.text.length); controls = new MockTextSelectionControls(); when(controls.buildHandle(any, any, any)).thenReturn(new Container()); - when(controls.buildToolbar(any, any, any, any)).thenReturn(new Container()); + when(controls.buildToolbar(any, any, any, any)) + .thenReturn(new Container()); }); testWidgets('are exposed', (WidgetTester tester) async { @@ -1005,63 +1214,73 @@ void main() { await tester.tap(find.byType(EditableText)); await tester.pump(); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + ], + )); when(controls.canCopy(any)).thenReturn(true); await _buildApp(controls, tester); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - SemanticsAction.copy, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + SemanticsAction.copy, + ], + )); when(controls.canCopy(any)).thenReturn(false); when(controls.canPaste(any)).thenReturn(true); await _buildApp(controls, tester); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - SemanticsAction.paste, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + SemanticsAction.paste, + ], + )); when(controls.canPaste(any)).thenReturn(false); when(controls.canCut(any)).thenReturn(true); await _buildApp(controls, tester); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - SemanticsAction.cut, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + SemanticsAction.cut, + ], + )); when(controls.canCopy(any)).thenReturn(true); when(controls.canCut(any)).thenReturn(true); when(controls.canPaste(any)).thenReturn(true); await _buildApp(controls, tester); - expect(semantics, includesNodeWith( - value: 'test', - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - SemanticsAction.cut, - SemanticsAction.copy, - SemanticsAction.paste, - ], - )); + expect( + semantics, + includesNodeWith( + value: 'test', + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + SemanticsAction.cut, + SemanticsAction.copy, + SemanticsAction.paste, + ], + )); semantics.dispose(); }); @@ -1079,38 +1298,44 @@ void main() { final SemanticsOwner owner = tester.binding.pipelineOwner.semanticsOwner; const int expectedNodeId = 4; - expect(semantics, hasSemantics(new TestSemantics.root( - children: [ - new TestSemantics.rootChild( - id: 1, - children: [ - new TestSemantics( - id: 2, - flags: [SemanticsFlag.scopesRoute], + expect( + semantics, + hasSemantics( + new TestSemantics.root( children: [ new TestSemantics.rootChild( - id: expectedNodeId, - flags: [ - SemanticsFlag.isTextField, - SemanticsFlag.isFocused + id: 1, + children: [ + new TestSemantics( + id: 2, + flags: [SemanticsFlag.scopesRoute], + children: [ + new TestSemantics.rootChild( + id: expectedNodeId, + flags: [ + SemanticsFlag.isTextField, + SemanticsFlag.isFocused + ], + actions: [ + SemanticsAction.moveCursorBackwardByCharacter, + SemanticsAction.setSelection, + SemanticsAction.copy, + SemanticsAction.cut, + SemanticsAction.paste + ], + value: 'test', + textSelection: new TextSelection.collapsed( + offset: controller.text.length), + textDirection: TextDirection.ltr, + ), + ], + ), ], - actions: [ - SemanticsAction.moveCursorBackwardByCharacter, - SemanticsAction.setSelection, - SemanticsAction.copy, - SemanticsAction.cut, - SemanticsAction.paste - ], - value: 'test', - textSelection: new TextSelection.collapsed(offset: controller.text.length), - textDirection: TextDirection.ltr, ), ], ), - ], - ), - ], - ), ignoreRect: true, ignoreTransform: true)); + ignoreRect: true, + ignoreTransform: true)); owner.performAction(expectedNodeId, SemanticsAction.copy); verify(controls.handleCopy(any)).called(1); @@ -1125,7 +1350,8 @@ void main() { }); }); - testWidgets('allows customizing text style in subclasses', (WidgetTester tester) async { + testWidgets('allows customizing text style in subclasses', + (WidgetTester tester) async { controller.text = 'Hello World'; await tester.pumpWidget(new MaterialApp( @@ -1138,11 +1364,13 @@ void main() { )); // Simulate selection change via tap to show handles. - final RenderEditable render = tester.allRenderObjects.firstWhere((RenderObject o) => o.runtimeType == RenderEditable); + final RenderEditable render = tester.allRenderObjects + .firstWhere((RenderObject o) => o.runtimeType == RenderEditable); expect(render.text.style.fontStyle, FontStyle.italic); }); - testWidgets('autofocus sets cursor to the end of text', (WidgetTester tester) async { + testWidgets('autofocus sets cursor to the end of text', + (WidgetTester tester) async { const String text = 'hello world'; final FocusScopeNode focusScopeNode = new FocusScopeNode(); final FocusNode focusNode = new FocusNode(); @@ -1177,12 +1405,12 @@ class CustomStyleEditableText extends EditableText { Color cursorColor, FocusNode focusNode, TextStyle style, - }): super( - controller:controller, - cursorColor: cursorColor, - focusNode: focusNode, - style: style, - ); + }) : super( + controller: controller, + cursorColor: cursorColor, + focusNode: focusNode, + style: style, + ); @override CustomStyleEditableTextState createState() => new CustomStyleEditableTextState(); @@ -1192,8 +1420,8 @@ class CustomStyleEditableTextState extends EditableTextState { @override TextSpan buildTextSpan() { return new TextSpan( - style: const TextStyle(fontStyle: FontStyle.italic), - text: widget.controller.value.text, + style: const TextStyle(fontStyle: FontStyle.italic), + text: widget.controller.value.text, ); } }