Skip to content

feat!: Use Flutter's paragraph layout for rich text documents - #4064

Merged
spydon merged 3 commits into
mainfrom
feat/flutter-paragraph-layout
Oct 1, 2026
Merged

spydon merged 3 commits into
mainfrom
feat/flutter-paragraph-layout

Conversation

@spydon

@spydon spydon commented Sep 28, 2026

Copy link
Copy Markdown
Member

Description

The rich text document pipeline (DocumentRoot, ParagraphNode, HeaderNode, and therefore flame_markdown) was the last place in Flame that broke lines itself. Its layout builder split on ASCII spaces, re-measured every tentative line with a fresh TextPainter, could not wrap CJK, threw on TextAlign.justify, lost kerning and ligatures at span boundaries, and needed glue heuristics for punctuation that were patched again in #4060 after #3192 and #3939.

Since InlineTextStyle always renders through TextPaint, that pipeline can hand the whole block to Flutter's paragraph engine without losing any capability:

  • InlineTextNode.layoutBuilder is replaced by InlineSpan toInlineSpan(). PlainTextNode and GroupTextNode build a TextSpan tree, the wrapper nodes delegate to their child.
  • TextBlockNode.format lays out one TextPainter per block and returns the new ParagraphTextElement, which exposes lines and lineMetrics. Justify, CJK wrapping, hard line breaks, and shaping across differently styled spans now work.
  • TextElement gained a no-op dispose(), forwarded by GroupElement, which ParagraphTextElement uses to release its painter.
  • TextBoxComponent lays out a dedicated painter for wrapping and reads lines through ParagraphTextElement. It previously called layout(maxWidth:) on the painter shared through the TextPaint cache, so the wrapped layout leaked into later format calls for the same string. Per-line rendering, align, growingBox and the typewriter effect are unchanged.

Checklist

  • I have followed the Contributor Guide when preparing my PR.
  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation in docs and added dartdoc comments with ///.
  • [-] I have updated/added relevant examples in examples or docs.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Migration instructions

  • TextNodeLayoutBuilder and InlineTextNode.layoutBuilder are removed. Custom InlineTextNode subclasses implement InlineSpan toInlineSpan() instead, returning a TextSpan built from style.asTextStyle().
  • GroupTextElement is removed. A text block now yields a single ParagraphTextElement instead of one inline element per line. Use its lines or lineMetrics to inspect the line breaking.
  • A word wider than the available width is now broken the way Flutter breaks it (filling the line first) rather than at node boundaries.

Related Issues

Follow-up to #4060, which deferred reusing Flutter's layout algorithm.

@spydon
spydon marked this pull request as ready for review September 28, 2026 20:07
@spydon
spydon requested a review from luanpotter September 28, 2026 20:07

@luanpotter luanpotter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@spydon
spydon merged commit 4566d89 into main Oct 1, 2026
8 checks passed
@spydon
spydon deleted the feat/flutter-paragraph-layout branch October 1, 2026 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants