Skip to content

Listen to text spacing overrides on the web#178081

Merged
auto-submit[bot] merged 13 commits into
flutter:masterfrom
Renzo-Olivares:typography-overrides-squash
Nov 11, 2025
Merged

Listen to text spacing overrides on the web#178081
auto-submit[bot] merged 13 commits into
flutter:masterfrom
Renzo-Olivares:typography-overrides-squash

Conversation

@Renzo-Olivares

@Renzo-Olivares Renzo-Olivares commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

Original PR/Discussion: #172915

Framework:

  • EditableText/SelectableText, applies lineHeightScaleFactorOverride, wordSpacingOverride, and letterSpacingOverride to it's TextStyle similarly to how we already do for bold platform overrides. Note SelectableText is built on EditableText so it also applies these overrides.
  • Text, applies lineHeightScaleFactorOverride, wordSpacingOverride, and letterSpacingOverride to it's TextStyle similarly to how we already do for bold platform overrides.
  • Exposes line height override through MediaQueryData.lineHeightScaleFactorOverride and maybeLineHeightScaleFactorOverrideOf(context).
  • Exposes letter spacing override through MediaQueryData.letterSpacingOverride and maybeLetterSpacingOverrideOf(context).
  • Exposes word spacing override through MediaQueryData.wordSpacingOverride and maybeWordSpacingOverrideOf(context).
  • Exposes paragraph spacing override through MediaQueryData.paragraphSpacingOverride and maybeParagraphSpacingOverrideOf(context).
  • MediaQuery.applyTextStyleOverrides() \ MediaQueryData.applyTextStyleOverrides() to be able to reset/override the text spacing settings on MediaQueryData.

Engine:

  • Introduces new members on PlatformDispatcher API that hold the text spacing properties that are overridden on the web.
  • We provide the lineHeightScaleFactorOverride, letterSpacingOverride, wordSpacingOverride, and paragraphSpacingOverride on the web by attaching a ResizeObserver to an off-screen hidden element, when its size changes we capture its text spacing CSS properties, and notify the framework through onMetricsChanged.

Fixes #142712

Screen.Recording.2025-07-30.at.2.50.37.PM.mov

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

This commit squashes 114 commits from the typography-overrides-squash branch. The changes include updates to StrutStyle, EditableText, MediaQuery, and other text-related widgets. It also includes changes to the platform dispatcher.
@github-actions github-actions Bot added a: tests "flutter test", flutter_test, or one of our tests a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. engine flutter/engine related. See also e: labels. f: cupertino flutter/packages/flutter/cupertino repository platform-web Web applications specifically labels Nov 5, 2025
@Renzo-Olivares Renzo-Olivares changed the title feat: Implement typography overrides Listen to text spacing overrides on the web Nov 5, 2025

@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 introduces typography overrides from the platform, allowing for better accessibility and consistency with system text settings. The changes span across the engine and framework, adding new properties to PlatformDispatcher and MediaQueryData, and applying them in Text, EditableText, and SelectableText widgets. The web engine implementation cleverly uses a ResizeObserver to detect CSS changes. My review has identified a critical issue in the web implementation that could lead to performance problems, a high-severity bug in a copyWith method, and a couple of medium-severity issues related to code correctness and duplication. Overall, this is a great feature addition, and with these fixes, it will be solid.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart
Comment thread engine/src/flutter/lib/ui/platform_dispatcher.dart
Comment thread packages/flutter/lib/src/widgets/editable_text.dart Outdated
Comment thread packages/flutter/lib/src/widgets/editable_text.dart
@Renzo-Olivares

Renzo-Olivares commented Nov 6, 2025

Copy link
Copy Markdown
Contributor Author

Changes since last review:

  • Added MediaQueryData.applyTextStyleOverrides to reset/set text spacing overrides, text spacing overrides removed from copyWith since they cannot be reset to null through it.
  • StrutStyle.merge added so we can create StrutStyle with height overridden with lineHeightScaleFactor.
  • Text spacing overrides now penetrate text span tree using _OverridingTextStyleTextSpanUtils, a wrapper that recreates text spans (excluding subclasses) with the provided overrides. This is temporary as we design an API that allows us to override styles for an entire InlineSpan tree. Work for that can be tracked here Provide a method for InlineSpan to override the styles of its children #177952

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

web side looks good to me!

@LongCatIsLooong LongCatIsLooong 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 with nits

Comment thread packages/flutter/lib/src/painting/strut_style.dart

spellCheckResults = SpellCheckResults(text, suggestions);
renderEditable.text = buildTextSpan();
final double? lineHeightScaleFactor = mounted

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.

why's the mounted check needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Because this logic is happening in an async method, the lint complained we were not checking mounted. https://dart.dev/tools/linter-rules/use_build_context_synchronously

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.

