refactor: 完成AppTypography Token化重构,统一UI样式调用
1. 将AppTypography从静态常量类重构为ThemeExtension+静态实例模式,支持全局响应式字体调整 2. 批量替换全量代码中的AppTypography.of(context)调用为静态直接调用 3. 移除多余的rank_provider、canvas_style_middleware导入和preload静态方法 4. 修复多处UI文本样式不一致的问题,统一字体样式调用逻辑
This commit is contained in:
@@ -85,7 +85,6 @@ enum AppPermission {
|
||||
CupertinoIcons.location_fill,
|
||||
'用于获取天气信息和节气提醒,仅使用粗略位置(城市级),不获取精确位置,不会后台追踪。',
|
||||
Color(0xFF007AFF),
|
||||
group: PermissionGroup.optional,
|
||||
usageScenes: ['天气信息 — 当前城市天气', '节气提醒 — 当地节气推送'],
|
||||
),
|
||||
bluetooth(
|
||||
@@ -94,7 +93,6 @@ enum AppPermission {
|
||||
CupertinoIcons.bluetooth,
|
||||
'用于文件传输助手的蓝牙配对和设备发现,仅在您使用文件传输功能时请求。',
|
||||
Color(0xFF5AC8FA),
|
||||
group: PermissionGroup.optional,
|
||||
usageScenes: ['文件传输 — 蓝牙配对', '设备发现 — 附近设备搜索'],
|
||||
),
|
||||
nearbyDevices(
|
||||
@@ -103,7 +101,6 @@ enum AppPermission {
|
||||
CupertinoIcons.antenna_radiowaves_left_right,
|
||||
'用于文件传输助手的局域网设备发现和连接,仅在您使用文件传输功能时请求。',
|
||||
Color(0xFF64D2FF),
|
||||
group: PermissionGroup.optional,
|
||||
usageScenes: ['文件传输 — 局域网发现', '设备连接 — WiFi直连'],
|
||||
),
|
||||
microphone(
|
||||
@@ -112,7 +109,6 @@ enum AppPermission {
|
||||
CupertinoIcons.mic_fill,
|
||||
'用于语音朗读句子、语音搜索、AI对话语音输入。仅在您主动使用语音功能时请求,不会后台录音。',
|
||||
Color(0xFFFF3B30),
|
||||
group: PermissionGroup.optional,
|
||||
usageScenes: ['语音朗读 — 朗读句子', '语音搜索 — 语音输入关键词', 'AI对话 — 语音输入消息'],
|
||||
),
|
||||
storage(
|
||||
@@ -121,7 +117,6 @@ enum AppPermission {
|
||||
CupertinoIcons.folder_fill,
|
||||
'用于保存编辑的卡片、壁纸到本地,导出字体文件和数据。Android 12及以下版本需要此权限。',
|
||||
Color(0xFFFF9500),
|
||||
group: PermissionGroup.optional,
|
||||
usageScenes: ['保存卡片 — 导出到本地', '字体管理 — 下载字体文件', '数据导出 — 导出用户数据'],
|
||||
),
|
||||
network(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// ============================================================
|
||||
/// ============================================================
|
||||
/// 闲言APP — 主题系统
|
||||
/// 创建时间: 2026-04-20
|
||||
/// 更新时间: 2026-05-24
|
||||
@@ -555,6 +555,8 @@ class AppTheme {
|
||||
fontFamily: extension.fontFamily,
|
||||
);
|
||||
|
||||
AppTypography.instance = typo;
|
||||
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
colorScheme: colorScheme,
|
||||
@@ -562,7 +564,7 @@ class AppTheme {
|
||||
|
||||
// ---- 字体 ----
|
||||
fontFamily: extension.fontFamily,
|
||||
textTheme: _buildTextTheme(colorScheme, typo),
|
||||
textTheme: _buildTextTheme(colorScheme),
|
||||
|
||||
// ---- Cupertino 交互 ----
|
||||
cupertinoOverrideTheme: CupertinoThemeData(
|
||||
@@ -571,16 +573,22 @@ class AppTheme {
|
||||
barBackgroundColor: extension.bgElevated,
|
||||
textTheme: CupertinoTextThemeData(
|
||||
primaryColor: colorScheme.primary,
|
||||
textStyle: typo.body.copyWith(color: colorScheme.onSurface),
|
||||
navTitleTextStyle: typo.title3.copyWith(color: colorScheme.onSurface),
|
||||
navLargeTitleTextStyle: typo.title1.copyWith(
|
||||
textStyle: AppTypography.body.copyWith(color: colorScheme.onSurface),
|
||||
navTitleTextStyle: AppTypography.title3.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
navActionTextStyle: typo.subhead.copyWith(color: colorScheme.primary),
|
||||
actionTextStyle: typo.subhead.copyWith(color: colorScheme.primary),
|
||||
tabLabelTextStyle: typo.caption1,
|
||||
pickerTextStyle: typo.body,
|
||||
dateTimePickerTextStyle: typo.body,
|
||||
navLargeTitleTextStyle: AppTypography.title1.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
navActionTextStyle: AppTypography.subhead.copyWith(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
actionTextStyle: AppTypography.subhead.copyWith(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
tabLabelTextStyle: AppTypography.caption1,
|
||||
pickerTextStyle: AppTypography.body,
|
||||
dateTimePickerTextStyle: AppTypography.body,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -594,7 +602,9 @@ class AppTheme {
|
||||
centerTitle: true,
|
||||
backgroundColor: colorScheme.surface.withValues(alpha: 0.85),
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
titleTextStyle: typo.title3.copyWith(color: colorScheme.onSurface),
|
||||
titleTextStyle: AppTypography.title3.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
|
||||
// ---- 底部导航栏 ----
|
||||
@@ -640,7 +650,7 @@ class AppTheme {
|
||||
borderRadius: AppRadius.mdBorder,
|
||||
borderSide: BorderSide(color: colorScheme.primary, width: 1.5),
|
||||
),
|
||||
hintStyle: typo.body.copyWith(color: extension.textHint),
|
||||
hintStyle: AppTypography.body.copyWith(color: extension.textHint),
|
||||
),
|
||||
|
||||
// ---- 按钮 ----
|
||||
@@ -648,7 +658,7 @@ class AppTheme {
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
textStyle: typo.callout,
|
||||
textStyle: AppTypography.callout,
|
||||
shape: RoundedRectangleBorder(borderRadius: AppRadius.mdBorder),
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
@@ -657,7 +667,7 @@ class AppTheme {
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: colorScheme.primary,
|
||||
textStyle: typo.callout,
|
||||
textStyle: AppTypography.callout,
|
||||
shape: RoundedRectangleBorder(borderRadius: AppRadius.mdBorder),
|
||||
side: BorderSide(color: colorScheme.primary, width: 1.2),
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
@@ -667,7 +677,9 @@ class AppTheme {
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: colorScheme.primary,
|
||||
textStyle: typo.subhead.copyWith(fontWeight: FontWeight.w500),
|
||||
textStyle: AppTypography.subhead.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -699,7 +711,9 @@ class AppTheme {
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: extension.bgSecondary,
|
||||
selectedColor: colorScheme.primary.withValues(alpha: 0.15),
|
||||
labelStyle: typo.caption1.copyWith(color: colorScheme.onSurface),
|
||||
labelStyle: AppTypography.caption1.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: AppRadius.fullBorder),
|
||||
side: BorderSide.none,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -731,22 +745,29 @@ class AppTheme {
|
||||
// TextTheme
|
||||
// ============================================================
|
||||
|
||||
static TextTheme _buildTextTheme(
|
||||
ColorScheme colorScheme,
|
||||
AppTypography typo,
|
||||
) {
|
||||
static TextTheme _buildTextTheme(ColorScheme colorScheme) {
|
||||
return TextTheme(
|
||||
displayLarge: typo.display.copyWith(color: colorScheme.onSurface),
|
||||
headlineLarge: typo.title1.copyWith(color: colorScheme.onSurface),
|
||||
headlineMedium: typo.title2.copyWith(color: colorScheme.onSurface),
|
||||
headlineSmall: typo.title3.copyWith(color: colorScheme.onSurface),
|
||||
titleLarge: typo.headline.copyWith(color: colorScheme.onSurface),
|
||||
bodyLarge: typo.body.copyWith(color: colorScheme.onSurface),
|
||||
bodyMedium: typo.subhead.copyWith(color: colorScheme.onSurface),
|
||||
bodySmall: typo.footnote.copyWith(color: colorScheme.onSurface),
|
||||
labelLarge: typo.callout.copyWith(color: colorScheme.onSurface),
|
||||
labelMedium: typo.caption1.copyWith(color: colorScheme.onSurface),
|
||||
labelSmall: typo.caption2.copyWith(color: colorScheme.onSurface),
|
||||
displayLarge: AppTypography.display.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
headlineLarge: AppTypography.title1.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
headlineMedium: AppTypography.title2.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
headlineSmall: AppTypography.title3.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
titleLarge: AppTypography.headline.copyWith(color: colorScheme.onSurface),
|
||||
bodyLarge: AppTypography.body.copyWith(color: colorScheme.onSurface),
|
||||
bodyMedium: AppTypography.subhead.copyWith(color: colorScheme.onSurface),
|
||||
bodySmall: AppTypography.footnote.copyWith(color: colorScheme.onSurface),
|
||||
labelLarge: AppTypography.callout.copyWith(color: colorScheme.onSurface),
|
||||
labelMedium: AppTypography.caption1.copyWith(
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
labelSmall: AppTypography.caption2.copyWith(color: colorScheme.onSurface),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
/// ============================================================
|
||||
/// 闲言APP — 字体令牌 (ThemeExtension)
|
||||
/// 闲言APP — 字体令牌 (ThemeExtension + 静态实例)
|
||||
/// 创建时间: 2026-04-20
|
||||
/// 更新时间: 2026-05-24
|
||||
/// 作用: 统一字体大小/字重定义,响应 fontScale/fontWeight 变化
|
||||
/// 上次更新: 从静态常量类重构为 ThemeExtension,支持动态字体缩放与字重调整
|
||||
/// 上次更新: 静态 getter 委托给 instance,无需 context 也能响应主题变化
|
||||
/// ============================================================
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 字体令牌 — ThemeExtension 实现
|
||||
/// 字体令牌 — ThemeExtension + 静态实例
|
||||
///
|
||||
/// 通过 [of] 获取当前主题下的 AppTypography 实例,
|
||||
/// 自动应用 fontScale / fontWeight / fontFamily,
|
||||
/// 无需手动乘算或覆盖字重。
|
||||
/// 访问方式:
|
||||
/// - `AppTypography.xxx` — 静态 getter,委托给 [instance],无需 context
|
||||
/// - `AppTypography.of(context)` — 从 ThemeExtension 获取完整实例
|
||||
///
|
||||
/// 静态 getter 自动应用 fontScale / fontWeight / fontFamily,
|
||||
/// 类似 iOS Dynamic Type,切换字号/字重即时生效。
|
||||
class AppTypography extends ThemeExtension<AppTypography> {
|
||||
const AppTypography({
|
||||
AppTypography({
|
||||
this.fontScale = 1.0,
|
||||
this.fontWeight = FontWeight.w400,
|
||||
this.fontFamily = 'Inter',
|
||||
});
|
||||
|
||||
/// 全局实例 — 主题构建时自动更新
|
||||
static AppTypography instance = AppTypography();
|
||||
|
||||
/// 字体缩放倍率
|
||||
final double fontScale;
|
||||
|
||||
@@ -31,44 +37,22 @@ class AppTypography extends ThemeExtension<AppTypography> {
|
||||
/// 字体族
|
||||
final String fontFamily;
|
||||
|
||||
// ---- 基准字号 (静态常量,供无 context 场景使用) ----
|
||||
// ---- 基准字号 (静态常量) ----
|
||||
|
||||
/// 大标题 (启动页品牌字) 34sp Bold
|
||||
static const double fontDisplay = 34.0;
|
||||
|
||||
/// 页面主标题 28sp Bold
|
||||
static const double fontTitle1 = 28.0;
|
||||
|
||||
/// 区块标题 22sp Bold
|
||||
static const double fontTitle2 = 22.0;
|
||||
|
||||
/// 导航栏标题 20sp Semibold
|
||||
static const double fontTitle3 = 20.0;
|
||||
|
||||
/// 列表标题 18sp Semibold
|
||||
static const double fontHeadline = 18.0;
|
||||
|
||||
/// 正文内容 16sp Regular
|
||||
static const double fontBody = 16.0;
|
||||
|
||||
/// 强调文字 16sp Medium
|
||||
static const double fontCallout = 16.0;
|
||||
|
||||
/// 辅助说明 14sp Regular
|
||||
static const double fontSubhead = 14.0;
|
||||
|
||||
/// 脚注 13sp Regular
|
||||
static const double fontFootnote = 13.0;
|
||||
|
||||
/// 标签/提示 12sp Regular
|
||||
static const double fontCaption1 = 12.0;
|
||||
|
||||
/// 极小文字 11sp Regular
|
||||
static const double fontCaption2 = 11.0;
|
||||
|
||||
// ---- 字重调整 ----
|
||||
|
||||
/// 根据基准字重偏移量调整目标字重
|
||||
FontWeight _adjustWeight(FontWeight target) {
|
||||
final diff = fontWeight.value - FontWeight.w400.value;
|
||||
final newValue = (target.value + diff).clamp(100, 900);
|
||||
@@ -76,84 +60,73 @@ class AppTypography extends ThemeExtension<AppTypography> {
|
||||
return FontWeight.values[idx.clamp(0, 8)];
|
||||
}
|
||||
|
||||
// ---- TextStyle 实例方法 (自动应用 fontScale + fontWeight) ----
|
||||
// ---- TextStyle 静态 getter (委托给 instance,无需 context) ----
|
||||
|
||||
/// 大标题 34sp Bold
|
||||
TextStyle get display => TextStyle(
|
||||
fontSize: fontDisplay * fontScale,
|
||||
fontWeight: _adjustWeight(FontWeight.bold),
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get display => TextStyle(
|
||||
fontSize: fontDisplay * instance.fontScale,
|
||||
fontWeight: instance._adjustWeight(FontWeight.bold),
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 页面主标题 28sp Bold
|
||||
TextStyle get title1 => TextStyle(
|
||||
fontSize: fontTitle1 * fontScale,
|
||||
fontWeight: _adjustWeight(FontWeight.bold),
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get title1 => TextStyle(
|
||||
fontSize: fontTitle1 * instance.fontScale,
|
||||
fontWeight: instance._adjustWeight(FontWeight.bold),
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 区块标题 22sp Bold
|
||||
TextStyle get title2 => TextStyle(
|
||||
fontSize: fontTitle2 * fontScale,
|
||||
fontWeight: _adjustWeight(FontWeight.bold),
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get title2 => TextStyle(
|
||||
fontSize: fontTitle2 * instance.fontScale,
|
||||
fontWeight: instance._adjustWeight(FontWeight.bold),
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 导航栏标题 20sp Semibold
|
||||
TextStyle get title3 => TextStyle(
|
||||
fontSize: fontTitle3 * fontScale,
|
||||
fontWeight: _adjustWeight(FontWeight.w600),
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get title3 => TextStyle(
|
||||
fontSize: fontTitle3 * instance.fontScale,
|
||||
fontWeight: instance._adjustWeight(FontWeight.w600),
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 列表标题 18sp Semibold
|
||||
TextStyle get headline => TextStyle(
|
||||
fontSize: fontHeadline * fontScale,
|
||||
fontWeight: _adjustWeight(FontWeight.w600),
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get headline => TextStyle(
|
||||
fontSize: fontHeadline * instance.fontScale,
|
||||
fontWeight: instance._adjustWeight(FontWeight.w600),
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 正文 16sp Regular
|
||||
TextStyle get body => TextStyle(
|
||||
fontSize: fontBody * fontScale,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get body => TextStyle(
|
||||
fontSize: fontBody * instance.fontScale,
|
||||
fontWeight: instance.fontWeight,
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 强调文字 16sp Medium
|
||||
TextStyle get callout => TextStyle(
|
||||
fontSize: fontCallout * fontScale,
|
||||
fontWeight: _adjustWeight(FontWeight.w500),
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get callout => TextStyle(
|
||||
fontSize: fontCallout * instance.fontScale,
|
||||
fontWeight: instance._adjustWeight(FontWeight.w500),
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 辅助说明 14sp Regular
|
||||
TextStyle get subhead => TextStyle(
|
||||
fontSize: fontSubhead * fontScale,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get subhead => TextStyle(
|
||||
fontSize: fontSubhead * instance.fontScale,
|
||||
fontWeight: instance.fontWeight,
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 脚注 13sp Regular
|
||||
TextStyle get footnote => TextStyle(
|
||||
fontSize: fontFootnote * fontScale,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get footnote => TextStyle(
|
||||
fontSize: fontFootnote * instance.fontScale,
|
||||
fontWeight: instance.fontWeight,
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 标签 12sp Regular
|
||||
TextStyle get caption1 => TextStyle(
|
||||
fontSize: fontCaption1 * fontScale,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get caption1 => TextStyle(
|
||||
fontSize: fontCaption1 * instance.fontScale,
|
||||
fontWeight: instance.fontWeight,
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
/// 极小文字 11sp Regular
|
||||
TextStyle get caption2 => TextStyle(
|
||||
fontSize: fontCaption2 * fontScale,
|
||||
fontWeight: fontWeight,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
static TextStyle get caption2 => TextStyle(
|
||||
fontSize: fontCaption2 * instance.fontScale,
|
||||
fontWeight: instance.fontWeight,
|
||||
fontFamily: instance.fontFamily,
|
||||
);
|
||||
|
||||
// ---- ThemeExtension 实现 ----
|
||||
|
||||
@@ -183,8 +156,7 @@ class AppTypography extends ThemeExtension<AppTypography> {
|
||||
|
||||
// ---- 便捷访问 ----
|
||||
|
||||
/// 从 BuildContext 获取当前主题的 AppTypography 实例
|
||||
static AppTypography of(BuildContext context) {
|
||||
return Theme.of(context).extension<AppTypography>() ?? const AppTypography();
|
||||
return Theme.of(context).extension<AppTypography>() ?? instance;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user