Flutter动态图标更新插件dynamic_icon_flutter的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter动态图标更新插件dynamic_icon_flutter的使用

flutter_dynamic_icon 是一个用于在移动平台上动态更改应用程序图标的Flutter插件,支持iOS(版本 > 10.3)和Android。

已知问题

  • 每个Android版本的行为可能不同,在Android 8上,您可能会注意到图标变化需要几秒钟。
  • 在某些Android版本中使用此功能会导致您的应用程序崩溃(第一次更改图标时会崩溃,之后不会),这种用户体验不佳,您可以在这里了解更多关于此问题的信息。

Android集成

步骤

  1. pubspec.yaml文件中的依赖项部分添加最新版本的插件。
  2. 执行flutter pub get
  3. 更新android/src/main/AndroidManifest.xml如下:
<application ...>
    <activity
        android:name=".MainActivity"
        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"
        android:enabled="true">
        <meta-data
            android:name="io.flutter.embedding.android.NormalTheme"
            android:resource="@style/NormalTheme" />
        <meta-data
            android:name="io.flutter.embedding.android.SplashScreenDrawable"
            android:resource="@drawable/launch_background" />
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <!-- FOR NOW USE "icon_1" AS ALTERNATIVE ICON NAME -->
    <activity-alias
        android:label="Your app"
        android:icon="@mipmap/ic_launcher_1"
        android:name=".icon_1"
        android:enabled="false"
        android:targetActivity=".MainActivity">
        <meta-data
            android:name="io.flutter.embedding.android.NormalTheme"
            android:resource="@style/NormalTheme" />
        <meta-data
            android:name="io.flutter.embedding.android.SplashScreenDrawable"
            android:resource="@drawable/launch_background" />
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity-alias>
</application>
  1. 可以拥有多个应用图标,在应用程序中可以使用:
    • 方法中传递的名称必须在AndroidManifest.xml中,并且对于每个图标,必须在AndroidManifest.xml中声明一个activity-alias
    • 声明一个字符串列表(可用的应用图标)。
    • 别忘了将MainActivity添加到列表中。
const List<String> list = ["icon_1", "icon_2", "icon_n", "MainActivity"];
DynamicIconFlutter.setIcon(icon: 'icon_1', listAvailableIcon: list);

iOS集成

步骤

  1. 放置一些用于应用图标的图片,例如teamfortress@2x.png, teamfortress@3x.png等。这些图标不应放在Assets.xcassets文件夹中,而是放在外面。
  2. 设置Info.plist
    • 添加Icon files (iOS 5)到信息属性列表。
    • 添加CFBundleAlternateIcons作为字典,用于替代图标。
    • 设置3个字典在CFBundleAlternateIcons下,分别对应teamfortressphotoschills
    • 对于每个字典,配置两个属性——UIPrerenderedIconCFBundleIconFiles
<key>CFBundleIcons</key>
<dict>
    <key>CFBundleAlternateIcons</key>
    <dict>
        <key>chills</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>chills</string>
            </array>
            <key>UIPrerenderedIcon</key>
            <false/>
        </dict>
        <key>photos</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>photos</string>
            </array>
            <key>UIPrerenderedIcon</key>
            <false/>
        </dict>
        <key>teamfortress</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>teamfortress</string>
            </array>
            <key>UIPrerenderedIcon</key>
            <false/>
        </dict>
    </dict>
    <key>CFBundlePrimaryIcon</key>
    <dict>
        <key>CFBundleIconFiles</key>
        <array>
            <string>chills</string>
        </array>
        <key>UIPrerenderedIcon</key>
        <false/>
    </dict>
</dict>

现在,您可以调用DynamicIconFlutter.setAlternateIconName并传入CFBundleAlternateIcons键作为参数来设置该图标。

Dart/Flutter集成

从Dart代码中,您需要导入插件并使用其静态方法:

import 'package:dynamic_icon_flutter/dynamic_icon_flutter.dart';

try {
  if (await DynamicIconFlutter.supportsAlternateIcons) {
    await DynamicIconFlutter.setAlternateIconName("photos");
    print("App icon change successful");
    return;
  }
} on PlatformException catch (e) {
  if (await DynamicIconFlutter.supportsAlternateIcons) {
    await DynamicIconFlutter.setAlternateIconName(null);
    print("Change app icon back to default");
    return;
  } else {
    print("Failed to change app icon");
  }
}

示例代码

