Flutter Web 优化之:定制 Flutter Engine 字体回滚策略
介绍
Flutter Web 目前(3.8.0-16.0.pre.21)采用的字体回滚策略是,若当前使用使用的字体不包含需要渲染的文字,则回滚到后备字体。默认的后备字体为 Roboto
,另外还有通过网络加载的 Noto Fonts
系列后备字体。
由于中文字体文件体积较大,Flutter Web 默认使用的字体文件 Roboto
又不含中文字体,因此在渲染中文时会回滚到网络后备字体(大概 8M)。常见的优化方式是通过字体提取工具提取出需要的字体,从而减小字体文件体积。问题正是出自这里,字体提取工具提取出的字体文件并不包含所有字体,因此在渲染时可能出现因缺少某个单一文字便回滚到网络后备字体的情况,这近乎直接致使字体提取带来的优化付之东流。
本文主要讲述如何对字体回滚策略进行修改,在字体文件缺少某些文字字体时在 Console 及终端打印相应警告提醒开发者对字体文件进行补充,从而避免回滚到网络后备字体。
实践
- 创建工程目录
1mkdir -p $HOME/Projects/flutter_engine
2cd $HOME/Projects/flutter_engine
- 设置网络代理
1git config --global http.proxy http://192.168.1.136:10809
2git config --global https.proxy http://192.168.1.136:10809
1cat >$PWD/.boto.cfg<EOF
2[Boto]
3proxy=192.168.1.136
4proxy_port=10809
5EFO
6export NO_AUTH_BOTO_CONFIG=$PWD/.boto.cfg
- 安装 depot_tools 工具
1git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git tools/depot_tools
2export PATH="$PATH:$PWD/tools/depot_tools"
- 下载 Flutter Engine 源码
1cat >$PWD/.gclient<EOF
2solutions = [
3 {
4 "managed": False,
5 "name": "src/flutter",
6 "url": "git@github.com:flutter/engine.git",
7 "custom_deps": {},
8 "deps_file": "DEPS",
9 "safesync_url": "",
10 "custom_vars": {
11 "download_emsdk": True,
12 },
13 },
14]
15EFO
1gclient sync
在 Windows 上,gclient sync 由于这个问题必须以管理员身份运行。另外,避免中断此脚本(gclient sync),因为这样做会使存储库处于不一致的状态,清理起来很乏味。
- 切换 Flutter Engine 版本
这一步主要是找到并将 Engine 切换至与当前 Flutter 对应的版本。可以通过 flutter --version
查看当前 Flutter 版本,例如以下输出:
1Flutter 3.8.0-16.0.pre.21 • channel master • https://github.com/flutter/flutter.git
2Framework • revision 56e1bddc59 (3 days ago) • 2023-02-23 12:12:11 -0500
3Engine • revision 19cf8e363f
4Tools • Dart 3.0.0 (build 3.0.0-266.0.dev) • DevTools 2.22.1
上述输出中,Engine 版本为 19cf8e363f
,因此我们需要将 Engine 切换至此版本:
1cd src/flutter
2git reset --hard 19cf8e363f
如果后续遇到很离奇的问题,可以尝试执行
gclient sync --with_tags
重新同步代码。
- 设置环境变量
1export PATH="$PATH:$PWD/src/flutter/lib/web_ui/dev"
- 修改源码
修改当前目录下 src/flutter/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart
文件的第 214 行。
将 findFontsForMissingCodeunits(codeUnits);
替换为如下代码:
1// 此行为新增代码
2printWarning('contains unsupported code units: '
3 '$codeUnits (${String.fromCharCodes(codeUnits)})');
4
5findFontsForMissingCodeunits(codeUnits);
随着 Flutter Engine 版本的更新,此处的行号可能会发生变化,因此请以实际情况为准。此处提供的是 3.8.0-16.0.pre.21 版本的行号。
- 编译引擎
1felt build
- 运行测试
1cd path/to/some/app
2flutter --local-web-sdk=wasm_release --local-engine-src-path=$HOME/Projects/flutter_engine/src run -d chrome --web-hostname 0.0.0.0 --web-port 9000 --web-renderer canvaskit
注意将
path/to/some/app
替换你的 Flutter Web 项目的实际路径。
演示
从下面演示图片中可以看出,通过修改字体回滚策略部分的代码,可以实现在 Console 中打印文字缺少字体的警告信息。
此处缺少的字体的文字为中文符号 :
,编码为 65306
。
补全字体文件后,再次运行测试,可以看到 Console 中不再有警告信息。
番外
由于原理相同,本文提及的方法通过简单修改也可实现诸如缺少字体时通过 API 进行上报或强行阻止字体回滚等操作。通过 API 上报缺失字体的文字,配合后端可实现自动补充并更新字体文件的功能,可以最小化开发者的工作负担。
另外,在此提供一个 Git Patch 文件,可直接应用于 Flutter Engine 3.8.0-16.0.pre.21 源码,以实现本文所述的功能。
1From a44f4c0239f1575a910477d0cae5a73d669f7e34 Mon Sep 17 00:00:00 2001
2From: zeronumber <main@woini.men>
3Date: Sun, 26 Feb 2023 23:07:41 +0800
4Subject: [PATCH] [web] add unsupported code units warning
5
6---
7 lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart | 4 ++++
8 1 file changed, 4 insertions(+)
9
10diff --git a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart
11index 90c2fb31d1..9734cc859f 100644
12--- a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart
13+++ b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart
14@@ -210,6 +210,10 @@ class FontFallbackData {
15 codeUnits.removeAt(i);
16 }
17 }
18+
19+ printWarning('contains unsupported code units: '
20+ '$codeUnits (${String.fromCharCodes(codeUnits)})');
21+
22 findFontsForMissingCodeunits(codeUnits);
23 }
24
25--
262.34.1