经过大量研发,我能够使用flutter的dart语言动态更改应用程序图标。
为了实现这一目标:
一开始我在寻找插件并找到了一个(flutter_dynamic_icon),但它仅适用于 iOS。但我需要同时实现两个平台(android 和 iOS),所以再次进行了一些研发,发现了一个 Blog,它实际上符合我的要求。它使用方法通道来实现这一点,我也做了同样的事情,并且成功了。
但是我发现了一个问题,当我点击功能来动态设置应用程序图标时,应用程序会自行关闭并显示应用程序图标已更改,但我不想关闭应用程序。
注意:这是我第一次使用方法通道来调用本机异步方法,并且从未使用过 kotlin 语言,所以我无法做到这一点意味着如何防止应用程序在动态设置应用程序图标后关闭。
代码:
Android端:
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的dart代码端:
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),
),
),
],
),
);
}
}
结果:视频
我认为你误解了,你尝试使用的包只能在 iOS 上运行。如果是这种情况,您将无法通过其方法通道调用其 Android 实现。
仅支持iOS(版本> 10.3)。
表示可能仅适用于 iOS > 10.3。
在自己调用方法通道之前,你尝试过在Android上使用它吗?