This article will take you deep into the “cold” knowledge of font and text rendering in Flutter development. It will help you understand and add to the “useless” knowledge of font rendering in Flutter.

After all, there is so little about it

Start with a simple Text display, as shown in the following code. When you run it, you’ll see an H appear in the screen, its fontSize is 100, and Text is placed in a Container of 200 height. Then if someone asks you: Text shows how high the H should be, you know?


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            alignment: Alignment.center,
            child: new Row(
              children: <Widget>[
                Container(
                  child: new Text(
                    "H",
                    style: TextStyle(
                      fontSize: 100,
                    ),
                  ),
                ),
                Container(
                  height: 100,
                  width: 100,
                  color: Colors.red,
                )
              ],
            ),
          )

        ),
      ),
    );
  }
Copy the code

A, TextStyle

To solve this problem, we first add a blue background to the Container where the Text is located and a small red square of 100 × 100 size for comparison, as shown below.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            alignment: Alignment.center,
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  child: new Text(
                    "H",
                    style: TextStyle(
                      fontSize: 100,
                    ),
                  ),

                ),
                Container(
                  height: 100,
                  width: 100,
                  color: Colors.red,
                )
              ],
            ),
          )

        ),
      ),
    );
  }
Copy the code

The blue Container is bigger than 100, but the black H itself is not bigger than the red square. So is the height of the blue area the height of the Text? How is the size of the blue area formed?

In fact, the blue area in front is the line height of the font. The first thing you need to explain about this line height is the height argument in the TextStyle.

By default, the height parameter is null. When we set it to 1, as shown below, we can see that the height of the blue area is aligned with the red square and becomes 100 height, i.e. the line height becomes 100, and the letter H is completely displayed in the blue area.

What is height? The height parameter in TextStyle is set to a multiple of fontSize.

  • whenheightWhen null, font is used by default for line heightmetrics(themetricsExplained later);
  • whenheightNot empty, row height isheight * fontSizeThe size of the;

As shown in the figure below, the contrast between the blue area and the red area is the contrast between the height of null and 1.

The BaseLine also explains why the H of fontSize 100 is not filled with the blue area of height 100.

According to the schematic effect in the figure above, in the red area with height of 1, the letter H should also be displayed above the baseline, while the bottom area of the baseline is reserved for letters such as G and J, so as shown below, add the letter G to the Text and enable the Text baseline display of Flutter debugging. The green baseline rendered by Flutter can also be seen as expected.

Forget screenshots by G, imagine.

Then, as shown in the code below, when we set height to 2 and add a purple background to the top Container of 200 height, the result is shown below, where the blue block is just full of purple squares. Because text with fontSize 100 is exactly 200 in height after X2.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            color: Colors.purple,
            alignment: Alignment.center,
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  child: new Text(
                    "Hg",
                    style: TextStyle(
                      fontSize: 100,
                      height: 2,
                    ),
                  ),

                ),
                Container(
                  height: 100,
                  width: 100,
                  color: Colors.red,
                )
              ],
            ),
          )

        ),
      ),
    );
  }
Copy the code

However, Hg here is offset downward, why this offset will be explained later, and there will be new comparisons.

Finally, the figure below shows the official comparison of the heights occupied by Text under the height parameters of different Textstyles.

Second, the StrutStyle

Let’s go back to the default font metrics. What are the default font metrics? Which brings us to StrutStyle.

Add StrutStyle to the previous code as shown below:

  • Set upforceStrutHeightTrue because onlyforceStrutHeightTo force a resetTextheightProperties;
  • Set upStrutStyleheightSet to1So that theTextStyleIn theheightIs equal to the2There is no effect.
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            color: Colors.purple,
            alignment: Alignment.center,
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  child: new Text(
                    "Hg",
                    style: TextStyle(
                      fontSize: 100,
                      height: 2,
                    ),
                    strutStyle: StrutStyle(
                      forceStrutHeight: true,
                      fontSize: 100,
                      height: 1
                    ),

                  ),

                ),
                Container(
                  height: 100,
                  width: 100,
                  color: Colors.red,
                )
              ],
            ),
          )

        ),
      ),
    );
  }
Copy the code

The effect is shown below. Although the TextStyle height is 2, the StrutStyle height is 1.

The effect of height is still a multiple of fontSize, but the effect is still a multiple of fontSize. Ascent + descent = fontSize

  • Ascent represents the portion above the baseline;

  • Descent represents half of the baseline

  • The combined effect is shown in the figure below:

Ascent and Descent cannot be set separately with codes in Flutter.

StrutStyle fontSize does not work the same as TextStyle fontSize: When we set StrutStyle fontSize to 50 and TextStyle fontSize to 100, as shown below, we can see that the black fontSize has not changed, while the blue fontSize has changed to 50.

Some people say what’s the use of a fontSize like StrutStyle?

If you change the Text into two lines of “hg-nhG”, StrutStyle fontSize will also affect the line height.

In addition, there is another parameter in StrutStyle that affects the row height: leading.

As shown in the figure below, the complete control combination of font row height in Flutter can be achieved by adding leading, which is null by default and its effect is also a multiple of fontSize, and its distribution is evenly divided up and down.

