ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Flutter应用图标不显示?Android与iOS完整排查指南

Flutter应用图标不显示?Android与iOS完整排查指南

刚接手一个 Flutter 项目时,碰到过一件让人特别窝火的事:代码跑得好好的,功能也都正常,可安装到真机上之后,桌面愣是看不到应用图标。应用确实装上了,能通过设置里的应用列表打开,就是桌面不显示。当时我还以为是手机抽风,重启了好几回,结果发现是项目里头的图标方案压根就没配对。

这个问题说大不大,说小不小,但特别耽误事。如果你也正被“flutter 在手机端不显示应用图标”这种问题卡住,这一篇建议完整看一遍。我会把 Android 和 iOS 两边常见的隐藏坑、图标资源生成逻辑、还有启动器缓存导致的“假性消失”都拆开讲清楚,保证你能照着排查一遍就把问题定位出来。

1. 先搞清楚“看不到图标”是哪一种情况

很多朋友一上来就急着改代码,其实效率很低。我在排查这种问题时,第一件事永远是先问自己:它到底属于下面哪种情况?因为不同情况对应的排查方向完全不一样,搞错了只会白忙活。

1.1 是桌面没有任何图标,还是只有默认 Flutter 图标

桌面上一片空白、完全找不到应用入口,和“图标有,但是显示的是 Flutter 默认的那个蓝色Logo”,这是两个截然不同的问题。

如果是后者,说明应用安装正常,只是没有把自定义图标资源替换上去。这种情况八成出在图标资源生成这一步,比如flutter_launcher_icons这个工具没有正确执行,或者执行了但生成的资源没有覆盖到对应平台。

如果是前者,也就是图标完全不存在,那就要往更底层想。我遇到得最多的原因是 Android 的AndroidManifest.xml里压根没声明android:icon,或者声明的资源引用了不存在的文件。系统不知道这个应用该用哪张图当图标,自然就会给出一个空白或者异常的占位。

还有个很容易被忽略的情况:某些手机厂商的系统桌面有自己的图标加载策略,安装完应用之后要等一会儿才刷新桌面。这种“假性不显示”不是项目的问题,你把桌面切换一下页面,或者等上几十秒,它可能自己就出现了。

1.2 是刚装完没有,还是升级后才消失

这一点同样关键,因为它对应完全不同的排查方向。

如果是第一次安装就没有图标,问题基本出在构建配置或者资源声明上。如果之前有图标、升级安装之后反而消失了,那就要怀疑是不是开启了什么“图标不更新”的优化逻辑。某些手机系统在应用升级后会暂时保留旧图标,甚至因为图标资源名称变化而找不到新资源。这时候最简单粗暴但有效的办法是:卸载重装。

我知道有朋友不想卸载,毕竟要重新登录账号、重新配环境,很麻烦。但说句实在话,应用图标这种问题,与其花一两个小时去看日志,不如先卸载重装一次,大概率能排除掉系统层面的缓存干扰。我基本上每次都会先做这步,再开始改代码,省下来很多无用功。

注意:如果卸载重装后图标出现了,说明项目配置其实没大问题,之前大概率是系统缓存或者增量安装的锅。如果卸载重装后还是没有图标,那才是真正需要动代码的场景。

2. Android 端排查链路:从图标资源到启动器缓存

安卓端相对容易排查,因为 Android 的开源生态决定了你能检查的维度很多,但也正因为多,经常让人不知道从哪里下手。我的建议是按“资源是否生成 -> 资源是否被引用 -> 系统是否正常读取”这个顺序往下走。

2.1 flutter_launcher_icons 配置常见误区

大多数 Flutter 项目生成图标都会用flutter_launcher_icons这个开发依赖。它用起来确实方便,但配置上藏着不少细节,稍不注意就会生成一堆残缺资源。

先看一个典型的pubspec.yaml配置:

dev_dependencies: flutter_launcher_icons: "^0.13.1" flutter_launcher_icons: android: "launcher_icon" ios: true image_path: "assets/icon/app_icon.png" adaptive_icon_background: "#FFFFFF" adaptive_icon_foreground: "assets/icon/foreground.png"

执行生成命令:

flutter pub get dart run flutter_launcher_icons

