December 27, 2025
3 min read
By devshan

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

一、字重设置不生效

1.1 问题现象

设置页面提供了四档字重选项(细、正常、中粗、粗),但除了”粗”之外,其他三档视觉上没有任何区别。

1.2 原因分析

原来的实现:

static TextTheme _applyFontWeight(TextTheme base, FontWeight weight, Color color) {
  return base.apply(fontWeightDelta: 0).copyWith(
    displayLarge: base.displayLarge?.copyWith(fontWeight: weight, color: color),
    // ...
  );
}

问题copyWith(fontWeight: ...) 只是修改了样式属性,但不会加载对应字重的字体文件。

Google Fonts 的 Noto Sans SC 实际上每个字重是独立的字体文件:

  • NotoSansSC-Light.ttf (300)
  • NotoSansSC-Regular.ttf (400)
  • NotoSansSC-Medium.ttf (500)
  • NotoSansSC-SemiBold.ttf (600)

1.3 解决方案

直接使用 GoogleFonts.notoSansSc(fontWeight: weight) 生成每个 TextStyle:

static TextTheme _buildTextTheme(TextTheme base, FontWeight weight, Color color) {
  return TextTheme(
    displayLarge: GoogleFonts.notoSansSc(
      textStyle: base.displayLarge,
      fontWeight: weight,
      color: color,
    ),
    displayMedium: GoogleFonts.notoSansSc(
      textStyle: base.displayMedium,
      fontWeight: weight,
      color: color,
    ),
    // ... 所有 14 个 TextStyle 都使用 GoogleFonts.notoSansSc 直接生成
  );
}

关键理解GoogleFonts.notoSansSc() 会根据 fontWeight 参数自动加载对应的字体文件。


二、文件图标颜色消失

2.1 问题现象

聊天界面发送文件后,文件消息的图标颜色不显示(灰色),而之前是有颜色的。

2.2 原因分析

FileMessageContent._buildFileIcon() 方法内部使用了固定颜色,而没有使用传入的 iconColor 参数:

// 问题代码
Widget _buildFileIcon(ThemeData theme, Color accentColor) {
  return Container(
    decoration: BoxDecoration(
      color: accentColor.withValues(alpha: 0.2),  // 使用了 accentColor 而非 iconColor
      // ...
    ),
    child: Icon(icon, color: accentColor, size: 24),  // 同样问题
  );
}

2.3 解决方案

修改为使用传入的 iconColor 参数:

Widget _buildFileIcon(ThemeData theme, Color accentColor) {
  final displayColor = iconColor;  // 使用传入的颜色
  return Container(
    decoration: BoxDecoration(
      color: displayColor.withValues(alpha: 0.2),
      borderRadius: BorderRadius.circular(8),
    ),
    child: Icon(icon, color: displayColor, size: 24),
  );
}

三、图片上传后缩略图无法加载

3.1 问题现象

聊天界面上传图片后,缩略图显示加载占位符,无法正常显示。但重新进入聊天界面后又能正常显示。

3.2 原因分析

这是一个之前解决过的问题(参见 memory: ImagePicker返回文件可能无扩展名)。

ImagePicker 在某些情况下返回的文件名没有扩展名(如 image_picker_xxxxx),而我们的文件类型判断依赖扩展名:

// 问题:safeFileName 可能没有扩展名
if (isImageFile(safeFileName)) {
  // 无法匹配,走不到这里
}

3.3 解决方案

同时检查文件名和文件路径:

if (isVideoFile(safeFileName) || isVideoFile(filePath)) {
  // 处理视频
} else if (isImageFile(safeFileName) || isImageFile(filePath)) {
  // 处理图片
}

原理:即使 safeFileName 没有扩展名,filePath(完整路径)通常包含正确的扩展名。


四、修改文件清单

4.1 app_theme.dart

  • _applyFontWeight() 改为 _buildTextTheme()
  • 使用 GoogleFonts.notoSansSc() 直接生成带字重的 TextStyle

4.2 chat_widgets.dart

  • 修复 FileMessageContent._buildFileIcon() 使用正确的 iconColor 参数

4.3 chat_page.dart(或相关文件)

  • 文件类型判断时同时检查 fileNamefilePath

五、经验教训

5.1 Google Fonts 字重加载

copyWith(fontWeight: ...) 只修改样式属性,不会触发字体文件加载。 必须使用 GoogleFonts.xxx(fontWeight: weight) 才能加载对应字重的字体文件。

5.2 ImagePicker 文件名问题

ImagePicker 返回的文件名可能:

  • 没有扩展名
  • 使用临时文件名(如 image_picker_xxxxx
  • 实际文件路径才有正确扩展名

最佳实践:使用 ensureFileExtensionAsync(fileName, filePath) 确保文件名有正确扩展名。

5.3 参数传递检查

当组件显示不符合预期时,检查:

  1. 参数是否正确传递
  2. 内部是否使用了传入的参数(而非其他变量)