Reland "Clipping if only one character text overflows (#99146)" (#102130)

Fixes a text clipping edge case.
diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart
index 2cbf171..f10d17d 100644
--- a/packages/flutter/lib/src/painting/text_painter.dart
+++ b/packages/flutter/lib/src/painting/text_painter.dart
@@ -599,6 +599,15 @@
     return _paragraph!.didExceedMaxLines;
   }
 
+  /// The distance from the left edge of the leftmost glyph to the right edge of
+  /// the rightmost glyph in the paragraph.
+  ///
+  /// Valid only after [layout] has been called.
+  double get longestLine {
+    assert(!_debugNeedsLayout);
+    return _paragraph!.longestLine;
+  }
+
   double? _lastMinWidth;
   double? _lastMaxWidth;
 
diff --git a/packages/flutter/lib/src/rendering/paragraph.dart b/packages/flutter/lib/src/rendering/paragraph.dart
index fdd3f36..d47b028 100644
--- a/packages/flutter/lib/src/rendering/paragraph.dart
+++ b/packages/flutter/lib/src/rendering/paragraph.dart
@@ -630,6 +630,12 @@
   @visibleForTesting
   bool get debugHasOverflowShader => _overflowShader != null;
 
+  /// Whether this paragraph currently has overflow and needs clipping.
+  ///
+  /// Used to test this object. Not for use in production.
+  @visibleForTesting
+  bool get debugNeedsClipping => _needsClipping;
+
   void _layoutText({ double minWidth = 0.0, double maxWidth = double.infinity }) {
     final bool widthMatters = softWrap || overflow == TextOverflow.ellipsis;
     _textPainter.layout(
@@ -781,7 +787,7 @@
     size = constraints.constrain(textSize);
 
     final bool didOverflowHeight = size.height < textSize.height || textDidExceedMaxLines;
-    final bool didOverflowWidth = size.width < textSize.width;
+    final bool didOverflowWidth = size.width < textSize.width || size.width < _textPainter.longestLine;
     // TODO(abarth): We're only measuring the sizes of the line boxes here. If
     // the glyphs draw outside the line boxes, we might think that there isn't
     // visual overflow when there actually is visual overflow. This can become
diff --git a/packages/flutter/test/rendering/paragraph_test.dart b/packages/flutter/test/rendering/paragraph_test.dart
index deddef6..0f7a24c 100644
--- a/packages/flutter/test/rendering/paragraph_test.dart
+++ b/packages/flutter/test/rendering/paragraph_test.dart
@@ -326,6 +326,24 @@
     expect(paragraph.debugHasOverflowShader, isFalse);
   }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61018
 
+  test('one character clip test', () {
+    // Regressing test for https://github.com/flutter/flutter/issues/99140
+    final RenderParagraph paragraph = RenderParagraph(
+      const TextSpan(
+        text: '7',
+        style: TextStyle(fontFamily: 'Ahem', fontSize: 60.0),
+      ),
+      textDirection: TextDirection.ltr,
+      maxLines: 1,
+    );
+
+    // Lay out in a narrow box to force clipping.
+    // The text width is 60 bigger than the constraints width.
+    layout(paragraph, constraints: BoxConstraints.tight(const Size(50.0, 200.0)));
+
+    expect(paragraph.debugNeedsClipping, true);
+  }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61018
+
   test('maxLines', () {
     final RenderParagraph paragraph = RenderParagraph(
       const TextSpan(