You are currently viewing Preparing Flutter App to Publish on Google Play Store – Adding Launcher Icon & Generating Signed AppBundle/APK

Preparing Flutter App to Publish on Google Play Store – Adding Launcher Icon & Generating Signed AppBundle/APK

Before we can publish our app on Google Play Store we need to give our app a digital signature by signing it. Also we need to provide a good launcher icon for the app instead of default flutter icon and confirm package name, uses permission or other values are set correctly as we needed.

So, if you are here then I assume you have already completed your app and it is working well. Now what you need is to complete some extra work to make your app to be able to publish on Google Play Store. Here two major steps before publishing an app on Play Store is adding a launcher icon and generating signed App Bundle.

Google made it mandatory to use App Bundles to publish on Play Store from August 2021. This means user must opt in for Google Play App Signing.

Adding a Launcher Icon

You have developed flutter app and have seen there was a default launcher icon with flutter logo. Launcher icon is the icon that shows up in Android launcher.

Adding launcher icon may be a tedious process but there is a package flutter_launcher_icons for it that makes this process super easy by doing all the work you may need to do. Check out that package for all the guides you need to add a launcher icon. I will also describe important parts here.

First of all, place your launcher icon in your flutter app. It is same as adding assets/images. Let us assume we place icon.png in assets/icon/

Then, add your Flutter Launcher Icons configuration in your pubspec.yaml file. Don’t forget to use latest version.

dev_dependencies: flutter_launcher_icons: "^0.8.0" flutter_icons: android: "launcher_icon" ios: true image_path: "assets/icon/icon.png"
Code language: YAML (yaml)

After setting up the configuration run the package.

flutter pub get flutter pub run flutter_launcher_icons:main
Code language: YAML (yaml)

After this all files are generated and configured for your new launcher icon. Next time when you build and install the app you can see default launcher icon will be replaced by new one.

Signing Your Flutter App

Without signing App Bundle, we can not publish it on Google Play Store and even we can’t install an unsigned APK on a device. But you have already installed your flutter app while developing, so how is it possible? The answer is, your app is already signed with a debug key. That debug key comes with Android SDK and while debugging, APK is automatically signed with the debug key by default. By the way the password of the debug keystore is android and you don’t need it now but I told you just for information. May be you will need it in future.

If you take a look at <project-root>/android/app/build.gradle. Then you will see something like this;

buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } }
Code language: Gradle (gradle)

But to publish app on Play Store App Bundle/APK should be signed with a private key that represents you and should be kept private. If you want you can take a look at the answers in Stackoverflow – Why should I sign my apk before releasing to PlayStore?

One important thing about app signing is that you need to sign future updates of this app with the same previous private key. If you loose the keystore or forgot the password then you will be unable to publish a new update to that app. However your can recover your key if you lost your keystore in some cases (I have not tried it personally and also don’t want to)I lost my .keystore file?

Since we can’t update our app without the key we used to sign you should always use different key if you are developing apps for others. Using a different key is always better because in future if I need to leave your company or sell that app to different developer then you do not have to share your private key and password.

So I suggest to create a different key if you are working for others or if you need/planed to sell the app in future and you should really consider having one key per app. Personally I always create a new keystore for a new project and save it outside the project location in a safe place with necessary information and copy them inside the project. The folder structure looks like this.

upload_keys ├── com.first.app │   ├── key.properties │   └── upload-keystore.jks ├── com.second.app │   ├── key.properties │   └── upload-keystore.jks └── com.third.app ├── key.properties └── upload-keystore.jks
Code language: Shell Session (shell)

This way I will not forget or lost my keystore and password, and I can transfer/sell my published apps easily to another developer. Also we should make sure not to upload this to public places or you can edit .gitignore file to exclude the folder or files from version control.

So I have told all the necessary things you should know before signing a App Bundle/APK. Now we will generate a private key and keystore using keytool and then sing our app.

Create a upload keystore

Create a folder to save all your keys. Then create another folder with the package name. This way we can easily know which key is for which app.

Navigate to directory where you want to create keystore and use following command:

keytool -genkey -v -keystore upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
Code language: Shell Session (shell)

This command stores the key.jks file in your current working directory. If you want to store it elsewhere, change the argument you pass to the -keystore parameter. Remember the alias also which is upload, we need to put it on key.properties file later.

While creating a keystore you will be asked to enter some details about you and password for keystore and key. For ease we can have same key password as keystore. You will be asked if you want to use key password same as keystore password. A screenshot while creating a keystore for one of my demo app is shown below.

