Compila un app bundle de release
Google Play solo acepta Android App Bundles (.aab) para apps nuevas. Flutter compila uno con un único comando y el modo release es el predeterminado. Antes, revisa dos cosas: ambas son difíciles de arreglar cuando los testers ya tienen la app.
Define el ID de aplicación
En
android/app/build.gradle.kts, da aapplicationIdun valor único con dominio invertido, comocom.yourcompany.app. No se puede cambiar después de la primera subida a Google Play.Revisa el permiso INTERNET
Si la app usa la red,
android/app/src/main/AndroidManifest.xmldebe declararandroid.permission.INTERNET. Los manifiestos de debug y profile lo incluyen; el de release, solo si lo añades tú.Compila el bundle
Ejecuta el comando de abajo en la raíz del proyecto. Flutter guarda el bundle en
build/app/outputs/bundle/release/.
flutter build appbundle¿Usas flavors? Compila el que vas a publicar, por ejemplo flutter build appbundle --flavor production, y comprueba que usa el nombre de paquete y las claves de API de producción.
Fírmalo: clave de subida y firma de aplicaciones de Play
La firma de aplicaciones de Play es obligatoria para apps nuevas: Google guarda la clave de firma de la app y tú firmas cada subida con tu propia clave de subida. Si alguna vez la pierdes, puedes pedir a Google que la restablezca; tu app no se pierde con ella. Crea la clave con keytool:
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
-storetype JKS -keysize 2048 -validity 10000 -alias uploadDespués crea android/key.properties con las contraseñas y la ruta del keystore. Ni este archivo ni el .jks deben entrar en el control de versiones:
storePassword=<keystore password>
keyPassword=<key password>
keyAlias=upload
storeFile=/Users/<you>/upload-keystore.jksCarga el archivo en android/app/build.gradle.kts y haz que el tipo de build release use la nueva configuración de firma:
import java.util.Properties
import java.io.FileInputStream
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// ...
signingConfigs {
create("release") {
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
storeFile = keystoreProperties.getProperty("storeFile")?.let { file(it) }
storePassword = keystoreProperties.getProperty("storePassword")
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}Los proyectos antiguos usan build.gradle (Groovy) en lugar de build.gradle.kts; los campos son los mismos. Ejecuta flutter clean después de cambiar archivos de Gradle.
Código de versión y nivel de API objetivo
Cada subida necesita un código de versión mayor que el de cualquier bundle que hayas subido antes, en cualquier canal. En Flutter es el número que va después del + en pubspec.yaml:
version: 1.0.0+11.0.0 es el nombre de versión que ven los usuarios y 1 es el código de versión. Súbelo a +2, +3, etc., o sobrescribe ambos al compilar con --build-name y --build-number.
Desde el 31 de agosto de 2026, las apps nuevas y las actualizaciones deben apuntar a Android 16 (nivel de API 36) o superior. El archivo de Gradle usa targetSdk = flutter.targetSdkVersion, así que actualizar Flutter es la forma habitual de subirlo. Si tu versión de Flutter es antigua, pon targetSdk = 36 tú mismo y prueba en una versión reciente de Android antes de subir.
Trampas de Flutter que retrasan la prueba cerrada
- Número de build repetido. Play Console rechaza un bundle cuyo código de versión ya se usó. Sube el
+Ndepubspec.yamlantes de cada build que pienses subir. - Ofuscación sin símbolos.
--obfuscatesolo funciona junto con--split-debug-info. Guarda la carpeta de símbolos de cada build que subas; sin ella no podrás leer los fallos que reporten los testers. - Flavor equivocado. Con flavors es fácil subir el build de staging con los endpoints de staging. Los testers reportan entonces errores que los usuarios de producción nunca verían.
- Sin red en release. Si falta el permiso INTERNET en el manifiesto principal, la app funciona en debug y falla en silencio a los testers.
- Bundle firmado en debug. Si el tipo de build
releasesigue usando la firma de debug, Play Console rechaza la subida. - Plugins con código nativo antiguo. Los plugins que incluyen bibliotecas
.sodeben admitir páginas de memoria de 16 KB. Play Console avisa de ello y, a partir del 1 de febrero de 2027, no se podrán publicar actualizaciones sin esa compatibilidad. Actualiza plugins y el SDK de Flutter antes de la prueba, no a mitad.
flutter build appbundle --obfuscate --split-debug-info=build/symbolsLee los informes de fallos de tus testers
Los testers pegan stack traces en sus informes de errores, y Play Console reúne los fallos en Supervisar y mejorar → Android vitals → Fallos y ANR. Un stack trace de Flutter tiene dos mitades: la parte de Dart, que --obfuscate convierte en nombres cortos y sin sentido, y la parte de Android, que R8 reduce. Flutter activa siempre la reducción de código en los builds de release, así que prepárate para ambas.
Guarda los símbolos de Dart
Un build hecho con
--split-debug-infoescribe un archivo de símbolos por arquitectura, comoapp.android-arm64.symbols. Guarda la carpeta junto al código de versión al que pertenece: el archivo de otro build no puede descifrar el stack trace.Descifra los stack traces de Dart
Guarda el stack trace en un archivo y pásalo a
flutter symbolizejunto con el archivo de símbolos de la arquitectura del dispositivo. La mayoría de los teléfonos actuales son arm64.Deja la parte Java a Play Console
Con el plugin de Android para Gradle 4.1 o posterior, el archivo de mapeo de R8 viaja dentro del
.aab, así que Play Console desofusca las líneas Java y Kotlin de los plugins sin subir nada aparte.
flutter symbolize -i crash.txt -d build/symbols/app.android-arm64.symbolsLa ofuscación solo renombra símbolos; no cifra nada y no es un lugar para secretos. El código que compara runtimeType.toString() con un nombre de clase se rompe al ofuscar, mientras que los nombres de los enum siguen siendo legibles.
Play Console en breve: de la prueba cerrada a producción
En cuanto tu bundle de Flutter esté compilado y firmado, el resto ocurre en Play Console y funciona igual con cualquier framework. Esta es la lista de comprobación; cada menú y cada pantalla están en la guía completa de Play Console.
- Termina la configuración de la app en el Panel: política de privacidad, acceso a la app, anuncios, clasificación de contenido, público objetivo, seguridad de los datos y ficha de Play Store.
- Crea una versión de prueba cerrada en Probar y publicar → Pruebas → Prueba cerrada: elige los países de tus testers, sube el
.aaby envíalo a revisión. - Añade testers con un Google Group en la pestaña Testers y comparte el enlace de opt-in. Funciona en cuanto el canal muestra Publicado.
- Mantén al menos 12 testers con opt-in durante 14 días seguidos. Las pruebas internas no cuentan, y quien sale y vuelve empieza sus 14 días de nuevo.
- Publica al menos una actualización durante la prueba y apunta qué encontraron los testers y qué cambiaste.
- Solicita acceso a producción desde el Panel y responde las tres secciones sobre tu prueba, tu app y tu preparación. La decisión es de Google; nadie puede garantizar la aprobación.
Cuenta con bajas: recluta más de 12 testers para que una baja no retrase tu calendario. Ver todos los pasos de Play Console.
La guía completa de Play Console
Canal cerrado, Google Group y enlace de opt-in, la regla de 12 testers / 14 días y la solicitud de producción, paso a paso en una sola página.
Envía correcciones a los testers durante la prueba
En Flutter: sube el número de build, vuelve a compilar y sube el nuevo bundle al mismo canal cerrado.
flutter build appbundle --build-name=1.0.1 --build-number=2Antes de cada subida, ejecuta el build de release en un teléfono real. flutter install instala el APK de release en un dispositivo conectado y detecta en minutos permisos que faltan, flavors equivocados y fallos que solo ocurren en release:
flutter build apk
flutter installPara probar el bundle exacto que vas a subir, conviértelo en APK de dispositivo con bundletool de Google, o súbelo al canal cerrado e instálalo desde Google Play con una cuenta de tester. Así pruebas lo que reciben los testers, incluidos los APK divididos por dispositivo de Play.
- Un cambio concreto por actualización. Las versiones pequeñas dejan claro qué corrección respondió a qué informe de un tester y te dan ejemplos concretos para el formulario de acceso a producción.
- No cambies de versión de Flutter a mitad de la prueba, salvo que un error informado por los testers lo exija. Una actualización del SDK cambia a la vez el motor, el código nativo de cada plugin y quizá
targetSdk. - Deja tiempo para la revisión. Cada nueva versión del canal cerrado se envía a Google antes de llegar a los testers, así que no dejes tu última corrección para el último día.
Preguntas frecuentes
¿Necesito --obfuscate para una prueba cerrada?
No. La ofuscación es opcional y solo hace que los símbolos de Dart sean más difíciles de leer. Si la usas, combínala siempre con --split-debug-info y archiva los símbolos, o los fallos que te envíen los testers serán ilegibles.
¿Qué código de versión ve Google Play?
El número después del + en pubspec.yaml, o el valor de --build-number si lo pasas. Flutter usa el nombre de build como versionName y el número de build como versionCode.
¿Puedo dar un APK a los testers?
No a través de Google Play: las apps nuevas deben subirse como .aab, y solo las instalaciones desde el canal cerrado cuentan para los 12 testers. Aun así, flutter build apk --split-per-abi es útil para pruebas rápidas en tus propios teléfonos.
¿Por qué Play Console avisa sobre las páginas de memoria de 16 KB?
Normalmente un plugin incluye bibliotecas nativas .so que no están alineadas para páginas de 16 KB. Actualiza el plugin y Flutter, vuelve a compilar y compruébalo en un emulador de Android con una imagen de sistema de páginas de 16 KB antes de la próxima subida.
Fuentes
Los detalles técnicos se han comprobado con la documentación oficial de abajo. Los menús y los valores por defecto cambian entre versiones; ante la duda, manda la página oficial.
- Flutter: Build and release an Android app
- Flutter: Obfuscate Dart code
- Android Developers: Sign your app
- Android Developers: Target API level requirements
- Android Developers: Support 16 KB page sizes
- Android Developers: bundletool
- Play Console Help: App testing requirements for new personal developer accounts
- Play Console Help: Set up an open, closed, or internal test
- Play Console Help: Deobfuscate or symbolicate crash stack traces
¿Es tu primera vez con la regla de los 12 testers?
La guía general explica el requisito de Google, cómo avanza día a día una campaña de TestBunny y qué podemos y qué no podemos garantizar.
Leer la guía general