· Workflow · 9 Min. Lesezeit

Flutter App-Store-Screenshots erstellen: integration_test, Golden Tests und ein schnellerer Weg (2026)

Flutter App-Store-Screenshots erstellen: integration_test, Golden Tests und ein schnellerer Weg (2026)
TL;DR. Flutter hat keinen nativen Befehl zum Erstellen von Store-Screenshots. Die realistischen Optionen sind: echte Frames mit integration_test + flutter drive aufnehmen, Widget-Snapshots mit Golden Tests rendern (gut für Regression, weniger geeignet für Store-Assets) oder beides in Fastlane/Codemagic im CI einbinden. Keine dieser Optionen stellt das Marketing-Karussell zusammen — Untertitel, Geräterahmen, Hintergründe, 50-Sprachen-Text. Das ist ein separater Schritt, und genau dort setzt Mokbi an: Es komponiert und lokalisiert die Screenshots, die du aufgenommen hast. Es nimmt sie nicht selbst auf.

Wer mit Flutter baut und bereits im App Store oder Play Store veröffentlicht hat, kennt die Lücke: Es gibt kein flutter screenshots. Apple verlangt pixelgenaue PNGs in Größen wie 1320 × 2868 (6.9-inch iPhone) und 2064 × 2752 (13-inch iPad); Flutter liefert dir eine Rendering-Engine und ein Test-Framework und überlässt die Store-Pipeline dir. Dieser Beitrag ist die ehrliche Übersicht dessen, was 2026 tatsächlich funktioniert — mit Code zum Kopieren und einer klaren Zuordnung, welches Tool welchen Job übernimmt. Wenn du zuerst die framework-spezifische Übersicht möchtest, lies Mokbi für Flutter-Entwickler.

Eine Vorbemerkung, die viel Verwirrung spart: Einen Frame aufnehmen (die App in einen bestimmten Zustand steuern und ein PNG des Bildschirms speichern) und einen Store-Screenshot zusammenstellen (diesen Frame in ein Geräte-Mockup eingebettet, mit einer Überschrift, einem Hintergrund und übersetztem Text, exportiert in jeder erforderlichen Abmessung) sind zwei verschiedene Probleme. Die meisten Flutter-Tools weiter unten lösen das erste. Keines löst das zweite.

Option 1: integration_test + flutter drive (echte aufgenommene Frames)

Das ist der offiziellste Screenshot-Weg, den Flutter bietet. Das integration_test-Paket startet die komplette App auf einem echten Gerät oder Emulator, steuert sie mit derselben Finder/Tester-API wie Widget-Tests und stellt binding.takeScreenshot() bereit. Der Haken: takeScreenshot gibt die Bytes an den Test-Prozess zurück — es schreibt keine Datei — daher brauchst du den erweiterten Driver, um diese Bytes auf dem Host-Rechner zu empfangen und zu speichern.

Zunächst der Test. Beachte die Android-spezifische Besonderheit: convertFlutterSurfaceToImage() muss aufgerufen (und ein Frame gepumpt) werden, bevor die Aufnahme erfolgt, weil Android Flutter in eine SurfaceView rendert, die der Framebuffer nicht direkt auslesen kann.

// 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');
  });
}

Dann der Driver, der auf dem Host-Rechner läuft und die PNG-Bytes schreibt, die der Test zurückschickt. Diese Datei liegt unter test_driver/ und verwendet integration_test_driver_extended.dart (das einfache integration_test_driver.dart hat keinen onScreenshot-Hook):

// 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)
    },
  );
}

Entscheidend ist, dass du dies mit flutter drive ausführen musst, nicht mit flutter test — nur der Driver-Pfad ruft onScreenshot auf:

flutter drive \
  --driver=test_driver/integration_test.dart \
  --target=integration_test/screenshot_test.dart \
  -d emulator-5554

Was du bekommst und was nicht:

  • Echte App-Pixel. Das sind Screenshots deiner tatsächlich laufenden Widgets, kein Neu-Rendering — Schriften, Theme, dynamische Daten, alles nativ.
  • Deterministische Zustände. Du steuerst die App exakt zu den Screens, die du möchtest, und nimmst auf Abruf auf.
  • Geräteklasse = das Gerät, auf dem du läufst. Die Ausgabeauflösung entspricht dem Emulator bzw. Gerät. Um Apples exakte 1320 × 2868 zu treffen, läufst du auf einem iOS-Simulator der 6.9-inch-Klasse; für das 13-inch iPad-Set nimmst du den entsprechenden Simulator. Es gibt kein magisches „Auf App-Store-Größe skalieren“-Flag.
  • Die Locale-Verwaltung liegt bei dir. Du kannst beim Start eine Locale in die App übergeben und pro Sprache neu ausführen, aber das nimmt die App-UI in dieser Locale auf — es schreibt nicht deinen Marketing-Untertitel.
  • Der Wartungsaufwand ist real. Die Finder (find.byKey, Taps) brechen bei UI-Änderungen, wie jeder UI-Test.