I have just entered my app name everywhere since it is only for learning purpose. However you need to put valid details of yours, like your real name, country etc. And always keeps this key in a safe place, don’t upload it on any public places and don’t forget its password. If you uploaded it to public places or if your key get stolen, your private key is safe until password is unknown to others. But they can bruteforce or guess so you don’t want to take a risk. Is the RSA private key useless without the password?

Create key.properties file and reference to keystore

At at same location where you created upload-keystore.jks create a file named key.properties and put info related to keystore like this;

storePassword=<password from previous step> keyPassword=<password from previous step> keyAlias=upload storeFile=../upload-keystore.jks
Code language: HTML, XML (xml)

If my password is myPassword123 then it looks like this:

storePassword=myPassword123 keyPassword=myPassword123 keyAlias=upload storeFile=../upload-keystore.jks

We need to read this file from app level build.gradle i.e. my_flutter_project/android/app/build.gradle. We also place both key.properties and upload-keystore.jks inside android folder later thats why storeFile location is ../upload-keystore.jks.

Complete the referencing to keystore file from the app

Now copy both key.properties and upload-keystore.jks inside <app_dir>/android/.

Keep the key.properties file private; don’t check it into public source control. To do so add this is .gitignore file inside android directory. Then those files will be greyed out.

key.properties upload-keystore.jks
Code language: CSS (css)

Configure signing in gradle

Configure signing for your app by editing the <app dir>/android/app/build.gradle file.

To load the key.properties file into the keystoreProperties object add following before android block:

def keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) }
Code language: JavaScript (javascript)

then it looks like:

Also add signing configurations info before buildTypes block and change signingConfig signingConfigs.debug to signingConfig signingConfigs.release inside buildTypes

signingConfigs { release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null storePassword keystoreProperties['storePassword'] } } buildTypes { release { signingConfig signingConfigs.release } }
Code language: Gradle (gradle)

Then is should look like this:

Now your App Bundle/APK will signed with release key automatically when create App Bundle or APK.

Reviewing the app manifest

App Manifest file is located at <app dir>/android/app/src/main/AndroidManifest.xml. Before publishing an app we should make sure app has correct name set at android:label in the application tag and has correct package name. By default package name is com.example.<app_name>. So you don’t want that package name to cause problem in future. Here I don’t describe how to change package name. You can see this answer in Stackoverflow – How to change package name in flutter?

Also your app may need internet connectivity or other permissions. In debug mode it is not necessary to add internet permission in Application Manifest for your app to use internet. But in release mode app must have uses permission added in order to function properly. To add internet permission in your app add <uses-permission android:name="android.permission.INTERNET" /> outside application tag. Other permission can be also added in similar way.

Reviewing the build configuration

Review the default Gradle build file file, build.gradle, located in <app dir>/android/app and verify the values are correct as you needed, especially the following values in the defaultConfig block:

  • applicationId – it is the package name as in the AndroidManifest.xml
  • versionCode and verisonName
  • minSdkVersion, compileSdkVersion and targetSdkVersion

Now you can build your APK or App Bundle. From August 2021 only App Bundle can be uploaded to Play Store Console. You can watch a video about App bundle if you want.

To build App bundle run flutter build appbundle and to build APK run flutter build apk --split-per-abi

For more info about splitting APK, building methods and official documentation about this article you can visit – Build and release an Android app.

Google don’t accept APK in Play Store console when publishing Android app instead appbundle is required.

After Publishing – App Integrity

So now we have published the app to play store. If you have opted int to Play App Signing then Google will manage your app signing key. It means user will get signed app by Google not you. So what happened to our key we signed before uploading? The key used to sing the APK is only used to upload the APK to Play Console which will identify your identity to Play Console.

Now lets assume you are using some service like Firebase or Facebook authentication then they need sha-1 key or key hash of the app signing key. So these service will stop working in your app because Google signed the final APK. You need to get the sha-1 key from Play Store Console from App Integrity page.

Facebook Developer Console requires key hash in base 64 which can be generated from sha-1.

echo YOUR SHA-1 (Hexadecimal) | xxd -r -p | openssl base64
Code language: Bash (bash)

Here is the original answer in Stack Overflow: https://stackoverflow.com/a/52785464/8880002

I hope you found this article helpful.

This Post Has 4 Comments

  1. Zeeshan

    Thanks a lot for the efforts

    1. Sangam

      Thank you.

  2. kamal bunkar

    Very helpful. I really want thanks to you for this amazing article. I also inspired by you and create a article How to Publish Flutter app on Apple Store. I hope you will like it.

    1. Sangam

      I have visited your article. Seems good.

Leave a Reply