Reputation: 21111
Flutter apps react on system large text making all Text widgets really large. I want to limit textScaleFactor for Text widgets but I want to do it globally not in each Text widget that I'am using.
Now after enabling large text I got exceptions like
flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ flutter: The following message was thrown during layout: flutter: A RenderFlex overflowed by 14 pixels on the bottom.
What is proper way to do it? One way is to create some wrapper widget instead of using Text, but maybe it can be solved other way?
Upvotes: 20
Views: 16108
Reputation: 1519
Generally most responses advice you to use MediaQuery.of(context), but that's super unefficiemt . Instead, what you can do is this:
// Much more efficient way of getting
// [MediaQueryData] without subscribing for
// MediaQuery updates to avoid unnecessary rebuilds
final MediaQueryData data = MediaQueryData.fromView(
WidgetsBinding.instance.platformDispatcher.views.first,
).copyWith(textScaler: TextScaler.linear(value));
// But we do need to update our layout if orientation changes so do
// this
MediaQuery.orientationOf(context);
return MediaQuery(data: data, child: child);
With this approach you do not subscribe for unnecessary rebuilds of your whole widget tree once something in MediaQuery has changed (for example when keyboard opens, etc).
Also, if you do not care about orientation, you can remove
MediaQuery.orientationOf(context);
call to avoid rebuild during orientation change
Upvotes: 0
Reputation: 2033
Answers have become outdated because MediaQueryData.fromWindow
is deprecated. In addition, MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
is not always desirable because it forces rebuilds frequently, for instance when a keyboard is displayed.
This solution solves both the deprecation and rebuild issues:
@override
Widget build(BuildContext context) {
MediaQueryData windowData = MediaQueryData.fromView(View.of(context));
windowData = windowData.copyWith(
textScaleFactor: 1.0, //or whatever you want
);
return MediaQuery(
data: windowData,
child: MaterialApp(
//...
),
);
}
Upvotes: 4
Reputation: 2757
To limit the textScaleFactor for ALL PAGES in your app, you can do this.
Wrap your MaterialApp
with a MediaQuery
widget with the desired MediaQueryData
data which is created from the window
.
And set useInheritedMediaQuery
to true
.
@override
Widget build(BuildContext context) {
MediaQueryData windowData =
MediaQueryData.fromWindow(WidgetsBinding.instance!.window);
windowData = windowData.copyWith(
textScaleFactor:
windowData.textScaleFactor > 1.4 ? 1.4 : windowData.textScaleFactor,
);
return MediaQuery(
data: windowData,
child: MaterialApp(
useInheritedMediaQuery: true,
//...
),
);
}
Upvotes: 3
Reputation: 690
You can set a limit after which you don't need to scale, as some apple apps do.
MaterialApp(
builder: (BuildContext context, Widget child) {
final MediaQueryData data = MediaQuery.of(context);
return MediaQuery(
data: data.copyWith(
textScaleFactor: data.textScaleFactor > 2.0 ? 2.0 : data.textScaleFactor),
child: child,
);
},
debugShowCheckedModeBanner: false,
title: 'Flutter app',
)
In addition, I added 2 functions that calculate the size for different cases.
double getFixedSize(double textSize) {
return textScaleFactor != 1.0 ? textSize / textScaleFactor : textSize;
}
For example, you can use it for app title text.
double getScaledOrMaxSize(double textSize, double maxScaleFactor) {
return textScaleFactor > maxScaleFactor
? textSize * maxScaleFactor / textScaleFactor
: textSize;
}
This can be used for example for headers that are already large and you don't need to increase them even more.
I hope this helps you make good apps for visually impaired people while still leaving them beautiful.
Upvotes: 11
Reputation: 21111
This solution was presented during Google I/O'19 (around the 20-minute mark):
MaterialApp(
builder: (BuildContext context, Widget child){
final MediaQueryData data = MediaQuery.of(context);
return MediaQuery(
data: data.copyWith(
textScaleFactor: data.textScaleFactor * (_isPassive ? 2 : 1)
),
child: child,
);
},
If you wish that everything had a fixed textScaleFactor
, you could replace data.textScaleFactor * (_isPassive ? 2 : 1)
for a single number. 1.0
would make your design to always follow your fontSize
s.
Upvotes: 42