win11 桌面开发 flutter3.x 中文字体和对齐问题

环境描述

  • 操作系统: win11
  • flutter版本相关版本
java 复制代码
Flutter 3.13.9 • channel stable • https://github.com/flutter/flutter.git
Framework • revision d211f42860 (8 days ago) • 2023-10-25 13:42:25 -0700
Engine • revision 0545f8705d
Tools • Dart 3.1.5 • DevTools 2.25.0

问题描述

默认情况下,flutter的中文字体会显示成这样

github 上相关 issue [Desktop-Windows]Chinese characters are incorrectly rendered in flutter 3 · Issue #103811 · flutter/flutter · GitHub

问题可能的原因

  • 可能是使用了日文字体
  • 为什么会使用日文字体? 可能是因为默认locale不正确

都只是可能,确切的原因不知道

如何解决?

无效的解决方式 : 通过配置locale解决

可能在2.x有效,但在我的环境下是无效的

有效解决方式

使用自定义中文字体。如: 鸿蒙字体小米字体OPPO Sans思源黑体

配置和使用自定义字体

pubspec.yaml

yaml 复制代码
fonts:
    - family: MiSans
      fonts:
        - asset: assets/fonts/MiSans-Regular.ttf
dart 复制代码
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

void main() {
  debugPrint(Platform.localeName);
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('zh', 'CN'), // 中文简体
        Locale('en', 'US'), // 美国英语
      ],
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
        // 默认字体: 这里使用的是 pubspec.yaml 文件中,自定义的字体
        fontFamily: 'MiSans',
        // 当默认字体中不包含对应文字时,会按顺序使用fallback中的字体渲染
        fontFamilyFallback: const [
          'MiSans',
          'Helvetica Neue',
          'PingFang SC',
          'Source Han Sans SC',
          'Noto Sans CJK SC'
        ],
      ),
      home: const MyHomePage(title: 'Flutter 中文测试'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(
          widget.title,
          style: const TextStyle(height: 1.5, fontSize: 18),
          locale: const Locale.fromSubtags(
              languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'Flutter 中文测试 如下是你点击按钮的次数:',
            ),
            const Text(
              '雷电将军',
            ),
            TextButton(
                onPressed: () => debugPrint('123'), child: const Text('角色按钮')),
            ElevatedButton(
                onPressed: () => debugPrint('123'), child: const Text('甘雨')),
            const Text('固件升级123'),
            const Text(
              'abc经营统计123!。;.;',
              style: TextStyle(fontWeight: FontWeight.bold),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

效果对比

左边是配置自定义字体之后的效果,右边是未配置自定义字体的效果

可以看出右边的中文存在如下问题:

  • 即使是中文也无法完全对齐. 如:这个字,这个字
  • 英文,数字和数字同时出现时,无法对齐。如: abc经营统计123右边的是没有底线对齐的

字重(fontWeight)问题

按照上面的字体配置,让我们看看下面的效果图

abc经营统计123!。;.; 这段文字,我将字重(fontWeight)从100-900都设置了一遍,但看效果好像只有2种字重

从源码中可以看出FontWeight.normal实际就是FontWeight.w400, 而FontWeight.bold实际就是FontWeight.w700

那为啥设置其他的FontWeight.wXXX无效呢?

因为没有配置字体字重!

默认约定:字体文件名与字重对应关系

scss 复制代码
100 - Thin
200 - Extra Light (Ultra Light)
300 - Light
400 - Regular (Normal、Book、Roman)
500 - Medium
600 - Semi Bold (Demi Bold)
700 - Bold
800 - Extra Bold (Ultra Bold)
900 - Black (Heavy)

修改pubspec.yaml配置

P.S. 小米字体文件中未找到字重为800的字体文件,因此w800实际效果和bold,也即是w700一致

yaml 复制代码
fonts:
    - family: MiSans
      fonts:
        - asset: assets/fonts/MiSans-Thin.ttf
          weight: 100
        - asset: assets/fonts/MiSans-ExtraLight.ttf
          weight: 200
        - asset: assets/fonts/MiSans-Light.ttf
          weight: 300
        - asset: assets/fonts/MiSans-Regular.ttf
          weight: 400
        - asset: assets/fonts/MiSans-Medium.ttf
          weight: 500
        - asset: assets/fonts/MiSans-Semibold.ttf
          weight: 600
        - asset: assets/fonts/MiSans-Bold.ttf
          weight: 700
        - asset: assets/fonts/MiSans-Heavy.ttf
          weight: 900

P.S. 字重与文件的对应关系只是弱关联,完全可以不这么对应,只要按设计师效果来对应即可,同时也不是一定要将所有字重都配置上去,应该根据实际需要,用到了就配置,没用到就不配置,毕竟多一个字体文件,最终安装包就会增加几兆

效果

font-weight失效移动安卓处理方法_fontweight500看不出加粗-CSDN博客

Regular、Normal、Medium、Light 对应的font-weight值_字重medium-CSDN博客

字体发虚问题

除开flutter本身的渲染问题之外,我们能做的就是根据实际硬件上的实际感官,选择不同的字号(fontSize)与字重(fontWeight)。

小字号必然要选小字重,大字号必然选大字重

中文与数字或英文同时出现时,文字无法对齐问题

这个应该是由于flutter使用的中文字体和英文字体不一致,导致他们单个的实际行高不一致导致,如上面所示,统一使用同一个字体之后,未出现该问题。

因为中文字体库通常都会包含英文字母以及数字和标点符号,所以,当将默认字体指定为中文字体之后,无论是中文,英文,数字还是标点符号,都会使用同一种字体渲染,那么单个字符的行高就必然一致了。也就不存在无法对齐的问题了。

如何在不同地方,设置默认的统一文字样式

如:导航栏,主题内容,按钮文字

参考上面的代码以及这个截图可以看出,当设置了bodyMedium样式之后普通的Text的样式就都是bodyMedium的样式了。其他样式好像并不会成为什么地方的默认值。那是不是就没用了呢?当然不是,我们依然可以结合他们的语义定义成不同的字体样式,然后所有地方都根据语义,都调用textTheme中的字体公共样式。调用方式:

dart 复制代码
Theme.of(context).textTheme.bodyLarge

// bodyLarge 创建新样式,新样式和bodyLarge的区别是颜色不同
TextStyle newStyle = Theme.of(context).textTheme.bodyLarge!.copyWith(color:Colors.green);

当然,也可以自己定义一个类,将公共样式都定义成这个类的静态属性,然后供其他地方统一调用。

Text和Icon对齐

默认会中线对齐(注意: 文字应该统一使用同一种字体,这能直接避免中文,英文,数字采用不同字体渲染,导致字体大小即使一致,但行高不一致问题)

像这样

加背景色是为了让我们直观的看出来,他们的实际大小,icon图标虽然垂直居中了,但实际高度是内容大小决定的,要调整icon的高度与Text的保持一致,就要设置Icon的size属性。由于这里将Text的fontSize设置成了44,我们想当然的认为将Icon的size也设置成44不就好了,然而实际效果是这样

这里就引出了一个注意点:Text的实际高度默认情况与fontSize是不一致的,Text的实际高度计算公式:Text实际高度 = height * fontSize,height相当于一个缩放因子,当height为1时,fontSize与Text的实际高度保持一致,但这会导致Text中如果存在g,j这类字符,那么将会超出Text的实际高度,可能会导致多行文本的文字重叠,因此height这个缩放因子,至少应该设置成大于1的值,让g,j这类字符不会超出,Text的实际高度。具体设置成多少,应该根据实际使用的字体的观感来决定。为了避免height这个缩放因子的混乱,应该在textTheme中统一设置,或统一定义成一个静态常量

而Icon的size是实际大小,因此,在这个例子中,可以这样设置

不改变Icon大小,达到同样的效果

改变Icon在粉红色框中的位置

Text和TextFormField或TextField对齐

暂未发现无法对齐

相关推荐
Zender Han1 小时前
Flutter自定义矩形进度条实现详解
android·flutter·ios
hello world smile1 小时前
关于Flutter空安全升级方案整理
flutter·移动端
君蓦16 小时前
Flutter 本地存储与数据库的使用和优化
flutter
problc1 天前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
lqj_本人1 天前
鸿蒙next选择 Flutter 开发跨平台应用的原因
flutter·华为·harmonyos
lqj_本人2 天前
Flutter&鸿蒙next 状态管理框架对比分析
flutter·华为·harmonyos
起司锅仔2 天前
Flutter启动流程(2)
flutter
hello world smile2 天前
最全的Flutter中pubspec.yaml及其yaml 语法的使用说明
android·前端·javascript·flutter·dart·yaml·pubspec.yaml
lqj_本人2 天前
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
flutter·harmonyos