`
iaiai
  • 浏览: 2203273 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

带你深入理解 FLUTTER 中的字体“冷”知识

 
阅读更多

本篇将带你深入理解 Flutter 开发过程中关于字体和文本渲染的“冷”知识,帮助你理解和增加关于 Flutter 中字体绘制的“无用”知识点。

毕竟此类相关的内容太少了

首先从一个简单的文本显示开始,如下代码所示,运行后可以看到界面内出现了一个 H 字母,它的fontSize是 100,Text被放在一个高度为 200 的Container中,然后如果这时候有人问你:Text显示 H 字母需要占据多大的高度,你知道吗?

  @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,
                )
              ],
            ),
          )

        ),
      ),
    );
  

一、TEXTSTYLE

如下代码所示,为了解答这个问题,首先我们给 Text 所在的 Container 增加了一个蓝色背景,并增加一个 100 * 100 大小的红色小方块做对比。

@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,
                )
              ],
            ),
          )

        ),
      ),
    );
  }

结果如下图所示,可以看到 H 字母的上下有着一定的 padding 区域,蓝色Container 的大小明显超过了 100 ,但是黑色的 H 字母本身并没有超过红色小方块,那蓝色区域的高度是不是 Text 的高度,它的大小又是如何组成的呢?
在这里插入图片描述
事实上,前面的蓝色区域是字体的行高,也就是 line height ,关于这个行高,首先需要解释的就是 TextStyle 中的 height 参数。

默认情况下 height 参数是 null,当我们把它设置为 1 之后,如下图所示,可以看到蓝色区域的高度和红色小方块对齐,变成了 100 的高度,也就是行高变成了 100 ,而 H 字母完整的显示在蓝色区域内。
在这里插入图片描述
那 height 是什么呢?根据文档可知,首先 TextStyle 中的 height 参数值在设置后,其效果值是 fontSize 的倍数:

  • 当 height 为空时,行高默认是使用字体的量度(这个量度后面会有解释);
  • 当 height 不是空时,行高为 height * fontSize 的大小;

如下图所示,蓝色区域和红色区域的对比就是 height 为 null 和 1 的对比高度。

在这里插入图片描述

另外上图的 BaseLine 也解释了:为什么 fontSize 为 100 的 H 字母,不是充满高度为 100 的蓝色区域。

根据上图的示意效果,在 height 为 1 的红色区域内,H 字母也应该是显示在基线之上,而基线的底部区域是为了如 g 和 j 等字母预留,所以如下图所示,在 Text 内加入 g 字母并打开 Flutter 调试的文本基线显示,由 Flutter 渲染的绿色基线也可以看到符合我们预期的效果。

忘记截图由 g 的了,脑补吧。

在这里插入图片描述
接着如下代码所示,当我们把 height 设置为 2 ,并且把上层的高度为 200 的 Container 添加一个紫色背景,结果如下图所示,可以看到蓝色块刚好充满紫色方块,因为 fontSize 为 100 的文本在 x2 之后恰好高度就是 200。

@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,
                )
              ],
            ),
          )

        ),
      ),
    );
  }

在这里插入图片描述

不过这里的 Hg 是往下偏移的,为什么这样偏移在后面会介绍,还会有新的对比。

最后如下图所示,是官方提供的在不同 TextStyle 的 height 参数下, Text 所占高度的对比情况。

在这里插入图片描述
二、STRUTSTYLE

那再回顾下前面所说的默认字体的量度,这个默认字体的量度又是如何组成的呢?这就不得不说到 StrutStyle 。

如下代码所示,在之前的代码中添加 StrutStyle :

  • 设置了 forceStrutHeight 为 true ,这是因为只有 forceStrutHeight 才能强制重置 Text 的 height 属性;
  • 设置了StrutStyle 的 height 设置为 1 ,这样 TextStyle 中的 height 等于 2 就没有了效果。
  @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,
                )
              ],
            ),
          )

        ),
      ),
    );
  }

效果如下图所示,虽然 TextStyle 的 height 是 2 ,但是显示出现是以 StrutStyle 中 height 为 1 的效果为准。

在这里插入图片描述
然后查看文档对于 StrutStyle 中 height 的描述,可以看到:height 的效果依然是 fontSize 的倍数,但是不同的是这里的对 fontSize 进行了补充说明 : ascent + descent = fontSize,其中:

  • ascent 代表的是基线上方部分;
  • descent 代表的是基线的半部分
  • 其组合效果如下图所示:

在这里插入图片描述

Flutter 中 ascent 和 descent 是不能用代码单独设置。

除此之外,StrutStyle 的 fontSize 和 TextStyle 的 fontSize 作用并不一样:当我们把 StrutStyle 的 fontSize 设置为 50 ,而 TextStyle 的 fontSize 依然是 100 时,如下图所示,可以看到黑色的字体大小没有发生变化,而蓝色部分的大小变为了 50 的大小。

在这里插入图片描述
有人就要说那 StrutStyle 这样的 fontSize 有什么用?

这时候,如果在上面条件不变的情况下,把 Text 中的文本变成 "Hg\nHg" 这样的两行文本,可以看到换行后的文本重叠在了一起,所以 StrutStyle的 fontSize 也是会影响行高。

在这里插入图片描述
另外,在 StrutStyle 中还有另外一个参数也会影响行高,那就是 leading 。

如下图所示,加上了 leading 后才是 Flutter 中对字体行高完全的控制组合,leading 默认为 null ,同时它的效果也是 fontSize 的倍数,并且分布是上下均分。

在这里插入图片描述
所以如下代码所示,当 StrutStyle 的 fontSize 为 100 ,height 为 1,leading 为 1 时,可以看到 leading 的大小让蓝色区域变为了 200,从而 和紫色区域高度又重叠了,不同的对比之前的 Hg 在这次充满显示是居中。


  @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,
                )
              ],
            ),
          )

        ),
      ),
    );
  }

因为 leading 是上下均分的,而 height 是根据 ascent 和 descent 的部分放大,明显 ascent 比 ascent 大得多,所以前面的 TextStyle 的 height 为 2 时,充满后整体往下偏移。

在这里插入图片描述
三、BACKGROUNDCOLOR

那么到这里应该对于 Flutter 中关于文本大小、度量和行高等有了基本的认知,接着再介绍一个属性:TextStyle 的 backgroundColor 。

介绍这个属性是为了和前面的内容产生一个对比,并且解除一些误解。

如下代码所示,可以看到 StrutStyle 的 fontSize 为 100 ,height 为 1,按照前面的介绍,蓝色的区域大小应该是和红色小方块一样大。

然后我们设置了 TextStyle 的 backgroundColor 为具有透明度的绿色,结果如下图所示,可以看到 backgroundColor 的区域超过了 StrutStyle,显示为默认情况下字体的度量。


  @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,
                )
              ],
            ),
          )

        ),
      ),
    );
  }

在这里插入图片描述
这是不是很有意思,事实上也可以反应出,字体的度量其实一直都是默认的 ascent + descent = fontSize,我们可以改变 TextStyle 的 height 或者 StrutStyle 来改变行高效果,但是本质上的 fontSize 其实并没有变。

如果把输入内容换成 "H\ng" ,如下图所示可以看到更有意思的效果。

在这里插入图片描述
四、TEXTBASELINE

最后再介绍一个属性 :TextStyle 的 TextBaseline,因为这个属性一直让人产生“误解”。

关于 TextBaseline 有两个属性,分别是 alphabetic 和 ideographic ,为了更方便解释他们的效果,如下代码所示,我们通过 CustomPaint 把不同的基线位置绘制出来。


  @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文字',
      style: textStyle,
    );
    final textPainter = TextPainter(
      text: textSpan,
      textDirection: TextDirection.ltr,
    );
    textPainter.layout(
      minWidth: 0,
      maxWidth: size.width,
    );

    final left = 0.0;
    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;
}

如下图所示,蓝色的线就是 baseLine,从效果可以直观看到不同 baseLine 下对齐的位置应该在哪里。

在这里插入图片描述
但是事实上 baseLine 的作用并不会直接影响 TextStyle 中文本的对齐方式,Flutter 中默认显示的文本只会通过 TextBaseline.alphabetic 对齐的,如下图所示官方人员也对这个问题有过描述 #47512

在这里插入图片描述

这也是为什么要用 CustomPaint 展示的原因,因为用默认 Text 展示不出来。

举个典型的例子,如下代码所示,虽然在 Row 和 Text 上都是用了 ideographic ,但是其实并没有达到我们想要的效果。

 @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(
                    '我是中文',
                    style: TextStyle(
                      fontSize: 55,
                      textBaseline: TextBaseline.ideographic,
                    ),
                  ),
                  Spacer(),
                  Text('123y56',
                      style: TextStyle(
                        fontSize: 55,
                        textBaseline: TextBaseline.ideographic,
                      )),
                ])),
      ),
    );
  }

关键就算 Row 设置了 center ,这段文本看起来还是不是特别“对齐”。

在这里插入图片描述
自从,关于 Flutter 中的字体相关的“冷”知识介绍完了,不知道你“无用”的知识有没有增多呢?

分享到:
评论

相关推荐

    Flutter官方文档详细翻译

    本文档翻译项目旨在为开发者提供全面、详尽的Flutter中文指南,帮助他们快速上手并深入理解Flutter的核心概念和技术。 1. **环境配置**:在Windows上搭建Flutter开发环境是开发的第一步。你需要安装Git、Java SDK、...

    flutter web登录和注册页

    在Flutter Web开发中,构建登录和注册页面是任何应用程序的基础组成部分。Flutter是一个强大的跨平台...通过实践这个项目,开发者能够深入理解Flutter Web开发的基本流程和技巧,为进一步的Web应用开发打下坚实基础。

    Flutter基础学习PDF

    你会学到如何创建新的Flutter项目,理解`main.dart`文件中的主要组件,如`StatefulWidget`和` StatelessWidget`,以及如何使用`build`方法构建用户界面。 4. **Flutter Widget框架**(06章节):Flutter的核心是...

    Flutter基础学习之进阶

    在“Flutter基础学习之进阶”这一主题中,我们将深入探讨Flutter开发的高级概念和实践技巧,这将有助于提升你的Flutter应用开发能力。以下是一些关键知识点的详细讲解: 1. **Flutter IDE的使用**(15 使用Flutter ...

    Flutter完整开发实战详解 PDF

    2. 讨论了Flutter中的主题系统,如何自定义主题颜色、字体等,以及全局应用主题的方法。 3. 介绍了实现多语言支持的步骤,包括资源文件的组织和本地化策略。 五、深入探索 1. 对Flutter的源码进行了深入研究,帮助...

    用Flutter做的漂亮仪表板

    【标题】:“用Flutter做的漂亮仪表板” ...同时,项目可能还涉及到动画效果、响应式设计、性能优化等高级技术,这些都是Flutter开发中的重要知识点。对于想要提升Flutter技能的开发者来说,这是一个极好的学习案例。

    flutter_uplab_challenge_fitness_dashboard.zip

    通过研究这个项目,你可以学习到以下 Flutter 开发中的关键知识点: 1. **布局组件**:了解并实践各种布局组件的用法,如何组合它们来创建响应式的界面。 2. **状态管理**:可能会涉及 Provider、Bloc、Riverpod 等...

    简洁且完全可定制的模拟时钟Flutter小部件

    总的来说,要创建这样一个“简洁且完全可定制的模拟时钟Flutter小部件”,你需要深入理解Flutter的开发流程,熟练掌握Dart编程,熟悉UI设计原则,以及具备一定的动画和响应式设计经验。同时,对于跨平台开发和版本...

    flutter_particle_clock.zip

    4. **插件使用**:如果项目中使用了第三方插件,可以研究其用法和源码,理解如何扩展 Flutter 的功能。 5. **响应式设计**:Flutter 提供了响应式框架,学习如何让时钟适应不同设备的屏幕尺寸和方向。 6. **性能优化...

    flutter_richtext.rar

    在本文中,我们将深入探讨`flutter_richtext.rar`中涉及的`Flutter`富文本编辑框的相关知识点。`Flutter`是Google开发的一款用于构建高性能、跨平台移动应用的开源UI工具包,它允许开发者用Dart语言编写代码,实现...

    Flutter练习项目包括完整UI设计图更贴近真实项目的练习

    其次,这个练习项目提供了完整的UI设计图,这意味着开发者有机会学习如何将设计图转化为实际的代码,理解布局、颜色、字体等视觉元素在Flutter中的实现方式。Flutter提供了一系列的Widget,如Container、Column、Row...

    使用Flutter创建的漂亮App集合受youtubeUI项目启发

    在创建漂亮的Flutter应用时,我们首先需要理解Flutter的Widget体系。Widgets是Flutter中的基本构建块,它们定义了应用程序的外观和行为。通过组合不同的Widgets,开发者可以构建出丰富的用户界面。例如,当我们想要...

    基于Flutter构建的stopwatch app..zip

    Flutter是一款由Google开发的开源UI工具包,用于构建高性能、高保真度的原生...此外,理解并实践这个项目还能帮助你深入理解Flutter的生命周期、事件处理和状态管理。如果你对移动应用开发感兴趣,这是一个很好的起点。

    flutter_blbl.zip

    在本文中,我们将深入探讨如何使用Flutter框架来模仿哔哩哔哩(Bilibili)的注册登录页面。Flutter是一款由Google开发的开源UI工具包,它允许开发者为Android和iOS等平台构建高性能、高保真度的应用程序。通过学习这...

    使用flutter构建的登录页面

    在本文中,我们将深入探讨如何使用Flutter框架构建一个精美的登录页面。Flutter是Google推出的一种开源的移动应用程序SDK,它允许开发者使用单个代码库为iOS和Android平台创建高性能、美观的应用程序。作为一款基于...

    一款Flutter实现的富文本编辑.zip

    总的来说,这个项目是一个很好的学习资源,对于想深入理解和实践Flutter中富文本编辑功能的开发者来说,无论是从理论知识还是实战经验,都能提供宝贵的学习机会。同时,通过研究和使用开源的Zefyr库,可以提升开发者...

    史上最全的Flutter 隐式动画源代码

    在本文中,我们将深入探讨 Flutter 的隐式动画,这是一个强大的工具,可以为用户界面增添流畅性和生动性。根据提供的文件名,我们可以看到一系列的演示示例,这些示例涵盖了 Flutter 中各种类型的隐式动画。我们将...

    Flutter-Book-Movie-UI.zip

    通过解压并研究这个文件夹,开发者可以深入理解以下知识点: 1. **Flutter Widgets**:Flutter的核心是Widget,它们定义了应用的视觉和交互部分。Movie UI项目中会包含各种自定义和内置的Widgets,如AppBar、...

    flutter资源.zip

    通过深入学习和理解上述知识点,开发者能够有效地利用Flutter构建出高效、美观且跨平台的应用程序。这份“flutter资源.pdf”文档将是你学习和进阶Flutter的宝贵资料,务必仔细研读并实践其中的示例。

    将文本转换为路径并使用此Flutter包为它们设置动画

    我们将深入探讨如何在Flutter应用中实现这一功能,并了解相关技术。 首先,文本到路径的转换是SVG(可缩放矢量图形)中的一个概念。SVG是一种基于XML的图形格式,它可以精确地表示文本、形状和线条,同时保持高质量...

Global site tag (gtag.js) - Google Analytics