Option 2: Golden Tests — und warum sie keine Store-Assets sind

Flutters Golden Tests (matchesGoldenFile oder das höherstufige testGoldens aus Paketen wie golden_toolkit) rendern einen Widget-Baum zu einem Bild und vergleichen es byte-genau mit einer eingecheckten Referenz. Sie sind ausgezeichnet — und für einen anderen Zweck als Store-Screenshots gedacht: Pixel-Regression. Der Sinn ist, den Tag zu erkennen, an dem eine Padding-Änderung oder ein Schriftwechsel deine UI stillschweigend verschiebt. Das Golden ist der Vertrag; eine Abweichung ist ein fehlgeschlagener Test.

Zwei Gründe, warum rohe Golden-Tests keine Store-Pipeline ergeben:

  • Goldens rendern Widgets in einer headlosen Test-Umgebung, nicht auf der echten Geräteoberfläche. Platform Views, bestimmte Shader und alles, was vom echten GPU-Compositor abhängt, können anders gerendert werden (oder gar nicht) als das, was ein Nutzer sieht. Für einen Regressions-Vertrag ist das in Ordnung — du vergleichst mit dir selbst. Für ein Store-Asset willst du den echten Frame, den Option 1 liefert.
  • Pixelgenaues Matching arbeitet gegen dich. Normale Goldens erwarten exakte Übereinstimmung, also verursachen Antialiasing- und Font-Hinting-Unterschiede zwischen Rechnern instabile Fehler. Das ist die richtige Strenge für Regression und die falsche für „produziere ein schönes Marketing-Bild.“

Dabei gibt es einen legitimen Mittelweg. Pakete wie golden_screenshot nutzen die Golden-Maschinerie bewusst für Store-Ausgaben: Sie verwenden testGoldens, umschließen deinen Screen mit echten Geräterahmen (Telefon/Tablet/Desktop), aktivieren einen unscharfen Komparator, der eine kleine (~0,1 %, konfigurierbar) Abweichung toleriert statt Pixel-Perfektion zu fordern, und schreiben Dateien in eine Fastlane-artige Ordnerstruktur (metadata/<locale>/images/...). Wenn deine Screens reines Flutter sind (keine Platform Views) und du Geräterahmen um ein Rendering haben möchtest, ohne einen Emulator zu starten, ist das ein vernünftiger und schneller Weg. Sei dir aber bewusst: Du renderst Widgets, nimmst nicht die live App auf — und das „Marketing“, das du bekommst, ist ein Geräterahmen, keine Überschriften, Hintergründe oder ein mehrteiliges Karussell.

Option 3: Fastlane / Codemagic für Flutter

Fastlane ist iOS/Android-Automatisierung, nicht Flutter-spezifisch, passt aber auf zwei Arten. Fastlanes eigene Aufnahme-Tools (snapshot für iOS via XCUITest, screengrab für Android via Espresso/UI Automator) erwarten native UI-Tests, die Flutter-Apps in der Regel nicht haben — daher nutzen die meisten Flutter-Teams snapshot nicht für die Aufnahme. Seinen Platz verdient Fastlane bei der Auslieferung: deliver (iOS) und supply (Android) laden deine bereits aufgenommenen PNGs und Metadaten in App Store Connect bzw. die Play Console — und sie lesen eine Fastlane-Ordnerstruktur, genau die, die golden_screenshot schreibt und die du deinem Option-1-Driver ebenfalls vorgeben kannst.

Codemagic ist der Flutter-native CI, den die meisten Teams hier wählen. Das realistische CI-Rezept: Führe deinen integration_test-Screenshot-Drive auf den macOS/Android-Runnern über die benötigten Geräteklassen aus, sammle die PNGs als Build-Artefakte, dann rufe deliver/supply auf, um sie zu pushen. Das ist wirklich nützlich, wenn Screenshots bei jedem Release abdriften und du häufig auslieferst. Den breiteren Trade-off „Automatisieren vs. No-Code“ — einschließlich wann dieses Setup überdimensioniert ist — behandelt Fastlane snapshot vs. no-code App-Store-Screenshots.

Option 4: manuelle Aufnahme (weiterhin vollkommen gültig)

Für viele Indie-Flutter-Apps lautet die ehrliche Antwort: Mach die Screenshots einfach einmal pro Release von Hand. Starte die App im iOS-Simulator in der richtigen Geräteklasse (6.9-inch iPhone, 13-inch iPad), navigiere zu jedem Screen und nutze den Screenshot-Befehl des Simulators — er nimmt in der exakten Geräteauflösung auf, die App Store Connect erwartet. Auf Android tut das die Kamera-Schaltfläche des Emulators ebenso. Gesamtzeit für eine Fünf-Screen-App: vielleicht zehn Minuten.

