flutter_dynamic_launcher_icon

pub version license

A Lightweight Flutter Plugin for Dynamic Launcher Icons

A lightweight Flutter plugin that lets you dynamically change your app's launcher icon at runtime โ€” with full Android and iOS support.


๐ŸŽฌ Demo

Demo of dynamic app icon change

๐Ÿ“ฑ Platform Behavior

Android Icon Change Behavior

On Android, icon changes work differently than iOS to ensure a smooth user experience:

  • Deferred Icon Application: - When you call changeIcon(), the request is saved but not immediately applied. - The icon change is deferred until the app goes to the background (e.g., when the user presses home or switches apps). - This prevents the app from being killed while the user is actively using it.

  • Why This Approach?: - Android requires disabling/enabling components to change icons, which can cause app restarts. - Immediate changes would interrupt the user's current session. - By waiting for the background state, the change happens at a natural transition point.


iOS Icon Change Behavior

iOS provides native support for alternate icons with a smooth, immediate experience:

  • Instant Application: - Icon changes apply immediately when changeIcon() is called. - Uses iOS's native UIApplication.shared.setAlternateIconName() API. - Available on iOS 10.3+.

Table of Contents


๐Ÿงฉ Android Setup

๐Ÿ”ง Installation

Add the dependency to your pubspec.yaml:

dependencies:
  flutter_dynamic_launcher_icon: ^<latest_version>

Fetch the packages:

flutter pub get

๐Ÿงน Clean & Full Restart

Since this plugin includes native Android code, perform a clean build and fully restart the app:

flutter clean
flutter pub get
flutter run

๐Ÿ–ผ๏ธ Add Launcher Icons

Place your launcher icon image files inside the res/mipmap folders:

android/app/src/main/res/
  โ”œโ”€ mipmap-mdpi/
  โ”œโ”€ mipmap-hdpi/
  โ”œโ”€ mipmap-xhdpi/
  โ”œโ”€ mipmap-xxhdpi/
  โ””โ”€ mipmap-xxxhdpi/

Example filenames:

mipmap-mdpi/ic_launcher_dart.png
mipmap-hdpi/ic_launcher_dart.png
mipmap-xhdpi/ic_launcher_dart.png
mipmap-xxhdpi/ic_launcher_dart.png
mipmap-xxxhdpi/ic_launcher_dart.png

Repeat this for all alternate icons you want to use.

๐Ÿงพ AndroidManifest.xml Setup

Update your AndroidManifest.xml to define activity aliases for alternate icons:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.app">

  <application
    android:icon="@mipmap/ic_launcher"
    android:label="My App">

    <activity
      android:name=".MainActivity"
      android:exported="true"
      android:launchMode="singleTop"
      android:theme="@style/LaunchTheme">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

    <!-- Alternate Icon 1 -->
    <activity-alias
      android:name=".AppIconDart"
      android:enabled="false"
      android:icon="@mipmap/ic_launcher_dart"
      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 -->
    <activity-alias
      android:name=".AppIconSwift"
      android:enabled="false"
      android:icon="@mipmap/ic_launcher_swift"
      android:targetActivity=".MainActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity-alias>
  </application>
</manifest>

Notes:

  • android:enabled="false" keeps alternate icons disabled until activated.
  • android:targetActivity must always point to .MainActivity.
  • If your project uses an applicationIdSuffix or a custom setup, you may need to specify the fully qualified class name in android:name.
<activity-alias
        android:name="${applicationId}.AppIconDart"
        android:enabled="false"
        android:icon="@mipmap/ic_launcher_dart"
        android:targetActivity=".MainActivity">
        <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
</activity-alias>

๐ŸŽ iOS Setup

๐Ÿงญ Steps in Xcode

  1. Open the ios/Runner project in Xcode.
  2. Navigate to Assets.xcassets and create a new folder: App Icons โ†’ New iOS App Icon.
  3. Add your alternate icons (e.g., AppIconDart, AppIconSwift) in the asset catalog.

App Icon Folder in Xcode

  1. Go to Runner โ†’ Build Settings and search for "Alternate App Icon Sets."
  2. Add the names of your alternate icons.

Alternate App Icon Setup

Notes:

  • Ensure the names in "Alternate App Icon Sets" match the icon set folder names.

๐Ÿš€ Usage

๐Ÿ“ฆ Import

import 'package:flutter_dynamic_launcher_icon/flutter_dynamic_launcher_icon.dart';

๐ŸŽฏ Change App Icon

To switch icons:

await FlutterDynamicLauncherIcon.changeIcon('AppIconDart');

To reset to the default icon:

await FlutterDynamicLauncherIcon.changeIcon(null);

โš™๏ธ Example

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

class IconSwitcherExample extends StatefulWidget {
  @override
  _IconSwitcherExampleState createState() => _IconSwitcherExampleState();
}

class _IconSwitcherExampleState extends State<IconSwitcherExample> {
  String? _currentIcon;

  @override
  void initState() {
    super.initState();
    _loadCurrentIcon();
  }

  Future<void> _loadCurrentIcon() async {
    final icon = await FlutterDynamicLauncherIcon.alternateIconName;
    setState(() => _currentIcon = icon);
  }

  Future<void> _changeIcon(String? iconName) async {
    await FlutterDynamicLauncherIcon.changeIcon(iconName);
    await _loadCurrentIcon();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Dynamic Icon Example')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Current Icon: ${_currentIcon ?? "Default"}'),
            ElevatedButton(
              onPressed: () => _changeIcon('AppIconDart'),
              child: Text('Switch to Dart Icon'),
            ),
            ElevatedButton(
              onPressed: () => _changeIcon('AppIconSwift'),
              child: Text('Switch to Swift Icon'),
            ),
            ElevatedButton(
              onPressed: () => _changeIcon(null),
              child: Text('Reset to Default'),
            ),
          ],
        ),
      ),
    );
  }
}

๐Ÿงฉ API Reference

Method Description
changeIcon(String? iconName, {bool silent}) Changes the launcher icon dynamically. Pass null to reset.
alternateIconName Returns the currently active icon name, or null if using the default icon.
isSupported Returns true if the platform supports dynamic icons.

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


๐Ÿ“œ License

This project is licensed under the MIT License.