Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage global textScaleFactor in Flutter app properly?

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?

image 1image 2

like image 362
moonvader Avatar asked May 08 '19 06:05

moonvader


3 Answers

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

like image 190
moonvader Avatar answered Nov 14 '22 04:11

moonvader


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,

      //...
    ),
  );
}

like image 31
Er1 Avatar answered Nov 14 '22 06:11

Er1


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.

  1. Always returns a fixed text size, for any textScaleFactor:
double getFixedSize(double textSize) {
  return textScaleFactor != 1.0 ? textSize / textScaleFactor : textSize;
}

For example, you can use it for app title text.

  1. Returns scaled textSize until specific scale is not reached, which is set by the second maxScaleFactor parameter:
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.

like image 37
Andrew Nodermann Avatar answered Nov 14 '22 04:11

Andrew Nodermann