Skip to content
charmotion

Alternate App Icons on iOS, Android, Expo and Flutter

By the Charmotion team · Updated

Cover illustration for the guide: Alternate App Icons on iOS, Android, Expo and Flutter, with Charmotion demo characters

Apps can let users pick from alternate icons, but only icons bundled in the app, and the change swaps one still image for another. iOS uses setAlternateIconName with icon sets declared at build time. Android has no dedicated API; apps commonly toggle activity-alias entries. Expo and Flutter wrap both through plugins such as expo-alternate-app-icons.

Key takeaways

  • Every alternate icon must ship inside the app; you cannot download a new one at runtime.
  • On iOS, check supportsAlternateIcons, then call setAlternateIconName; nil restores the primary icon.
  • iOS alternate icons need their own dark, clear and tinted variants and must resemble your app.
  • Android icon switching through activity-alias is a common pattern, not an official icon API, and has reported side effects.
  • Cycling icons is not animation; for motion, use a launch intro or in-app mascot.

Already have a character or logo? See your character in real app screens: splash, onboarding, loading and empty-state screens. It's free, runs in your browser and uploads nothing.

How do alternate app icons work on iOS?

Add the extra icon sets to your asset catalog and list them in the target's "Alternate App Icon Sets" build setting; Xcode generates the CFBundleAlternateIcons entries under CFBundleIcons. Then switch at runtime (Apple Developer Documentation):

func setIcon(_ name: String?) {
    guard UIApplication.shared.supportsAlternateIcons else { return }
    UIApplication.shared.setAlternateIconName(name) { error in
        if let error { print("Icon change failed: \(error)") }
    }
}

setIcon("IconNight")  // a declared alternate icon set
setIcon(nil)          // back to the primary icon

The completion handler runs on a UIKit-provided queue, not necessarily the main queue, so hop to the main actor before updating UI. Apple notes that the method still works for compatible iPhone and iPad apps running in visionOS, but alternate icons are unavailable in apps built with the visionOS SDK.

Design rules come from the HIG: alternate icons in iOS and iPadOS need their own dark, clear and tinted variants, all subject to App Review (Apple HIG), and guideline 2.3.8 asks that alternate icons stay similar to your app's other icons (App Review Guidelines).

Can you change the app icon programmatically on Android?

There is no Android API named "set icon". The pattern apps commonly use is built from two official pieces: an <activity-alias>, which presents your launcher activity as a separate entry with its own android:icon and an android:enabled flag (Android Developers), and PackageManager.setComponentEnabledSetting to turn aliases on and off.

<activity-alias
    android:name=".MainActivityNight"
    android:targetActivity=".MainActivity"
    android:icon="@mipmap/ic_launcher_night"
    android:enabled="false"
    android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity-alias>
fun switchIcon(context: Context, enable: String, disable: String) {
    val pm = context.packageManager
    pm.setComponentEnabledSetting(ComponentName(context, enable),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
    pm.setComponentEnabledSetting(ComponentName(context, disable),
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
}

Because this changes which launcher components exist, behavior depends on the launcher. Developers have reported the app closing on the switch and deep links needing care with several aliases (Back Market Engineering). Test on several launchers, switch while the user is in settings, and keep exactly one alias enabled at a time.

How do you switch app icons in Expo?

Use a config plugin, since icons are native resources. expo-alternate-app-icons copies your icons into the native projects at prebuild and exposes supportsAlternateIcons and setAlternateAppIcon(name), with null resetting to the default (GitHub). Its README lists Expo SDK 51+ and iOS and Android support, with per-icon iOS light, dark and tinted images and an Android adaptive foreground plus background color.

// app.json (excerpt)
["expo-alternate-app-icons", [
  { "name": "IconNight",
    "ios": { "light": "./assets/night.png", "dark": "./assets/night-dark.png", "tinted": "./assets/night-tinted.png" },
    "android": { "foregroundImage": "./assets/night-fg.png", "backgroundColor": "#0E1116" } }
]]

// in a settings screen
import { setAlternateAppIcon } from "expo-alternate-app-icons";
await setAlternateAppIcon("IconNight");

Run npx expo prebuild --clean after editing the plugin, and use a development build: config plugins and native modules do not apply in Expo Go (Expo).

How do you switch app icons in Flutter?

Through a plugin that calls the platform mechanisms above. flutter_dynamic_icon_plus describes itself as a plugin for dynamically changing the app icon on iOS 10.3+ and Android (pub.dev). You still declare every icon natively: alternate icon sets in the iOS project and activity-alias entries in the Android manifest, as the plugin's README describes.

Check the plugin's maintenance status and open issues before relying on it, because icon switching touches platform behavior that changes between OS releases. The native setup in the two sections above is what the plugin wraps, so it is worth understanding either way.

Can you cycle alternate icons to fake an animated icon?

No. Each switch is a deliberate change between still icons, not playback, and the 2024 demo that cycled icons fast enough to look animated relied on a private method to skip the user-facing confirmation (9to5Mac). Guideline 2.5.1 allows only public APIs. On Android, toggling launcher components repeatedly would fight the launcher and, per the reports above, can close the app.

If the goal is motion, put it where it is supported: a launch intro of your icon on the first screen, an in-app mascot, or your website. The animated app icon guide lists every option.

What are good uses for alternate app icons?

  • Personal choice: a dark, light or minimal version, or a team or theme, which is the example Apple's HIG gives with a sports app.
  • A perk: extra icons for subscribers or for reaching a milestone.
  • Seasonal variants, chosen by the user rather than switched silently.

Keep every variant recognizably your app. Apple's HIG warns against icons someone might mistake for another app, and alternate icons go through App Review like your primary icon.

Frequently asked questions

Can I download a new app icon from my server?

No. On iOS, setAlternateIconName only accepts icons declared in the app bundle at build time, and Android activity-alias icons are resources in your APK or bundle. New icons require an app update.

Does iOS tell the user when an app changes its icon?

Yes. Changing the icon through setAlternateIconName comes with a user-facing confirmation, which is why the 2024 animated-icon demo had to call the API through a private method to skip it. Tie icon changes to an explicit choice in your settings screen, and check supportsAlternateIcons first.

Do alternate icons support dark and tinted mode?

On iOS and iPadOS they must: Apple's HIG says alternate icons require their own dark, clear and tinted variants. On Android, give each alias an adaptive icon with a monochrome layer so themed icons work on Android 13 and later.

Quick answers from this guide

Sources

Related guides