You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter应用内实现Launcher Icon(应用启动图标)自定义?

Flutter实现应用启动图标自定义方案

首先要明确:系统桌面的应用启动图标无法在运行时直接动态修改,因为这由Android/iOS的系统规则限制,只能通过预配置多套图标,让用户选择后切换应用的图标配置来实现类似效果。结合你常用的flutter_launcher_icons包,具体实现步骤如下:

一、预生成多套启动图标

用flutter_launcher_icons为不同图标样式生成对应平台资源:

  1. 在项目根目录创建多个图标源文件,比如assets/icons/icon_default.png、assets/icons/icon_custom1.png、assets/icons/icon_custom2.png
  2. 修改pubspec.yaml配置,为每套图标单独指定生成规则:
    flutter_launcher_icons:
      android: true
      ios: true
      image_path: "assets/icons/icon_default.png"
      flavors:
        custom1:
          image_path: "assets/icons/icon_custom1.png"
        custom2:
          image_path: "assets/icons/icon_custom2.png"
    
  3. 执行命令生成全平台图标资源:
    flutter pub run flutter_launcher_icons:main
    
    生成后,Android的res/mipmap-*目录、iOS的Assets.xcassets中会自动生成对应尺寸的多套图标文件。

二、Android平台配置图标切换

Android通过activity-alias实现多图标切换逻辑:

  1. 打开android/app/src/main/AndroidManifest.xml,在主Activity外添加多个activity-alias节点,每个对应一套自定义图标:
    <!-- 主Activity保留默认启动配置 -->
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    
    <!-- 自定义图标1的启动入口 -->
    <activity-alias
        android:name=".CustomIcon1"
        android:enabled="false"
        android:exported="true"
        android:icon="@mipmap/ic_launcher_custom1"
        android:label="@string/app_name_custom1"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>
    
    <!-- 自定义图标2的启动入口 -->
    <activity-alias
        android:name=".CustomIcon2"
        android:enabled="false"
        android:exported="true"
        android:icon="@mipmap/ic_launcher_custom2"
        android:label="@string/app_name_custom2"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>
    
  2. 通过MethodChannel在Flutter和Android原生间通信,切换activity-alias的启用状态:
    原生Kotlin代码示例:
    class MainActivity : FlutterActivity() {
        private val CHANNEL = "com.your_app/icon_switch"
    
        override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
            super.configureFlutterEngine(flutterEngine)
            MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
                when (call.method) {
                    "switchIcon" -> {
                        val targetIcon = call.argument<String>("iconType") ?: "default"
                        switchLauncherIcon(targetIcon)
                        result.success(true)
                    }
                    else -> result.notImplemented()
                }
            }
        }
    
        private fun switchLauncherIcon(iconType: String) {
            // 先禁用所有启动入口
            packageManager.setComponentEnabledSetting(
                ComponentName(this, MainActivity::class.java),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP
            )
            packageManager.setComponentEnabledSetting(
                ComponentName(this, ".CustomIcon1"),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP
            )
            packageManager.setComponentEnabledSetting(
                ComponentName(this, ".CustomIcon2"),
                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP
            )
    
            // 启用目标图标对应的入口
            when(iconType) {
                "default" -> packageManager.setComponentEnabledSetting(
                    ComponentName(this, MainActivity::class.java),
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                    PackageManager.DONT_KILL_APP
                )
                "custom1" -> packageManager.setComponentEnabledSetting(
                    ComponentName(this, ".CustomIcon1"),
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                    PackageManager.DONT_KILL_APP
                )
                "custom2" -> packageManager.setComponentEnabledSetting(
                    ComponentName(this, ".CustomIcon2"),
                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                    PackageManager.DONT_KILL_APP
                )
            }
        }
    }
    
  3. Flutter层调用代码:
    static const MethodChannel _iconChannel = MethodChannel('com.your_app/icon_switch');
    
    Future<void> switchLauncherIcon(String iconType) async {
      await _iconChannel.invokeMethod('switchIcon', {'iconType': iconType});
      // 提示用户重启应用生效
      if (Platform.isAndroid) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('图标已切换,请重启应用生效'))
        );
      }
    }
    

