我的项目目前在 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,你可以节省一些空间。不过,我不会为此费心。
-
- 将其放入
assets文件夹。
- 将其放入
我拿了一个常规可变字体并重新命名了它。

剩下的字体是用于实验的;我不打算在同一个应用中使用所有这些字体。😎
-
- 在
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
我知道如何用 TextStyle 为 Text 组件设置 fontFamily。但我想全局设置它。
原来,这可以通过** ThemeData **来实现,真是个惊喜:
dart
static ThemeData getTheme(
ColorScheme colorScheme,
Brightness brightness,
) {
return ThemeData(
brightness: brightness,
colorScheme: colorScheme,
fontFamily: 'OpenSans', //<-here
appBarTheme: AppBarTheme(
...
上面是我的方法,它为应用创建了 ThemeData 对象。修复很简单------只需添加 fontFamily 属性。但如果这么简单,我就不会写这篇文章了。

如你所见(你可能看不到 😀),只有 Text 和 TextFields 组件中的字体发生了变化。(提示:看看字母 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 仍然使用默认字体。这个方法实际上是有效的;它可以用来替代设置 ThemeData 的 fontFamily 属性。但它的作用方式是同样的局限。
下一个建议(这次奏效了)是分别在 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。
- 将字体添加到
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
- 最后,修改
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.theme、Get.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对象。
就是这样。
感谢您的阅读。
如果您学到了新东西,您就欠我鼓掌。开玩笑 😀。半开玩笑 😉。