如果你只用image_path指定了图标,没管自适应图标,那么 Android 8.0 及以上系统可能会因为缺少前景层和背景层而显示得特别奇怪,甚至有某些桌面直接不渲染。注意,我这里的android字段指定的是子目录名称,而不是具体文件名。它的实际含义是:在mipmap系列目录下生成一个叫launcher_icon.png的文件。很多朋友看不懂这个字段就乱填,填成android: true或者一个不存在的名字,结果图标文件生成到了错误的位置,应用引用不到,桌面自然就“看不到图标”了。

所以建议增加adaptive_icon_background和adaptive_icon_foreground,这两个字段才是现代安卓桌面真正会用到的东西。很多手机系统显示的都是自适应图标,而不是单纯一张方图。只给普通图标文件、不给自适应层,在不同机型上显示效果千奇百怪,这是 Flutter 手机端图标问题里被吐槽最多的点之一。

2.2 AndroidManifest.xml 的图标声明到底检查什么

就算flutter_launcher_icons成功生成了图标,如果AndroidManifest.xml里的入口 Activity 没有正确声明引用,依然白搭。

打开android/app/src/main/AndroidManifest.xml,找到<application>标签,重点看这两个属性:

<application android:label="你的应用名" android:icon="@mipmap/launcher_icon" android:roundIcon="@mipmap/launcher_icon">

这里最容易出的问题就是资源名对不上。比如你生成的文件叫ic_launcher.png,但这里写的是@mipmap/launcher_icon,那系统编译时直接报错,不会让你出包。就算侥幸编译通过(有些 CI 配置比较乱),装到手机上也是空白图标或者默认机器人图标。

还有一种情况:你在某个渠道的AndroidManifest.xml(比如src/debug/AndroidManifest.xml或src/profile/AndroidManifest.xml)里写了不同的android:icon配置,结果 Debug 包和 Release 包显示不一致。这种问题最坑,因为看起来代码都一样,实际打包时不同版本用了不同清单文件。

排查技巧很简单:直接搜索整个android目录下的android:icon,看看到底有多少处,每一处分别引用的是什么资源。如果你发现 debug 清单里引用的资源根本不存在,那不管你怎么改主清单都不会生效。

2.3 自适应图标(adaptive icon)下的“看不见”陷阱

Android 8.0 之后引入了自适应图标,系统会根据前景层、背景层和圆角遮罩来动态裁剪图标。如果你的应用只提供了传统的png方图,在部分原生系统桌面上可能显示异常,表现为图标区域灰一块或者干脆不出现。

我遇到过一种情况:mipmap目录下只有一个ic_launcher.png,但在支持自适应图标的手机上,桌面读取的是mipmap-anydpi-v26/ic_launcher.xml。这个 XML 文件内部会引用<background>和<foreground>两个资源。如果这个 XML 文件存在,但引用的前景层资源路径不对,那系统渲染图标时就会静默失败,桌面就什么也不显示。

改法是在android/app/src/main/res/mipmap-anydpi-v26/下放一个正确的 XML:

<?xml version="1.0" encoding="utf-8"?> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <background android:drawable="@color/ic_launcher_background"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/> </adaptive-icon>

同时保证res/values/colors.xml里有对应的颜色定义,mipmap各密度目录里有对应的前景图。如果你使用flutter_launcher_icons并且配置了adaptive_icon_background和adaptive_icon_foreground,这个 XML 文件通常会自动生成,不需要手写。但如果你发现自动生成的 XML 引用了不存在的资源,那就得手动来。

提示:安卓桌面千奇百怪,有的桌面只认自适应图标,有的只认传统图标。所以两种资源都必须存在,缺一个就会出现“部分机型看不到、部分机型正常”的现象。这也是很多人说“我在模拟器上正常,到了真机就没图标”的经典原因。

3. iOS 端同样会踩:Assets.xcassets 与 Info.plist 里藏着的问题

很多人以为 iOS 只有一台“苹果手机”,图标显示应该比 Android 简单。其实不然,iOS 端出现“图标不显示”的概率也不低,只是表现形式更集中:安装后桌面上出现一个灰扑扑的空白占位图标,或者直接显示默认图标。

3.1 AppIcon.appiconset 的正确姿势

iOS 的图标放在ios/Runner/Assets.xcassets/AppIcon.appiconset/目录下,里面有个Contents.json文件,它描述了不同尺寸和设备对应的图片文件。如果你用flutter_launcher_icons生成,它会帮你更新Contents.json并填充图片文件。

