Skip to content

Add breaking change page for soft hyphen rendering - #13945

Merged
sfshaza2 merged 10 commits into
flutter:mainfrom
dbebawy:hyphens-breaking-change
Oct 26, 2026
Merged

sfshaza2 merged 10 commits into
flutter:mainfrom
dbebawy:hyphens-breaking-change

Conversation

@dbebawy

@dbebawy dbebawy commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Description of what this PR is changing or adding, and why:

Adds a breaking change page for flutter/flutter#185152. Soft hyphens (U+00AD) now render as a visible hyphen at line breaks, controlled by a new Hyphens enum, and TextStyle.getParagraphStyle gained a hyphens parameter, which breaks classes that override it. The page covers both, with a migration for each.

Issues fixed by this PR (if any):

None.

PRs or commits this PR depends on (if any):

flutter/flutter#185152, which landed on master on October 3.

Presubmit checklist

  • If you are unwilling, or unable, to sign the CLA, even for a tiny, one-word PR, please file an issue instead of a PR.
  • If this PR is not meant to land until a future stable release, mark it as draft with an explanation.
  • This PR follows the Google Developer Documentation Style Guidelines—for example, it doesn't use i.e. or e.g., and it avoids I and we (first-person pronouns).
  • This PR uses semantic line breaks
    of 80 characters or fewer.

Covers flutter/flutter#185152: soft hyphens (U+00AD) now render at line
breaks, and TextStyle.getParagraphStyle gained a hyphens parameter.
Lines still break in the same places, and hit testing isn't affected.
Note that the web doesn't render soft hyphens yet, and that editable
text and SelectableText can't opt out yet. Use \u00AD escapes in the
code samples so the soft hyphen is visible.
@lamek
lamek self-requested a review October 2, 2026 17:30
mboetger pushed a commit to mboetger/flutter that referenced this pull request Oct 3, 2026
…5152)

## Summary

Adds visible soft hyphen (U+00AD) rendering at line breaks, with a new
`Hyphens` API to opt out. Fixes flutter#18443 on native
platforms; web rendering is tracked in flutter#193506.

A line break that falls on a soft hyphen now renders a visible `-` by
default. A new `Hyphens { manual, hidden }` enum controls this:

- `Hyphens.manual` (default): render a hyphen at the break.
- `Hyphens.hidden`: don't render the hyphen. The soft hyphen is still a
break opportunity.

`hyphens` is a parameter on `Text` / `RichText`, `TextPainter`,
`RenderParagraph`, `TextStyle.getParagraphStyle` and `dart:ui`'s
`ParagraphStyle`, following `textAlign` / `maxLines`. It is not a
`TextStyle` field. Customizing the hyphen string is deferred to flutter#189617.