Manuelle Aufnahme hat keinen Wartungsaufwand, keine instabilen Finder und keinen CI, dem man hinterherlaufen muss. Einzige Schwäche: Nichts regeneriert sich automatisch — wenn du eine UI-Änderung auslieferst, nimmst du neu auf. Für ein Team, das ein paarmal im Jahr veröffentlicht, ist das überwiegend die bessere Wahl. Die Automatisierung aus den Optionen 1–3 zahlt sich genau dann aus, wenn „von Hand neu aufnehmen“ aufhört, ein Zehn-Minuten-Job zu sein, weil du wöchentlich und in vielen Locales auslieferst.

Was keines der obigen Tools erledigt: Komposition und 50-Sprachen-Lokalisierung

Hier ist der abgegrenzte, ehrliche Absatz. Jede der obigen Optionen produziert rohe Frames — deine App-UI in einer Geräteauflösung. App-Store- und Play-Store-Karussells, die konvertieren, sind keine rohen Frames: Sie zeigen den Frame in einem Geräte-Mockup, auf einem Hintergrund, unter einer kurzen Überschrift, oft als mehrteilige Abfolge — und das für jede Locale, die du ansteuerst. Dieser Kompositionsschritt ist das, wofür Mokbi gemacht ist. Du bringst die aufgenommenen Frames (aus integration_test, golden_screenshot, dem Simulator, egal woher), lädst sie in einen Browser-Editor, fügst Geräterahmen, Untertitel und Hintergründe hinzu, übersetzt die Untertitel per Klick in bis zu 50 App-Store-Sprachen und exportierst alle erforderlichen Größen per Batch. Das Design ist kostenlos mit Wasserzeichen-Vorschau; unbegrenzter Export und Veröffentlichung sind im Abo enthalten — Solo für €29.99/mo (1 App) oder Studio für €49.99/mo (bis zu 5 Apps), kein einmaliger Kauf. Die Grenze explizit: Mokbi nimmt keine Quell-Screenshots auf — es startet weder deine App noch steuert es deine Widgets. Die Aufnahme übernimmt das Flutter-Tooling oben; Mokbi komponiert und lokalisiert, was dieses Tooling produziert.

Ein realistischer kombinierter Flutter-Workflow

  1. Quell-Frames aufnehmen. Schreibe einen integration_test-Screenshot-Drive (Option 1) für die Screens, die du im Karussell zeigen möchtest — oder nimm bei seltenen Releases einfach manuell aus dem Simulator auf. Führe ihn auf einem 6.9-inch iPhone-Simulator und einem 13-inch iPad-Simulator aus, damit die Auflösungen stimmen.
  2. CI einbinden (optional). Wenn Abdriften ein echtes Problem ist, führe den Drive auf Codemagic aus und sammle die PNGs als Artefakte (Option 3). Wenn nicht, lass diesen Schritt komplett weg.
  3. Das Marketing-Karussell zusammenstellen. Lade die Frames in Mokbi, füge Geräterahmen, Untertitel und Hintergründe hinzu und baue die mehrteilige Abfolge. Das ist der Schritt, den das Flutter-Tooling nicht übernimmt.
  4. Lokalisieren und exportieren. Übersetze die Untertitel per Klick in deine Ziel-App-Store-Locales, exportiere alle erforderlichen Abmessungen per Batch und lege die Ausgabe bei Bedarf in einen Fastlane-Ordner, damit deliver/supply sie hochlädt.
  5. Bei der nächsten UI-Änderung neu aufnehmen und erneut öffnen. Führe den Drive neu aus (oder nimm manuell auf), öffne das gespeicherte Mokbi-Projekt, tausche die Frames aus, exportiere neu. Komposition und Übersetzungen bleiben erhalten.

Wann du die Automatisierung ganz weglassen kannst

Wenn du 1–3 Releases im Jahr veröffentlichst, kostet der Aufbau eines integration_test-Screenshot-Gerüsts und einer CI-Pipeline mehr Stunden, als sie je einspart. Nimm manuell aus dem Simulator auf, komponiere und lokalisiere einmal und mach weiter. Die Automatisierung aus den Optionen 1–3 ist für Teams gedacht, bei denen Screenshot-Abdriften eine wiederkehrende Last ist — häufige Releases, viele Locales oder mehrere Apps im Portfolio. Passe die Tools deinem Release-Rhythmus an, nicht daran, was rigoros wirkt.

Bevor du irgendetwas exportierst, lohnt es sich, kurz zu prüfen: die genauen Pixelabmessungen im Leitfaden für App-Store-Screenshot-Größen und die Formatregeln (PNG/JPEG, RGB, kein Alpha-Kanal, 1–10 pro Geräteklasse) im Leitfaden für App-Store-Screenshot-Anforderungen. Apple lehnt Abweichungen um einen Pixel ohne Toleranz ab — das sind dreißig Sekunden, die sich lohnen.

Was du als Nächstes liest

Editor öffnen →