0

After doing a lot of R&D , I'm able to change app icon dynamically with dart language of flutter.

For achieve this:

At first I was looking for plugin & had found one (flutter_dynamic_icon) but that works only on iOS. But I need to implement for both platforms (android and iOS) so did some R&D again and found a Blog, that actually matched my requirements. It used method channel for achieve this and I did the same and it worked.

But there is an issue I have discovered , when I tap on function to set app icon the dynamically , app closes itself and shows app icon has been changed but I don't want to app get closed.

NOTE : This is the first I have used method channel to call native asynchronous method and never worked with kotlin language so I'm not able to do that means how to prevent from app getting closed after setting the app icon dynamically.

Code:

Android side :

MainActivity.kt

package com.app.demo import android.content.BroadcastReceiver import android.content.ComponentName import android.content.pm.PackageManager import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "com.dynamicIcon" var methodChannelResult: MethodChannel.Result? = null @Override override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result -> try { methodChannelResult = result; if (call.method.equals("launcherFirst")) { setIcon("launcherAlias.FirstLauncherAlias", 1) result.success(true) } else if (call.method.equals("launcherSecond")) { setIcon("launcherAlias.SecondLauncherAlias", 2) result.success(true) }else if (call.method.equals("launcherThird")) { setIcon("launcherAlias.ThirdLauncherAlias", 3) result.success(true) }else if (call.method.equals("launcherFourth")) { setIcon("launcherAlias.FourthLauncherAlias", 4) result.success(true) }else if (call.method.equals("launcherFifth")) { setIcon("launcherAlias.FifthLauncherAlias", 5) result.success(true) } else { // setIcon("launcherAlias.DefaultLauncherAlias", 0) result.success(true) } } catch (e: Exception) { print(e) } } } //dynamically change app icon private fun setIcon(targetIcon: String, index: Int) { try { val packageManager: PackageManager = applicationContext!!.packageManager val packageName = applicationContext!!.packageName val className = StringBuilder() className.append(packageName) className.append(".") className.append(targetIcon) when (index) { 0 -> { packageManager.setComponentEnabledSetting( ComponentName(packageName, "$packageName.$targetIcon"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.FirstLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.SecondLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.ThirdLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FourthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FifthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) } 1 -> { packageManager.setComponentEnabledSetting( ComponentName(packageName, "$packageName.$targetIcon"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.DefaultLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.SecondLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.ThirdLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FourthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FifthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) } 2 -> { packageManager.setComponentEnabledSetting( ComponentName(packageName, "$packageName.$targetIcon"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.DefaultLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.FirstLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.ThirdLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FourthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FifthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) } 3 -> { packageManager.setComponentEnabledSetting( ComponentName(packageName, "$packageName.$targetIcon"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.DefaultLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.FirstLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.SecondLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FourthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FifthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) } 4 -> { packageManager.setComponentEnabledSetting( ComponentName(packageName, "$packageName.$targetIcon"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.DefaultLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.FirstLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.SecondLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.ThirdLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FifthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) } 5 -> { packageManager.setComponentEnabledSetting( ComponentName(packageName, "$packageName.$targetIcon"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.DefaultLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName!!, "$packageName.launcherAlias.FirstLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.SecondLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.ThirdLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) packageManager.setComponentEnabledSetting( ComponentName( packageName, "$packageName.launcherAlias.FourthLauncherAlias" ), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ) } } } catch (e: Exception) { print(e) } } } 

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.app.demo"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.VIBRATE"/> <application android:label="Demo" android:name="${applicationName}" android:icon="@drawable/black_app_icon"> <!-- tools:replace="android:icon" --> <activity android:name=".MainActivity" android:exported="true" android:enabled="false" 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"> <!-- Specifies an Android theme to apply to this Activity as soon as the Android process has started. This theme is visible to the user while the Flutter UI initializes. After that, this theme continues to determine the Window background behind the Flutter UI. --> <meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity-alias android:name=".launcherAlias.FirstLauncherAlias" android:enabled="false" android:icon="@drawable/black_app_icon" android:label="Demo" android:targetActivity=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity-alias> <activity-alias android:name=".launcherAlias.SecondLauncherAlias" android:enabled="false" android:icon="@drawable/blue_app_icon" android:label="Demo" android:targetActivity=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity-alias> <activity-alias android:name=".launcherAlias.ThirdLauncherAlias" android:enabled="false" android:icon="@drawable/red_app_icon" android:label="Demo" android:targetActivity=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity-alias> <activity-alias android:name=".launcherAlias.FourthLauncherAlias" android:enabled="false" android:icon="@drawable/yellow_app_icon" android:label="Demo" android:targetActivity=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity-alias> <activity-alias android:name=".launcherAlias.FifthLauncherAlias" android:enabled="false" android:icon="@drawable/green_app_icon" android:label="Demo" android:targetActivity=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity-alias> <activity-alias android:name=".launcherAlias.DefaultLauncherAlias" android:enabled="true" android:icon="@drawable/white_app_icon" android:label="Demo" android:targetActivity=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity-alias> <!-- Don't delete the meta-data below. This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> <meta-data android:name="flutterEmbedding" android:value="2"/> </application> </manifest> 

Flutter's dart code side:

dynamic_launcher.dart

import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class DynamicLauncher extends StatefulWidget { const DynamicLauncher({super.key}); @override State<DynamicLauncher> createState() => _DynamicLauncherState(); } class _DynamicLauncherState extends State<DynamicLauncher> { static const platform = MethodChannel('com.dynamicIcon'); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text('Dynamic Launcher Icons'), ), body: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Change Launcher Icon'), const SizedBox(height: 50), TextButton.icon( onPressed: () async { try{ var result = await platform.invokeMethod('launcherFirst'); debugPrint("Set Green AppIcon Success :$result"); }catch(e){ debugPrint("Set Black AppIcon Failed :${e.toString()}"); } }, icon: const Icon( Icons.android, color: Colors.black, ), label: const Text( 'Set Black AppIcon', style: TextStyle(color: Colors.black), ), ), TextButton.icon( onPressed: () async { try{ var result = await platform.invokeMethod('launcherSecond'); debugPrint("Set Blue AppIcon Success:$result"); }catch(e){ debugPrint("Set Blue AppIcon Failed :${e.toString()}"); } }, icon: const Icon( Icons.android, color: Colors.blue, ), label: const Text( 'Set Blue AppIcon', style: TextStyle(color: Colors.blue), ), ), TextButton.icon( onPressed: () async { try{ var result = await platform.invokeMethod('launcherThird'); debugPrint("Set Red AppIcon Success:$result"); }catch(e){ debugPrint("Set Red AppIcon Failed :${e.toString()}"); } }, icon: const Icon( Icons.android, color: Colors.red, ), label: const Text( 'Set Red AppIcon', style: TextStyle(color: Colors.red), ), ), TextButton.icon( onPressed: () async { try{ var result = await platform.invokeMethod('launcherFourth'); debugPrint("Set Yellow AppIcon Success:$result"); }catch(e){ debugPrint("Set Yellow AppIcon Failed :${e.toString()}"); } }, icon: const Icon( Icons.android, color: Colors.yellow, ), label: const Text( 'Set Yellow AppIcon', style: TextStyle(color: Colors.yellow), ), ), TextButton.icon( onPressed: () async { try{ var result = await platform.invokeMethod('launcherFifth'); debugPrint("Set Green AppIcon Success:$result"); }catch(e){ debugPrint("Set Green AppIcon Failed :${e.toString()}"); } }, icon: const Icon( Icons.android, color: Colors.green, ), label: const Text( 'Set Green AppIcon', style: TextStyle(color: Colors.green), ), ), TextButton.icon( onPressed: () async { try{ var result = await platform.invokeMethod('default'); debugPrint("Set Default appIcon Success :$result"); }catch(e){ debugPrint("Set Default appIcon Failed :${e.toString()}"); } }, icon: const Icon( Icons.restore, color: Colors.grey, ), label: const Text( 'Set Default App Icon', style: TextStyle(color: Colors.grey), ), ), ], ), ); } } 

Look at this video that I want to achieve : reference

Result that I'm getting : output_Video

5
  • You could try to save the aliases to be enabled/disabled in memory and actually enable/disable them with setComponentEnabledSetting inside onPause or onDestroy. Commented May 16, 2024 at 12:09
  • How do I do? can you please attach some piece of code?? That would be easier to understand because I said earlier I'm not familiar with Kotlin language Commented May 16, 2024 at 12:35
  • Do you get any output in the debug window when it crashes Commented May 16, 2024 at 12:43
  • @Thomas please check Videos That I have attached , then you will find what I have done and I wan to achieve. Commented May 20, 2024 at 10:42
  • I understand what you want to achive. do you get any output in the debug pane of your debugger when that happens Commented May 20, 2024 at 11:48

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.