Programatically change iOS app icon in Flutter

Alessandro Favero
Flutter Community
Published in
5 min readAug 27, 2019

Since iOS 10.3, Apple introduced the ability to programmatically change the launcher icon of your app. This nice feature increases the user experience by providing a way of customization to the launcher screen, which is generally lacking in the iOS ecosystem. Adding this feature to your Flutter app it’s a nice touch and it can increase the feel of the app being perceived as native.

Flutter doesn’t provide any API for this purpose so we need to write some platform-specific code. In this guide, I will use Swift to write the custom code.

Setting Swift as the iOS coding language

By default, new Flutter projects support writing iOS code using Objective-C. To use Swift you can:

Terminal

From your terminal window run

flutter create -i swift appname

VSCode

Settings>Extensions>Dart&Flutter>Dart: Flutter Create IOSLanguage, then create a new Flutter project from VSCode.

Android Studio

Start a new Flutter project, in the section called “Platform channel language” select “Include Swift support for iOS code”.

Now we have a new Flutter app with Swift enabled, it’s time to move on.

Creating the icons

In this guide, I will target just the iPhone launcher icon, but the same concept can be applied to different sized icon.

The sizes required for the iPhone are 60pt@2x (120x120) and 60pt@3x (180x180). I created 3 simple icons at 180px and then converted them with a tool called App Icon Generator.

The tool provides you with the right image size and the right naming scheme that should be: “icon-name@2x.png” and “icon-name@3x.png”.

icon-normal, icon-blue, icon-red (from left to right)

Now that we have the icons ready, it’s time to open the iOS folder of your Flutter app with XCode. You can place your main app icon (in my case “icon-normal”) in the Assets.xcassets but, for the alternative ones, you need to create a separate folder inside the Runner>Runner folder and copy them there. You can name the folder whatever you prefer, I called it “AlternativeIcons”.

XCode project view

Icons are ready. Let’s move to the Info.plist file.

Setup the Info.plist file

In Xcode, open the Info.plist file and add the following parameters to it.

  • Add “Icon files (iOS5)”.
  • Under “Icon files (iOS5)”, add “CFBundleAlternateIcons” as a Dictionary.
  • Under the “CFBundleAlternateIcons”, add a Dictionary corresponding to each of your alternative icons (in my case “Blu” and “Red”).
  • For each of the icon Dictionary, create a property called “CFBundleIconFiles” as an Arrey.
  • Set the type of the first Array item to String and it’s value to the name of the alternative icon file (without the @2x.png and @3x.png).

Your Info.plist file should look like this

Info.plist file

and the source code:

Info.plist snippet

The Info.plist is ready and it’s now time for the fun part.

The fun part

Flutter implementation

Now that all the prerequisites are satisfied, it’s time to code a bit.

For this guide, I created a simple app that shows the icon alternatives and let the user change it by tapping on it.

Main app page

In your lib folder, create a new file called app_icon.dart.

In this, file let’s create an enum to store the icon types.

enum IconType {Normal, Blue, Red}

Create a class called AppIcon, the class will take care of the communication with the Swift code that we will write.

First, we need to construct the channel to connect the Flutter code to the Swift code. The channel constructor needs a unique name to identify it (in this case “appIconChannel”). Inside the class, add

static const MethodChannel platform = MethodChannel('appIconChannel');

Then, we need a way to invoke a method on the method channel.

static Future<void> setLauncherIcon(IconType icon) async {
if (!Platform.isIOS) return null;

String iconName;

switch(icon) {
case IconType.Blue:
iconName = "Blue";
break;
case IconType.Red:
iconName = "Red";
break;
default:
iconName = "Normal";
break;
}
return await platform.invokeMethod('changeIcon', iconName);
}

We need to pass a method name, “changeIcon”, and an argument “iconName”.

The app_icon.dart file should look like this

app_icon.dart

After linking the setLauncherIcon method to the tap gesture on the main page, the Flutter code is done!

iOS-specific code

It’s now time to tackle the Swift part of the project so let’s get back to Xcode.

Open the AppDelegate.swift file, located in Runner > Runner.

Inside the override, we need to create a channel tied to the channel that we created previously “appIconChannel”.

let controller : FlutterViewController = window?.rootViewController as! FlutterViewControllerlet appIconChannel = FlutterMethodChannel(name: "appIconChannel", binaryMessenger: controller)

Then we need a handler for the method call. We need to check if the method called is the one that we invoked in the flutter code “changeIcon” and then call a function to change the launcher icon, changeAppIcon.

appIconChannel.setMethodCallHandler({[weak self](call: FlutterMethodCall, result: FlutterResult) -> Void inguard call.method == "changeIcon" else {result(FlutterMethodNotImplemented)return}self?.changeAppIcon(call: call)})

Finally, let’s implement the changeAppIcon function.

private func changeAppIcon(call: FlutterMethodCall){if #available(iOS 10.3, *) {guard UIApplication.shared.supportsAlternateIcons else {return}let arguments : String = call.arguments as! Stringif arguments == "Blue" {UIApplication.shared.setAlternateIconName("Blue")} else if arguments == "Red"{UIApplication.shared.setAlternateIconName("Red")} else {UIApplication.shared.setAlternateIconName(nil)}} else {// Fallback on earlier versions}}

Now, your AppDelegate.swift should look like this

AppDelegate.swift

And that’s it! Now test your app and you will be able to change the launcher icon.

Thanks for your time and if you liked the article please leave a clap down here. If you want to check the full project you can find it on GitHub.

--

--