How do I auto scale down a font in a Text widget to fit the max number of lines?

you can use the auto text package

https://pub.dartlang.org/packages/auto_size_text

after install it, just replace all of 'Text' to 'AutoSizeText' and you will find every things will be good :)


We got 2 ready-to-use solutions to this depending on the constraints you want to have.

1. Scale only

This solution works if you like how the layout looks on some reference screen size, want to treat the text area as a box and just scale up or down the entire box given other sized screens.

Take a reference screen. Lay everything out as intended. Find out the size of the box containing the text (by showing the render tree by pressing 't' during debug for instance). Then wrap the text containing box with a SizedBox of the same size as measured. Then wrap that with your FittedBox.

The restriction is basically that your text is laid out exactly the same way on all screens and you'll have the same aspect ratio for the text box.

2. Multiple references

Given a parent with variable size, first use a LayoutBuilder to get the parent's size during runtime, and then manually adjust the font size in the child text widget to make sure it fits.

The text layout is more fluid and the containing box can have different aspect ratios, but you'd have to check on as many screens sizes as the font size switches you have. Though it's easy to make sure that the text doesn't overflow at any screen size using widget tests


We can also create more complex but automatic means if there are more concrete examples of generalized use cases involving dependent font scaling


An easy drop-in solution is to use FittedBox with fit: BoxFit.scaleDown:

Container(
  width: 100.0,
  child: FittedBox(
    fit: BoxFit.scaleDown,
    // Optionally apply `alignment` to position the text inside the box:
    //alignment: Alignment.topRight,
    child: SizedBox(
      child: Text('\$1,299.99'),
  )
)

This happened in my app:

This is with a long title This is the long title in the horizontal mode

I solved by calculating the maximum number of chars shown in one line and reducing dynamically the Text FontSize if its string length is greater.

Get the char width of your text

First of all, I calculated the width of every char shown. For doing that I needed the effective container width and the number of chars shown. To get the screen width (that actually is the container width), I put my Text widget inside a LayoutBuilder:

new LayoutBuilder(builder: (context, constraint) {
   //screen width got from the LayoutBuilder
   double maxWidth = constraint.biggest.width;

   //I'm going to change this dinamically
   double fontSize = 33.0;

   //return my widget
   return new Text(
        _brand.name,
        maxLines: 1,
        style: new TextStyle(fontSize: fontSize),
    );
}

To get the chars shown before a new line, I put a big string in the Text and I manually counted how many chars were shown in one line. I put this value in a const (maxCharInOneLine) and then I was able to calculate the single char width: I divided the maximum witdh by maxCharInOneLine:

new LayoutBuilder(builder: (context, constraint) {
   //screen width got from the LayoutBuilder
   double maxWidth = constraint.biggest.width;

   //chars shown manually counted
   const int maxCharInOneLine = 17;
   
   //single char width
   int charWidth = (maxWidth / maxCharInOneLine).toInt();
   
   //TO RUN IN DEBUG MODE
   print("Char size with this screen and Text's TextStyle: " + charWidth.toString());
   ...
}

I runned the code above in the debug mode in order to get the charSize value, then I put is in a const and I removed that code that is useless at runtime.

Change dynamically the font

Now, with a constant char width and a variable container width (got from the LayoutBuilder) you can have every time the maximum number of chars that fits in one line.

new LayoutBuilder(builder: (context, constraint) {
    const int charWidth=16;
    //screen width got from the LayoutBuilder
    double maxWidth = constraint.biggest.width;

    //dinamically get the char number that fits in one line with this screen
    int charsPerLine = (maxWidth / charWidth).toInt();

    //if it doesn't fit in a line, reduce the fontSize
    double fontSize = (_brand.name.length <= charsPerLine) ? 33.0 : 23.0;

    return new Text(
        _brand.name,
        maxLines: 1,
        style: new TextStyle(fontSize: fontSize),
    );
}

The result:

The text is dynamically reduced The long title in the horizontal mode has the same size

My solution is not the best, but it works well in my case.

I'd like to get feedbacks. :)

EDIT Aug 2020: You can now simply use the library AutoSizeText

Tags:

Dart

Flutter