Steps to generate an APK and ABB signed in Release mode in Flutter using VSC in Google Play

- Andrés Cruz

ES En español

Steps to generate an APK and ABB signed in Release mode in Flutter using VSC in Google Play

When the time comes to take a Flutter application to production on Android, one of the most important (and also most error-prone) steps is correctly generating a signed APK or AAB. Google Play requires any app to be signed before it can be published, and if something fails in this process, the compilation or the upload to the Play Console simply won't work.

In this guide, I will explain step-by-step how to generate a signed APK and AAB in Flutter using Visual Studio Code or Android Studio, working on Windows (though the steps apply equally to macOS and Linux). Additionally, we will look at real errors that often appear and how to solve them.

We will see several tips, steps, and considerations that you should take into account when you want to generate the production release for your Flutter application using Visual Studio Code; the Android app/apk we are going to generate will be signed, which is the requirement by Google to be able to upload your app to the Google Play Store.

Prerequisites before signing your Flutter app

Before diving into commands and configurations, it is worth reviewing some basic points.

Have Flutter and Android correctly configured

Make sure your environment is properly set up by running:

$ flutter doctor -v

Here you can verify:

  • Correct Java (JDK) path
  • Android SDK installed
  • Licenses accepted

Difference between upload key and app signing key (Play App Signing)

Google Play uses two keys:

  • Upload key: the one you use to upload the AAB or APK.
  • App signing key: managed by Google and the one received by users.

The normal (and recommended) practice is to use Play App Signing, so you don't risk the app's final key.

Where NOT to store the keystore

Never upload the .jks file to:

  • GitHub
  • GitLab
  • Public or private repositories

Save it in a secure location and make backups. If you lose the keystore, you could lose the app forever.

Create the keystore (JKS) to sign your Flutter application

These steps can also be taken into account if you use Android Studio.

Finally, we are going to use Windows to perform these steps; this is the official documentation from the Flutter team:

https://docs.flutter.dev/deployment/android

To generate a signed APK in Release mode for a Flutter application on Android using Visual Studio Code (VSC), you must follow these steps:

1 Generate the keystore with keytool

  • Open a terminal in VSC.
  • Run the following command to generate a key (keystore) using keytool (you can do this from any location on your system):

    keytool -genkey -v -keystore release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias
  • Follow the instructions to configure the key (password, name, etc.); it is a form like the following that you must fill out with basic information; from the previous command, you can customize the location of the release-key.jks as well as the alias key-alias.
C:\Users\andre\flutter
.\keytool.exe -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias
Enter keystore password:  
Re-enter new password:
What is your first and last name?
[Unknown]:  Your Name 
What is the name of your organizational unit?
[Unknown]:  YourCompany
What is the name of your organization?
[Unknown]:  YourCompany
What is the name of your City or Locality?
[Unknown]:  YourCity
What is the name of your State or Province?
[Unknown]:  YourState
What is the two-letter country code for this unit?
[Unknown]:  (EG US)
Is CN=andres cruz, OU=desarrollolibre, O=desarrollolibre, L=caracas, ST=distrito capital, C=VE correct?
[no]: yes

If you have problems with the keytool command, we address that in the next section.

In summary:

  • During the process you will be asked for:
  • Store password
  • Alias

Name, organization, country, etc.

On Windows, you can specify a full path if you have permission issues:

keytool -genkey -v -keystore C:\Users\your_user\release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias

2 Create the key.properties file

  • In the android folder of your Flutter project, create a file named key.properties.
  • Add the following information to the file:

    storePassword=your_keystore_password
    keyPassword=your_key_password
    keyAlias=my-key-alias
    storeFile=release-key.jks
  • ⚠️ On Windows, remember to use double backslashes \\.

