diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart index f080abc45b..f12f54f12f 100644 --- a/packages/flutter/lib/src/painting/text_painter.dart +++ b/packages/flutter/lib/src/painting/text_painter.dart @@ -780,7 +780,7 @@ class TextPainter { final double caretEnd = box.end; final double dx = box.direction == TextDirection.rtl ? caretEnd - caretPrototype.width : caretEnd; - return Rect.fromLTRB(min(dx, _paragraph!.width), box.top, min(dx, _paragraph!.width), box.bottom); + return Rect.fromLTRB(dx.clamp(0, _paragraph!.width), box.top, dx.clamp(0, _paragraph!.width), box.bottom); } return null; } @@ -822,7 +822,7 @@ class TextPainter { final TextBox box = boxes.last; final double caretStart = box.start; final double dx = box.direction == TextDirection.rtl ? caretStart - caretPrototype.width : caretStart; - return Rect.fromLTRB(min(dx, _paragraph!.width), box.top, min(dx, _paragraph!.width), box.bottom); + return Rect.fromLTRB(dx.clamp(0, _paragraph!.width), box.top, dx.clamp(0, _paragraph!.width), box.bottom); } return null; } diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index d266d0b6a1..b07de47ffc 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -1664,8 +1664,8 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, final Offset start = Offset(0.0, preferredLineHeight) + caretOffset + paintOffset; return [TextSelectionPoint(start, null)]; } else { - final Offset start = Offset(boxes.first.start, boxes.first.bottom) + paintOffset; - final Offset end = Offset(boxes.last.end, boxes.last.bottom) + paintOffset; + final Offset start = Offset(boxes.first.start.clamp(0, _textPainter.size.width), boxes.first.bottom) + paintOffset; + final Offset end = Offset(boxes.last.end.clamp(0, _textPainter.size.width), boxes.last.bottom) + paintOffset; return [ TextSelectionPoint(start, boxes.first.direction), TextSelectionPoint(end, boxes.last.direction), @@ -2741,14 +2741,19 @@ class _TextHighlightPainter extends RenderEditablePainter { } highlightPaint.color = color; - final List boxes = renderEditable._textPainter.getBoxesForSelection( + final TextPainter textPainter = renderEditable._textPainter; + final List boxes = textPainter.getBoxesForSelection( TextSelection(baseOffset: range.start, extentOffset: range.end), boxHeightStyle: selectionHeightStyle, boxWidthStyle: selectionWidthStyle, ); for (final TextBox box in boxes) - canvas.drawRect(box.toRect().shift(renderEditable._paintOffset), highlightPaint); + canvas.drawRect( + box.toRect().shift(renderEditable._paintOffset) + .intersect(Rect.fromLTWH(0, 0, textPainter.width, textPainter.height)), + highlightPaint, + ); } @override diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 996a0205ba..c690f50b32 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -1097,6 +1097,33 @@ void main() { expect(cursorOffsetSpaces.dx, inputWidth - kCaretGap); }); + testWidgets('Overflowing a line with spaces stops the cursor at the end (rtl direction)', (WidgetTester tester) async { + await tester.pumpWidget( + overlay( + child: const TextField( + textDirection: TextDirection.rtl, + maxLines: null, + ), + ), + ); + + const String testValueOneLine = 'enough text to be exactly at the end of the line.'; + const String testValueSpaces = '$testValueOneLine '; + + // Positioning the cursor at the end of a line overflowing with spaces puts + // it inside the input still. + await tester.enterText(find.byType(TextField), testValueSpaces); + await skipPastScrollingAnimation(tester); + await tester.tapAt(textOffsetToPosition(tester, testValueSpaces.length)); + await tester.pump(); + + final Offset cursorOffsetSpaces = findRenderEditable(tester).getLocalRectForCaret( + const TextPosition(offset: testValueSpaces.length), + ).topLeft; + + expect(cursorOffsetSpaces.dx >= 0, isTrue); + }); + testWidgets('mobile obscureText control test', (WidgetTester tester) async { await tester.pumpWidget( overlay(