Design doc:
[flutter.dev/go/soft-hyphens](https://flutter.dev/go/soft-hyphens)
(tracking issue flutter#185154).

## Background

Flutter already breaks lines at U+00AD but never draws the hyphen, so it
behaves like a zero-width space. This change doesn't add any new break
opportunities or change where lines break, but the rendered hyphen makes
its line wider, so it can change `Paragraph.longestLine` and the size of
text laid out with `TextWidthBasis.longestLine`.

The Skia side landed in
[`98daf58d`](google/skia@98daf58d4c) (Skia CL
[1208837](https://skia-review.googlesource.com/c/skia/+/1208837)),
behind `ParagraphStyle::setRenderSoftHyphens(bool)`, which defaults to
`false` for other Skia clients.

## Changes

**Engine**
- `txt::ParagraphStyle` gains a `render_soft_hyphens` flag (default
`true`), which `ParagraphBuilderSkia::TxtToSkia` forwards to
`setRenderSoftHyphens`. The Impeller interop toolkit builds
`txt::ParagraphStyle` too, so it also gets the new default.
- `paragraph_builder.cc` decodes `ParagraphStyle.hyphens` from the FFI
buffer (mask bit 13, slot 7).
- `dart:ui` adds the public `Hyphens` enum and `ParagraphStyle.hyphens`.
The web `dart:ui` accepts and stores the value; web rendering is
flutter#193506.

**Framework**
- `Hyphens` is re-exported from `dart:ui` via
`painting/basic_types.dart`.
- `hyphens` is threaded through `TextStyle.getParagraphStyle` →
`TextPainter` → `RenderParagraph` → `RichText` → `Text` / `Text.rich`,
including the selectable-text path.

## Breaking change

- **API:** adding `hyphens` to `TextStyle.getParagraphStyle` breaks
classes that override it. In practice that's test doubles like the
`_TextStyleProxy` in `test/material/theme_test.dart`, updated here;
material_ui's copy was removed in flutter/packages#12728. The fix is to
add `Hyphens? hyphens` to the override. Migration guide:
flutter/website#13945.
- **Rendering:** text containing U+00AD now shows a hyphen at
soft-hyphen line breaks by default. `Hyphens.hidden` restores the old
look.

## Known limitations

- The hyphen is appended after line breaking, so a line can exceed
`maxWidth` by the hyphen's advance.
- The rendered hyphen isn't included in selection highlight rects
(flutter#189234).
- `EditableText` (and so `TextField`) and `SelectableText` don't take a
`hyphens` parameter yet, so they always render the hyphen.
- Skia also draws the hyphen when a line ends in U+00AD without breaking
there (text ending in U+00AD, or U+00AD right before a space or
newline). The Skia fix is in review ([CL
1378356](https://skia-review.googlesource.com/c/skia/+/1378356)) and
will come in with a Skia roll once it lands.

## Test plan

- [ ] `paragraph_builder_skia_tests.cc::RenderSoftHyphensEnabled`:
`render_soft_hyphens` maps onto Skia in both states
- [ ] `testing/dart/text_test.dart`: `ParagraphStyle.hyphens` encodes
and round-trips in `toString`
- [ ] `testing/dart/paragraph_test.dart`: `ParagraphStyle.hyphens`
reaches the text layout (a line that breaks at a soft hyphen is wider
with `manual` than with `hidden`, and unset matches `manual`), which
covers decoding the setting in the engine
- [ ] `test/widgets/rich_text_test.dart`: `RichText` exposes `hyphens`
via `debugFillProperties`
- [ ] `test/widgets/text_test.dart`: `Text` forwards `hyphens` to
`RenderParagraph` (incl. selectable path); default resolves to
`Hyphens.manual`
- [ ] `test/rendering/paragraph_test.dart`: `RenderParagraph.hyphens`
setter triggers relayout
- [ ] `test/painting/text_painter_test.dart`: `TextPainter.hyphens`
setter; the same width check through `TextPainter`, and changing
`hyphens` after layout changes the width
- [ ] Google internal testing
- [ ] Manual smoke: `Text('inter\u00ADnational')` in a narrow container
shows `inter-` / `national`

@sfshaza2 sfshaza2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dbebawy, thanks for this! I have a couple minor tweaks.

Say the hyphen shows wherever a line breaks at a soft hyphen, and fill
in the version where the change landed on master.
@dbebawy
dbebawy marked this pull request as ready for review October 5, 2026 18:52
@dbebawy
dbebawy requested a review from a team as a code owner October 5, 2026 18:52

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request adds a new breaking change document explaining that soft hyphens (U+00AD) now render at line breaks in Flutter, introducing the Hyphens enum and a new hyphens parameter in TextStyle.getParagraphStyle. The review feedback suggests improving the grammar and style guide compliance of a sentence regarding editable text widgets, and updating the migration code example to use the ui. prefix for the Hyphens type to ensure correct compilation.

SelectableText isn't editable, but it's built on EditableText, as
TextField is.
@dbebawy

dbebawy commented Oct 13, 2026

Copy link
Copy Markdown
Contributor Author

@sfshaza2 updated per your comments; the one open thread is a small tweak to your wording suggestion. PTAL when you get a chance.

@conooi

conooi commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Thanks @dbebawy! The updates addressing the earlier review comments look great (keeping "at a soft hyphen" on line 34 makes sense).

It looks like sites/docs/src/content/release/breaking-changes/index.md now has a merge conflict with recent changes on main (including #13988). Could you merge or rebase main to resolve the conflict?

…s-breaking-change

# Conflicts:
#	sites/docs/src/content/release/breaking-changes/index.md
@dbebawy

dbebawy commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Thank you, @conooi. I merged main and resolved the conflict in index.md.

@sfshaza2 sfshaza2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm

@sfshaza2

Copy link
Copy Markdown
Contributor

I resolved the conflicts. Landing once the build is happy.

@sfshaza2

Copy link
Copy Markdown
Contributor

/gcbrun

@flutter-website-bot

Copy link
Copy Markdown
Collaborator

@sfshaza2
sfshaza2 merged commit 2235c91 into flutter:main Oct 26, 2026
14 checks passed
@dbebawy
dbebawy deleted the hyphens-breaking-change branch October 26, 2026 22:18
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
…5152)

## Summary

Adds visible soft hyphen (U+00AD) rendering at line breaks, with a new
`Hyphens` API to opt out. Fixes flutter#18443 on native
platforms; web rendering is tracked in flutter#193506.

A line break that falls on a soft hyphen now renders a visible `-` by
default. A new `Hyphens { manual, hidden }` enum controls this:

- `Hyphens.manual` (default): render a hyphen at the break.
- `Hyphens.hidden`: don't render the hyphen. The soft hyphen is still a
break opportunity.

`hyphens` is a parameter on `Text` / `RichText`, `TextPainter`,
`RenderParagraph`, `TextStyle.getParagraphStyle` and `dart:ui`'s
`ParagraphStyle`, following `textAlign` / `maxLines`. It is not a
`TextStyle` field. Customizing the hyphen string is deferred to flutter#189617.

Design doc:
[flutter.dev/go/soft-hyphens](https://flutter.dev/go/soft-hyphens)
(tracking issue flutter#185154).

## Background

Flutter already breaks lines at U+00AD but never draws the hyphen, so it
behaves like a zero-width space. This change doesn't add any new break
opportunities or change where lines break, but the rendered hyphen makes
its line wider, so it can change `Paragraph.longestLine` and the size of
text laid out with `TextWidthBasis.longestLine`.

The Skia side landed in
[`98daf58d`](google/skia@98daf58d4c) (Skia CL
[1208837](https://skia-review.googlesource.com/c/skia/+/1208837)),
behind `ParagraphStyle::setRenderSoftHyphens(bool)`, which defaults to
`false` for other Skia clients.

## Changes

**Engine**
- `txt::ParagraphStyle` gains a `render_soft_hyphens` flag (default
`true`), which `ParagraphBuilderSkia::TxtToSkia` forwards to
`setRenderSoftHyphens`. The Impeller interop toolkit builds
`txt::ParagraphStyle` too, so it also gets the new default.
- `paragraph_builder.cc` decodes `ParagraphStyle.hyphens` from the FFI
buffer (mask bit 13, slot 7).
- `dart:ui` adds the public `Hyphens` enum and `ParagraphStyle.hyphens`.
The web `dart:ui` accepts and stores the value; web rendering is
flutter#193506.

**Framework**
- `Hyphens` is re-exported from `dart:ui` via
`painting/basic_types.dart`.
- `hyphens` is threaded through `TextStyle.getParagraphStyle` →
`TextPainter` → `RenderParagraph` → `RichText` → `Text` / `Text.rich`,
including the selectable-text path.

## Breaking change

- **API:** adding `hyphens` to `TextStyle.getParagraphStyle` breaks
classes that override it. In practice that's test doubles like the
`_TextStyleProxy` in `test/material/theme_test.dart`, updated here;
material_ui's copy was removed in flutter/packages#12728. The fix is to
add `Hyphens? hyphens` to the override. Migration guide:
flutter/website#13945.
- **Rendering:** text containing U+00AD now shows a hyphen at
soft-hyphen line breaks by default. `Hyphens.hidden` restores the old
look.

## Known limitations

- The hyphen is appended after line breaking, so a line can exceed
`maxWidth` by the hyphen's advance.
- The rendered hyphen isn't included in selection highlight rects
(flutter#189234).
- `EditableText` (and so `TextField`) and `SelectableText` don't take a
`hyphens` parameter yet, so they always render the hyphen.
- Skia also draws the hyphen when a line ends in U+00AD without breaking
there (text ending in U+00AD, or U+00AD right before a space or
newline). The Skia fix is in review ([CL
1378356](https://skia-review.googlesource.com/c/skia/+/1378356)) and
will come in with a Skia roll once it lands.

## Test plan

- [ ] `paragraph_builder_skia_tests.cc::RenderSoftHyphensEnabled`:
`render_soft_hyphens` maps onto Skia in both states
- [ ] `testing/dart/text_test.dart`: `ParagraphStyle.hyphens` encodes
and round-trips in `toString`
- [ ] `testing/dart/paragraph_test.dart`: `ParagraphStyle.hyphens`
reaches the text layout (a line that breaks at a soft hyphen is wider
with `manual` than with `hidden`, and unset matches `manual`), which
covers decoding the setting in the engine
- [ ] `test/widgets/rich_text_test.dart`: `RichText` exposes `hyphens`
via `debugFillProperties`
- [ ] `test/widgets/text_test.dart`: `Text` forwards `hyphens` to
`RenderParagraph` (incl. selectable path); default resolves to
`Hyphens.manual`
- [ ] `test/rendering/paragraph_test.dart`: `RenderParagraph.hyphens`
setter triggers relayout
- [ ] `test/painting/text_painter_test.dart`: `TextPainter.hyphens`
setter; the same width check through `TextPainter`, and changing
`hyphens` after layout changes the width
- [ ] Google internal testing
- [ ] Manual smoke: `Text('inter\u00ADnational')` in a narrow container
shows `inter-` / `national`
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.

4 participants