So, as shown in the following code, when StrutStyle fontSize is 100, height is 1, leading is 1, you can see that the size of leading changes the blue area to 200, and thus overlapped the purple area again. The contrast with previous Hg is centered in this filling display.


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            color: Colors.purple,
            alignment: Alignment.center,
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  child: new Text(
                    "Hg",
                    style: TextStyle(
                      fontSize: 100,
                      height: 2,
                    ),
                    strutStyle: StrutStyle(
                      forceStrutHeight: true,
                      fontSize: 100,
                      height: 1,
                      leading: 1
                    ),

                  ),

                ),
                Container(
                  height: 100,
                  width: 100,
                  color: Colors.red,
                )
              ],
            ),
          )

        ),
      ),
    );
  }
Copy the code

Because leading is evenly divided up and down, and height is amplified according to the part of ascent and Descent, ascent is obviously much larger than Descent, so when the height of the preceding TextStyle is 2, the whole will shift downward after filling.

Third, backgroundColor

This should give you a basic understanding of the text size, measurement, and line height of Flutter, followed by a property called the backgroundColor of the TextStyle.

This property is introduced to create a contrast with the previous content and to clear up some misunderstandings.

As shown in the following code, you can see that StrutStyle has a fontSize of 100 and a height of 1. As described above, the blue area should be the same size as the red square.

We then set the backgroundColor of the TextStyle to green with transparency. The result is shown in the image below. You can see that the area of backgroundColor exceeds StrutStyle and is displayed as the default font metric.


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            color: Colors.purple,
            alignment: Alignment.center,
            child: new Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  color: Colors.blue,
                  child: new Text(
                    "Hg",
                    style: TextStyle(
                      fontSize: 100,
                      backgroundColor: Colors.green.withAlpha(180)
                    ),
                    strutStyle: StrutStyle(
                      forceStrutHeight: true,
                      fontSize: 100,
                      height: 1,
                    ),

                  ),

                ),
                Container(
                  height: 100,
                  width: 100,
                  color: Colors.red,
                )
              ],
            ),
          )

        ),
      ),
    );
  }
Copy the code

Isn’t it interesting that the font metric has always been ascent + Descent = fontSize by default, we can change the height of TextStyle or StrutStyle to change the line height, But essentially the fontSize hasn’t changed.

If you change the input to “H\ng”, you can see a more interesting effect as shown below.

Four, TextBaseline

Finally, one more property: TextBaseline of TextStyle, because this property has been misleading.

TextBaseline has two attributes, Alphabetic and ideographic. To make it easier to explain their effects, we use CustomPaint to map the different baseline positions as shown in the code below.


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
          alignment: Alignment.center,
          child: Container(
            height: 200,
            width: 400,
            color: Colors.purple,
            child: CustomPaint(
              painter: Text2Painter(),
            ),
          )

        ),
      ),
    );
  }
  
class Text2Painter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    var baseLine = TextBaseline.alphabetic;
    //var baseLine = TextBaseline.ideographic;

    final textStyle =
        TextStyle(color: Colors.white, fontSize: 100, textBaseline: baseLine);
    final textSpan = TextSpan(
      text: 'My word', style: textStyle, ); final textPainter = TextPainter( text: textSpan, textDirection: TextDirection.ltr, ); textPainter.layout( minWidth: 0, maxWidth: size.width, ); The final left = 0.0; The final top = 0.0; final right = textPainter.width; final bottom = textPainter.height; final rect = Rect.fromLTRB(left, top, right, bottom); final paint = Paint() .. color = Colors.red .. style = PaintingStyle.stroke .. strokeWidth = 1; canvas.drawRect(rect, paint); // draw the baseline final distanceToBaseline = textPainter.computeDistanceToActualBaseline(baseLine); canvas.drawLine( Offset(0, distanceToBaseline), Offset(textPainter.width, distanceToBaseline), paint.. color = Colors.blue.. strokeWidth = 5, ); // draw the text final offset = Offset(0, 0); textPainter.paint(canvas, offset); } @override bool shouldRepaint(CustomPainter oldDelegate) =>true;
}
Copy the code

As shown in the figure below, the blue line is the baseLine. From the effect, you can intuitively see where the alignment should be under the different baseLine.

However, the effect of the baseLine does not directly affect the alignment of TextStyle text. The default text displayed in a Flutter will only be aligned through TextBaseline.alphabetic. As shown in the picture below, officials have also described this problem #47512.

That’s why CustomPaint is displayed, because it doesn’t show up with the default Text.

A typical example of this is the following code, which uses the ideographic on both Row and Text, but does not achieve the desired effect.

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Container(
        color: Colors.lime,
        alignment: Alignment.center,
        child: Container(
            alignment: Alignment.center,
            child: Row(
                crossAxisAlignment: CrossAxisAlignment.baseline,
                textBaseline: TextBaseline.ideographic,
                mainAxisSize: MainAxisSize.max,
                children: [
                  Text(
                    'I am Chinese',
                    style: TextStyle(
                      fontSize: 55,
                      textBaseline: TextBaseline.ideographic,
                    ),
                  ),
                  Spacer(),
                  Text('123y56',
                      style: TextStyle(
                        fontSize: 55,
                        textBaseline: TextBaseline.ideographic,
                      )),
                ])),
      ),
    );
  }
Copy the code

The key is that even with Row center set, the text doesn’t look particularly “aligned.”

Since the “cold” knowledge about fonts in Flutter is over, have your “useless” knowledge increased?