Skip to content

[WebParagraph] Fixing edge cases for wrapping text (with newlines) - #189858

Merged
auto-submit[bot] merged 32 commits into
flutter:masterfrom
Rusino:edge_cases
Sep 29, 2026
Merged

auto-submit[bot] merged 32 commits into
flutter:masterfrom
Rusino:edge_cases

Conversation

@Rusino

@Rusino Rusino commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes newline (\n) handling and caret/selection geometry in WebParagraph to match SkParagraph behavior, resolving misplaced cursor issues in multiline text fields on the web.

Fixes #188320


Background

In Flutter text editing (RenderEditable), caret positioning, line navigation, and selection rely directly on Paragraph query APIs (getPositionForOffset, getBoxesForRange, getLineBoundary, getLineNumberAt, and getGlyphInfoAt).

Previously, WebParagraph diverged from SkParagraph in several key newline edge cases:

  1. Physical vs. Logical Width: Browser Canvas/DOM text measurement treats \n as whitespace with positive advance width. In SkParagraph, newlines occupy zero physical width while retaining their logical text ranges.
  2. Trailing Newlines: Text ending in a newline (e.g. "Hello\n") must generate an additional empty line where the cursor resides after pressing Enter.
  3. Query Parity: Hit-testing and boundary queries on lines ending with or consisting entirely of newlines returned incorrect offsets or collapsed bounds.

Changes

  • Explicit Hard Line Break Tracking (wrapper.dart):

    • Separated hard line breaks (\n) from collapsible whitespace in _LineBuilder.
    • Added support for generating the trailing empty line when text terminates with a hard line break.
  • Logical vs. Physical Text Ranges (layout.dart, paragraph.dart):

    • Introduced physicalTextRange on TextBlock to exclude \n from physical geometry calculations (bounds, selection rects, line widths) while preserving textRange for logical text operations.
    • Adjusted getGlyphInfoAt to report zero layout width for \n and synthesize end-of-line glyph bounds for newlines outside visual blocks.
    • Aligned getPositionForOffset and getLineBoundary with SkParagraph hit-testing rules, correctly handling empty trailing lines and cluster boundary affinity.
    • Implemented SkParagraph-matching binary search in getLineNumberAt.

Testing

  • Added test/webparagraph/paragraph_edge_cases_test.dart (newline wrapping and empty line creation).
  • Added test/webparagraph/paragraph_selection_caret_test.dart (caret hit-testing, affinities, and selection bounding boxes across multiline text).
  • Added test/webparagraph/paragraph_line_metrics_test.dart (line geometry and width validation).
  • Updated existing webparagraph unit tests to ensure 1:1 parity with SkParagraph.

@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jul 22, 2026
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team labels Jul 22, 2026

@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 updates the web paragraph layout and wrapping logic to explicitly track and handle hard line breaks and empty lines, and optimizes line boundary queries using binary search. The review feedback identifies a missing return statement in TextLayout that results in dead code, a potential StateError when accessing visual blocks on empty lines, leftover debugging code that introduces performance overhead, and redundant control flow statements in TextWrapper.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/paragraph.dart Outdated
Rusino added 3 commits July 22, 2026 11:20
But not for GetBoxesForRanges - it should return a list with an empty
rectange, not an empty list
Apparently, text editing operates on it all the time, so
everything has to be exactly as in SkParagraph
and reacting to Gemini code review
@Rusino Rusino self-assigned this Jul 22, 2026
@Rusino
Rusino requested a review from mdebbar July 29, 2026 17:31
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/layout.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/test/webparagraph/paragraph_queries_test.dart Outdated
@Rusino Rusino changed the title Fixing edge cases for wrapping text (with newlines) [WebParagraph] Fixing edge cases for wrapping text (with newlines) Aug 20, 2026
@Rusino
Rusino requested a review from mdebbar August 27, 2026 17:16
@flutter-dashboard

Copy link
Copy Markdown

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #189858 at sha 645ae6a

@Rusino
Rusino requested a review from mdebbar September 24, 2026 16:41
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/web_paragraph/wrapper.dart Outdated
@Rusino
Rusino requested a review from mdebbar September 24, 2026 18:54
@Rusino

Rusino commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor Author

I updated tests to use generic Paragraph API so I can always test CanvasKit on them (as a source of truth).

@mdebbar mdebbar 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

