[web] Listen to text spacing overrides#172915
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for platform typography settings on the web, allowing Flutter apps to respect user-defined text spacing for properties like line height, letter spacing, and word spacing. The changes are well-structured, adding a TypographySettings class and plumbing it through the platform dispatcher and MediaQuery. My main feedback is on the web implementation of the DomMutationObserver, which has some robustness issues in detecting style changes. I've provided a suggestion to make it more reliable.
ec1dcf9 to
496ed5b
Compare
TypographySettingsTypographySettings
8e12a12 to
899258a
Compare
8dce1f5 to
85239f2
Compare
| return; | ||
| } | ||
| // Disable text spacing properties. | ||
| _updateTypographySettings(const ui.TypographySettings()); |
There was a problem hiding this comment.
I'm not a fan of setting this to an empty TypographySettings to disable this but I don't see another way since configuration.copyWith(typographySettings:null) won't work. I could build a method that makes a new PlatformConfiguration with TypographySettings cleared but it wouldn't be very maintainable if we keep adding members to PlatformConfiguration.
There was a problem hiding this comment.
I vaguely remember somewhere we do something like
void copyWith({SomeClass? object = _somePivateStaticObject}) {
if (object != _somePivateStaticObject) {
// do replace
}
}
is assigning typographySettings an empty TypographySettings or null the same thing? if so we should only pick one of them to avoid this ambiguous. For example just make typographySettings to be not nullable and default to a static/const empty value.
| }); | ||
|
|
||
| /// The height of the text, as a multiple of the font size. | ||
| final double? lineHeight; |
There was a problem hiding this comment.
Should this be lineHeightFactor instead? It seems like a more appropriate name.
In CSS line-height can accept:
- Default: normal
Normalkeyword- Unitless, relative to font size, for example
2. <length>, number followed by units10px<percentage>, number followed by % sign10%, relative to font size
but there seems to be a strong preference for unitless numbers, i.e. numbers relative to font size.
In the extension and bookmarklet I tried both are using unitless values for their CSS.
There was a problem hiding this comment.
Do we need to support other type of unit? If so, can a double support both unitless and px ?
There was a problem hiding this comment.
IMO it doesn't make sense to set line height to an absolute number for a11y purposes. Percentage makes sense but it's basically the same as unitless, i.e. 150% == 1.5.
There was a problem hiding this comment.
I think double can support unitless and px, from what I understand and my observations getComputedStyle will return px values, for word-spacing, letter-spacing, and margin-bottom we can use these 1:1 for their framework counter parts TextStyle.wordSpacing, TextStyle.letterSpacing, and Padding. For line-height we can divide it by the font size and get the lineHeightFactor which would be 1:1 with the frameworks TextStyle.height. So lineHeight would be a factor of fontSize, similar to the frameworks representation.
| ); | ||
| } | ||
| if (typographySettings != null && typographySettings.paragraphSpacing != null) { | ||
| result = Padding( |
There was a problem hiding this comment.
I'm curious to hear people's thoughts about implementing paragraphSpacing like this. On the web the extension and bookmarklet i've been using implement it by using margin-bottom on the p element.
There was a problem hiding this comment.
I think gpay has internal code that turns html into TextSpans
There was a problem hiding this comment.
Interesting, if they're converting for example <p> elements into TextSpans with the expectation that those <p> elements have paragraphSpacing applied to them, then that will likely not work. They would have to instead turn <p> elements into Text widgets for that expectation to be true.
chunhtai
left a comment
There was a problem hiding this comment.
applies paragraph spacing (margin-bottom) by wrapping its contents with a bottom Padding.
Will this handle cases where developer use \n\n to generate paragraph within a single text widget? or is this something that we don't care about?
| }); | ||
|
|
||
| /// The height of the text, as a multiple of the font size. | ||
| final double? lineHeight; |
There was a problem hiding this comment.
Do we need to support other type of unit? If so, can a double support both unitless and px ?
| return; | ||
| } | ||
| // Disable text spacing properties. | ||
| _updateTypographySettings(const ui.TypographySettings()); |
There was a problem hiding this comment.
I vaguely remember somewhere we do something like
void copyWith({SomeClass? object = _somePivateStaticObject}) {
if (object != _somePivateStaticObject) {
// do replace
}
}
is assigning typographySettings an empty TypographySettings or null the same thing? if so we should only pick one of them to avoid this ambiguous. For example just make typographySettings to be not nullable and default to a static/const empty value.
| autofillHints: null, | ||
| contextMenuBuilder: widget.contextMenuBuilder, | ||
| ); | ||
| final ui.TypographySettings? typographySettings = MediaQuery.maybeTypographySettingsOf(context); |
There was a problem hiding this comment.
Not something that blocks this pr
I noticed we start to put more and more text related settings into MediaQuery, but we also have DefaultTextStyle. What are the guideline to decide whether something should be in one vs the other?
There was a problem hiding this comment.
Good point, I feel like this feature is sort of outside the scope of DefaultTextStyle. I think of DefaultTextStyle as a configuration provider that is set up by the application developer, TypographySettings is a configuration set up by the user, and cannot really be updated on the application developers end. So maybe DefaultTextStyle should only include app level configuration, while MediaQuery/TypographySettings includes user level configurations. What do you think about this? I think text scale factor and maybe bolt text would be in line with the type of settings included in TypographySettings.
There was a problem hiding this comment.
What if one day they start to have overlaps? for example, if one day Android let's user to set the system wide font style. Do we duplicate both in DefaultTextStyle and mediaQuery?
There was a problem hiding this comment.
In this case it feels like it would still be important to distinguish between application level configuration and system wide configuration in the case one is preferred over the other. It also feels like this type of system setting is unlikely to exposed because it is a setting that would affect many other apps as well, so the overlap might not be something we have to consider.
There was a problem hiding this comment.
MediaQuery should be considered application level configuration. It is just by default Material app will inherited system level configuration into default MediaQuery. The developer can still override them if they want.
I guess my question is why don't we put TextStyle direclty in MediaQuery to carry the things like text space, or let TextStyle carry a TypographySettings as its property and remove the text spacing, line high...etc.
There was a problem hiding this comment.
I don't have enough context to make a suggestion on which paradigm is better, so it is your call. I am just feel a bit confused at how we structure these settings in different part of widget tree. For example. system provides a,b,c,d all meant for Text-related widget.
when it reaches mediaquery, it bundles (a,b) into a class so it carries (a,b), c, d.
but when it reach widget it unwrap and re-bundled b and c, and it becomes a, (b, c), d.
It is a bit hard to wrap my head around these logical data class in each layer
There was a problem hiding this comment.
Thank you for the clarification, I think I understand your concerns better. As a user it is strange that different text settings are spread across MediaQuery instead of consolidated in one.
In the current state a user needs to pull three members from MediaQuery and apply them across 2 different classes with majority being in TextStyle:
TypographySettings- applies toTextStyle, andPaddingwidget.boldText- applies toTextStyle.textScaler- applies toTextStyle.
it would make sense if a user only had to pull one member from MediaQuery to access text related settings.
final ui.TypographySettings? typographySettings = MediaQuery.maybeTypographySettingsOf(context);
effectiveTextStyle = effectiveTextStyle!.merge(
TextStyle(
height: typographySettings?.lineHeight,//a
letterSpacing: typographySettings?.letterSpacing,//b
wordSpacing: typographySettings?.wordSpacing,//c
fontWeight: MediaQuery.boldTextOf(context) ? FontWeight.bold : null,//d
),
);
if (typographySettings != null && typographySettings.paragraphSpacing != null) {
result = Padding(
padding: EdgeInsets.only(bottom: typographySettings.paragraphSpacing!),//e
child: result,
);
}There was a problem hiding this comment.
(I'm loving this excellent discussion!)
I think what Chun-Heng is suggesting is putting all TypographySettings on MediaQueryData directly.
Something like:
final double? lineScaleFactor = MediaQuery.lineScaleFactorOf(context);
final double? letterSpacing = MediaQuery.letterSpacingOf(context);
final double? wordSpacing = MediaQuery.wordSpacingOf(context);
final double? paragraphSpacing = MediaQuery.paragraphSpacingOf(context);
final FontWeight? fontWeight = MediaQuery.boldTextOf(context) ? FontWeight.bold : null;
if (lineScaleFactor != null || letterSpacing != null || ... ) {
effectiveTextStyle = effectiveTextStyle!.merge(
TextStyle(
height: lineScaleFactor,//a
letterSpacing: letterSpacing,//b
wordSpacing: wordSpacing,//c
fontWeight: fontWeight,//d
),
);
if (paragraphSpacing != null) {
result = Padding(
padding: EdgeInsets.only(bottom: paragraphSpacing!),//e
child: result,
);
}This API does seem more consistent to me.
There was a problem hiding this comment.
Thank you for the API suggestions @loic-sharma! I agree putting the settings directly on MediaQueryData looks more clean and consistent.
There was a problem hiding this comment.
yup that sounds good to me.
Yes I was suggesting two extreme, this or bundle all text related property in on class, anything in between seems weird. Since MediaQuery has been going with putting everything directly in mediaQuery. so I think the safest for now is to go with the current pattern. we can revisit it later if we want to change that and bundle every thing together in the furture
| /// supported. | ||
| final bool supportsShowingSystemContextMenu; | ||
|
|
||
| /// The typography settings for the view this media query is derived from. |
There was a problem hiding this comment.
Maybe,
The user's preferred typography settings.
| ); | ||
| } | ||
| if (typographySettings != null && typographySettings.paragraphSpacing != null) { | ||
| result = Padding( |
There was a problem hiding this comment.
I think gpay has internal code that turns html into TextSpans
I want to say we don't really care about that case since the web doesn't seem to properly handle that case either. The extensions and bookmarklet's i've tried only apply "paragraphSpacing" between "p" elements. |
| if (parsed != null && !parsed.isNaN) { | ||
| styleProperty = parsed; | ||
| } |
There was a problem hiding this comment.
| if (parsed != null && !parsed.isNaN) { | |
| styleProperty = parsed; | |
| } | |
| styleProperty = parsed; |
The extra checks are not necessary. parseFloat() already handles the isNaN case.
| }); | ||
|
|
||
| /// The height of the text, as a multiple of the font size. | ||
| final double? lineHeight; |
There was a problem hiding this comment.
IMO it doesn't make sense to set line height to an absolute number for a11y purposes. Percentage makes sense but it's basically the same as unitless, i.e. 150% == 1.5.
| style.lineHeight = '${spacingDefault}px'; | ||
| style.letterSpacing = '${spacingDefault}px'; | ||
| style.wordSpacing = '${spacingDefault}px'; | ||
| style.margin = '0px 0px ${spacingDefault}px 0px'; |
There was a problem hiding this comment.
Applying these default styles directly on the element, are browser extensions able to override them via global styles?
There was a problem hiding this comment.
If I'm understanding correctly yes they are able to override them via global styles, at least from the ones i've tried.
There was a problem hiding this comment.
Normally, inline styles that are set directly on the element (which is what you are doing here) will override any global styles. But extensions are using !important to force their styles to take precedence: https://github.com/actum/text-spacing-editor/blob/6782a09bfd55d5ad7791c498804ecfbf28ece0dc/helpers/buildCSSToInject.ts#L20-L30
So you are good 🙂
b45142c to
582f2e5
Compare
8bae26c to
dccfc06
Compare
…ake into account strutStyle.forceStrutHeight
4f31b33 to
572abd3
Compare
| bool? forceStrutHeight, | ||
| String? debugLabel, | ||
| String? package, | ||
| }) : assert(fontSize == null || fontSize > 0), |
There was a problem hiding this comment.
Still the font size can't be negative? or this is checked in the unnamed constructor?
There was a problem hiding this comment.
That is checked in the unnamed constructor.
| String? package, | ||
| }) : assert(fontSize == null || fontSize > 0), | ||
| assert(leading == null || leading >= 0), | ||
| assert(package == null || fontFamily != null || fontFamilyFallback != null), |
There was a problem hiding this comment.
Are these two asserts also in the unnamed constructor?
There was a problem hiding this comment.
Yes these asserts are also present in the unnamed constructor.
| }) : this( | ||
| fontFamily: fontFamily != null | ||
| ? (package == null ? fontFamily : 'packages/$package/$fontFamily') | ||
| : textStyle.fontFamily, |
There was a problem hiding this comment.
I guess this is not a bug introduced in this patch, but the fontFamilyFallback needs to embed the package path too.
There was a problem hiding this comment.
I think it does end up doing this but through a getter.
List<String>? get fontFamilyFallback {
if (_package != null && _fontFamilyFallback != null) {
return _fontFamilyFallback.map((String family) => 'packages/$_package/$family').toList();
}
return _fontFamilyFallback;
}
|
|
||
| spellCheckResults = SpellCheckResults(text, suggestions); | ||
| renderEditable.text = buildTextSpan(); | ||
| final double? lineHeightScaleFactor = mounted |
There was a problem hiding this comment.
why's the mounted check needed? Generally the state should unregister itself from these services in dispose?
There was a problem hiding this comment.
There were no mounted checks before.
There was a problem hiding this comment.
Because this logic is happening in an async method, the lint complained we were not checking mounted.
| // must also be updated. | ||
| // 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 { |
There was a problem hiding this comment.
nit: can these just be two private functions without the utility class?
| return TextSpan( | ||
| text: textSpan.text, | ||
| children: textSpan.children?.map((InlineSpan child) { | ||
| if (child is TextSpan && child.runtimeType == TextSpan) { |
There was a problem hiding this comment.
why the runtimeType == check? IIRC the reason we need the is TextSpan is that InlineSpan doesn't specify its child model. But TextSpan does specify its child model.
There was a problem hiding this comment.
If someone is adding new features to their TextSpan subclass they would be lost during this recreation because we don’t own the subclass and cannot recreate it accurately.
| /// supported. | ||
| final bool supportsShowingSystemContextMenu; | ||
|
|
||
| /// The height of the text, as a multiple of the font size. |
There was a problem hiding this comment.
nit: height override? same for the other two parameters since they take precedence over existing local text style values?
|
Closing this in favor of #178081 |
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 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>
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>
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>
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>
Original PR/Discussion: flutter/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 #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>
Framework:
EditableText/SelectableText, applieslineHeightScaleFactorOverride,wordSpacingOverride, andletterSpacingOverrideto it'sTextStylesimilarly to how we already do for bold platform overrides. NoteSelectableTextis built onEditableTextso it also applies these overrides.Text, applieslineHeightScaleFactorOverride,wordSpacingOverride, andletterSpacingOverrideto it'sTextStylesimilarly to how we already do for bold platform overrides.MediaQueryData.lineHeightScaleFactorOverrideandmaybeLineHeightScaleFactorOverrideOf(context).MediaQueryData.letterSpacingOverrideandmaybeLetterSpacingOverrideOf(context).MediaQueryData.wordSpacingOverrideandmaybeWordSpacingOverrideOf(context).MediaQueryData.paragraphSpacingOverrideandmaybeParagraphSpacingOf(context).Engine:
PlatformDispatcherAPI that hold the text spacing properties that are overridden on the web.lineHeightScaleFactorOverride,letterSpacingOverride,wordSpacingOverride, andparagraphSpacingOverrideon the web by attaching aResizeObserverto an off-screen hidden element, when its size changes we capture its text spacing CSS properties, and notify the framework throughonMetricsChanged.Fixes #142712
Screen.Recording.2025-07-30.at.2.50.37.PM.mov
Pre-launch Checklist
///).