但这里有个很容易忽略的点:flutter_launcher_icons生成 iOS 图标时,默认需要ios: true打开。有些人只配置了android字段,以为 iOS 也会跟着生成,结果完全没有。iOS 端依然使用项目创建初期的默认图标。

还有一点,就算Contents.json里写好了文件名,如果对应文件缺失、或者文件名大小写对不上(iOS 对文件名大小写敏感),Xcode 编译时会报错,但如果走的不是标准 Xcode 流程而是某些三方打包工具,可能不会及时暴露,最后装到手机上是空白。

建议手动检查一遍:

ios/Runner/Assets.xcassets/AppIcon.appiconset/ ├── Contents.json ├── Icon-App-20x20@1x.png ├── Icon-App-20x20@2x.png ├── Icon-App-20x20@3x.png ├── Icon-App-29x29@1x.png ├── Icon-App-29x29@2x.png ├── Icon-App-29x29@3x.png ├── Icon-App-40x40@1x.png ├── Icon-App-40x40@2x.png ├── Icon-App-40x40@3x.png ├── Icon-App-60x60@2x.png └── Icon-App-60x60@3x.png

注意,iOS 图标不允许包含透明通道,如果生成的 png 带 alpha 通道,系统会拒绝应用,桌面就会出现空白。这是 Xcode 自家都会明显报错的问题,但轮到你用时不会报,所以也列为一项。

3.2 没有配置文件时的默认图标逻辑

如果你用的是 Flutter 默认模板创建的项目,即便不配置任何图标,它也会打包出一个默认的 Flutter Logo 图标。如果连这个默认图标都没看到,那问题的重点可能不在图标资源本身,而在Info.plist的图标声明。

打开ios/Runner/Info.plist,正常情况下不需要手动声明图标数组,因为 Xcode 会通过Assets.xcassets自动处理。但如果你的项目经过某些迁移、或者手动改坏了构建配置,ASSETCATALOG_COMPILER_APPICON_NAME这个 Build Setting 可能没指向AppIcon,导致图标集根本没参与编译。

你可以用一个简单方式验证:在 Xcode 里打开项目,看 Build Settings 里是否有:

ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;

如果没有,加上这一句,然后重新构建,图标基本就能回来。

说实话,iOS 端大部分“图标消失”问题都出在资源没进编译产物这个环节。你看着文件都在,但编译时没被引进去,那一切都等于零。

4. 重新生成图标后的构建细节:为什么看到的不对

很多朋友在修改完配置、重新执行图标生成命令之后,直接就开始热重载,发现图标还是没变,就以为修改无效。其实不是修改无效,而是刷新级别错了。

4.1 flutter clean 与冷启动的关系

移动端应用图标属于资源文件,热重载(hot reload)压根不会重新执行原生资源打包流程。你改了pubspec.yaml、重新生成了 mipmap 文件,不重新构建原生工程是不会生效的。

我推荐的做法是来一套完整的“冷构建”:

flutter clean flutter pub get dart run flutter_launcher_icons flutter run --release

这里用--release而不是默认 debug,是因为部分手机在 debug 模式下显示的是模板启动图标,flutter run默认的 debug 图标和 release 图标在某些项目里不是一回事。如果你平时开发用的是 debug 包,看到桌面没图标,换 release 装一次可能就好了。

flutter clean的作用是把build目录、.dart_tool里的一些中间产物全清掉,强制让下一个构建过程重新生成R.java和资源索引。有时候不改任何配置,光执行一次flutter clean再重新构建,图标就恢复正常了,原因就是之前的构建产物里有脏数据。

4.2 图标缓存问题与启动器刷新

还有一类情况比较邪门:应用图标文件确实已经更新了,APK 解压出来看也是新图,但装到手机上桌面还是显示旧图标。这就涉及桌面启动器的图标缓存了。

手机上应用安装和卸载之后,桌面并不会立刻清掉旧的缓存图。尤其是当你用 adb 安装多个渠道包相同 applicationId 的应用时,桌面可能把前一个版本的图标残留拿过来。遇到这种情况,别急着改代码,试试以下操作顺序:

  1. 先卸载应用。
  2. 重启手机(这一步常常被省略,但很管用)。
  3. 重新安装新的 APK。