@mdebbar mdebbar added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 29, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into flutter:master with commit 9c93149 Sep 29, 2026
23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 29, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Sep 30, 2026
flutter/flutter@55b8f88...d649d2b

2026-09-30 43054281+camsim99@users.noreply.github.com [Android] Update the CLI to reject passing engine configuration flags with a prebuilt binary in release mode (flutter/flutter#190870)
2026-09-30 engine-flutter-autoroll@skia.org Roll Skia from af1e8b356dd1 to c7b323126bf0 (1 revision) (flutter/flutter#193569)
2026-09-30 zhongliu88889@gmail.com [web] Respect text affinity in getLineBoundary at a soft wrap (flutter/flutter#192664)
2026-09-30 bkonyi@google.com [tool] Migrate TestCommand and platform runner to modular dependency injection (flutter/flutter#190789)
2026-09-30 engine-flutter-autoroll@skia.org Roll Skia from 3a20a0464d25 to af1e8b356dd1 (1 revision) (flutter/flutter#193564)
2026-09-30 engine-flutter-autoroll@skia.org Roll Skia from 554ef62d11a9 to 3a20a0464d25 (6 revisions) (flutter/flutter#193556)
2026-09-30 116356835+AbdeMohlbi@users.noreply.github.com Use null aware elements in `platform_views.dart` (flutter/flutter#193214)
2026-09-30 116356835+AbdeMohlbi@users.noreply.github.com Replace deprecated `withOpacity` in `flutter_logo.dart` (flutter/flutter#192583)
2026-09-30 engine-flutter-autoroll@skia.org Roll Skia from f441ca223b2b to 554ef62d11a9 (72 revisions) (flutter/flutter#193548)
2026-09-30 joel.winarske@linux.com Fix StrcmpFixed matching prefixes in the Vulkan embedder tests (flutter/flutter#192999)
2026-09-30 jesswon@google.com Update Engine to test Android 17 AVD (flutter/flutter#193239)
2026-09-29 joel.winarske@linux.com Give the Vulkan test context the extensions Impeller requires (flutter/flutter#193002)
2026-09-29 55765052+MohanadAbdallah-mv@users.noreply.github.com Rename "subtext" to "supportingTextPadding" in `FormField`'s documentation (flutter/flutter#183582)
2026-09-29 markzipan@google.com Set --no-js-strongly-connected-components for DDC compiles by default (flutter/flutter#193485)
2026-09-29 jesswon@google.com Split pre-AGP 8.3 module AAR test into a Java 17 pinned target (flutter/flutter#193467)
2026-09-29 32538273+ValentinVignal@users.noreply.github.com Remove no-shuffle from gen_defaults_test (flutter/flutter#192280)
2026-09-29 katelovett@google.com Update guidance on bumping Dart (flutter/flutter#193131)
2026-09-29 dbebawy@users.noreply.github.com Remove vestigial `download_jdk` gclient var (flutter/flutter#188571)
2026-09-29 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#193522)
2026-09-29 bkonyi@google.com [flutter_tools] Lazily initialize AndroidSdk platform and build-tools discovery (flutter/flutter#191972)
2026-09-29 bkonyi@google.com [tool] Migrate Web build subcommands and toolchain to modular dependency injection (flutter/flutter#190783)
2026-09-29 matt.boetger@gmail.com Pin androidx.test dependencies in integration_test (flutter/flutter#193316)
2026-09-29 Deil.Christoph@gmail.com [flutter_tools] Include flutter.js.map in web builds (flutter/flutter#192257)
2026-09-29 Rusino@users.noreply.github.com [WebParagraph] Fixing edge cases for wrapping text (with newlines) (flutter/flutter#189858)
2026-09-29 ryjohn@google.com Bump customer testing version for flutter/devtools update (flutter/flutter#193511)
2026-09-29 engine-flutter-autoroll@skia.org Roll Packages from ba0364a to 3c6ce59 (14 revisions) (flutter/flutter#193507)
2026-09-29 bkonyi@google.com [flutter_tools] Guard socket streams and done futures against socket reset errors (flutter/flutter#192941)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC quncheng@google.com,stuartmorgan@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Rusino added a commit to Rusino/flutter that referenced this pull request Oct 2, 2026
…tHeightBehavior and placeholder metrics

Squash of webparagraph_height_and_placeholders rebased onto master after flutter#189858.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-web Web applications specifically team-web Owned by Web platform team will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[WebParagraph] Text Editing: cursor misplaced in multi-line text fields

3 participants