4、文字主题定制:TextTheme 详解,定义标题、正文、按钮等各级文字样式。
好,咱们进入主题定制的核心环节——文字。你想想看,一个App好不好看,一半的功劳得给字体和排版。Flutter 里管这个叫 TextTheme,说白了就是一套「文字样式模板」。你只要定义好这套模板,全App的标题、正文、按钮文字就都统一了。
我个人习惯,在项目一开始就把 TextTheme 定死。为什么?因为后期改字体大小、颜色,那真是牵一发动全身,改到想哭。我早期一个项目就吃过这个亏,当时没统一管理,结果设计师说「标题再大2像素吧」,我改了十几个页面……嗯,从那以后,我再也不敢偷懒了。
4.1 TextTheme 的结构:它到底包含哪些样式?
Flutter 的 TextTheme 其实是一个「样式集合」。它把文字分成了几个层级,从最大的标题到最小的说明文字。咱们先看一张表,你就明白了:
| 属性名 | 用途 | 默认大小(Material 3) |
|---|---|---|
displayLarge |
超大标题,比如欢迎页、数字展示 | 57px |
displayMedium |
次大标题 | 45px |
displaySmall |
稍小的大标题 | 36px |
headlineLarge |
页面主标题,比如文章标题 | 32px |
headlineMedium |
区块标题 | 28px |
headlineSmall |
卡片标题 | 24px |
titleLarge |
列表项标题、对话框标题 | 22px |
titleMedium |
列表项副标题 | 16px |
titleSmall |
小标题,比如按钮文字 | 14px |
bodyLarge |
正文,默认字体 | 16px |
bodyMedium |
次要正文 | 14px |
bodySmall |
辅助文字、说明 | 12px |
labelLarge |
按钮、标签 | 14px |
labelMedium |
小标签、chip | 12px |
labelSmall |
极小文字,比如角标 | 11px |
headlineLarge、titleLarge、bodyLarge、labelLarge 这四个,其他用默认值。够用,还省事。
4.2 如何自定义 TextTheme?
直接上代码。你需要在 ThemeData 里传入一个 TextTheme 对象。看这个例子:
ThemeData(
textTheme: TextTheme(
// 页面主标题:加粗,深色
headlineLarge: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
// 卡片标题:中等粗细
titleLarge: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
// 正文:常规大小,灰色
bodyLarge: TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black54,
),
// 按钮文字:全大写,加粗
labelLarge: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
letterSpacing: 1.2,
color: Colors.white,
),
),
)
你看,这样定义之后,你在任何地方用 Text('标题', style: Theme.of(context).textTheme.headlineLarge),它都会自动应用这个样式。是不是很爽?
TextStyle 里只写 通用属性。比如颜色、大小、字重。不要写背景色、边框这些,那些应该用 Container 或 ButtonStyle 去控制。
4.3 避坑指南:我踩过的三个坑
讲几个实战中容易翻车的地方。我曾经……嗯,都经历过。
- 坑一:字体大小写不一致。 我早期定义
labelLarge时忘了加letterSpacing,结果按钮文字挤在一起,设计师说「不够大气」。后来我统一给按钮加了letterSpacing: 1.0,效果立竿见影。 - 坑二:颜色写死。 如果你在
TextTheme里直接写Colors.black,那暗黑模式就废了。我建议用Theme.of(context).colorScheme.onSurface这种动态颜色。这样亮暗模式自动切换。 - 坑三:忘了适配不同平台。 在 iOS 上,系统默认字体是 San Francisco,Android 是 Roboto。如果你自定义字体,记得在
TextStyle里指定fontFamily,或者用GoogleFonts包。不然跨平台效果不一样。
ThemeData 的 textTheme 默认对象。比如 ThemeData().textTheme.headlineLarge 这样取出来再改,可能会影响其他组件。一定要新建一个 TextTheme 实例传进去。
4.4 实战:给按钮文字加点「个性」
按钮文字通常用 labelLarge。但有时候你需要按钮文字有不同颜色(比如禁用状态)。这时候怎么办?
我的做法是:在 TextTheme 里只定义基础样式,然后在 ButtonStyle 里覆盖颜色。看代码:
ThemeData(
textTheme: TextTheme(
labelLarge: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 1.0,
// 这里不写颜色
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
textStyle: WidgetStatePropertyAll(
Theme.of(context).textTheme.labelLarge?.copyWith(
color: Colors.white, // 按钮文字颜色在这里控制
),
),
),
),
)
这样,按钮文字的大小、字重由 TextTheme 统一管理,颜色由 ButtonStyle 单独控制。职责清晰,后期改起来也方便。
4.5 总结一下
文字主题定制,说白了就是「先分层,再定义」。你想想看,一个App里文字层级就那么几种:标题、正文、按钮、说明。你只要把这几个层级的样式定好,全App就统一了。
我个人建议,每个项目都建一个 app_text_theme.dart 文件,专门放 TextTheme 的定义。这样以后换字体、改大小,只需要改这一个文件。我现在的项目就是这么干的,设计师改需求时,我改一行代码就行,省心得很。
下一节,咱们聊聊颜色主题。颜色比文字更复杂,因为还有亮暗模式、色盲适配这些坑。到时候我把我踩过的坑都抖出来,你注意避让。