在 Flutter 项目中,启动页(Splash Screen) 和 App 桌面图标(Logo) 是最容易被忽视、却又最容易踩坑的部分。
很多教程直接推荐使用 flutter_native_splash、flutter_launcher_icons 等插件,但在以下场景中,你可能不想或不能使用插件:
- 公司项目对三方插件有严格限制
- 接手老项目,Android 已高度定制
- 希望完全掌控 Android 原生行为
- 想理解 Flutter 启动页的底层机制
本文将不使用任何 Flutter 插件,从 Android 原生角度,完整讲清楚:
✅ 如何替换 Android App 图标
✅ Android 8--11 启动页怎么做
⚠️ Android 12+ 启动页的系统限制
🚫 常见坑位与规避方案
一、先区分两个概念(非常重要)
很多问题都源于概念混淆:
| 名称 | 说明 |
|---|---|
| App 图标(Launcher Icon) | 安装后,桌面显示的应用图标 |
| 启动页(Splash Screen) | App 冷启动时显示的过渡页面 |
二者完全不同实现机制,下面分别说明。
二、不用插件替换 Android App 图标(桌面 Logo)
1️⃣ 图标资源要求(硬性)
- 必须是 正方形
- 不要自带圆角或阴影
- 推荐 PNG / WebP
- 多分辨率适配
推荐尺寸如下:
| 目录 | 尺寸 |
|---|---|
| mipmap-mdpi | 48 × 48 |
| mipmap-hdpi | 72 × 72 |
| mipmap-xhdpi | 96 × 96 |
| mipmap-xxhdpi | 144 × 144 |
| mipmap-xxxhdpi | 192 × 192 |
2️⃣ 放置图标资源
路径如下:
android/app/src/main/res/
├─ mipmap-mdpi/ic_launcher.png
├─ mipmap-hdpi/ic_launcher.png
├─ mipmap-xhdpi/ic_launcher.png
├─ mipmap-xxhdpi/ic_launcher.png
└─ mipmap-xxxhdpi/ic_launcher.png
如果项目使用 Adaptive Icon,还会看到:
ic_launcher_foreground.png
ic_launcher_background.png
3️⃣ 检查 AndroidManifest.xml
xml
<application
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round">
- android:icon:必须
- roundIcon:部分设备(Pixel)使用
4️⃣ 验证方式
⚠️ Android 对图标缓存非常激进
bash
flutter clean
- 卸载 App
- 重新安装
- 真机验证优先
三、Flutter 启动页的本质是什么?
在 Flutter 中,启动页不是 Flutter Widget,而是:
👉 Android Activity 的 windowBackground
Flutter 启动流程简化如下:
Activity 启动
↓
显示 windowBackground(启动页)
↓
Flutter Engine 初始化
↓
Flutter 首帧渲染
因此,启动页 100% 属于 Android 原生层。
四、Android 8--11 启动页实现(核心方案)
1️⃣ 启动页文件位置
Flutter 默认生成:
android/app/src/main/res/
├─ drawable/launch_background.xml
└─ drawable-v21/launch_background.xml
👉 两个都要修改,否则部分设备不生效。
2️⃣ 标准 launch_background.xml 示例
xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 启动页背景色 -->
<item android:drawable="@android:color/white" />
<!-- 居中 Logo -->
<item>
<bitmap
android:gravity="center"
android:src="@drawable/splash_logo" />
</item>
</layer-list>
3️⃣ 放置启动页图片
android/app/src/main/res/drawable/splash_logo.png
图片建议:
- PNG 格式
- 512×512 或 1024×1024
- 中心内容,四周留白
- 尽量 < 500KB(避免 OOM)
五、Android 12+ 启动页(必须单独说明)
⚠️ Android 12 的重大变化
Android 12 强制系统 Splash Screen:
- 强制圆形裁切
- 强制背景色
- 不支持自定义布局
- 不支持动画
这是 系统级行为,无法绕过。
Android 12 启动页配置位置
android/app/src/main/res/values/styles.xml
示例配置
xml
<style name="LaunchTheme"
parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
<item name="android:windowSplashScreenBackground">#FFFFFF</item>
<item name="android:windowSplashScreenAnimatedIcon">
@drawable/splash_logo
</item>
<item name="android:windowSplashScreenIconBackgroundColor">
#FFFFFF
</item>
</style>
📌 注意事项:
- Logo 一定会被裁成圆形
- 只能显示 Icon,不能全屏
- Flutter 无法干预
六、避免启动页白屏 / 黑屏的关键经验
1️⃣ 启动页背景色 = Flutter 首屏背景色
dart
Scaffold(
backgroundColor: Colors.white,
)
颜色不一致就会"闪一下"。
2️⃣ 不要在 main() 中做耗时操作
dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// ❌ 不要网络请求
// ❌ 不要 Future.delayed
runApp(const MyApp());
}
3️⃣ 启动页图片不要太大
- 大图 + 低端机 = 启动崩溃
- 启动页只是过渡,不是广告页
七、不使用插件的维护成本
| 风险 | 说明 |
|---|---|
| Flutter 升级 | styles.xml 可能被覆盖 |
| Android 12 行为 | 无法统一体验 |
| ROM 差异 | MIUI / EMUI 略有差异 |
| 自动化 | CI 维护成本高 |
八、什么时候不建议"死磕不用插件"
以下任一情况,建议直接用插件:
- 要 Android 12 和低版本体验一致
- 需要 CI 自动生成
- Logo 经常变
- 多人协作项目
九、实践总结(给 Flutter 项目的建议)
站在工程和维护角度
- App 图标:✔ 手动配置完全 OK
- 启动页:✔ Android 8--11:原生 launch_background.xml;⚠️ Android 12:接受系统 Splash
- ❌ 不要尝试自定义启动动画
- ❌ 不要在启动阶段做业务逻辑
结语
Flutter 的启动页不是 Flutter 的问题,而是 Android 系统能力的映射。
理解这一点,很多"诡异问题"都会迎刃而解。