Skip to content

When using letterSpacing and maxlines with richtext, the last few characters are not displayed. #184815

Open

Description

@hatano0x06

Steps to reproduce

  1. run sample code

Expected results

All characters are displayed

Actual results

The last “90” in the first, second, and third lines of “leftterspaing + maxlines” is missing.

Code sample

Code sample
import 'package:flutter/material.dart';



void main() {
  runApp(MaterialApp(home: SamplePage()));
}


class SamplePage extends StatefulWidget {

  SamplePage(): super();

  @override
  SamplePageState createState() => SamplePageState();
}


class SamplePageState extends State<SamplePage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('sample'),
      ),
      body : body(),
      // body: 
    );
  }

  Widget body(){

    TextSpan letterSpacingSpan = TextSpan(
      children: [
        TextSpan( text: "-1 12345", style: TextStyle(letterSpacing: 30, color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-2 12345", style: TextStyle(letterSpacing: 30, color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-3 12345", style: TextStyle(letterSpacing: 30, color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-4 12345", style: TextStyle(letterSpacing: 30, color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-5 12345", style: TextStyle(letterSpacing: 30, color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), )
      ]);

    TextSpan normalSpan = TextSpan(
      children: [
        TextSpan( text: "-1 12345", style: TextStyle(color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-2 12345", style: TextStyle(color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-3 12345", style: TextStyle(color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-4 12345", style: TextStyle(color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), ),
        TextSpan( text: "-5 12345", style: TextStyle(color: Colors.black), ),        
        TextSpan( text: "67890\n", style: TextStyle(color: Colors.black), )
      ]);

    return Column(children: [
      SizedBox(height: 20,),
      Text("letterSpacing + maxlines"),
      RichText(text: letterSpacingSpan, maxLines: 3,),

      SizedBox(height:20),
      Divider(),
      SizedBox(height:20),

      Text("letterSpacing"),
      RichText(text: letterSpacingSpan, ),

      SizedBox(height:20),
      Divider(),
      SizedBox(height:20),

      Text("maxlines"),
      RichText(text: normalSpan, maxLines: 3,),

      SizedBox(height:20),
      Divider(),
      SizedBox(height:20),

      Text("nothing"),
      RichText(text: normalSpan, ),

    ],);

  }
}


Screenshots or Video

Screenshots / Video demonstration Image

Logs

Logs
W/cr_media(12894): Requires BLUETOOTH permission
D/HostConnection(12894): HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_native_sync_v2 ANDROID_EMU_native_sync_v3 ANDROID_EMU_native_sync_v4 ANDROID_EMU_dma_v1 ANDROID_EMU_direct_mem ANDROID_EMU_host_composition_v1 ANDROID_EMU_host_composition_v2 ANDROID_EMU_vulkan ANDROID_EMU_deferred_vulkan_commands ANDROID_EMU_vulkan_null_optional_strings ANDROID_EMU_vulkan_create_resources_with_requirements ANDROID_EMU_YUV_Cache ANDROID_EMU_vulkan_ignored_handles ANDROID_EMU_has_shared_slots_host_memory_allocator ANDROID_EMU_vulkan_free_memory_sync ANDROID_EMU_vulkan_shader_float16_int8 ANDROID_EMU_vulkan_async_queue_submit ANDROID_EMU_vulkan_queue_submit_with_commands ANDROID_EMU_sync_buffer_data ANDROID_EMU_read_color_buffer_dma ANDROID_EMU_hwc_multi_configs GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_host_side_tracing ANDROID_EMU_gles_max_version_2 
D/EGL_emulation(12894): eglCreateContext: 0x75c5e6e222d0: maj 2 min 0 rcv 2
D/EGL_emulation(12894): eglMakeCurrent: 0x75c5e6e222d0: ver 2 0 (tinfo 0x75c80d816200) (first time)
D/EGL_emulation(12894): eglCreateContext: 0x75c5e6e222d0: maj 2 min 0 rcv 2
D/EGL_emulation(12894): eglCreateContext: 0x75c5e6e22d50: maj 2 min 0 rcv 2
I/Ads     (12894): JS: The jsLoaded GMSG has been sent (https://googleads.g.doubleclick.net/mads/static/sdk/native/sdk-core-v40.html:1071)
I/chromium(12894): [INFO:CONSOLE(1071)] "The jsLoaded GMSG has been sent", source: https://googleads.g.doubleclick.net/mads/static/sdk/native/sdk-core-v40.html (1071)
I/TRuntime.CctTransportBackend(12894): Making request to: https://firebaselogging-pa.googleapis.com/v1/firelog/legacy/batchlog
D/TrafficStats(12894): tagSocket(255) with statsTag=0xffffffff, statsUid=-1
I/TRuntime.CctTransportBackend(12894): Status Code: 200
Application finished.

Exited (-1).

Flutter Doctor output

Doctor output
flutter doctor -v
[√] Flutter (Channel stable, 3.41.6, on Microsoft Windows [Version 10.0.26200.8117], locale ja-JP) [483ms]
    • Flutter version 3.41.6 on channel stable at C:\Users\81708\Desktop\develop\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision db50e20168 (2 weeks ago), 2026-03-25 16:21:00 -0700
    • Engine revision 425cfb54d0
    • Dart version 3.11.4
    • DevTools version 2.54.2
    • Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration

[√] Windows Version (11 Pro 64-bit, 25H2, 2009) [1,741ms]

[√] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [9.4s]
    • Android SDK at C:\Users\81708\AppData\Local\Android\sdk
    • Emulator version 31.3.14.0 (build_id 9322596) (CL:N/A)
    • Platform android-36, build-tools 35.0.0
    • Java binary at: C:\Program Files\Android\Android Studio3\jbr\bin\java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 21.0.3+-12282718-b509.11)
    • All Android licenses accepted.

[√] Chrome - develop for the web [233ms]
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.14.8) [232ms]
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.14.36301.6
    • Windows 10 SDK version 10.0.26100.0

[√] Connected device (4 available) [597ms]
    • sdk gphone64 x86 64 (mobile) • emulator-5554 • android-x64    • Android 13 (API 33) (emulator)
    • Windows (desktop)            • windows       • windows-x64    • Microsoft Windows [Version 10.0.26200.8117]
    • Chrome (web)                 • chrome        • web-javascript • Google Chrome 146.0.7680.178
    • Edge (web)                   • edge          • web-javascript • Microsoft Edge 146.0.3856.109

[√] Network resources [506ms]
    • All expected network resources are available.

• No issues found!

Activity

  1. hatano0x06 commented on Apr 9, 2026

    @hatano0x06
    Author

    I believe this is likely related to the issue below, but since the symptoms are different, I have submitted it as a separate issue.
    #184818

  2. added
    in triagePresently being triaged by the triage team
    engineflutter/engine related. See also e: labels.
    a: typographyText rendering, possibly libtxt
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    on Apr 9, 2026
  3. mbcorona commented on Apr 9, 2026

    @mbcorona
    Member

    @hatano0x06, thank you for the sample codes across both issues. I can confirm this reproduces on the latest stable channel.

    I am routing this to the Engine team for review.

    Note to Team: When letterSpacing is applied to a TextSpan within a constrained RichText, the text layout engine miscalculates the line breaks and drops characters at the truncation points. This happens both with maxLines (as reported here) and when constrained by a fixed width (as reported in duplicate issue #184818).

    Code sample for fixed width constraint (from duplicate #184818)
    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(home: SamplePage()));
    
    class SamplePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        TextSpan letterSpacingSpan = TextSpan(
          children: [
            TextSpan(text: "12345", style: TextStyle(letterSpacing: 30, color: Colors.black)),        
            TextSpan(text: "67890" + "1234567890" * 20, style: TextStyle(color: Colors.black)),
          ]
        );
    
        return Scaffold(
          body: Center(
            child: Container(
              width: 300,
              color: Colors.red.withAlpha(100),
              child: RichText(text: letterSpacingSpan),
            ),
          ),
        );
      }
    }

    Consolidated #184818 into this issue.

  4. removed
    in triagePresently being triaged by the triage team
    on Apr 9, 2026
  5. gaaclarke commented on Apr 13, 2026

    @gaaclarke
    Member

    fyi @Rusino, this seems related to Skia's text layout.

  6. added
    P2Important issues not at the top of the work list
    on Apr 13, 2026
  7. self-assigned this
    on Nov 5, 2026
  8. Rusino commented on Nov 5, 2026

    @Rusino
    Contributor

    Thanks for the clear repro, and for the dup with the fixed width.

    This is in SkParagraph, but it is not a line breaking bug. Line breaking and the intrinsic widths are right. The problem is how the line is painted.

    To center glyphs in their letter spaced cells (half the spacing before the glyph and half after it), SkParagraph shifts the whole line right by half the letter spacing of the first character of the line. Each character keeps the whole spacing after the glyph, so this works when the line starts and ends with the same spacing: the shift uses up the trailing half of the last character.

    In your sample each line starts with letterSpacing: 30 and ends with plain text. The line still moves right by 15 px, but the plain "67890" has no trailing spacing to absorb it, so the last glyphs end 15 px past the line width (and past maxIntrinsicWidth). With maxLines set, RichText clips at its size (overflow: TextOverflow.clip), so "90" gets cut off. Without maxLines nothing clips, so in #184818 the text runs past the 300 px box.

    Proposed fix: limit the shift to the half spacing of the last character of the line (never negative). Lines with the same spacing at both ends render exactly as before. Fix and test, now in review: https://skia-review.googlesource.com/c/skia/+/1418376

    Until it lands and rolls into Flutter, overflow: TextOverflow.visible on the RichText keeps the last characters visible, but they still draw up to half the letter spacing past the box.

  9. hatano0x06 commented on Nov 6, 2026

    @hatano0x06
    Author

    @Rusino
    Thank you for providing the details.

    It is very reassuring to hear that improvements are underway, and I look forward to seeing them implemented.

    In the meantime, I will follow your advice and use overflow: TextOverflow.visible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work lista: typographyText rendering, possibly libtxtengineflutter/engine related. See also e: labels.found in release: 3.41Found to occur in 3.41has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions