Flutter:如何更改默认字体

我的项目目前在 Windows 上运行并显示注册页面。这个页面本身有点过时了。最近的趋势是先显示 OAuth 供应商的按钮,因为大多数用户更喜欢它们。

但是,对于这篇文章来说,这样没问题。

因为它运行在 Windows 上,所以默认字体是 SegoeUI。在 Android 和 Web 上默认是 Roboto,在 iOS 上默认是 SF Pro。

我们为什么要更改它?实际上我们不必更改。

但我的理由是:

  • 在所有平台上使用相同的字体。
  • 不使用默认字体,因为使用默认字体的应用程序看起来很"默认"。
  • 我查看了 Google Fonts 并选择了 Open Sans。

它比 Roboto 更透气、更轻盈一些,是继 Roboto 之后第二受欢迎的 Google 字体。但由于 Roboto 是 Android 上的默认字体(并被 Google 广泛使用),我们可以假设 Open Sans 在没有 Google 影响的情况下会是最受欢迎的。

它也比 Apple 专有的 SF Pro 更轻盈、更透气:

那么,如何用 Open Sans 替换默认字体呢?

我所知道的:

  • 下载字体并解压。

有两种 可变字体(Variable Fonts):常规 (regular)和斜体 (italic)。可变 意味着它的宽度字重 (weight)可以被代码动态地改变:

dart 复制代码
TextStyle(  
fontFamily: 'OpenSans',  
fontVariations: [  
FontVariation('wdth', 77),  
FontVariation('wght', 477),  
]);

还有一个 static 文件夹,里面是静态字体。可变字体文件是 500 KB,而静态字体只有 130 KB。如果你最终只使用一个变体,例如 Open Sans Medium,你可以节省一些空间。不过,我不会为此费心。

    1. 将其放入 assets 文件夹。

我拿了一个常规可变字体并重新命名了它。

剩下的字体是用于实验的;我不打算在同一个应用中使用所有这些字体。😎

    1. pubspec.yaml 中指定字体。
yaml 复制代码
flutter:
  uses-material-design: true
  assets: 
    - assets/images/
    - assets/images/background/
    - assets/audio/
  fonts:
    - family: OpenSans
      fonts: 
        - asset: assets/fonts/OpenSans.ttf

我知道如何用 TextStyleText 组件设置 fontFamily。但我想全局设置它。

原来,这可以通过** ThemeData **来实现,真是个惊喜:

dart 复制代码
 static ThemeData getTheme(
    ColorScheme colorScheme,
    Brightness brightness,
  ) {
    return ThemeData(
      brightness: brightness,
      colorScheme: colorScheme,
      fontFamily: 'OpenSans',    //<-here
      appBarTheme: AppBarTheme(
...

上面是我的方法,它为应用创建了 ThemeData 对象。修复很简单------只需添加 fontFamily 属性。但如果这么简单,我就不会写这篇文章了。

如你所见(你可能看不到 😀),只有 TextTextFields 组件中的字体发生了变化。(提示:看看字母 g)。但 AppBar 标题和按钮仍然使用默认字体。

首先,我得说这很令人失望。如果这种设置整个 ThemeData 字体的方式对整个 ThemeData 不起作用,那设置它的意义何在?

或许我做错了什么?可能吧,因为我是第一次做。请帮忙。

好吧,我咨询了 Claude,这位又大又漂亮的 LLM 建议:

dart 复制代码
textTheme: Typography.englishLike2021
          .copyWith(
            bodyMedium: const TextStyle(fontSize: 16),
            bodySmall: const TextStyle(fontSize: 15),
            headlineMedium: TextStyle(color: colorScheme.primary),
            labelLarge: const TextStyle(fontSize: 15),
            labelMedium: const TextStyle(fontSize: 14),
            bodyLarge: TextStyle(
              fontSize: 17,
              color: colorScheme.onSurface,
            ),
          )
          .apply(  //<-this
            fontFamily: 'OpenSans',
          ),

添加 apply(fontFamily: 'OpenSans',)textTheme 中。没用。按钮和 AppBar 仍然使用默认字体。这个方法实际上是有效的;它可以用来替代设置 ThemeDatafontFamily 属性。但它的作用方式是同样的局限。

下一个建议(这次奏效了)是分别在 AppBarTheme 和每个按钮的主题中指定 fontFamily

dart 复制代码
 appBarTheme: AppBarTheme(
        foregroundColor: colorScheme.primary,
        elevation: 0,
        scrolledUnderElevation: 2,
        centerTitle: true,
        titleTextStyle: TextStyle(
          fontSize: 17,
          fontWeight: FontWeight.w600,
          color: colorScheme.onSurface,
          fontFamily: 'OpenSans',   //<-here
        ),
      ),

我使用了所有的 Material 按钮(Elevated、Filled、Outlined 和 Text),所以我需要为它们每一个修改以下内容:

dart 复制代码
 textStyle: WidgetStateProperty.resolveWith<TextStyle?>(
        (states) {
          if (states.contains(WidgetState.hovered)) {
            return TextStyle(
              fontSize: 16,
             // fontWeight: FontWeight.w800,
              fontFamily: 'OpenSans',  //<-here
              fontVariations: [
                      FontVariation('wght', 800),
                    ]
            );
          }
          return TextStyle(
            fontSize: 17,
           // fontWeight: FontWeight.w600,
            fontFamily: 'OpenSans',    //<-and here
            fontVariations: [
                      FontVariation('wght', 600),
                    ]
          );
        },
      ),

fontWeight 属性不适用于 Open Sans;我们应该改用 fontVariations

然后瞧(终于 😴),Open Sans 无处不在:

我已经觉得这篇文章值得写了,但无论如何......

奖励 1

让文本字段的标签使用等宽字体(monospace)。

奖励 2

对标题使用另一种(更有趣的)字体。

将字体添加到 assets

(这两种字体都是和 Open Sans 一起从 Google Fonts 下载的)。

顺便说一下,VS Code 有一个非常有用的扩展叫 Font Preview

  1. 将字体添加到 pubspec.yaml
yaml 复制代码
flutter:
  uses-material-design: true
  assets: 
    - assets/images/
    - assets/images/background/
    - assets/audio/
  fonts:
    - family: OpenSans
      fonts: 
        - asset: assets/fonts/OpenSans.ttf
    - family: KodeMono
      fonts: 
        - asset: assets/fonts/KodeMono.ttf
    - family: StardosStencil
      fonts: 
        - asset: assets/fonts/StardosStencil.ttf  
  1. 最后,修改 textTheme
dart 复制代码
 textTheme: Typography.englishLike2021
          .copyWith(
            bodyMedium: const TextStyle(fontSize: 15),
            bodySmall: const TextStyle(fontSize: 14),
            headlineMedium: TextStyle(
              color: colorScheme.primary,
              fontFamily: 'StardosStencil',   //<-here
              fontSize: 25,
            ),
            labelLarge: const TextStyle(fontSize: 15),
            labelMedium: const TextStyle(fontSize: 14),
            bodyLarge: TextStyle(
              fontSize: 16,
              color: colorScheme.onSurface,
              fontFamily: 'KodeMono',        //<-and here
            ),
          )
          // .apply(
          //   fontFamily: 'OpenSans',
          // )
          ,

👉(别忘了移除 apply 方法,它会覆盖所有指定的字体。)

这是我的注册页面,有一个有趣的标题和等宽的文本字段标签:

顺便说一句,我的显示"Create Account"文本的 Text 组件看起来像:

dart 复制代码
       Text(
            'Create Accountg',
            style: Get.textTheme.headlineMedium,                       
          ),

所以,我们明确地使用了 textTheme 中的 headlineMedium 样式。

关于文本字段标签,情况有所不同:

dart 复制代码
TextField(
            controller: _emailController,
            decoration: InputDecoration(
              labelText: 'Emailg',
              prefixIcon: Icon(
                Icons.email_outlined,
              ),
            ),
          ),

正如我们所见,labelStyle 没有定义。TextField 隐式地使用 textTheme 中的 bodyLarge TextStyle 对象作为其标签。

我一直以为 GetX 会在内部的某个地方缓存 ThemeData,并且所有的 Get.themeGet.textTheme 之类的调用都不会以 Theme.of(context) 查找结束。运气不好。

这是 GetX 内部的 theme getter 实现:

dart 复制代码
 ThemeData get theme {
    var theme = ThemeData.fallback();
    if (context != null) {
      theme = Theme.of(context!);
    }
    return theme;
  }

只是要意识到(如果您使用 GetX)。

那些 .of(context) 查找可能会对性能造成负担,最好在 build 方法的顶部缓存 ThemeData 对象。

就是这样。

感谢您的阅读。

如果您学到了新东西,您就欠我鼓掌。开玩笑 😀。半开玩笑 😉。

相关推荐
英俊潇洒美少年1 天前
Vue 生产环境打包:SourceMap、压缩、混淆、加密全解 + 最佳实践
前端·javascript·vue.js
巴博尔1 天前
UNIAPP中NVUE页面 动画
android·前端·javascript·ios·uni-app
她说人狗殊途1 天前
基于 vue-cli 创建
前端·javascript·vue.js
AZaLEan__1 天前
前端移动端适配与 Bootstrap
前端·bootstrap·html
大家的林语冰1 天前
Deno 2.8 正式发布,再次超越 Bun,史上最大的次版本升级诞生!
前端·javascript·node.js
渣渣xiong1 天前
从零开始:前端转型AI agent直到就业第五十七天-第五十八天
前端·人工智能·python
AI周红伟1 天前
周红伟:长鑫科技(CXMT)财务全景分析
前端·chrome·科技
excel1 天前
JS 正则在多次 test() 时为什么会出现 lastIndex 缓存问题?
前端
IT_陈寒1 天前
为什么 Java 的 Optional 让我调试到深夜?
前端·人工智能·后端
米丘1 天前
React 19.x 的 lazy 与 Suspense
前端·javascript·react.js