乐于分享
好东西不私藏

App安装包200MB被用户骂?3步把Flutter包体积砍到50MB,下载量翻倍

App安装包200MB被用户骂?3步把Flutter包体积砍到50MB,下载量翻倍

上线第一天看到用户评论"一个工具App要200MB?抢钱吗",我脸都红了。打开构建分析一看,assets目录里的图片占了120MB,没用到的abi全打包进去了,字体文件一套塞了5个。

咱们今天直接把包体积从200MB砍到50MB以下,3步搞定,下载转化率肉眼可见地上涨。


一、先看真相:你的App里到底装了什么

别瞎猜,Flutter自带构建分析工具,打开终端跑:

bash
flutter build apk --analyze-size=build/apk_size.json

然后上传到官方分析器:

bash
# 用浏览器打开 https://flutter.github.io/split-per-abi/
# 或者直接查看分析报告
flutter build apk --split-per-abi

跑完你会看到类似这张表:

code
Total APK size: 198MB
├── lib/ (native libs): 65MB
├── assets/: 120MB
├── Flutter framework: 8MB
├── Dart code: 3MB
└── 其他: 2MB

老实说,Dart代码只占3MB,真正拖后腿的是资源和native库。找到大头,才有方向。


二、第一步:拆分ABI包(效果最猛,直接砍一半)

Flutter默认打出来的apk包含所有CPU架构的so库——armeabi-v7a、arm64-v8a、x86_64全塞进去。但用户的手机只需要其中一个。

❌ 默认全包写法:

bash
flutter build apk --release
# 输出: app-release.apk (198MB) — 所有abi都在里面

✅ 按ABI拆分:

bash
flutter build apk --split-per-abi --release

输出三个独立的apk:

code
app-armeabi-v7a-release.apk    68MB  (32位老机型)
app-arm64-v8a-release.apk      72MB  (64位主流机型)
app-x86_64-release.apk         58MB  (模拟器用)

直接砍掉60%以上的体积。现在Google Play和华为/小米应用商店都支持自动按用户设备分发对应abi的包,上传这三个就行了。

如果只用一个渠道分发(比如企业内部分发),只保留arm64-v8a就够了,现在99%的新设备都是64位。

在 android/app/build.gradle 里确认:

gradle
android {
    defaultConfig {
// 确保只打包64位
        ndk {
            abiFilters 'arm64-v8a'
        }
    }
}

这一步做完,198MB → 72MB,效果立竿见影。


三、第二步:压缩图片资源(120MB → 15MB的秘诀)

这是最多人踩的坑。设计师给一套PNG图标,每张2~5MB,全丢进assets目录,编译时原样打包。

方案A:转WebP(推荐)

WebP是Google亲儿子格式,Flutter原生支持,不用任何额外插件。体积比PNG小60~80%。

bash
# macOS安装转换工具
brew install webp

# 批量转换assets下所有PNG
find assets/ -name "*.png" -exec sh -c \
  'cwebp -q 80"$1" -o "${1%.png}.webp" && rm "$1"' _ {} \;

转换后更新 pubspec.yaml 里的引用:

yaml
flutter:
  assets:
    - assets/images/

代码里不需要改,Image.asset('assets/images/logo.webp') 直接用。

方案B:移除未使用的资源

很多项目里躺着半年前设计稿的图片,早就没人用了但没删。用这个脚本扫一遍:

bash
#!/bin/bash
# 查找assets里有但代码里没引用的图片
cd assets/images
for f in *.webp *.png *.jpg; do
    name=$(basename "$f")
if ! grep -rq "$name" ../../lib/; then
        echo "未引用: $name"
    fi
done

老实说,咱们跑完这个脚本,删掉了47张没人用的图,直接省了60MB。

方案C:用网络CDN代替本地资源

启动图、引导页图片、运营活动图——这些不该打包进App。放CDN上,按需加载:

dart
// 运营图片从CDN加载,不占安装包体积
Image.network(
  'https://cdn.yourdomain.com/promo/banner_2026.webp',
  cacheWidth: 720,  // 关键:限制解码尺寸
  cacheHeight: 400,
);

这一步做完,assets从120MB降到15MB以内。


四、第三步:裁剪字体和移除无用代码

字体瘦身

一套中文字体(比如思源黑体)完整打包要20MB以上。用fonttools只保留用到的字符:

bash
# 安装fonttools
pip install fonttools brotli

# 从完整字体中抽取只包含常用汉字的子集
pyftmerge \
  --output-file=assets/fonts/NotoSansSC-subset.ttf \
  --unicodes="U+4E00-U+9FFF,U+3000-U+303F,U+FF00-U+FFEF" \
  assets/fonts/NotoSansSC-Regular.ttf

提取后的子集字体通常只有2~3MB,覆盖了99%的日常使用场景。

开启代码混淆和Tree-shaking

Flutter release模式自动做tree-shaking,但很多人开发时引了一堆只在debug用的包:

yaml
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  dio: ^5.4.0
  provider: ^6.1.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  # 这些只在开发时用,不会打进release包
  flutter_lints: ^3.0.0
  build_runner: ^2.4.8
  json_serializable: ^6.7.1

确认 flutter build apk --release 时没有warning提示未使用的依赖。

构建时加上混淆参数:

bash
flutter build apk --release --obfuscate --split-debug-info=build/symbols

--obfuscate 会混淆Dart代码中的符号名,--split-debug-info 把符号表单独存一份(崩溃分析时用),apk体积再少几MB。


五、效果对比汇总

ABI拆分:198MB → 72MB(↓64%)

图片压缩(WebP + 清理):72MB → 48MB(↓33%)

字体裁剪 + 代码混淆:48MB → 42MB(↓12%)

总计:200MB → 42MB,缩减79%

优化项
优化前
优化后
缩减
APK总体积
198MB
42MB
79%
native库
65MB
22MB
ABI拆分
图片资源
120MB
15MB
WebP+清理
字体文件
20MB
3MB
子集裁剪

避坑清单(血泪经验)

1.别用 flutter build apk 不加 --release — debug模式的apk比release大3倍以上,因为包含了完整的Dart VM和调试符号。发给用户的永远是 --release

2.iOS的ipa也要做类似优化 — iOS用的是 flutter build ipa,同样可以用 --split-debug-info。另外iOS的资源压缩用Xcode的 Compress PNG Files 选项自动处理。

3.WebP兼容性不是问题 — Flutter从2.0开始原生支持WebP,Android 4.0+和iOS全版本都能正常解码。不用担心老设备。

4.CDN资源要加降级策略 — 网络图片加载失败时,给个占位图,别让用户看到空白:

dart
Image.network(
  'https://cdn.yourdomain.com/banner.webp',
  errorBuilder: (context, error, stackTrace) {
returnContainer(
      height: 200,
      color: Colors.grey[200],
      child: constCenter(child: Text('图片加载失败')),
    );
  },
);

老实说,包体积这件事不是"越小越好",而是"不该大的别大"。Dart代码本来就只有几MB,没必要在上面折腾。真正该砍的是资源文件和多余的abi库。3步做完,你的App从200MB掉到40MB出头,用户下载意愿至少翻一倍。

你的App安装包现在多大?跑完构建分析后最大的那块是什么?在评论区说说你的数据,咱们一起看看还能怎么砍。