Repository navigation
mixin_markdown_widget: measure text less and highlight code before the first frame - #525
Merged
Merged
Conversation
A pretext layout measured every word and CJK character with its own text painter, and measured the fragments of each line again, up to three times, while it fitted the line. Each measurement used a new cache, and line heights had no cache. One post preview with inline code took about 20 ms to lay out on a slow Linux desktop. Segment widths and line heights depend only on the text, style, scale, and padding, so layouts now share one cache. The cache clears when the system fonts change and when it reaches 20000 entries. Layout results do not change. A post preview now takes about 8 ms the first time its characters appear, and less after that. Add benchmark/pretext_layout_benchmark.dart.
Inline code fragments measured their tight text metrics with a new text painter on every layout. On macOS the first measurement of new glyphs parses their outlines, and this was the largest cost of a post preview with inline code. Tight text metrics depend only on the text, style, and scale, so they use the shared measurement cache too. Layout results do not change.
A pretext layout measured every CJK character and word on its own, and measured every fragment of each line again up to four times. Plain fragments carried zero padding, so they missed the cache entries of their segments, and each lookup hashed a TextStyle. - Segments of a run take their widths from one layout of the run. - A plain fragment of a whole segment keeps the segment width, and zero padding shares the cache entry of no padding. - A line measures its width once, and fragments of one style share one line height lookup. On a Linux desktop the first layout of a chat post made about 1400 cache lookups and 235 text layouts in one frame; it now makes about 390 lookups and 154 layouts.
Each CJK character is a fragment of its line, and each fragment became its own text span, so a line of Chinese text had dozens of style runs to shape. Adjacent plain fragments with the same style, cursor and recognizer now share one span. The text and its offsets are the same, and a screenshot of a chat post is pixel-identical.
Add MarkdownController.parseDocument, which parses data as a new controller does and uses no Flutter bindings, and a document argument that skips the parse. An app can then parse in an isolate, before the widget that shows the document builds.
… in the caller's isolate
A width measured inside a run can differ from a width of the segment alone when a font shapes across the segment edge. Both used one cache entry, so a layout used whichever width was measured first. In-run widths now have their own entry.
boyan01
approved these changes
Oct 16, 2026
Contributor
Author
|
Mixin desktop uses this release in MixinNetwork/flutter-app#2070. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This makes
mixin_markdown_widgetlay out text with fewer measurements, and lets an app parse and highlight a document before the widget builds. It also prepares the 0.3.3 release.Changes
MarkdownController.parseDocumentand adocumentargument toMarkdownController.parseDocumentuses no Flutter bindings, so an app can parse in an isolate.MarkdownController.highlightCodeBlocksandMarkdownController.cacheCodeHighlights. An app can highlight the code blocks of a parsed document in an isolate, then cache the results on the UI isolate.No existing API changes.
Performance
Measured in Mixin desktop on Linux (i7-8850H), profile build:
The background highlight costs more time in the isolate: 20 posts take 27.8 ms instead of 16.8 ms.
To compare branches, run
flutter test benchmark/pretext_layout_benchmark.dartinpackages/mixin_markdown_widget.Risks
AVis 0.64 px narrower thanAplusV). In-run widths have their own cache entry, so a layout never depends on which width was measured first.Testing
flutter analyze: no issues.flutter test: 192 tests pass. New tests cover parsing into a controller, the first-frame highlight, and highlight preparation in an isolate.Related