Cómo generar capturas de pantalla para la App Store con Flutter: integration_test, golden tests y un camino más rápido (2026)
integration_test + flutter drive, renderizar instantáneas de widgets con golden tests (excelentes para regresión, algo forzadas como recursos de tienda) o envolver cualquiera de las dos en Fastlane/Codemagic CI. Ninguna de ellas compone el carrusel de marketing — textos, marcos, fondos y textos en 50 idiomas. Ese es un paso independiente, y es donde encaja Mokbi: compone y localiza las capturas de pantalla que tú capturaste. No las captura.Si desarrollas con Flutter y ya has publicado en la App Store o Play Store, ya te habrás topado con el vacío: no existe ningún flutter screenshots. Apple exige PNG a dimensiones exactas como 1320 × 2868 (iPhone de 6.9 pulgadas) y 2064 × 2752 (iPad de 13 pulgadas); Flutter te entrega un motor de renderizado y un entorno de pruebas y deja el flujo de la tienda en tus manos. Este artículo es el mapa honesto de lo que realmente funciona en 2026, con código que puedes copiar, y una línea clara sobre qué herramienta hace qué trabajo. Si primero quieres ver el panorama general específico del framework, visita Mokbi para desarrolladores de Flutter.
Un punto de partida importante, porque ahorra mucha confusión: capturar un fotograma (llevar la app a un estado y guardar un PNG de lo que hay en pantalla) y componer una captura de pantalla para la tienda (ese fotograma dentro de un mockup de dispositivo, con un titular, un fondo y textos traducidos, exportado en todas las dimensiones requeridas) son dos problemas distintos. La mayoría de las herramientas de Flutter que se mencionan a continuación resuelven el primero. Ninguna resuelve el segundo.
Opción 1: integration_test + flutter drive (fotogramas reales capturados)
Es lo más parecido que tiene Flutter a un flujo oficial de capturas de pantalla. El paquete integration_test lanza tu app completa en un dispositivo real o emulador, la controla con la misma API de finder/tester que los widget tests, y expone binding.takeScreenshot(). El problema es que takeScreenshot devuelve los bytes al proceso de prueba — no escribe ningún archivo —, por lo que necesitas el driver extendido para recibir esos bytes en el host y guardarlos.
Primero, la prueba. Observa el paso específico de Android: convertFlutterSurfaceToImage() debe llamarse (y bombearse un fotograma) antes de la captura, porque Android renderiza Flutter en un SurfaceView que el framebuffer no puede leer directamente.
// integration_test/screenshot_test.dart
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('home + detail screenshots', (tester) async {
app.main();
await tester.pumpAndSettle();
// Android needs the surface converted to an image before capture.
if (Platform.isAndroid) {
await binding.convertFlutterSurfaceToImage();
await tester.pumpAndSettle();
}
await binding.takeScreenshot('01_home');
await tester.tap(find.byKey(const Key('open-detail')));
await tester.pumpAndSettle();
await binding.takeScreenshot('02_detail');
});
}Luego el driver, que se ejecuta en el equipo host y escribe los bytes PNG que la prueba envía de vuelta. Este archivo vive en test_driver/ y usa integration_test_driver_extended.dart (el integration_test_driver.dart básico no tiene el hook onScreenshot):
// test_driver/integration_test.dart
import 'dart:io';
import 'package:integration_test/integration_test_driver_extended.dart';
Future<void> main() async {
await integrationDriver(
onScreenshot: (String name, List<int> bytes,
[Map<String, Object?>? args]) async {
final file = await File('screenshots/$name.png').create(recursive: true);
file.writeAsBytesSync(bytes);
return true; // false fails the test (e.g. on a diff mismatch)
},
);
}Es fundamental ejecutarlo con flutter drive, no con flutter test — solo la ruta del driver invoca onScreenshot:
flutter drive \
--driver=test_driver/integration_test.dart \
--target=integration_test/screenshot_test.dart \
-d emulator-5554Lo que obtienes y lo que no obtienes:
- Píxeles reales de la app. Son capturas de pantalla de tus widgets en ejecución real, no un re-renderizado — tipografías, tema, datos dinámicos, todo nativo.
- Estados deterministas. Llevas la app exactamente a las pantallas que quieres y capturas a demanda.
- La clase de dispositivo es el dispositivo que usas. La resolución de salida es la del emulador o dispositivo. Para obtener exactamente 1320 × 2868 de Apple necesitas un simulador iOS de la clase de 6.9 pulgadas; para el conjunto del iPad de 13 pulgadas, ese simulador. No existe ningún flag mágico de «redimensionar para la App Store».
- La configuración regional es tu responsabilidad. Puedes pasar una configuración regional a tu app al arrancar y re-ejecutar por idioma, pero eso captura la interfaz de la app en ese idioma — no escribe los textos de tu titular de marketing.
- El coste de mantenimiento es real. Los finders (
find.byKey, taps) se rompen cuando cambia la interfaz, igual que cualquier prueba de UI.
Opción 2: golden tests — y por qué no son recursos de tienda
Los golden tests de Flutter (matchesGoldenFile, o el nivel superior testGoldens de paquetes como golden_toolkit) renderizan un árbol de widgets a una imagen y la comparan byte a byte con una referencia comprometida. Son excelentes y están construidos para un propósito distinto al de las capturas de pantalla de la tienda: la regresión de píxeles. El objetivo es detectar el día en que un cambio de padding o de tipografía desplaza silenciosamente tu interfaz. El golden es el contrato; una diferencia es una prueba fallida.
Dos razones para no usar golden tests directamente como tu flujo de la tienda:
- Los goldens renderizan widgets en un entorno de pruebas sin interfaz gráfica, no en la superficie real del dispositivo. Las vistas de plataforma, algunos shaders y cualquier cosa que dependa del compositor real de GPU pueden renderizarse de forma diferente (o no renderizarse en absoluto) respecto a lo que ve el usuario. Para un contrato de regresión eso está bien — te comparas contigo mismo. Para un recurso de tienda quieres el fotograma real, que es lo que te da la Opción 1.
- La coincidencia perfecta de píxeles juega en tu contra. Los goldens básicos exigen una coincidencia exacta, por lo que las diferencias de antialiasing y hinting de tipografías entre equipos causan fallos intermitentes. Esa es la precisión correcta para regresión y la incorrecta para «producir una buena imagen de marketing».
Dicho esto, existe un camino intermedio legítimo. Paquetes como golden_screenshot reutilizan deliberadamente el mecanismo de goldens para la salida de la tienda: usan testGoldens, envuelven tu pantalla en marcos de dispositivo reales (teléfono/tablet/escritorio), habilitan un comparador flexible que tolera una pequeña discrepancia (~0,1%, configurable) en lugar de exigir perfección de píxeles, y escriben los archivos en un directorio con la estructura de Fastlane (metadata/<locale>/images/...). Si tus pantallas son Flutter puro (sin vistas de plataforma) y quieres marcos alrededor de un renderizado sin necesidad de levantar un emulador, esta es una ruta razonable y rápida. Solo ten claro que estás renderizando widgets, no capturando la app en vivo, y que el «marketing» que obtienes es un marco de dispositivo — no titulares, fondos ni un carrusel de varios paneles.
Opción 3: Fastlane / Codemagic para Flutter
Fastlane es automatización de iOS/Android, no específica de Flutter, pero encaja de dos maneras. Las herramientas de captura propias de Fastlane (snapshot para iOS vía XCUITest, screengrab para Android vía un entorno Espresso/UI Automator) esperan pruebas de UI nativas, que las apps de Flutter generalmente no tienen — por eso la mayoría de los equipos de Flutter no usa snapshot para capturar. Donde Fastlane justifica su lugar es en la entrega: deliver (iOS) y supply (Android) suben tus PNG ya capturados y sus metadatos a App Store Connect / Play Console, y leen una carpeta con la estructura de Fastlane — que es exactamente el directorio que escribe golden_screenshot y el que puedes hacer que escriba también el driver de la Opción 1.
Codemagic es el CI nativo de Flutter al que recurre la mayoría de los equipos en este punto. La receta realista de CI es: ejecutar el drive de capturas de pantalla con integration_test en los runners de macOS/Android para las clases de dispositivo que necesitas, recoger los PNG como artefactos del build y luego llamar a deliver/supply para subirlos. Esto resulta genuinamente útil cuando las capturas de pantalla se desactualizan en cada versión y publicas con frecuencia. Para el balance más amplio entre «automatizar vs sin código» — incluyendo cuándo esta configuración es excesiva — consulta Fastlane snapshot vs capturas de pantalla para la App Store sin código.
Opción 4: captura manual (completamente válida)
Para muchas apps indie de Flutter, la respuesta honesta es: simplemente toma las capturas de pantalla a mano una vez por versión. Ejecuta la app en el iOS Simulator en la clase de dispositivo correcta (iPhone de 6.9 pulgadas, iPad de 13 pulgadas), navega a cada pantalla y usa el comando de captura del simulador — captura a la resolución exacta del dispositivo, que es lo que pide App Store Connect. En Android, el botón de cámara del emulador hace lo mismo. Tiempo total para una app de cinco pantallas: quizás diez minutos.
La captura manual no tiene coste de mantenimiento, no tiene finders intermitentes y no tiene ningún CI que vigilar. Su única debilidad es que nada se regenera automáticamente — si publicas un cambio de interfaz, vuelves a capturar. Para un equipo que lanza unas pocas veces al año, ese intercambio merece claramente la pena. La automatización de las Opciones 1–3 se rentabiliza precisamente cuando «volver a capturar a mano» deja de ser un trabajo de diez minutos porque publicas semanalmente y en muchos idiomas.
Lo que ninguna de las opciones anteriores hace: composición y localización en 50 idiomas
Aquí va el párrafo delimitado y honesto. Cada opción anterior produce fotogramas sin procesar — la interfaz de tu app a la resolución del dispositivo. Los carruseles de la App Store y Play Store que convierten no son fotogramas sin procesar: son el fotograma dentro de un mockup de dispositivo, sobre un fondo, bajo un titular breve, a menudo como una secuencia de varios paneles, y repetido en cada idioma que soportas. Ese paso de composición es para lo que sirve Mokbi. Traes los fotogramas que capturaste (desde integration_test, golden_screenshot, el simulador o donde sea), los sueltas en un editor de navegador, añades marcos de dispositivo, textos y fondos, traduces los textos a hasta 50 idiomas de la App Store prácticamente de un clic y exportas por lotes en todas las dimensiones requeridas. Diseñar es gratuito con una vista previa con marca de agua; la exportación y la publicación ilimitadas vienen con una suscripción — Solo a €29.99/mo (1 app) o Studio a €49.99/mo (hasta 5 apps), sin compra única. Para ser explícito sobre el límite: Mokbi no captura las capturas de pantalla de origen — no ejecuta tu app ni controla tus widgets. La captura es responsabilidad de las herramientas de Flutter anteriores; Mokbi compone y localiza lo que esas herramientas producen.
Un flujo de trabajo realista combinado con Flutter
- Captura los fotogramas de origen. Escribe un drive de capturas de pantalla con
integration_test(Opción 1) para las pantallas que quieres en el carrusel, o — si publicas con poca frecuencia — simplemente captúralas a mano desde el simulador. Ejecuta en un dispositivo de la clase iPhone de 6.9 pulgadas y en uno de la clase iPad de 13 pulgadas para que las resoluciones sean correctas. - (Opcional) Conéctalo a CI. Si el desfase es un problema real, ejecuta el drive en Codemagic y recoge los PNG como artefactos (Opción 3). Si no lo es, omite este paso por completo.
- Compón el carrusel de marketing. Suelta los fotogramas en Mokbi, añade marcos, textos y fondos, construye la secuencia de varios paneles. Este es el paso que las herramientas de Flutter no pueden hacer.
- Localiza y exporta. Traduce los textos a tus idiomas objetivo de la App Store de un clic, exporta por lotes en todas las dimensiones requeridas y, si quieres, coloca la salida en una carpeta de Fastlane para que
deliver/supplyla suba. - En el siguiente cambio de interfaz, vuelve a capturar y reabre. Vuelve a ejecutar el drive (o repite la captura manual), reabre el proyecto guardado de Mokbi, intercambia los fotogramas y vuelve a exportar. La composición y las traducciones quedan conservadas.
Cuándo prescindir de la automatización por completo
Si publicas entre uno y tres lanzamientos al año, construir un entorno de capturas con integration_test y un pipeline de CI te costará más horas de las que jamás ahorrará. Captura manualmente desde el simulador, compón y localiza una vez, y sigue adelante. La automatización de las Opciones 1–3 es para equipos en los que el desfase de capturas de pantalla es un coste recurrente — publicaciones frecuentes, muchos idiomas o varias apps en un portfolio. Adapta la maquinaria a la cadencia de publicación, no a lo que parece más riguroso.
Antes de exportar cualquier cosa, verifica los objetivos: consulta las dimensiones exactas de píxeles en la guía de tamaños de capturas de pantalla de la App Store y las reglas de formato (PNG/JPEG, RGB, sin canal alfa, entre 1 y 10 por clase de dispositivo) en la guía de requisitos de capturas de pantalla de la App Store. Apple rechaza dimensiones con diferencias de un solo píxel sin ninguna tolerancia, así que vale la pena dedicar treinta segundos a esto.