diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 83e4929..2735665 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -9,9 +9,6 @@
android:label="Komet"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
-
+
+
-
+
+ when (call.method) {
+ "setAppIcon" -> {
+ val name = call.argument("name")
+ if (name == null || !iconAliases.contains(name)) {
+ result.error("INVALID_ICON", "Unknown icon: $name", null)
+ return@setMethodCallHandler
+ }
+ try {
+ applyIcon(name)
+ result.success(null)
+ } catch (e: Exception) {
+ result.error("APPLY_FAILED", e.message, null)
+ }
+ }
+ else -> result.notImplemented()
+ }
+ }
+
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"ru.komet.app/upload_service",
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 6266644..948ca04 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -8,6 +8,43 @@ import UIKit
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
+
+ let controller = window?.rootViewController as? FlutterViewController
+ if let messenger = controller?.binaryMessenger {
+ let channel = FlutterMethodChannel(
+ name: "ru.komet.app/app_icon",
+ binaryMessenger: messenger
+ )
+ channel.setMethodCallHandler { (call, result) in
+ guard call.method == "setAppIcon" else {
+ result(FlutterMethodNotImplemented)
+ return
+ }
+ let args = call.arguments as? [String: Any]
+ let name = args?["name"] as? String
+ let iconName: String? = (name == "DefaultIcon") ? nil : name
+ if !UIApplication.shared.supportsAlternateIcons {
+ result(FlutterError(
+ code: "UNSUPPORTED",
+ message: "Alternate icons are not supported",
+ details: nil
+ ))
+ return
+ }
+ UIApplication.shared.setAlternateIconName(iconName) { error in
+ if let error = error {
+ result(FlutterError(
+ code: "APPLY_FAILED",
+ message: error.localizedDescription,
+ details: nil
+ ))
+ } else {
+ result(nil)
+ }
+ }
+ }
+ }
+
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
diff --git a/lib/core/config/app_icon.dart b/lib/core/config/app_icon.dart
index 9c3ef8d..96c662d 100644
--- a/lib/core/config/app_icon.dart
+++ b/lib/core/config/app_icon.dart
@@ -1,23 +1,25 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
-import 'package:flutter_dynamic_icon_plus/flutter_dynamic_icon_plus.dart';
+import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum AppIcon {
- defaultIcon('default', 'Default', 'assets/komet_icon.png', null),
+ defaultIcon('default', 'Default', 'assets/komet_icon.png', 'DefaultIcon'),
minimal('minimal', 'Minimal', 'assets/meteor_icon.png', 'MinimalIcon');
final String id;
final String title;
final String previewAsset;
- final String? platformName;
+ final String platformName;
const AppIcon(this.id, this.title, this.previewAsset, this.platformName);
}
class AppIconConfig {
static const prefKey = 'app_icon';
+ static const _channel = MethodChannel('ru.komet.app/app_icon');
+
static final ValueNotifier current = ValueNotifier(
AppIcon.defaultIcon,
);
@@ -34,12 +36,12 @@ class AppIconConfig {
static Future apply(AppIcon icon) async {
if (!isSupported) return;
if (current.value == icon) return;
- await FlutterDynamicIconPlus.setAlternateIconName(
- iconName: icon.platformName,
- );
- current.value = icon;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(prefKey, icon.id);
+ current.value = icon;
+ await _channel.invokeMethod('setAppIcon', {
+ 'name': icon.platformName,
+ });
}
static AppIcon _parse(String? val) {
diff --git a/lib/frontend/screens/profile/app_icon_screen.dart b/lib/frontend/screens/profile/app_icon_screen.dart
index 2f4e9d6..4534725 100644
--- a/lib/frontend/screens/profile/app_icon_screen.dart
+++ b/lib/frontend/screens/profile/app_icon_screen.dart
@@ -74,7 +74,7 @@ class _AppIconScreenState extends State {
const SizedBox(height: 4),
Text(
AppIconConfig.isSupported
- ? 'Изменение применится сразу — приложение может ненадолго закрыться'
+ ? 'На Android приложение закроется — лаунчер подхватит новую иконку. На iOS — мгновенно с системным диалогом.'
: 'Доступно только на Android и iOS',
style: TextStyle(
color: cs.onSurfaceVariant,
diff --git a/pubspec.lock b/pubspec.lock
index 77ba49f..f44d5e2 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -318,14 +318,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.1"
- flutter_dynamic_icon_plus:
- dependency: "direct main"
- description:
- name: flutter_dynamic_icon_plus
- sha256: "7721ef2666691064dece879d968fa7e1c4d64aa743ea998e6b1e65a1b482f212"
- url: "https://pub.dev"
- source: hosted
- version: "1.4.0"
flutter_launcher_icons:
dependency: "direct dev"
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index e4bd3f8..ebb0e75 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -58,7 +58,6 @@ dependencies:
firebase_core: ^4.1.1
firebase_messaging: ^16.0.2
flutter_local_notifications: ^21.0.0
- flutter_dynamic_icon_plus: ^1.4.0
dev_dependencies:
flutter_test: