在 Flutter 中正确处理文本缩放

本教程的结构旨在优先呈现最简单且最具影响力 的解决方案。靠后的章节涵盖了那些实现难度更大、总体影响较低的解决方案,但它们对于解决特定情况下的问题非常有用。

欢迎关注我的公众号:OpenFlutter,谢谢

限制文本缩放的可能范围

你可以在你的 MaterialApp 中设置最小和最大的缩放因子,这将确保所有的文本都在指定的限制范围内进行缩放。更紧密的边界 能让你在维持可读性和美观性上投入更少的精力。然而,边界的选择应该取决于你的目标用户。例如,如果你的应用是为老年用户设计的,你应该考虑使用更宽松的边界来满足他们的需求。

dart 复制代码
    MaterialApp(
      ...
      builder: (_, child) => MediaQuery(
        data: MediaQuery.of(context).copyWith(
          textScaler: MediaQuery.of(context)
              .textScaler
              .clamp(minScaleFactor: 0.8, maxScaleFactor: 1.6),
        ),
        child: child!,
      ),
    );

不要为包含文本的元素设置固定的高度

请看这段代码:

less 复制代码
          //不要
          SizedBox(
            height: 100,
            child: Card(
              child: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text("Title", style: TextStyle(fontSize: 30), maxLines: 1),
                    Text("Subtitle", maxLines: 1),
                  ],
                ),
              ),
            ),
          ),

出了什么问题呢?

正如你可能猜到的那样,增大文本字号会导致 SizedBox 内的内容占用过多空间。

一个更好的解决方案是让元素的高度基于内容高度和内边距。此外,你可以使用 ConstrainedBox 来设置最小高度。

dart 复制代码
          ConstrainedBox(
            constraints: const BoxConstraints(minHeight: 100),
            child: const Card(
              child: Center(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text("Title", style: TextStyle(fontSize: 30), maxLines: 1),
                    Text("Subtitle", maxLines: 1),
                  ],
                ),
              ),
            ),
          ),

结果是,我们在 100% 缩放比例下得到了相同的布局,而在 160% 缩放比例下得到了一个有效的布局。

ListView 的情况也是如此。如果你正在使用 itemExtent ,请考虑使用字体缩放来计算它,或者提供一个 prototypeItem 。关于这方面的更多信息,请参考这篇文章

让我们继续。想象一下有这样一个布局:

Item A 来自上一个例子。Item B 带有一定的内边距(padding),并且也应该能够处理文本缩放的增加。屏幕底部有足够的空间。那么,到底哪里可能会出问题呢?

不要忘记那些小屏幕的手机。此外,当语言发生变化时,文本长度也会随之变化。

确保内容可滚动

首先,我们应该消除任何溢出,以确保我们的用户可以访问所有内容。添加一个简单的 SingleChildScrollView 就能解决这个问题。

考虑为边距和内边距使用自适应值

这可能是一个有争议的做法,但想象一下你是一个需要使用更大字体尺寸的用户,你更喜欢看到大量的空白空间,还是清晰可读的文本?

让我们使用依赖于逻辑像素数量的值来显示文本。你可以根据你的应用需求,将 smallScreenThreshold 调整为最合适的数值。

dart 复制代码
class Dimens {
  static const smallScreenThreshold = 300;
  static bool isSmallWidth(BuildContext context) {
    return MediaQuery.of(context).size.width /
            MediaQuery.textScalerOf(context).scale(1) <
        smallScreenThreshold;
  }

  static double small(BuildContext context) => isSmallWidth(context) ? 4 : 8;
  static double medium(BuildContext context) => isSmallWidth(context) ? 8 : 16;
  static double large(BuildContext context) => isSmallWidth(context) ? 16 : 32;
}

请注意,如果你想遵循 **人机界面指南(Human Interface Guidelines)**和 Material Design(材质设计) ,这些数值应该可以被 4 整除

基于这些尺寸(Dimens) ,我们可以创建一个内边距(insets)类:

dart 复制代码
class Insets {
  static EdgeInsets small(BuildContext context) =>
      EdgeInsets.all(Dimens.small(context));
      
  static EdgeInsets medium(BuildContext context) =>
      EdgeInsets.all(Dimens.medium(context));

  static EdgeInsets large(BuildContext context) =>
      EdgeInsets.all(Dimens.large(context));
}

在代码中我们这样替换它:

dart 复制代码
//padding: const EdgeInsets.all(16),  
padding: Insets.medium(context),  
  
//SizedBox(height: 16),  
SizedBox(height: Dimens.medium(context)),

结果,我们赢得了更多的空间来在屏幕上绘制文本:

限制标题文本的缩放范围

增大字体缩放的主要目的是为了让视力衰退的用户能够清晰地阅读内容。然而,应用中的某些部分,例如标题,可能本身字号就很大,已经足够易读了。为了解决这个问题,我们可以限制文本可以放大的程度。实现这一目的的方法之一是创建一个用于标题的自定义 Widget:

dart 复制代码
class TitleText extends StatelessWidget {
  final String text;
  final TextStyle style;

  const TitleText(this.text, {required this.style, super.key});

  static const double maxRealFontSize = 30;

  @override
  Widget build(BuildContext context) {
    if (MediaQuery.textScalerOf(context).scale(style.fontSize!) >
        maxRealFontSize) {
      return Text(
        text,
        style: style.copyWith(
          fontSize: maxRealFontSize / MediaQuery.textScalerOf(context).scale(1),
        ),
      );
    }
    return Text(text, style: style);
  }
}

通过这样做,我们可以在不损失可读性 的前提下,赢得更多的空间。你可以将那个 maxRealFontSize(最大实际字号)调整为你应用更合适的任何值

指定最大行数和文本溢出

别忘了,有些文本在屏幕大、文本缩放正常的情况下看起来很正常,但在某些条件下(比如用户放大了字体),它可能会占据过多的垂直空间,但你又不需要总是显示全部内容,例如在副标题中。这种情况下,只需给 Text Widget 添加一个 maxLines 值即可。

设置 maxLines 为 1 看起来不错。主要信息仍然可见。

使用字符串的替代版本

但并非总是能以一种仍包含有用信息的方式来缩短字符串。此外,不同语言中词语的顺序是不同的。在英语中排在最前面的词,在另一种语言的句子中可能在末尾。让我们考虑一下国际化(i18n)字符串的这个例子:

bash 复制代码
"tasksDone": {  
"one": "You have done $completed of $n tasks",  
"other": "You have done $completed of $n tasks"  
},  
"tasksDoneShort": {  
"one": "$completed/$n tasks done",  
"other": "$completed/$n tasks done"  
},

最重要的部分是展示数字的部分。在较短的版本中,我们把它放在了开头,让整个字符串变得更短。在代码中,你可以这样使用它:

dart 复制代码
        Text(
          Dimens.isSmallWidth(context)
              ? t.tasksDoneShort(n: 10, completed: 5)
              : t.tasksDone(n: 10, completed: 5),
           maxLines: 1,
        )

正如您在屏幕截图上看到的,缩短版本有助于显示所需的信息。

相关推荐
深蓝电商API2 小时前
HTML 解析入门:用 BeautifulSoup 轻松提取网页数据
前端·爬虫·python·beautifulsoup
excel3 小时前
JavaScript 运算符与 Vue 中的 1 << n 应用
前端
上单带刀不带妹3 小时前
Vue3 全局 API 转移详解
前端·javascript·vue.js·vue3·api
怕冷的火焰(~杰)3 小时前
yarn安装electron和better-sqlite3失败问题(rebuild:better-sqlite3)
前端·javascript·electron
IT_陈寒4 小时前
JavaScript性能优化:7个90%开发者不知道的V8引擎黑科技
前端·人工智能·后端
摸鱼的春哥4 小时前
“全栈模式”必然导致“质量雪崩”!和个人水平关系不大
前端·javascript·后端
Eme丶4 小时前
Nginx部署vue以及转发配置记录
前端·vue.js·nginx
大气层煮月亮4 小时前
Oracle EBS ERP之报表开发—嵌入Web中的报表预览、报表打印
前端·数据库·oracle
excel4 小时前
Vue 中 v-show 与 v-if 的全面解析
前端