环境描述
- 操作系统: 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对齐
暂未发现无法对齐