3 Modify the build.gradle file

  • Open the android/app/build.gradle file.
  • Add the following configuration; the ones with the + symbol are the ones you must add:

    +   def keystoreProperties = new Properties()
    +   def keystorePropertiesFile = rootProject.file('key.properties')
    +   if (keystorePropertiesFile.exists()) {
    +       keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
    +   }
    +
       android {
          ...
       }
       
       +   signingConfigs {
    +       release {
    +           keyAlias keystoreProperties['keyAlias']
    +           keyPassword keystoreProperties['keyPassword']
    +           storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
    +           storePassword keystoreProperties['storePassword']
    +       }
    +   }
       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
    +           signingConfig signingConfigs.release
          }
       }

In summary, we are adding at the code level what the compiler will use as parameters based on the previously generated keystore to generate our signed app.

Common error: alias not found in keystore

Typical error:

No key with alias 'upload' found in keystore

This means that:

  • The keyAlias in key.properties
  • Does not match the alias used when generating the keystore

Check it carefully. It is one of the most common errors.

4 Generate the signed APK

  • Finally, we run the command to generate the signed app in apk format:

    flutter build apk
  • or in aab:
  • flutter build appbundle 
  • The signed APK file (app-release.apk) will be generated in the build/app/outputs/apk folder of your Flutter project. This is the one you must upload to Google Play. Before uploading, I recommend installing the apk on a physical device; to do this, copy it to the Android device and follow the installation steps.

Do not publicly share the keystore file (release-key.jks). Store it securely.

Done! Now you have a signed APK in Release mode for your Flutter application on Android.

Common issues with keytool and how to fix them

❌ keytool is not recognized as a command

This happens because keytool is not in the PATH. The quick fix:

Run:

flutter doctor -v

Look for the line:

Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java

Go to that folder (excluding java):

cd "C:\Program Files\Android\Android Studio\jbr\bin"

Run the keytool command from there.

Problems when generating the signed apk

Of course, many things can go wrong when generating your signed application, and this is what we will address next; if when running the command it gives you an error like the following:

You must add the reference to the installer to your system path or simply move to the keystore location; for this, we first need to locate where the .exe is. We can use the flutter command for that:

flutter doctor -v

And look for something like:

cd 'C:\Program Files\Android\Android Studio\jbr\bin\\'  
C:\Program Files\Android\Android Studio\jbr\bin\java
Android toolchain - develop for Android devices (Android SDK version 34.0.0)
   • Android SDK at C:\Users\andre\AppData\Local\Android\sdk
   • Platform android-34, build-tools 34.0.0
   • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
   • Java version OpenJDK Runtime Environment (build 17.0.10+0--11572160)
   • All Android licenses accepted.

Where it indicates where Java is:

C:\Program Files\Android\Android Studio\jbr\bin\java

Now, move the terminal to that location without including the java folder (you can add this same address to your system path if you wish):

cd C:\Program Files\Android\Android Studio\jbr\bin

And run:

keytool -genkey -v -keystore release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias

It will probably indicate that it is read-only:

keytool error: java.io.FileNotFoundException: my-release-key.jks (Acceso denegado)
java.io.FileNotFoundException: my-release-key.jks (Acceso denegado)
       at java.base/java.io.FileOutputStream.open0(Native Method)
       at java.base/java.io.FileOutputStream.open(FileOutputStream.java:293)
       at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:235)
       at java.base/java.io.FileOutputStream.<init>(FileOutputStream.java:123)
       at java.base/sun.security.tools.keytool.Main.doCommands(Main.java:1375)
       at java.base/sun.security.tools.keytool.Main.run(Main.java:423)
       at java.base/sun.security.tools.keytool.Main.main(Main.java:416)

So, place a different location to generate the release-key.jks, for example:

keytool -genkey -v -keystore C:\Users\andres\release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias

And now, it should generate the file successfully in the specified location.

The next step consists of generating the signed apk. Before that, you should update your app's namespace, as it cannot contain "example" as part of the name. For this, you can use the following package:

https://pub.dev/packages/change_app_package_name

There are many errors that can occur if you don't correctly set the parameters in Android Studio, such as the project getting stuck in a loop when running on Android, or the signed apk generation indicating, for example, that you made a mistake when defining the alias:

