Flutter Web如何异步加载MaterialIcons-Regular.otf优化首屏
Flutter Web 异步加载Material图标解除首屏阻塞方案
核心逻辑是把默认强制同步加载的Material图标字体从首屏关键资源中剥离,等首屏渲染完成后再动态加载,完全可以实现无图标首屏优先展示的效果,具体操作如下:
- 第一步:关闭默认的图标同步打包配置
打开项目根目录pubspec.yaml,找到flutter配置段下的uses-material-design: true,把值改成false。这个配置是Flutter默认把MaterialIcons-Regular.otf标记为首屏必需同步资源的开关,关掉之后构建产物就不会把这个字体放到首屏阻塞加载的资源列表里。 - 第二步:托管图标字体资源
从本地Flutter SDK目录(路径通常是bin/cache/artifacts/material_fonts/)下找到MaterialIcons-Regular.otf文件,放到项目的web/fonts/目录下随项目部署即可,不需要依赖第三方外部资源。 - 第三步:实现异步加载逻辑
编写专门的字体加载方法,在首屏渲染完成后再调用,参考代码:
加载触发时机放在首帧渲染结束后即可,比如在首页组件的import 'dart:html' as html; import 'dart:typed_data'; import 'package:flutter/widgets.dart'; // 全局标记图标是否加载完成 bool isMaterialIconsLoaded = false; Future<void> loadMaterialIcons() async { if (isMaterialIconsLoaded) return; const fontPath = 'fonts/MaterialIcons-Regular.otf'; final response = await html.window.fetch(fontPath); final fontBytes = (await response.arrayBuffer() as ByteBuffer).asUint8List(); final fontLoader = FontLoader('MaterialIcons'); fontLoader.addFont(Future.value(ByteData.sublistView(fontBytes))); await fontLoader.load(); isMaterialIconsLoaded = true; }initState里加如下逻辑:@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) async { // 可根据需求加延迟,等首屏交互稳定后再加载,避免抢占首屏带宽 await Future.delayed(const Duration(seconds: 1)); await loadMaterialIcons(); // 加载完成后刷新页面,图标即可正常显示 if (mounted) setState(() {}); }); } - 第四步:处理加载前的兜底状态
图标字体没加载完成时,原生Icon组件会显示空白,建议封装全局通用的图标组件:字体未加载时用和图标尺寸一致的占位块、本地内联SVG或者emoji临时占位,等字体加载完成后自动切换为Material图标,避免页面布局跳动。
额外优化提示
- 做完上述配置后,检查构建生成的
web/index.html文件,如果里面有针对MaterialIcons-Regular.otf的<link rel="preload">标签,直接删掉,这类预加载标签依然会触发同步阻塞加载。 - 可以用图标子集化工具,只提取项目中实际用到的图标生成裁剪后的字体文件,体积可以从1.6MB压缩到几十KB,加载耗时几乎可以忽略。
- 给字体文件配置长效缓存,用户二次访问时不需要重新下载。
内容的提问来源于stack exchange,提问作者Alexey Inkin
相关产品推荐
相关产品推荐

