Repository navigation
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work lista: typographyText rendering, possibly libtxtText rendering, possibly libtxtengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.found in release: 3.41Found to occur in 3.41Found to occur in 3.41has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onteam-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team
Description
Steps to reproduce
- 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
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!Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work lista: typographyText rendering, possibly libtxtText rendering, possibly libtxtengineflutter/engine related. See also e: labels.flutter/engine related. See also e: labels.found in release: 3.41Found to occur in 3.41Found to occur in 3.41has reproducible stepsThe issue has been confirmed reproducible and is ready to work onThe issue has been confirmed reproducible and is ready to work onteam-engineOwned by Engine teamOwned by Engine teamtriaged-engineTriaged by Engine teamTriaged by Engine team

Activity
hatano0x06 commentedon Apr 9, 2026
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
mbcorona commentedon Apr 9, 2026
@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)
Consolidated #184818 into this issue.
gaaclarke commentedon Apr 13, 2026
fyi @Rusino, this seems related to Skia's text layout.
Rusino commentedon Nov 5, 2026
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: 30and 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 pastmaxIntrinsicWidth). WithmaxLinesset,RichTextclips at its size (overflow: TextOverflow.clip), so "90" gets cut off. WithoutmaxLinesnothing 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.visibleon theRichTextkeeps the last characters visible, but they still draw up to half the letter spacing past the box.hatano0x06 commentedon Nov 6, 2026
@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.[skparagraph] Cap letter spacing line shift
Revert "[skparagraph] Cap letter spacing line shift"
[web] Accept the capped letter spacing line shift in kitchensink (flu…
Reland "[skparagraph] Cap letter spacing line shift"