Flutter App Store ekran görüntüleri oluşturma: integration_test, altın testler ve daha hızlı bir yol (2026)
integration_test + flutter drive ile gerçek kareleri yakalamak, altın testlerle widget anlık görüntüleri oluşturmak (regresyon için mükemmel, mağaza varlıkları için zorlaştırılmış bir kullanım) ya da ikisini de Fastlane/Codemagic CI'a sarmak. Bunların hiçbiri pazarlama karuselini oluşturmaz — metinler, çerçeveler, arka planlar, 50 yerel metni. Bu ayrı bir adımdır ve tam burada Mokbi devreye girer: yakaladığın ekran görüntülerini düzenler ve yerelleştirir. Yakalamaz.Flutter ile uygulama geliştirip App Store veya Play Store'a gönderdiysen bu boşluğa zaten çarpmışsındır: flutter screenshots diye bir komut yoktur. Apple, 1320 × 2868 (6.9 inç iPhone) ve 2064 × 2752 (13 inç iPad) gibi boyutlarda piksel düzeyinde hassas PNG'ler ister; Flutter sana bir renderlama motoruyla bir test koşumu verir ve mağaza hattını sana bırakır. Bu yazı, 2026'da gerçekten işe yarayan yöntemlerin dürüst haritasıdır — yapıştırabileceğin kodlarla birlikte — ve hangi aracın hangi işi yaptığına dair net bir sınır çizer. Çerçeve-spesifik genel bakışa önce göz atmak istersen bkz. Flutter geliştiricileri için Mokbi.
Baştan bir çerçeveleme yapalım, çünkü bu pek çok karışıklığı önler: kare yakalamak (uygulamayı bir duruma götürmek ve ekrandakinin PNG'sini kaydetmek) ile mağaza ekran görüntüsü oluşturmak (o kareyi bir cihaz mockup'ının içine, bir başlıkla, bir arka planla ve çevrilmiş metinlerle yerleştirip her gereken boyutta dışa aktarmak) iki farklı sorundur. Aşağıdaki Flutter araçlarının çoğu birinci sorunu çözer. Hiçbiri ikinciyi çözmez.
Seçenek 1: integration_test + flutter drive (gerçek yakalanan kareler)
Bu, Flutter'ın resmi ekran görüntüsü yoluna en yakın olanıdır. integration_test paketi, tam uygulamanı gerçek bir cihazda veya emülatörde başlatır, widget testlerindeki aynı finder/tester API'si ile sürer ve binding.takeScreenshot()'ı açığa çıkarır. Asıl sorun, takeScreenshot'ın baytları test sürecine döndürmesidir — bir dosya yazmaz — dolayısıyla bu baytları ana makinede alıp kaydetmek için genişletilmiş sürücüye ihtiyaç duyarsın.
Önce test. Android'e özel dansa dikkat et: convertFlutterSurfaceToImage() çağrılmalı (ve bir kare pompanmalı) çünkü Android, Flutter'ı framebuffer'ın doğrudan geri okuyamadığı bir SurfaceView'e render eder.
// 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');
});
}Ardından ana makinede çalışan ve testin geri gönderdiği PNG baytlarını yazan sürücü. Bu dosya test_driver/ klasöründe yer alır ve integration_test_driver_extended.dart kullanır (onScreenshot kancası olmayan düz integration_test_driver.dart değil):
// 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)
},
);
}Bunu flutter test değil flutter drive ile çalıştırman zorunlu — yalnızca sürücü yolu onScreenshot'ı çağırır:
flutter drive \
--driver=test_driver/integration_test.dart \
--target=integration_test/screenshot_test.dart \
-d emulator-5554Ne elde edersin, ne etmezsin:
- Gerçek uygulama pikselleri. Bunlar yeniden render değil, çalışan widget'larının gerçek ekran görüntüleridir — fontlar, tema, dinamik veriler; tamamı native.
- Belirlenimci durumlar. Uygulamayı tam olarak istediğin ekranlara götürür ve talep üzerine yakalarsın.
- Cihaz sınıfı = çalıştırdığın cihaz. Çıktı çözünürlüğü emülatör/cihazın ne olduğuna bağlıdır. Apple'ın tam 1320 × 2868 boyutuna ulaşmak için 6.9 inç sınıfı bir iOS simülatöründe çalıştırman gerekir; 13 inç iPad seti için o simülatörü kullanman gerekir. "App Store boyutuna otomatik yeniden boyutlandır" diye bir sihirli bayrak yoktur.
- Yerel seçimi sana kalmış. Uygulamanı başlangıçta bir yerel geçirip her dil için yeniden çalıştırabilirsin, ancak bu uygulama arayüzünü o dilde yakalar — pazarlama başlık metinlerini yazmaz.
- Bakım maliyeti gerçektir. Finder'lar (
find.byKey, tap'lar) arayüz değiştiğinde bozulur, tıpkı herhangi bir UI testinde olduğu gibi.
Seçenek 2: altın testler — ve neden mağaza varlığı değillerdir
Flutter'ın altın testleri (matchesGoldenFile veya golden_toolkit gibi paketlerden daha üst seviye testGoldens), bir widget ağacını görüntüye render eder ve bayt bayt taahhüt edilmiş referansla karşılaştırır. Bunlar mükemmeldir ve mağaza ekran görüntülerinden farklı bir amaç için geliştirilmiştir: piksel regresyon. Nokta, bir dolgu değişikliğinin veya font değişikliğinin arayüzü sessizce kaydırdığı günü yakalamaktır. Altın, sözleşmedir; fark, başarısız bir testtir.
Ham altın testlere mağaza hattı olarak yönelmemek için iki neden:
- Altın testler, widget'ları gerçek cihaz yüzeyi değil başsız bir test ortamında render eder. Platform görünümleri, bazı shader'lar ve gerçek GPU kompozitörüne bağlı her şey, kullanıcının gördüğünden farklı render edilebilir (ya da hiç render edilmeyebilir). Regresyon sözleşmesi için bu sorun değil — kendinizle kendi içinizde karşılaştırıyorsunuz. Mağaza varlığı için gerçek kareyi istersiniz; bunu Seçenek 1 verir.
- Piksel-hassas eşleştirme seni yorar. Vanilla altın testler tam eşleşme bekler; bu yüzden makineler arasındaki anti-aliasing ve font ipucu farkları kararsız başarısızlıklara yol açar. Bu, regresyon için doğru sertliktir; "güzel bir pazarlama görseli üret" için yanlış sertliktir.
Bununla birlikte, meşru bir orta yol var. golden_screenshot gibi paketler, altın makinesini kasıtlı olarak mağaza çıktısı için yeniden kullanır: testGoldens kullanır, ekranını gerçek cihaz çerçevelerine (telefon/tablet/masaüstü) sarar, piksel mükemmelliği talep etmek yerine küçük bir (~%0.1, yapılandırılabilir) uyumsuzluğu tolere eden bulanık bir karşılaştırıcı etkinleştirir ve dosyaları Fastlane tarzı bir dizin düzenine (metadata/<locale>/images/...) yazar. Ekranların saf Flutter (platform görünümü yok) ise ve emülatör başlatmadan render etrafında çerçeve istiyorsan, bu makul ve hızlı bir yoldur. Yalnızca şunu net gör: widget'ları render ediyorsun, canlı uygulamayı yakalamıyorsun; aldığın "pazarlama" bir cihaz çerçevesidir — başlıklar, arka planlar veya çok panelli karusel değil.
Seçenek 3: Flutter için Fastlane / Codemagic
Fastlane, Flutter'a özgü değil iOS/Android otomasyonudur; ancak iki yolla devreye girer. Fastlane'in kendi yakalama araçları (iOS için XCUITest üzerinden snapshot, Espresso/UI Automator koşumu üzerinden Android için screengrab), Flutter uygulamalarının genellikle sahip olmadığı native UI testleri bekler — dolayısıyla çoğu Flutter ekibi yakalama için snapshot kullanmaz. Fastlane'in değer kattığı yer teslimattır: deliver (iOS) ve supply (Android), halihazırda yakalanmış PNG'leri ve meta verileri App Store Connect / Play Console'a yükler; bunlar Fastlane-biçimli bir klasör okur — ki bu tam olarak golden_screenshot'ın yazdığı ve Seçenek 1 sürücünüzün de yazabileceği düzendir.
Codemagic, çoğu ekibin başvurduğu Flutter'a uygun CI'dır. Gerçekçi CI tarifi şudur: ihtiyaç duyduğun cihaz sınıflarında macOS/Android koşucularında integration_test ekran görüntüsü sürüşünü çalıştır, PNG'leri derleme artifaktları olarak topla, ardından bunları iletmek için deliver/supply çağır. Bu, ekran görüntülerinin her sürümde kaymasını önlemek istediğinde ve sık gönderdiğinde gerçekten işe yarıyor. Daha kapsamlı "otomasyon vs kodsuz" dengesi için — bu kurulumun ne zaman fazla olduğu dahil — bkz. Fastlane snapshot ve kodsuz App Store ekran görüntüleri.
Seçenek 4: manuel yakalama (hâlâ tamamen geçerli)
Pek çok bağımsız Flutter uygulaması için dürüst cevap şudur: ekran görüntülerini her sürümde elle al. Uygulamayı iOS Simulator'da doğru cihaz sınıfında (6.9 inç iPhone, 13 inç iPad) çalıştır, her ekrana git ve simülatörün ekran görüntüsü komutunu kullan — tam cihaz çözünürlüğünde yakalar; App Store Connect'in istediği de budur. Android'de emülatörün kamera düğmesi aynı işi yapar. Beş ekranlı bir uygulama için toplam süre: belki on dakika.
Manuel yakalamada bakım maliyeti yok, kararsız finder yok, bakacak CI yok. Tek zayıflığı, hiçbir şeyin otomatik olarak yeniden oluşturulmamasıdır — bir arayüz değişikliği gönderirsen yeniden çekene kadar yakalamalar eskir. Yılda birkaç kez sürüm gönderen bir ekip için bu denge açıkça değer. 1–3. Seçeneklerdeki otomasyon, "elle yeniden çek" artık on dakikalık iş olmaktan çıktığında — haftalık gönderiyorsun ve pek çok yerel var — tam karşılığını verir.
Yukarıdakilerin hiçbirinin yapmadığı kısım: kompozisyon ve 50 dil yerelleştirmesi
İşte sınırlandırılmış, dürüst paragraf. Yukarıdaki her seçenek ham kareler üretir — cihaz çözünürlüğünde uygulama arayüzün. Dönüşüm sağlayan App Store ve Play Store karuselleri ham kare değildir: kare, bir cihaz mockup'ının içindedir; arka plan üzerindedir; kısa bir başlık altındadır; çoğunlukla çok panelli bir sıra olarak düzenlenmiştir; ve hedeflediğin her yerel için tekrarlanmıştır. Bu kompozisyon adımı tam olarak Mokbi'nin işidir. Yakaladığın kareleri alırsın (integration_test'ten, golden_screenshot'tan, simülatörden, nereden olursa olsun), bir tarayıcı editörüne bırakırsın, cihaz çerçeveleri, metinler ve arka planlar eklersin, metinleri yaklaşık tek tıkla 50 App Store diline çevirirsin ve her gereken boyutta toplu dışa aktarırsın. Filigranla önizleme ile tasarım ücretsizdir; sınırsız dışa aktarma ve yayımlama bir abonelikle gelir — Solo €29.99/mo (1 uygulama) ya da Studio €49.99/mo (5 uygulamaya kadar), tek seferlik satın alım yok. Sınırı açıkça belirtelim: Mokbi kaynak ekran görüntülerini yakalamaz — uygulamanı çalıştırmaz ya da widget'larını sürmez. Yakalama yukarıdaki Flutter araçlarında kalır; Mokbi bu araçların ürettiğini düzenler ve yerelleştirir.
Gerçekçi birleşik Flutter iş akışı
- Kaynak kareleri yakala. Karuselde istediğin ekranlar için bir
integration_testekran görüntüsü sürüşü (Seçenek 1) yaz ya da — seyrek gönderiyorsan — simülatörden elle yakala. 6.9 inç iPhone sınıfı ve 13 inç iPad sınıfı cihazda çalıştır; çözünürlükler doğru olsun. - İsteğe bağlı olarak CI'a bağla. Sapma gerçek bir sorunsa sürüşü Codemagic üzerinde çalıştır ve PNG'leri artifakt olarak topla (Seçenek 3). Değilse bunu tamamen atla.
- Pazarlama karuselini oluştur. Kareleri Mokbi'ye bırak, çerçeveler/metinler/arka planlar ekle, çok panelli sırayı oluştur. Flutter araçlarının yapamadığı adım budur.
- Yerelleştir ve toplu dışa aktar. Metinleri hedef App Store yerellerine tek tıkla çevir, her gereken boyutu toplu dışa aktar ve (istersen) çıktıyı bir Fastlane klasörüne bırak;
deliver/supplyyüklesin. - Bir sonraki arayüz değişikliğinde yeniden yakala ve yeniden aç. Sürüşü yeniden çalıştır (ya da yeniden çek), kaydedilen Mokbi projesini aç, kareleri değiştir, yeniden dışa aktar. Kompozisyon ve çeviriler korunur.
Otomasyonu tamamen atlayabileceğin durumlar
Yılda bir ila üç sürüm gönderiyorsan, bir integration_test ekran görüntüsü koşumu ve CI hattı kurmak kazandıracağından çok daha fazla saatine mal olur. Simülatörden elle yakala, bir kez oluştur ve yerelleştir, ilerle. 1–3. Seçeneklerdeki otomasyon, ekran görüntüsü sapmasının sürekli bir maliyet haline geldiği ekipler için geçerlidir — sık sürümler, pek çok yerel veya portföyde birden fazla uygulama. Makineyi sürüm hızına göre seç, titiz görünen şeye göre değil.
Herhangi bir şeyi dışa aktarmadan önce hedefleri doğrula: gerçekten yüklemen gereken piksel boyutlarını App Store ekran görüntüsü boyutları rehberinden ve format kurallarını (PNG/JPEG, RGB, alfa kanalı yok, cihaz sınıfı başına 1–10) App Store ekran görüntüsü gereksinimleri rehberinden al. Apple tek piksel hatasında boyutları sıfır toleransla reddeder; bu yüzden otuz saniyelik bir kontrol değer.