feat(text-editor): add an outline to text layers and scale shadows with the text - #875
Merged
Merged
Conversation
Text over video needs to stay readable over whatever is behind it, and a background box covers the picture. TextLayer now carries outlineWidth and outlineColor, drawn as a stroke pass under the glyphs, and the text editor sets it via TextEditorState.setOutline and previews it while typing. With an outline, the shadows are cast by the outlined glyphs through an opaque silhouette in its own layer; a text shadow on the stroke pass would only follow the bare glyphs. Outline and shadows are measured at a scale and fontScale of 1 and grow with the rendered font size, and a text layer reserves room for them so a layer capture no longer cuts them off. Also fixes two ways the new values were lost: the layer copies made for editing, duplicating, grouping and crop/rotate listed every field by hand and dropped the outline, and a history diff never recorded removed shadows, so an import merged the earlier ones back.
Merged
3 of 7 tasks
…erhang - TextLayer.toMap wrote outlineColor only with a width, so a color set before the width fell back to black once a later history step turned the outline on and was imported as a diff. - The input's background text, which draws the outline preview, ignored TextEditorStyle.textHeight while the editable text used it, so with a style height the outline sat a line fraction off the typed glyphs. - The silhouette shadow layer was bounded by the line boxes and clipped the shadow of glyphs that paint beyond them.
hm21
marked this pull request as ready for review
September 29, 2026 08:40
hm21
added a commit
to divinevideo/divine-mobile
that referenced
this pull request
Sep 29, 2026
hm21/pro_image_editor#875 shipped as 14.5.0, so the git override pinning its unreleased commit is replaced by the pub.dev release. The release also carries its review fixes: the outline color is stored even without a width, the outline shadow is no longer clipped at overhanging glyphs, and the editing preview lays its outline out at the same line height as the text.
1 of 7 tasks
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.
Description
Text over video has to stay readable over whatever is behind it. Today the only way to separate text from a busy or bright background is a background box, which covers the picture. This adds an outline to text layers and makes their shadows behave, so text reads well without a box.
Outline
TextLayer.outlineWidthandTextLayer.outlineColor(default: no outline), serialized in import/export with minified keysow/oc.RoundedBackgroundTextdraws the outline as a stroke pass under the glyphs. Half of the stroke lies inside the glyph, sooutlineWidthis the visible thickness outside the glyph edge.TextEditorState.setOutline({width, color}), initialised from the edited layer and written onto the layerdone()returns. The input field previews it while typing.Shadows
textStyle(and the outline) are measured at ascaleandfontScaleof 1 and grow with the rendered font size. Before, pinching a text layer or raising its font scale kept the unscaled shadow offset and blur, so the shadow looked thinner the larger the text got. Unscaled layers look exactly as before.TextEditorStyle.inputShadows: nullthe input previews the selected style's shadows at the size the layer draws them.Fixes the feature depends on
reserveEffectSpace), symmetrically so its center stays put. A layer capture reads the repaint boundary's bounds, so a shadow below the glyphs was cut off in exports.LayerCopyManager.createCopyTextLayerandLayerInteractionManager._copyLayerlist every field by hand and dropped the outline, which lost it when a layer is edited, duplicated, grouped or transformed by crop/rotate.TextLayer.toMapFromReferencewrote shadows only while they were non-empty and even when unchanged. The import merges each history step over the previous one, so a shadow removed in a later step came back after export/import. It now writes shadows only when they changed, including an empty list.RoundedBackgroundTextPainter.shouldRepaintalso compares the text and the outline, so a style-only change repaints.Related Issue: none (needed for readable text and captions in a video editor built on this package).
Type of Change
Tests
TextLayer: outline defaults, map round trip,copyWith, and the reference diff (unchanged shadows omitted, removed shadows written as an empty list, outline changes written).stablebecause the removed shadows come back.RoundedBackgroundText: one paint pass without an outline, the outline pass under the text, the silhouette shadow layer, a transparent outline drawing nothing, and the reserved space growing by exactly the outline and shadow reach.LayerWidgetTextItem: outline and shadows scale withscale * fontScale.TextEditor:setOutlinereaches the returned layer; editing a layer previews its outline at the current font scale.flutter test(695 tests),flutter analyzeanddart formatpass.