下面是一个完整的示例代码,展示了如何在Flutter应用中使用dynamic_icon_flutter插件:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:dynamic_icon_flutter/dynamic_icon_flutter.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String currentIconName = "?";

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

    DynamicIconFlutter.getAlternateIconName().then((v) {
      setState(() {
        currentIconName = v ?? "`primary`";
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey();
    return MaterialApp(
      home: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: const Text('Dynamic App Icon'),
        ),
        body: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 28),
          child: ListView(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                  "Current Icon Name: $currentIconName",
                  textAlign: TextAlign.center,
                  style: Theme.of(context).textTheme.headline4,
                ),
              ),
              OutlineButton.icon(
                icon: const Icon(Icons.ac_unit),
                label: const Text("Team Fortress"),
                onPressed: () async {
                  try {
                    if (await DynamicIconFlutter.supportsAlternateIcons) {
                      await DynamicIconFlutter.setAlternateIconName("teamfortress");
                      _scaffoldKey.currentState?.hideCurrentSnackBar();
                      _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                        content: Text("App icon change successful"),
                      ));
                      DynamicIconFlutter.getAlternateIconName().then((v) {
                        setState(() {
                          currentIconName = v ?? "`primary`";
                        });
                      });
                      return;
                    }
                  } on PlatformException catch (e) {
                    print(e.toString());
                    _scaffoldKey.currentState?.hideCurrentSnackBar();
                    _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                      content: Text("Failed to change app icon"),
                    ));
                  }
                },
              ),
              OutlineButton.icon(
                icon: const Icon(Icons.ac_unit),
                label: const Text("Photos"),
                onPressed: () async {
                  try {
                    if (await DynamicIconFlutter.supportsAlternateIcons) {
                      await DynamicIconFlutter.setAlternateIconName("photos");
                      _scaffoldKey.currentState?.hideCurrentSnackBar();
                      _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                        content: Text("App icon change successful"),
                      ));
                      DynamicIconFlutter.getAlternateIconName().then((v) {
                        setState(() {
                          currentIconName = v ?? "`primary`";
                        });
                      });
                      return;
                    }
                  } on PlatformException catch (e) {
                    _scaffoldKey.currentState?.hideCurrentSnackBar();
                    _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                      content: Text("Failed to change app icon"),
                    ));
                  }
                },
              ),
              OutlineButton.icon(
                icon: const Icon(Icons.ac_unit),
                label: const Text("Chills"),
                onPressed: () async {
                  try {
                    if (await DynamicIconFlutter.supportsAlternateIcons) {
                      await DynamicIconFlutter.setAlternateIconName("chills");
                      _scaffoldKey.currentState?.hideCurrentSnackBar();
                      _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                        content: Text("App icon change successful"),
                      ));
                      DynamicIconFlutter.getAlternateIconName().then((v) {
                        setState(() {
                          currentIconName = v ?? "`primary`";
                        });
                      });
                      return;
                    }
                  } on PlatformException catch (e) {
                    _scaffoldKey.currentState?.hideCurrentSnackBar();
                    _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                      content: Text("Failed to change app icon"),
                    ));
                  }
                },
              ),
              const SizedBox(
                height: 28,
              ),
              OutlineButton.icon(
                icon: const Icon(Icons.restore_outlined),
                label: const Text("Restore Icon"),
                onPressed: () async {
                  try {
                    if (await DynamicIconFlutter.supportsAlternateIcons) {
                      await DynamicIconFlutter.setAlternateIconName(null);
                      _scaffoldKey.currentState?.hideCurrentSnackBar();
                      _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                        content: Text("App icon restore successful"),
                      ));
                      DynamicIconFlutter.getAlternateIconName().then((v) {
                        setState(() {
                          currentIconName = v ?? "`primary`";
                        });
                      });
                      return;
                    }
                  } on PlatformException catch (e) {
                    _scaffoldKey.currentState?.hideCurrentSnackBar();
                    _scaffoldKey.currentState?.showSnackBar(const SnackBar(
                      content: Text("Failed to change app icon"),
                    ));
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

通过上述步骤和示例代码,您应该能够在Flutter应用中成功实现动态图标更新的功能。希望这对您有所帮助!


更多关于Flutter动态图标更新插件dynamic_icon_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动态图标更新插件dynamic_icon_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用dynamic_icon_flutter插件来动态更新应用图标的示例代码。请注意,这个插件的功能和可用性可能会随着版本更新而变化,因此请参考最新的官方文档以确保最佳实践。

首先,确保你已经在pubspec.yaml文件中添加了dynamic_icon_flutter依赖:

dependencies:
  flutter:
    sdk: flutter
  dynamic_icon_flutter: ^最新版本号 # 请替换为最新版本号

然后,运行flutter pub get来获取依赖。

接下来,在你的Flutter项目中,你可以按照以下步骤使用dynamic_icon_flutter插件:

  1. 导入插件

在你的Dart文件中导入插件:

import 'package:dynamic_icon_flutter/dynamic_icon_flutter.dart';
  1. 请求动态图标权限

在Android上,你需要在AndroidManifest.xml中请求CHANGE_APP_ICON权限(如果插件需要的话,通常插件会处理这部分)。

<uses-permission android:name="com.android.launcher.permission.CHANGE_APP_ICON" />

此外,你可能需要在MainActivity.ktMainActivity.java中处理一些初始化逻辑(具体取决于插件的要求)。

  1. 设置动态图标

以下是一个简单的示例,展示如何使用dynamic_icon_flutter来更改应用图标:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Dynamic Icon Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              try {
                // 检查是否支持动态图标
                bool isSupported = await DynamicIconFlutter.isSupported();
                if (isSupported) {
                  // 设置新的图标(假设你有一个名为'new_icon.png'的图标文件放在assets中)
                  await DynamicIconFlutter.setAppIcon(
                    iconName: 'assets/new_icon.png',
                  );
                  
                  // 提示用户图标已更改
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('App icon changed successfully!')),
                  );
                } else {
                  // 提示用户设备不支持动态图标
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('This device does not support dynamic icons.')),
                  );
                }
              } catch (e) {
                // 处理错误
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Failed to change app icon: $e')),
                );
              }
            },
            child: Text('Change App Icon'),
          ),
        ),
      ),
    );
  }
}

在上面的代码中,我们首先检查设备是否支持动态图标。如果支持,我们尝试将应用图标更改为assets/new_icon.png。注意,你需要将new_icon.png文件添加到你的assets目录中,并在pubspec.yaml中正确配置它:

flutter:
  assets:
    - assets/new_icon.png
  1. 处理图标变化后的逻辑

某些平台(如iOS)可能需要额外的配置或代码来处理图标变化后的逻辑,例如通知系统图标缓存已更改。这些步骤通常会在插件的官方文档中有详细说明。

  1. 测试

最后,不要忘记在支持动态图标的设备上测试你的应用,以确保一切正常工作。

请注意,由于动态图标功能可能涉及操作系统的限制和权限要求,因此在实际部署之前,务必进行充分的测试,并遵循相关平台的最佳实践。

回到顶部