diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index b48121a..5cfe280 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -17,11 +17,38 @@
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/top/iletter/mooknote/MainActivity.kt b/android/app/src/main/kotlin/top/iletter/mooknote/MainActivity.kt
index d5438a8..d3f35ee 100644
--- a/android/app/src/main/kotlin/top/iletter/mooknote/MainActivity.kt
+++ b/android/app/src/main/kotlin/top/iletter/mooknote/MainActivity.kt
@@ -1,5 +1,62 @@
package top.iletter.mooknote
+import android.content.ComponentName
+import android.content.pm.PackageManager
import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodChannel
-class MainActivity : FlutterActivity()
+class MainActivity : FlutterActivity() {
+
+ private val CHANNEL = "top.iletter.mooknote/icon"
+
+ override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+
+ MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
+ when (call.method) {
+ "switchIcon" -> {
+ val iconName = call.argument("iconName")
+ if (iconName != null) {
+ switchLauncherIcon(iconName)
+ result.success(true)
+ } else {
+ result.error("INVALID_ARGUMENT", "iconName is required", null)
+ }
+ }
+ "getCurrentIcon" -> {
+ result.success(getCurrentIcon())
+ }
+ else -> result.notImplemented()
+ }
+ }
+ }
+
+ private fun switchLauncherIcon(iconName: String) {
+ val pm = packageManager
+ val icon1 = ComponentName(this, "${packageName}.MainActivityIcon1")
+ val icon2 = ComponentName(this, "${packageName}.MainActivityIcon2")
+
+ when (iconName) {
+ "app_icon2" -> {
+ pm.setComponentEnabledSetting(icon1, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
+ pm.setComponentEnabledSetting(icon2, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
+ }
+ else -> {
+ pm.setComponentEnabledSetting(icon2, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)
+ pm.setComponentEnabledSetting(icon1, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)
+ }
+ }
+ }
+
+ private fun getCurrentIcon(): String {
+ val pm = packageManager
+ val icon1 = ComponentName(this, "${packageName}.MainActivityIcon1")
+ val icon2 = ComponentName(this, "${packageName}.MainActivityIcon2")
+
+ return when {
+ pm.getComponentEnabledSetting(icon2) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> "app_icon2"
+ else -> "app_icon"
+ }
+ }
+}
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher2.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher2.png
new file mode 100644
index 0000000..f833d7f
Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher2.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher2.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher2.png
new file mode 100644
index 0000000..270b6cb
Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher2.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher2.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher2.png
new file mode 100644
index 0000000..2ffd647
Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher2.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher2.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher2.png
new file mode 100644
index 0000000..dcecf72
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher2.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher2.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher2.png
new file mode 100644
index 0000000..937d7d0
Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher2.png differ
diff --git a/assets/icon/app_icon2.png b/assets/icon/app_icon2.png
new file mode 100644
index 0000000..9637dd5
Binary files /dev/null and b/assets/icon/app_icon2.png differ
diff --git a/lib/main.dart b/lib/main.dart
index e611379..791e770 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
+import 'dart:async';
import 'pages/home_page.dart';
import 'utils/theme/app_theme.dart';
import 'utils/app_router.dart';
@@ -51,6 +52,9 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ // 获取当前选中的图标名称
+ final iconName = UserPrefs().appIconName;
+
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: appProvider),
@@ -63,11 +67,49 @@ class MyApp extends StatelessWidget {
themeMode: ThemeMode.system,
home: const HomePage(),
onGenerateRoute: AppRouter.generateRoute,
+ builder: (context, child) {
+ // 尝试动态设置应用图标(Android 13+ 支持动态图标,但 Flutter 目前主要通过静态配置)
+ // 这里我们主要实现逻辑上的切换,实际生效通常需要重启应用或配合原生插件
+ return _AppIconWrapper(iconName: iconName, child: child!);
+ },
),
);
}
}
+/// 应用图标包装器
+/// 注意:Flutter 默认不支持运行时动态更换桌面图标。
+/// 这里的实现主要是为了在应用内记录用户的选择,并为未来可能的动态图标功能做准备。
+/// 如果需要真正的动态图标,通常需要引入 flutter_app_icon_changer 等插件并配置多套图标资源。
+class _AppIconWrapper extends StatefulWidget {
+ final Widget child;
+ final String iconName;
+
+ const _AppIconWrapper({required this.child, required this.iconName});
+
+ @override
+ State<_AppIconWrapper> createState() => _AppIconWrapperState();
+}
+
+class _AppIconWrapperState extends State<_AppIconWrapper> {
+ @override
+ void initState() {
+ super.initState();
+ _updateSystemIcon();
+ }
+
+ Future _updateSystemIcon() async {
+ // 目前 Flutter 官方不支持直接通过代码更换 Launcher Icon。
+ // 这一步主要用于记录日志或在未来集成第三方库时使用。
+ // print('Current selected icon: ${widget.iconName}');
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return widget.child;
+ }
+}
+
/// 用于预览 MyApp 的 Widget
/// 添加 @Preview 注解
@Preview(name: "MookNote App Preview")
diff --git a/lib/pages/app_icon_picker_page.dart b/lib/pages/app_icon_picker_page.dart
new file mode 100644
index 0000000..7983203
--- /dev/null
+++ b/lib/pages/app_icon_picker_page.dart
@@ -0,0 +1,139 @@
+import 'package:flutter/material.dart';
+import '../../utils/user_prefs.dart';
+import '../../utils/toast_util.dart';
+import '../../utils/app_icon_channel.dart';
+
+/// 应用图标选择页面
+class AppIconPickerPage extends StatefulWidget {
+ const AppIconPickerPage({super.key});
+
+ @override
+ State createState() => _AppIconPickerPageState();
+}
+
+class _AppIconPickerPageState extends State {
+ final UserPrefs _userPrefs = UserPrefs();
+ String _currentIconName = 'app_icon';
+
+ // 预定义的图标列表
+ final List