chameleon_icons 0.0.1 copy "chameleon_icons: ^0.0.1" to clipboard
chameleon_icons: ^0.0.1 copied to clipboard

A Flutter plugin to dynamically change and reset app launcher icons at runtime on Android and iOS.

Chameleon Icons #

CI Pub Version Pub Points License: MIT

A cross-platform Flutter plugin and automated CLI tool to dynamically change app launcher icons at runtime on Android and iOS with zero manual native boilerplate.


Overview #

Dynamically changing app icons has traditionally been complex and error-prone:

  • Android: Requires manual <activity-alias> declarations in AndroidManifest.xml, multi-density mipmap generation (mdpi to xxxhdpi), and complex PackageManager component-state manipulation.
  • iOS: Requires manual configuration of nested CFBundleAlternateIcons dictionaries in Info.plist, retina @2x / @3x asset linking, and UIApplication.setAlternateIconName handling.

Chameleon Icons solves both sides:

  1. Unified Dart API: A clean, idiomatic Flutter API (changeIcon, resetIcon, getCurrentIcon, isAlternateIconsSupported) that feels natural to Flutter developers.
  2. Automated CLI Engine (Coming in v0.1.0): Define icons once in pubspec.yaml, run dart run chameleon_icons:generate, and let the CLI resize all densities and inject all Android XML and iOS Plist configurations automatically.

Platform Support #

Platform Supported Switching Engine
Android Yes (API 24+) PackageManager.setComponentEnabledSetting via <activity-alias>
iOS Yes (iOS 15.0+) UIApplication.shared.setAlternateIconName(_:) via CFBundleAlternateIcons

Installation #

Add chameleon_icons to your pubspec.yaml:

dependencies:
  chameleon_icons: ^0.0.1

Then run:

flutter pub get

Platform Setup (v0.0.1 Manual Setup) #

Note: In version 0.1.0, this setup will be 100% automated using dart run chameleon_icons:generate. For version 0.0.1, follow the manual setup below.


1. Android Configuration #

Open android/app/src/main/AndroidManifest.xml and make the following adjustments:

Step A: Remove the launcher intent filter from <activity android:name=".MainActivity">

Comment out or remove the <intent-filter> from your main activity so it is not launched directly without an alias:

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:launchMode="singleTop"
    android:theme="@style/LaunchTheme"
    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
    android:hardwareAccelerated="true"
    android:windowSoftInputMode="adjustResize">
    
    <meta-data
        android:name="io.flutter.embedding.android.NormalTheme"
        android:resource="@style/NormalTheme" />
    
    <!-- REMOVE or COMMENT OUT the launcher intent-filter from MainActivity:
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
    -->
</activity>

Step B: Declare the default icon metadata

Inside the <application> tag, declare the name of your primary default alias:

<application ...>

    <!-- Required: Tells ChameleonIcons which alias represents the default icon -->
    <meta-data
        android:name="com.farookjamal.chameleon_icons.DEFAULT_ICON_ALIAS"
        android:value="MainActivityDefault" />

    <!-- ... activities ... -->

Step C: Add <activity-alias> for Default and Alternate Icons

Directly below <activity android:name=".MainActivity">, define an <activity-alias> for each icon:

    <!-- Primary Default Icon (Must have android:enabled="true") -->
    <activity-alias
        android:name=".MainActivityDefault"
        android:enabled="true"
        android:exported="true"
        android:icon="@mipmap/ic_launcher"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

    <!-- Alternate Icon 1: Dark (Must have android:enabled="false") -->
    <activity-alias
        android:name=".MainActivityDark"
        android:enabled="false"
        android:exported="true"
        android:icon="@mipmap/ic_launcher_dark"
        android:roundIcon="@mipmap/ic_launcher_dark_round"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

    <!-- Alternate Icon 2: Gold (Must have android:enabled="false") -->
    <activity-alias
        android:name=".MainActivityGold"
        android:enabled="false"
        android:exported="true"
        android:icon="@mipmap/ic_launcher_gold"
        android:roundIcon="@mipmap/ic_launcher_gold_round"
        android:targetActivity=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>

</application>

2. iOS Configuration #

Step A: Add PNG Assets to your iOS App Bundle

Add your alternate icon PNG files inside ios/Runner/ at @2x (120×120 px) and @3x (180×180 px) resolutions:

  • ios/Runner/dark_icon@2x.png
  • ios/Runner/dark_icon@3x.png
  • ios/Runner/gold_icon@2x.png
  • ios/Runner/gold_icon@3x.png

Important: In Xcode, ensure these image files are checked under Target Membership -> Runner.

Step B: Configure Info.plist

Open ios/Runner/Info.plist and add the CFBundleIcons dictionary under the root <dict> tag:

<key>CFBundleIcons</key>
<dict>
    <!-- Alternate Icons Configuration -->
    <key>CFBundleAlternateIcons</key>
    <dict>
        <key>MainActivityDark</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>dark_icon</string>
            </array>
            <key>UIPrerenderedIcon</key>
            <false/>
        </dict>
        <key>MainActivityGold</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>gold_icon</string>
            </array>
            <key>UIPrerenderedIcon</key>
            <false/>
        </dict>
    </dict>
    <!-- Primary Default Icon Configuration -->
    <key>CFBundlePrimaryIcon</key>
    <dict>
        <key>CFBundleIconFiles</key>
        <array>
            <string>AppIcon</string>
        </array>
    </dict>
</dict>

Dart API Usage #

import 'package:flutter/material.dart';
import 'package:chameleon_icons/chameleon_icons.dart';

final chameleon = ChameleonIcons();

// 1. Check if alternate icons are supported on the device:
final isSupported = await chameleon.isAlternateIconsSupported();
if (isSupported) {
  // 2. Switch to an alternate icon:
  await chameleon.changeIcon("MainActivityDark");

  // 3. Query the currently active icon:
  final currentIcon = await chameleon.getCurrentIcon();
  print("Active icon: $currentIcon"); // Outputs: "MainActivityDark"

  // 4. Reset back to the primary default app icon:
  await chameleon.resetIcon();
}

Platform Comparison #

Feature Android Implementation iOS Implementation
Switching Mechanism <activity-alias> with PackageManager.setComponentEnabledSetting UIApplication.shared.setAlternateIconName(_:)
Default Icon Reset Re-enables default launcher alias and disables others Passes nil to setAlternateIconName(nil)
Current Icon Detection Resolves active launcher intent from PackageManager Inspects alternateIconName or CFBundlePrimaryIcon
Asset Location android/app/src/main/res/mipmap-* ios/Runner/ bundle resources
Min Supported OS Android SDK 24+ (Android 7.0+) iOS 15.0+

Roadmap #

  • Phase 1 (v0.0.1): Core native runtime engine for Android & iOS with decoupled changeIcon() and resetIcon(). (Completed)
  • Phase 2 (v0.1.0): Automated CLI image generation (dart run chameleon_icons:generate) for automatic multi-density asset resizing.
  • Phase 3 (v0.1.0): Automated injection into AndroidManifest.xml and Info.plist.
  • Phase 4 (v0.2.0): Ready-to-use customizable UI components (Telegram-style ChameleonIconGrid).

License #

MIT License. Copyright (c) 2026 Farook Jamal.

0
likes
160
points
75
downloads

Documentation

API reference

Publisher

verified publisherfarookjamal.com

Weekly Downloads

A Flutter plugin to dynamically change and reset app launcher icons at runtime on Android and iOS.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on chameleon_icons

Packages that implement chameleon_icons