三、iOS平台配置图标切换

iOS通过系统原生的Alternate App Icons实现:

  1. 打开ios/Runner/Info.plist,添加CFBundleAlternateIcons配置:
    <key>CFBundleIcons</key>
    <dict>
        <key>CFBundlePrimaryIcon</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>AppIcon</string>
            </array>
        </dict>
        <key>CFBundleAlternateIcons</key>
        <dict>
            <key>CustomIcon1</key>
            <dict>
                <key>CFBundleIconFiles</key>
                <array>
                    <string>AppIcon-Custom1</string>
                </array>
                <key>UIPrerenderedIcon</key>
                <false/>
            </dict>
            <key>CustomIcon2</key>
            <dict>
                <key>CFBundleIconFiles</key>
                <array>
                    <string>AppIcon-Custom2</string>
                </array>
                <key>UIPrerenderedIcon</key>
                <false/>
            </dict>
        </dict>
    </dict>
    
  2. 通过MethodChannel实现Flutter与iOS原生的通信:
    原生Swift代码示例:
    import UIKit
    import Flutter
    
    @UIApplicationMain
    @objc class AppDelegate: FlutterAppDelegate {
        let CHANNEL = "com.your_app/icon_switch"
    
        override func application(
            _ application: UIApplication,
            didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
        ) -> Bool {
            let controller = window?.rootViewController as! FlutterViewController
            let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
            channel.setMethodCallHandler { [weak self] call, result in
                guard call.method == "switchIcon" else {
                    result(FlutterMethodNotImplemented)
                    return
                }
                if let args = call.arguments as? [String: String], let iconType = args["iconType"] {
                    self?.switchLauncherIcon(iconType: iconType, result: result)
                } else {
                    result(FlutterError(code: "INVALID_ARG", message: "缺少图标类型参数", details: nil))
                }
            }
            GeneratedPluginRegistrant.register(with: self)
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }
    
        private func switchLauncherIcon(iconType: String, result: FlutterResult) {
            guard UIApplication.shared.supportsAlternateIcons else {
                result(FlutterError(code: "NOT_SUPPORTED", message: "设备不支持图标切换", details: nil))
                return
            }
            let iconIdentifier: String? = iconType == "default" ? nil : iconType
            UIApplication.shared.setAlternateIconName(iconIdentifier) { error in
                if let error = error {
                    result(FlutterError(code: "SWITCH_FAILED", message: error.localizedDescription, details: nil))
                } else {
                    result(true)
                }
            }
        }
    }
    
  3. Flutter层复用Android的调用代码即可,iOS切换后会自动弹出系统提示。

四、用户交互界面实现

在Flutter应用内添加图标选择入口,比如设置页的列表选项:

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    const Text("选择应用图标", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
    ListTile(
      leading: Image.asset("assets/icons/icon_default.png", width: 40),
      title: const Text("默认图标"),
      onTap: () => switchLauncherIcon("default"),
    ),
    ListTile(
      leading: Image.asset("assets/icons/icon_custom1.png", width: 40),
      title: const Text("自定义图标1"),
      onTap: () => switchLauncherIcon("custom1"),
    ),
    ListTile(
      leading: Image.asset("assets/icons/icon_custom2.png", width: 40),
      title: const Text("自定义图标2"),
      onTap: () => switchLauncherIcon("custom2"),
    ),
  ],
)

注意事项

  • Android切换图标后必须重启应用才能生效,需主动提示用户
  • iOS的Alternate Icons功能仅支持真机,模拟器可能无法正常显示切换效果
  • 所有图标源文件需符合平台尺寸规范,flutter_launcher_icons会自动生成适配各分辨率的图标

内容的提问来源于stack exchange,提问作者Guim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:05:14