On line 4476 we return if suggestions == null. Could we update that to return also if !mounted, and then simplify this logic? I don't feel strongly, just a thought.

@Renzo-Olivares Renzo-Olivares Nov 8, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think we should not exit early if suggestions != null and !mounted in that case we still want the spell check to work and that should continue to work as expected as it didn't access the context before.

suggestions == null || !mounted - returns early even when spell check might be expected

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.

Uhm the state should stop doing spell checking and stop receiving spell checking updates as soon as it's disposed so there should be no need for the mount check? Do we have that lint enabled in the framework?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah this lint is currently enabled in the framework

- use_build_context_synchronously
.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

On line 4476 we return if suggestions == null. Could we update that to return also if !mounted, and then simplify this logic? I don't feel strongly, just a thought.

I ended up going with this suggestion. It makes sense that we don't spell check at all while unmounted.

// TODO(Renzo-Olivares): Remove after investigating a solution for overriding all
// styles for children in an [InlineSpan] tree, see: https://github.com/flutter/flutter/issues/177952.
class _OverridingTextStyleTextSpanUtils {
static TextSpan applyTextSpacingOverrides({

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.

nit: maybe remove the utility class an expose the functions instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think I like the class because i'm able to place the TODO on the entire class. Are there any benefits to placing them directly in Text as functions?

/// supported.
final bool supportsShowingSystemContextMenu;

/// The height of the text, as a multiple of the font size.

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.

nit: maybe: text override, same for the other two fields

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated to Overrides the height of the text...

gestureSettings,
Object.hashAll(displayFeatures),
supportsShowingSystemContextMenu,
Object.hash(

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.

why are these 4 grouped together?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We have exceeded the 20 objects that this method takes, so I ended up grouping them. Should I do something else instead in this case?

required double? wordSpacingOverride,
required double? paragraphSpacingOverride,
required super.child,
}) : data = MediaQuery.of(context).applyTextStyleOverrides(

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.

This looks error-prone. data is evaluated on construction so if the user caches this widget this doesn't get rebuilt even if the ancestor media query changes.

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.

I'd just wrap this in a builder, and you don't have to take context if you do that.

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.

Or move this to MediaQueryData.

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.

Oh good catch. +1 to wrapping this in a builder and removing the context argument.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Went with the Builder suggestion. Thank you!

Comment thread packages/flutter/lib/src/widgets/text.dart Outdated
Comment thread packages/flutter/test/widgets/selectable_text_test.dart
wordSpacingOverride: 9.0,
paragraphSpacingOverride: 9.0,
),
child: Builder(

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.

If you switch to Weiyu's suggestion of adding a Builder in MediaQuery.applyTextStyleOverrides, don't forget to remove the Builders from these tests :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good call, I did end up going with the Builder suggestion.

@loic-sharma loic-sharma 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, excellent work!

engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 12, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 13, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 14, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 14, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 14, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 14, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 15, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 15, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 16, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 16, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 16, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 17, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 17, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 17, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 17, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 17, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Nov 17, 2025
IvoneDjaja pushed a commit to IvoneDjaja/flutter that referenced this pull request Nov 22, 2025
Original PR/Discussion: flutter#172915

# Framework:
* `EditableText`/`SelectableText`, applies
`lineHeightScaleFactorOverride`, `wordSpacingOverride`, and
`letterSpacingOverride` to it's `TextStyle` similarly to how we already
do for bold platform overrides. Note `SelectableText` is built on
`EditableText` so it also applies these overrides.
* `Text`, applies `lineHeightScaleFactorOverride`,
`wordSpacingOverride`, and `letterSpacingOverride` to it's `TextStyle`
similarly to how we already do for bold platform overrides.
* Exposes line height override through
`MediaQueryData.lineHeightScaleFactorOverride` and
`maybeLineHeightScaleFactorOverrideOf(context)`.
* Exposes letter spacing override through
`MediaQueryData.letterSpacingOverride` and
`maybeLetterSpacingOverrideOf(context)`.
* Exposes word spacing override through
`MediaQueryData.wordSpacingOverride` and
`maybeWordSpacingOverrideOf(context)`.
* Exposes paragraph spacing override through
`MediaQueryData.paragraphSpacingOverride` and
`maybeParagraphSpacingOverrideOf(context)`.
* `MediaQuery.applyTextStyleOverrides()` \
`MediaQueryData.applyTextStyleOverrides()` to be able to reset/override
the text spacing settings on `MediaQueryData`.

# Engine:
* Introduces new members on `PlatformDispatcher` API that hold the text
spacing properties that are overridden on the web.
* We provide the `lineHeightScaleFactorOverride`,
`letterSpacingOverride`, `wordSpacingOverride`, and
`paragraphSpacingOverride` on the web by attaching a `ResizeObserver` to
an off-screen hidden element, when its size changes we capture its text
spacing CSS properties, and notify the framework through
`onMetricsChanged`.

Fixes flutter#142712


https://github.com/user-attachments/assets/aaaa3e74-c232-4956-acd2-ae1a4487e415

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: Renzo Olivares <roliv@google.com>
mboetger pushed a commit to mboetger/flutter that referenced this pull request Dec 2, 2025
Original PR/Discussion: flutter#172915

# Framework:
* `EditableText`/`SelectableText`, applies
`lineHeightScaleFactorOverride`, `wordSpacingOverride`, and
`letterSpacingOverride` to it's `TextStyle` similarly to how we already
do for bold platform overrides. Note `SelectableText` is built on
`EditableText` so it also applies these overrides.
* `Text`, applies `lineHeightScaleFactorOverride`,
`wordSpacingOverride`, and `letterSpacingOverride` to it's `TextStyle`
similarly to how we already do for bold platform overrides.
* Exposes line height override through
`MediaQueryData.lineHeightScaleFactorOverride` and
`maybeLineHeightScaleFactorOverrideOf(context)`.
* Exposes letter spacing override through
`MediaQueryData.letterSpacingOverride` and
`maybeLetterSpacingOverrideOf(context)`.
* Exposes word spacing override through
`MediaQueryData.wordSpacingOverride` and
`maybeWordSpacingOverrideOf(context)`.
* Exposes paragraph spacing override through
`MediaQueryData.paragraphSpacingOverride` and
`maybeParagraphSpacingOverrideOf(context)`.
* `MediaQuery.applyTextStyleOverrides()` \
`MediaQueryData.applyTextStyleOverrides()` to be able to reset/override
the text spacing settings on `MediaQueryData`.

# Engine:
* Introduces new members on `PlatformDispatcher` API that hold the text
spacing properties that are overridden on the web.
* We provide the `lineHeightScaleFactorOverride`,
`letterSpacingOverride`, `wordSpacingOverride`, and
`paragraphSpacingOverride` on the web by attaching a `ResizeObserver` to
an off-screen hidden element, when its size changes we capture its text
spacing CSS properties, and notify the framework through
`onMetricsChanged`.

Fixes flutter#142712


https://github.com/user-attachments/assets/aaaa3e74-c232-4956-acd2-ae1a4487e415

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: Renzo Olivares <roliv@google.com>
reidbaker pushed a commit to AbdeMohlbi/flutter that referenced this pull request Dec 10, 2025
Original PR/Discussion: flutter#172915

# Framework:
* `EditableText`/`SelectableText`, applies
`lineHeightScaleFactorOverride`, `wordSpacingOverride`, and
`letterSpacingOverride` to it's `TextStyle` similarly to how we already
do for bold platform overrides. Note `SelectableText` is built on
`EditableText` so it also applies these overrides.
* `Text`, applies `lineHeightScaleFactorOverride`,
`wordSpacingOverride`, and `letterSpacingOverride` to it's `TextStyle`
similarly to how we already do for bold platform overrides.
* Exposes line height override through
`MediaQueryData.lineHeightScaleFactorOverride` and
`maybeLineHeightScaleFactorOverrideOf(context)`.
* Exposes letter spacing override through
`MediaQueryData.letterSpacingOverride` and
`maybeLetterSpacingOverrideOf(context)`.
* Exposes word spacing override through
`MediaQueryData.wordSpacingOverride` and
`maybeWordSpacingOverrideOf(context)`.
* Exposes paragraph spacing override through
`MediaQueryData.paragraphSpacingOverride` and
`maybeParagraphSpacingOverrideOf(context)`.
* `MediaQuery.applyTextStyleOverrides()` \
`MediaQueryData.applyTextStyleOverrides()` to be able to reset/override
the text spacing settings on `MediaQueryData`.

# Engine:
* Introduces new members on `PlatformDispatcher` API that hold the text
spacing properties that are overridden on the web.
* We provide the `lineHeightScaleFactorOverride`,
`letterSpacingOverride`, `wordSpacingOverride`, and
`paragraphSpacingOverride` on the web by attaching a `ResizeObserver` to
an off-screen hidden element, when its size changes we capture its text
spacing CSS properties, and notify the framework through
`onMetricsChanged`.

Fixes flutter#142712


https://github.com/user-attachments/assets/aaaa3e74-c232-4956-acd2-ae1a4487e415

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

---------

Co-authored-by: Renzo Olivares <roliv@google.com>
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: tests "flutter test", flutter_test, or one of our tests a: text input Entering text in a text field or keyboard related problems engine flutter/engine related. See also e: labels. f: cupertino flutter/packages/flutter/cupertino repository framework flutter/packages/flutter repository. See also f: labels. platform-web Web applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[web:a11y] respect text spacing browser settings

6 participants