如果重启后图标还是没有,再考虑是不是图标资源本身的问题。这个方法听起来太基础,甚至有点蠢,但我实测下来真的能解决很大比例“升级后图标消失”的 bug。你可以把系统桌面看成一个带图片缓存的软件,它不会每次都老老实实去读最新资源,有时候老缓存就是赖着不走。

注意:很多国产手机桌面的图标缓存机制更激进一些,安装完应用后它甚至会延迟几分钟才刷新图标。别在装完那一刻就下结论,等一等再去翻桌面,往往就正常了。

5. 我踩过的几个跟前人不太一样的坑

前面讲的都是常规排查路径,接下来聊几个我真实碰到过的、也比较偏门的情况。这几个坑在网上不太容易搜到,但遇到了非常浪费时间,写出来给大家做个参考。

5.1 图像格式和路径大小写的细节

有一次我在项目里配置好了flutter_launcher_icons,执行生成命令时的输出也显示成功,但 Android 端的图标始终是默认的。后来我手动打开生成的mipmap目录,发现里面的图片全部是 0 字节,也就是说资源文件是创建出来了,但内容没写进去。

原因是我用来做源图的assets/icon/app_icon.png其实是一个改扩展名的 WebP 文件。flutter_launcher_icons在读取时没报错,但最终生成的 PNG 数据不完整。换成真正的 PNG 文件之后,问题立刻消失。

所以建议源图统一用标准 PNG,且不要包含 alpha 通道(iOS 会拒绝),但 Android 的普通图标可以带透明底。如果你非要 WebP,就同时把 Android 端图标格式也换成 WebP,别混着用。

路径大小写方面别小看,尤其安卓资源目录命名,Mipmap和mipmap在某些文件系统上不会报错,但在 Linux 构建机或者某些严格环境的 CI 上直接罢工。保持全小写,别手动改目录名。

5.2 多设备测试时“有的显示、有的不显示”

这个问题是最玄乎的。同一个 APK,装到 A 手机上正常,装到 B 手机上没图标。一开始我也以为是系统差异,后来把 B 手机连上电脑,用adb shell pm list packages看了半天才发现,B 手机安装的根本不是同一个包名。

原来我有多套 flavor(风味构建),比如dev和prod两个渠道。冒烟测试时随手执行了flutter run --flavor dev,装上去的就是 dev 渠道包。而桌面上找不到图标,很可能是因为你找的是 prod 渠道的入口,或者 dev 渠道本身图标配置就有问题。

解决方案是按渠道分别生成图标:

flutter_launcher_icons: android: true ios: true image_path: "assets/icon/app_icon.png" dev: android: true ios: true image_path: "assets/icon/app_icon_dev.png"

然后在运行时通过--dart-define或者 flavor 来区分。注意,不同渠道的 applicationId 不要一样,否则装来装去会把之前那个覆盖掉,你以为装的是新的,实际上旧的还在。

还有一个更隐蔽的情况:某些手机默认开启“隐藏应用”功能。比如桌面上有个“隐藏应用”的入口,你不小心把图标拖进去了,那它当然不会显示在桌面。还有“抽屉模式”桌面的手机,安装后应用会进应用抽屉,而不是直接铺在首屏。这种情况跟代码半毛钱关系都没有,纯属对手机系统的操作习惯差异。

我在实际项目中,至少有两三次是这类原因:不是资源配置的问题,而是设备桌面设置弄的。所以如果你怎么排查都找不到问题,不妨先换个桌面模式试试,或者换个手机装,一测就能分离变量。

最后再分享一点个人习惯:每次改完图标配置,我不会只跑flutter run看一眼完事。我会单独打一次 release 包,在至少两个不同厂商的设备上各装一遍确认。因为开发模式下和正式模式下的资源编译路径并不完全一致,开发模式看着正常,真机用户拿到的 release 包可能还是旧图标。多一步验证,省掉的是一堆用户反馈工单。

这个问题的本质,就是图标资源从“源图”到“系统桌面”中间隔了太多层:生成工具、资源编译、清单声明、系统缓存、桌面启动器。任何一层出问题,最终表现都是桌面看不到图标。但只要你把这些层拆清楚,一层一层去验证,基本都能在半小时内定位到根因。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进