Deprecate textScaleFactor in favor of TextScaler
Summary
#In preparation for adopting the Android 14 nonlinear font scaling feature, all occurrences of textScaleFactor
in the Flutter framework have been deprecated and replaced by TextScaler
.
Context
#Many platforms allow users to scale textual contents up or down globally in system preferences. In the past, the scaling strategy was captured as a single double
value named textScaleFactor
, as text scaling was proportional: scaledFontSize = textScaleFactor x unScaledFontSize
. For example, when textScaleFactor
is 2.0 and the developer-specified font size is 14.0, the actual font size is 2.0 x 14.0 = 28.0.
With the introduction of Android 14 nonlinear font scaling, larger text gets scaled at a lesser rate as compared to smaller text, to prevent excessive scaling of text that is already large. The textScaleFactor
scalar value used by "proportional" scaling is not enough to represent this new scaling strategy. The Replaces textScaleFactor
with TextScaler
pull request introduced a new class TextScaler
to replace textScaleFactor
in preparation for this new feature. Nonlinear text scaling is introduced in a different pull request.
Description of change
#Introducing a new interface TextScaler
, which represents a text scaling strategy.
abstract class TextScaler {
double scale(double fontSize);
double get textScaleFactor; // Deprecated.
}
Use the scale
method to scale font sizes instead of textScaleFactor
. The textScaleFactor
getter provides an estimated textScaleFactor
value, it is for backward compatibility purposes and is already marked as deprecated, and will be removed in a future version of Flutter.
The new class has replaced double textScaleFactor
(double textScaleFactor
-> TextScaler textScaler
), in the following APIs:
Painting library
#Affected APIs | Error Message |
---|---|
InlineSpan.build({ double textScaleFactor = 1.0 }) argument | The named parameter 'textScaleFactor' isn't defined. |
TextStyle.getParagraphStyle({ double TextScaleFactor = 1.0 }) argument | The named parameter 'textScaleFactor' isn't defined. |
TextStyle.getTextStyle({ double TextScaleFactor = 1.0 }) argument | 'textScaleFactor' is deprecated and shouldn't be used. |
TextPainter({ double TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
TextPainter.textScaleFactor getter and setter | 'textScaleFactor' is deprecated and shouldn't be used. |
TextPainter.computeWidth({ double TextScaleFactor = 1.0 }) argument | 'textScaleFactor' is deprecated and shouldn't be used. |
TextPainter.computeMaxIntrinsicWidth({ double TextScaleFactor = 1.0 }) argument | 'textScaleFactor' is deprecated and shouldn't be used. |
Rendering library
#Affected APIs | Error Message |
---|---|
RenderEditable({ double TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
RenderEditable.textScaleFactor getter and setter | 'textScaleFactor' is deprecated and shouldn't be used. |
RenderParagraph({ double TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
RenderParagraph.textScaleFactor getter and setter | 'textScaleFactor' is deprecated and shouldn't be used. |
Widgets library
#Affected APIs | Error Message |
---|---|
MediaQueryData({ double TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
MediaQueryData.textScaleFactor getter | 'textScaleFactor' is deprecated and shouldn't be used. |
MediaQueryData.copyWith({ double? TextScaleFactor }) argument | 'textScaleFactor' is deprecated and shouldn't be used. |
MediaQuery.maybeTextScaleFactorOf(BuildContext context) static method | 'maybeTextScaleFactorOf' is deprecated and shouldn't be used. |
MediaQuery.textScaleFactorOf(BuildContext context) static method | 'textScaleFactorOf' is deprecated and shouldn't be used. |
RichText({ double TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
RichText.textScaleFactor getter | 'textScaleFactor' is deprecated and shouldn't be used. |
Text({ double? TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
Text.rich({ double? TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
Text.textScaleFactor getter | 'textScaleFactor' is deprecated and shouldn't be used. |
EditableText({ double? TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
EditableText.textScaleFactor getter | 'textScaleFactor' is deprecated and shouldn't be used. |
Material library
#Affected APIs | Error Message |
---|---|
SelectableText({ double? TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
SelectableText.rich({ double? TextScaleFactor = 1.0 }) constructor argument | 'textScaleFactor' is deprecated and shouldn't be used. |
SelectableText.textScaleFactor getter | 'textScaleFactor' is deprecated and shouldn't be used. |
Migration guide
#Widgets provided by the Flutter framework are already migrated. Migration is needed only if you're using any of the deprecated symbols listed in the previous tables.
Migrating your APIs that expose textScaleFactor
#Before:
abstract class _MyCustomPaintDelegate {
void paint(PaintingContext context, Offset offset, double textScaleFactor) {
}
}
After:
abstract class _MyCustomPaintDelegate {
void paint(PaintingContext context, Offset offset, TextScaler textScaler) {
}
}
Migrating code that consumes textScaleFactor
#If you're not currently using textScaleFactor
directly, but rather passing it to a different API that receives a textScaleFactor
, and the receiver API has already been migrated, then it's relatively straightforward:
Before:
RichText(
textScaleFactor: MediaQuery.textScaleFactorOf(context),
...
)
After:
RichText(
textScaler: MediaQuery.textScalerOf(context),
...
)
If the API that provides textScaleFactor
hasn't been migrated, consider waiting for the migrated version.
If you wish to compute the scaled font size yourself, use TextScaler.scale
instead of the *
binary operator:
Before:
final scaledFontSize = textStyle.fontSize * MediaQuery.textScaleFactorOf(context);
After:
final scaledFontSize = MediaQuery.textScalerOf(context).scale(textStyle.fontSize);
If you are using textScaleFactor
to scale dimensions that are not font sizes, there are no generic rules for migrating the code to nonlinear scaling, and it might require the UI to be implemented differently. Reusing the MyTooltipBox
example:
MyTooltipBox(
size: chatBoxSize * textScaleFactor,
child: RichText(..., style: TextStyle(fontSize: 20)),
)
You could choose to use the "effective" text scale factor by applying the TextScaler
on the font size 20: chatBoxSize * textScaler.scale(20) / 20
, or redesign the UI and let the widget assume its own intrinsic size.
Overriding the text scaling strategy in a widget subtree
#To override the existing TextScaler
used in a widget subtree, override the MediaQuery
like so:
Before:
MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 2.0),
child: child,
)
After:
MediaQuery(
data: MediaQuery.of(context).copyWith(textScaler: _myCustomTextScaler),
child: child,
)
However, it's rarely needed to create a custom TextScaler
subclass. MediaQuery.withNoTextScaling
(which creates a widget that disables text scaling altogether for its child subtree), and MediaQuery.withClampedTextScaling
(which creates a widget that restricts the scaled font size to within the range [minScaleFactor * fontSize, maxScaleFactor * fontSize]
), are convenience methods that cover common cases where the text scaling strategy needs to be overridden.
Examples
#Disabling Text Scaling For Icon Fonts
Before:
MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: IconTheme(
data: ..,
child: icon,
),
)
After:
MediaQuery.withNoTextScaling(
child: IconTheme(
data: ...
child: icon,
),
)
Preventing Contents From Overscaling
Before:
final mediaQueryData = MediaQuery.of(context);
MediaQuery(
data: mediaQueryData.copyWith(textScaleFactor: math.min(mediaQueryData.textScaleFactor, _kMaxTitleTextScaleFactor),
child: child,
)
After:
MediaQuery.withClampedTextScaling(
maxScaleFactor: _kMaxTitleTextScaleFactor,
child: title,
)
Disabling Nonlinear Text Scaling
If you want to temporarily opt-out of nonlinear text scaling on Android 14 until your app is fully migrated, put a modified MediaQuery
at the top of your app's widget tree:
runApp(
Builder(builder: (context) {
final mediaQueryData = MediaQuery.of(context);
final mediaQueryDataWithLinearTextScaling = mediaQueryData
.copyWith(textScaler: TextScaler.linear(mediaQueryData.textScaler.textScaleFactor));
return MediaQuery(data: mediaQueryDataWithLinearTextScaling, child: realWidgetTree);
}),
);
This trick uses the deprecated textScaleFactor
API and will stop working once it's removed from the Flutter API.
Timeline
#Landed in version: 3.13.0-4.0.pre
In stable release: 3.16
References
#API documentation:
TextScaler
MediaQuery.textScalerOf
MediaQuery.maybeTextScalerOf
MediaQuery.withNoTextScaling
MediaQuery.withClampedTextScaling
Relevant issues:
Relevant PRs:
Unless stated otherwise, the documentation on this site reflects the latest stable version of Flutter. Page last updated on 2024-04-04. View source or report an issue.