Cuando llega el momento de llevar una aplicación Flutter a producción en Android, uno de los pasos más importantes —y también más propensos a errores— es generar correctamente un APK o AAB firmado. Google Play exige que cualquier app esté firmada para poder publicarse, y si algo falla en este proceso, la compilación o la subida a la Play Console simplemente no funcionará.
En esta guía te explico paso a paso cómo generar un APK y un AAB firmado en Flutter usando Visual Studio Code o Android Studio, trabajando en Windows (aunque los pasos aplican igualmente para macOS y Linux). Además, veremos errores reales que suelen aparecer durante el proceso y cómo resolverlos.
Veremos varios tips, pasos y consideraciones que debes tener en cuenta al querer generar el release de producción de tu aplicación Flutter usando Visual Studio Code. La APK o AAB que generaremos estará firmada, que es el requisito de Google para poder cargar tu app en la Google Play Store.
Antes vimos cómo generar el launcher, lo cual es fundamental antes de exportar la app a Google Play.
Requisitos previos antes de firmar tu app Flutter
Antes de entrar en comandos y configuraciones, conviene revisar algunos puntos básicos para asegurarte de que el entorno está bien preparado.
Tener Flutter y Android correctamente configurados
Asegúrate de que tu entorno esté bien configurado ejecutando:
$ flutter doctor -vAquí podrás verificar:
- Ruta correcta de Java (
JDK) Android SDKinstalado- Licencias aceptadas (
flutter doctor --android-licenses)
Si flutter doctor reporta algún problema, resuélvelo antes de continuar. Un entorno incompleto es la causa raíz de muchos errores de compilación.
Diferencia entre clave de carga y clave de firma (Play App Signing)
Google Play maneja dos claves distintas, y es importante no confundirlas:
- Clave de carga: es la que tú usas para subir el AAB o APK a Play Console.
- Clave de firma de la app: la gestiona Google internamente y es la que reciben los usuarios finales en sus dispositivos.
Lo normal y recomendado es activar Play App Signing, de modo que Google custodie la clave de firma final. Así, aunque pierdas tu clave de carga, puedes solicitar a Google una nueva sin perder la app.
Dónde NO guardar el keystore
Nunca subas el archivo .jks a repositorios de código, ya sean públicos o privados. Esto incluye:
- GitHub
- GitLab
- Cualquier repositorio público o privado
Guárdalo en una ubicación segura fuera del proyecto y haz copias de respaldo en al menos dos lugares distintos (por ejemplo, un disco externo y un servicio de almacenamiento cifrado). Si pierdes el keystore sin tener configurado Play App Signing, podrías perder la capacidad de actualizar tu app para siempre.
Crear el keystore (JKS) para firmar tu aplicación Flutter
Estos pasos también aplican si usas Android Studio en lugar de Visual Studio Code. Trabajaremos en Windows, pero el proceso es equivalente en macOS y Linux. Esta es la documentación oficial del equipo de Flutter.
Para generar un APK firmado en modo Release para una aplicación Flutter en Android utilizando Visual Studio Code (VSC), debes seguir estos pasos:
1. Generar el keystore con keytool
- Abre una terminal en VSC.
Ejecuta el siguiente comando para generar una clave (keystore) usando
keytool. Puedes hacerlo desde cualquier ubicación de tu sistema:keytool -genkey -v -keystore release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias- Sigue las instrucciones del asistente para configurar la clave. Es un formulario interactivo que debes completar con datos básicos. Los parámetros
release-key.jksykey-aliasson personalizables; puedes cambiarlos por los nombres que prefieras.
El asistente se ve así en consola:
C:\Users\andre\flutter
.\keytool.exe -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias
Introduzca la contraseña del almacén de claves:
Volver a escribir la contraseña nueva:
¿Cuáles son su nombre y su apellido?
[Unknown]: Tu Nombre
¿Cuál es el nombre de su unidad de organización?
[Unknown]: TuEmpresa
¿Cuál es el nombre de su organización?
[Unknown]: TuEmpresa
¿Cuál es el nombre de su ciudad o localidad?
[Unknown]: TuCiudad
¿Cuál es el nombre de su estado o provincia?
[Unknown]: TuEstado
¿Cuál es el código de país de dos letras de la unidad?
[Unknown]: (EJ VE)
¿Es correcto CN=andres cruz, OU=desarrollolibre, O=desarrollolibre, L=caracas, ST=distrito capital, C=VE?
[no]: siSi tienes problemas al ejecutar keytool, los tratamos en detalle en el apartado siguiente.
Durante el proceso se te pedirá:
- Contraseña del almacén (
storePassword) - Alias (
keyAlias) - Nombre, organización, ciudad, estado y código de país
En Windows, si tienes problemas de permisos, especifica una ruta absoluta donde tengas permisos de escritura:
keytool -genkey -v -keystore C:\Users\tu_usuario\release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias2. Crear el archivo key.properties
- En la carpeta
androidde tu proyecto Flutter, crea un archivo llamadokey.properties. Agrega la siguiente información al archivo:
storePassword=your_keystore_password keyPassword=your_key_password keyAlias=my-key-alias storeFile=release-key.jks
- ⚠️ En Windows, si especificas una ruta absoluta en
storeFile, recuerda usar doble barra invertida (\\), por ejemplo:storeFile=C:\\Users\\tu_usuario\\release-key.jks.
Asegúrate también de agregar key.properties y *.jks a tu .gitignore para evitar que las contraseñas del keystore queden expuestas en el repositorio.
3. Modificar el archivo build.gradle
- Abre el archivo
android/app/build.gradle. Agrega la siguiente configuración. Las líneas marcadas con
+son las que debes añadir:+ 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 } }
En resumen, estamos indicando al compilador que lea los parámetros del keystore desde
key.propertiesy los use para firmar la app en modo Release, en lugar de usar las claves de debug por defecto.
Error común: alias no encontrado en el keystore
Error típico:
No key with alias 'upload' found in keystoreEsto significa que el valor de keyAlias en key.properties no coincide con el alias que usaste al generar el keystore. Revísalo cuidadosamente, ya que es uno de los errores más frecuentes y fáciles de pasar por alto.
4. Generar el APK o AAB firmado
Para generar la app firmada en formato APK, ejecuta:
flutter build apkPara generar en formato AAB (recomendado para Google Play):
flutter build appbundle- El archivo
app-release.apkfirmado se generará en la carpetabuild/app/outputs/apk/release/de tu proyecto Flutter. El AAB, por su parte, quedará enbuild/app/outputs/bundle/release/. Antes de subir a Google Play, te recomiendo instalar la APK en un dispositivo físico para validar que todo funciona correctamente: simplemente cópiala al dispositivo Android y sigue los pasos de instalación manual.
No compartas públicamente el archivo del keystore (
release-key.jks). Guárdalo de forma segura y nunca lo incluyas en el repositorio del proyecto.
¡Listo! En este punto ya tienes un APK o AAB firmado en modo Release para tu aplicación Flutter en Android.
Problemas habituales con keytool y cómo solucionarlos
❌ keytool no se reconoce como comando
Esto ocurre porque keytool no está en el PATH del sistema. La solución más rápida es ubicar el ejecutable a través de Flutter:
Ejecuta:
flutter doctor -vBusca la línea:
Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\javaNavega hasta esa carpeta (sin incluir el archivo java):
cd "C:\Program Files\Android\Android Studio\jbr\bin"Desde ahí ya puedes ejecutar keytool directamente. Alternativamente, puedes agregar esa ruta a las variables de entorno de tu sistema para no tener que repetir este paso en el futuro.
Problemas al generar la APK firmada
Hay muchas cosas que pueden salir mal al generar tu app firmada. Si al ejecutar keytool recibes un error de acceso denegado como el siguiente:
error de herramienta de claves: 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)El problema es que estás intentando escribir el archivo .jks en una carpeta sin permisos de escritura (como la carpeta de instalación de Android Studio). La solución es especificar una ruta de tu carpeta de usuario:
keytool -genkey -v -keystore C:\Users\andres\release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key-aliasCon eso, el archivo release-key.jks se generará correctamente en la ubicación especificada.
Antes de generar la APK firmada, también deberías actualizar el namespace de tu app, ya que el identificador por defecto no puede contener example como parte del nombre. Para cambiarlo fácilmente, puedes usar el paquete:
change_app_package_name en pub.dev
Otro error frecuente al compilar es el de alias incorrecto. Si ves algo como esto:
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,5sLa clave del error está en: No key with alias 'upload' found in keystore. Significa que el valor de keyAlias en tu key.properties:
storePassword=your_keystore_password
keyPassword=your_key_password
keyAlias=my-key-alias
storeFile=release-key.jks…no coincide con el alias que especificaste al generar el keystore con keytool. Verifica que ambos sean exactamente iguales, incluyendo mayúsculas y minúsculas.
App abre pero no carga datos
Otro error muy común ocurre cuando la app requiere conexión a internet pero no tiene declarados los permisos correspondientes en AndroidManifest.xml. Asegúrate de incluir lo siguiente antes del bloque <application>:
<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" />
<applicationSin estos permisos, toda la data que tu app trae de internet simplemente no cargará en modo Release, aunque en modo debug sí funcione. Esto se debe a que el modo Release puede aplicar restricciones adicionales de red según la configuración de seguridad del manifiesto.
Una vez generada exitosamente, la APK firmada estará disponible en:
build\app\outputs\flutter-apk\app-release.apk
Error de versionamiento en Google Play
Si al intentar subir tu AAB o APK a Google Play ves un error como:
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.
Significa que ya existe una versión con ese versionCode en la Play Console. Debes incrementar la versión de tu app en el archivo pubspec.yaml. El formato es version: nombre_version+versionCode:
version: 1.0.4+4El número después del + es el versionCode que Google Play usa internamente para identificar cada release. Cada subida debe tener un código mayor al anterior.
APK vs AAB en Flutter: cuál usar y cuándo
Por qué Google Play exige AAB
Desde agosto de 2021, Google Play exige el formato AAB (Android App Bundle) para nuevas aplicaciones, principalmente porque:
- Genera descargas optimizadas para cada dispositivo
- Produce archivos de menor tamaño comparado con el APK universal
- Permite mejor distribución por arquitectura de CPU (
arm64-v8a,x86_64, etc.)
Si publicas en Google Play: usa siempre AAB (
flutter build appbundle).
Cuándo sigue teniendo sentido el APK
- Pruebas manuales en dispositivos físicos
- Distribución fuera de Play Store (instalación directa o sideloading)
- AppGallery de Huawei u otras tiendas alternativas
Error de importaciones en build.gradle.kts (Kotlin DSL)
Si tu proyecto usa el formato .kts (Kotlin DSL) en lugar del Groovy tradicional, necesitas ser más explícito con las importaciones y los tipos. Puede generar errores como estos:
e: file:///C:/Users/andre/Desktop/proy/flutter/mios/aprendeingles/android/app/build.gradle.kts:9:26: Unresolved reference: Properties
e: file:///C:/Users/andre/Desktop/proy/flutter/mios/aprendeingles/android/app/build.gradle.kts:12:29: Unresolved reference: FileInputStream
e: file:///C:/Users/andre/Desktop/proy/flutter/mios/aprendeingles/android/app/build.gradle.kts:26:69: Unresolved reference: it
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\andre\Desktop\proy\flutter\mios\aprendeingles\android\app\build.gradle.kts' line: 9
* What went wrong:
Script compilation errors:
Line 09: val keystoreProperties = Properties()
^ Unresolved reference: Properties
Line 12: keystoreProperties.load(FileInputStream(keystorePropertiesFile))
^ Unresolved reference: FileInputStream
Line 24: keyAlias = keystoreProperties["keyAlias"] as String
^ No cast needed
Line 25: keyPassword = keystoreProperties["keyPassword"] as String
^ No cast needed
Line 26: storeFile = keystoreProperties["storeFile"]?.let { file(it) }
^ Unresolved reference: it
Line 27: storePassword = keystoreProperties["storePassword"] as String
^ No cast needed
Line 38: jvmTarget = JavaVersion.VERSION_17.toString()
^ 'jvmTarget: String' is deprecated. Please migrate to the compilerOptions DSL. More details are here: https://kotl.in/u1r8ln
7 errors
* 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.La solución es agregar las importaciones necesarias al inicio del archivo android\app\build.gradle.kts:
import java.util.Properties
import java.io.FileInputStream
Con esas dos líneas, Kotlin DSL podrá resolver las referencias a Properties y FileInputStream correctamente.
Subir tu AAB firmado a Google Play Console
- Entra a Google Play Console
- Ve a la sección Producción o a la pista de pruebas que corresponda (Alfa, Beta, etc.)
- Crea un nuevo lanzamiento
- Sube el archivo
.aabgenerado - Revisa los errores y advertencias que muestre la consola
- Envía a revisión
Recomendaciones finales antes de publicar
- Guarda el keystore (
.jks) en un lugar seguro, fuera del repositorio - Haz backup del
.jksen al menos dos ubicaciones distintas - Prueba siempre en un dispositivo físico real antes de publicar
- Controla el versionado en
pubspec.yamle incrementa elversionCodeen cada release - Usa AAB (
flutter build appbundle) siempre que sea posible para Google Play - Agrega
key.propertiesy*.jksal.gitignoredesde el inicio del proyecto
Preguntas frecuentes sobre APK y AAB en Flutter
- ¿Es obligatorio firmar una app Flutter para publicarla?
- Sí. Google Play no acepta apps sin firmar bajo ninguna circunstancia.
- ¿Puedo generar el AAB sin Android Studio?
- Sí. Con la línea de comandos es suficiente:
flutter build appbundle.
- Sí. Con la línea de comandos es suficiente:
- ¿Qué pasa si pierdo el keystore?
- Si no tienes Play App Signing activado, podrías perder la posibilidad de actualizar la app en Google Play para siempre. Por eso es crítico respaldarlo desde el primer día.
- ¿Google Play acepta APK hoy en día?
- Solo en casos muy específicos (apps heredadas). Para nuevas publicaciones, AAB es el estándar obligatorio desde agosto de 2021.
- ¿Puedo usar FVM para generar la APK o AAB?
- Sí. Si usas Flutter Version Manager (FVM), simplemente antepón
fvmal comando:fvm flutter build apkofvm flutter build appbundle.
- Sí. Si usas Flutter Version Manager (FVM), simplemente antepón