e: C:/Users/andre/.gradle/caches/transforms-3/c1e3cec58f97b65c118bb2f68fab94a8/transformed/jetified-core-ktx-1.10.1/jars/classes.jar!/META-INF/core-ktx_release.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.
e: C:/Users/andre/.gradle/caches/transforms-3/a3842a17fe7307c5bcdac869078c73a0/transformed/core-1.10.1/jars/classes.jar!/META-INF/core_release.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.        
e: C:/Users/andre/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.8.22/636bf8b320e7627482771bbac9ed7246773c02bd/kotlin-stdlib-1.8.22.jar!/META-INF/kotlin-stdlib-jdk7.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.
e: C:/Users/andre/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.8.22/636bf8b320e7627482771bbac9ed7246773c02bd/kotlin-stdlib-1.8.22.jar!/META-INF/kotlin-stdlib.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.
e: C:/Users/andre/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.8.22/636bf8b320e7627482771bbac9ed7246773c02bd/kotlin-stdlib-1.8.22.jar!/META-INF/kotlin-stdlib-jdk8.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.
e: C:/Users/andre/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.8.22/1a8e3601703ae14bb58757ea6b2d8e8e5935a586/kotlin-stdlib-common-1.8.22.jar!/META-INF/kotlin-stdlib-common.kotlin_module: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:packageRelease'.
> A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable
  > com.android.ide.common.signing.KeytoolException: Failed to read key upload from store "C:\Users\andre\flutter\release-key-flutter.jks": No key with alias 'upload' found in keystore C:\Users\andre\flutter\release-key-flutter.jks
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 11s
Running Gradle task 'assembleRelease'...                           12,5s

That is:

key.properties

storePassword=your_keystore_password
keyPassword=your_key_password
keyAlias=my-key-alias
storeFile=release-key.jks

Which obviously must correspond to the alias specified when generating the keystore.

App opens but data doesn't load

Another common error is if the app requires an internet connection to function; in AndroidManifest.xml, you must include the permissions:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
   <uses-permission android:name="android.permission.INTERNET" />
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
   <application

Otherwise, upon opening it, you would see that all the data fetched from the internet would not load.

Finally, the app will be generated in your project:

C:\Users\andre\OneDrive\Escritorio\proyectos\flutter\desarrollolibre\build\app\outputs\flutter-apk

Versioning error in Google Play

In Google Play, if you get an error like:

You must use a different version code for your APK or your Android App Bundle because code 1 is already assigned to another APK or Android App Bundle.

You must change the version of the published app:

pubspec.yaml

***

version: 1.0.4+4

APK vs AAB in Flutter: which to use and when

Why Google Play requires AAB

  • Optimized downloads
  • Smaller size
  • Better distribution by architecture

If you publish on Google Play: use AAB.

When APK still makes sense

  • Manual testing
  • Distribution outside the Play Store
  • AppGallery or other stores

Uploading your signed AAB to Google Play Console

  1. Log in to Google Play Console
  2. Go to Production or Testing
  3. Create a new release
  4. Upload the .aab file
  5. Review errors and warnings
  6. Submit for review

Final recommendations before publishing

  • Store the keystore in a safe place
  • Back up the .jks
  • Always test on a real device
  • Control versioning properly
  • Use AAB whenever possible

Frequently Asked Questions about APK and AAB in Flutter

  • Is it mandatory to sign a Flutter app?
    • Yes. Google Play does not accept unsigned apps.
  • Can I generate the AAB without Android Studio?
    • Yes. Using the command line (flutter build appbundle) is sufficient.
  • What happens if I lose the keystore?
    • You could lose the ability to update the app. This is why backing it up is critical.
  • Does Google Play accept APK nowadays?
    • Only in very specific cases. For new apps, AAB is the standard.

We will talk about many important steps when generating an APK and ABB signed in Release mode in Flutter using VSC that are not discussed in the official documentation.

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español