Claude Code ile Flutter UI yinelemesi: kullanıcı hikayelerini widget ağaçlarına, duruma ve navigasyona çevirirken değişiklikleri modüler ve gözden geçirilebilir tutmaya yönelik pratik bir döngü.

Hızlı Flutter UI çalışmaları genelde iyi başlar. Bir düzeni ayarlarsınız, bir buton eklersiniz, bir alanı taşırsınız ve ekran hızla düzelir. Sorun birkaç tur sonra ortaya çıkar: hız, kimsenin gözden geçirmek istemediği bir değişiklik yığınına dönüşür.
Takımlar genellikle aynı başarısızlıklarla karşılaşır:
Büyük bir sebep "tek büyük prompt" yaklaşımıdır: tüm özelliği tanımlayıp, tüm ekran setini isteyip büyük bir çıktı kabul edersiniz. Asistan yardım etmeye çalışır ama aynı anda çok fazla parçaya dokunur. Bu, değişiklikleri dağınık, gözden geçirilmesi zor ve birleştirmesi riskli yapar.
Tekrarlanabilir bir döngü netlik sağlar ve etki alanını sınırlar. "Özelliği inşa et" demek yerine şu adımları tekrarlayın: bir kullanıcı hikayesi seçin, bunu kanıtlayan en küçük UI dilimini üretin, o dilim için sadece gerekli durumu ekleyin, sonra tek bir yol için navigasyonu bağlayın. Her geçiş incelemeye yetecek kadar küçük kalır ve hatalar kolayca geri alınır.
Amaç, kullanıcı hikayelerini somut ekranlara, durum yönetimine ve navigasyon akışlarına dönüştüren pratik bir iş akışı sunmaktır. İyi yapıldığında modüler UI parçaları, küçük diff’ler ve gereksinimler değiştiğinde daha az sürpriz elde edersiniz.
Kullanıcı hikayeleri insanlar için yazılır, widget ağaçları için değil. Bir şey üretmeden önce hikayeyi görünür davranışı tanımlayan küçük bir UI spesine çevirin. "Tamam" test edilebilir olmalı: kullanıcının ne görebileceği, dokunabileceği ve doğrulayabileceği; "tasarımın modern hissetmesi" değil.
Kapsamı somut tutmanın basit yolu hikayeyi dört kovaya bölmektir:
Hikaye hâlâ belirsizse bu soruları düz bir dille cevaplayın:
Erken kısıtlamalar ekleyin çünkü bunlar her düzen seçimini yönlendirir: tema temelleri (renkler, boşluk, tipografi), cevap verebilirlik (önce telefon portresi, sonra tablet), ve erişilebilirlik asgari koşulları (dokunma hedefi boyutu, okunabilir metin ölçekleme, ikonlar için anlamlı etiketler).
Son olarak, neyin kararlı neyin esnek olduğunu belirleyin ki kod tabanında gereksiz tekrarlar oluşmasın. Kararlı öğeler diğer özelliklerin dayandığı şeylerdir: rota isimleri, veri modelleri ve mevcut API’ler. Esnek öğeler ise düzen yapısı, mikro metin ve kesin widget bileşimi gibi üzerinde daha rahat deneme yapabileceğiniz öğelerdir.
Örnek: "Kullanıcı olarak detay ekranından bir öğeyi Favorilere kaydedebilmeliyim." Yapılabilir bir UI spesifikasyonu şu olabilir:
Bu, tahmin yapmadan inşa etmek, gözden geçirmek ve yinelemek için yeterlidir.
Küçük diff’ler daha yavaş çalışmakla ilgili değildir. Her UI değişikliğini incelemesi, geri alması ve bozmaması kolay hale getirir. En basit kural: yinelemede bir ekran ya da bir etkileşim olsun.
Başlamadan önce sıkı bir dilim seçin. "Orders ekranına boş durum ekle" iyi bir dilimdir. "Tüm Orders akışını yeniden düzenle" değil. Amaç, bir takım arkadaşının bir dakikada anlayabileceği bir diff elde etmektir.
Stabil bir klasör yapısı da değişikliklerin sınırda kalmasına yardımcı olur. Basit, özellik-öncelikli bir yapı widget’ları ve rotaları uygulama genelinde dağıtmaktan alıkoyar:
lib/
features/
orders/
screens/
widgets/
state/
routes.dart
Widget’ları küçük ve bileşik tutun. Bir widget net girişler ve çıkışlar aldığında, durumu etkilemeden düzeni değiştirebilir veya düzeni değiştirmeden durumu. Düz değerler ve callback’ler alan widget’ları tercih edin; global state okumayan.
İncelemesi kolay bir döngü:
Sert bir kural koyun: her değişiklik kolayca geri alınabilir veya izole edilebilir olmalı. Yineleme yaparken rastgele refaktörlerden kaçının. İlgisiz problemler görürseniz not alın ve ayrı bir commit’te düzeltin.
Araçlarınız snapshot ve rollback destekliyorsa, her dilimi bir snapshot noktası olarak kullanın. Koder.ai gibi bazı vibe-coding platformları snapshot ve rollback içerir; cesur UI denemelerini güvenli hale getirebilir.
Erken yinelemeleri sakin tutan bir başka alışkanlık: paylaşılan bileşenleri düzenlemek yerine yeni widget eklemeyi tercih edin. Paylaşılan bileşenler küçük değişikliklerin büyük diff’lere dönüşmesine neden olur.
Hızlı UI çalışması, düşünmeyi yazmaktan ayırdığında güvenli kalır. Kod üretmeden önce net bir widget ağacı planı çıkarın.
Sadece bir widget ağacı taslağı isteyin. Widget adları, hiyerarşi ve her parçanın ne gösterdiğini isteyin. Henüz kod yok. Bu aşamada eksik durumlar, boş ekranlar ve tuhaf düzen seçimleri ucuza yakalanır.
Sorumlulukları belirten bir bileşen dökümü isteyin. Her widget odaklı olsun: biri header render etsin, biri listeyi, diğeri boş/hata UI’sını. Bir şeyin daha sonra duruma ihtiyacı varsa şimdi not edin ama uygulamayın.
Ekran iskeleti ve stateless widget’lar oluşturun. Başlangıç için tek bir ekran dosyası, yer tutucu içerik ve net TODO’larla başlayın. Girişleri (constructor parametreleri) açık tutun ki gerçek durumu daha sonra plugin etmeden ağaç yeniden yazılmasın.
Stil ve düzen ayrık bir geçişte ekleyin: boşluk, tipografi, tema davranışı ve duyarlı davranış. Stil değişikliğini ayrı bir diff olarak ele alın ki incelemeler basit kalsın.
Asistanın uygulanamaz UI icat etmemesi için kısıtları başta koyun:
Somut örnek: kullanıcı hikayesi "Kullanıcı kaydedilmiş öğelerimi gözden geçirebilsin ve bir öğeyi kaldırabilsin." Bir app bar, öğe satırları olan bir liste ve boş durum içeren bir widget ağacı isteyin. Sonra SavedItemsScreen, SavedItemTile, EmptySavedItems gibi bir dağılım talep edin. Sadece ondan sonra stateless iskeleti sahte verilerle oluşturun ve son olarak stil (divider, padding, net kaldır butonu) için ayrı bir geçiş yapın.
Her widget karar vermeye başladığında UI yinelemesi bozulur. Widget ağacını aptal tutun: durumu okuyup render etsin, iş kurallarını barındırmasın.
Önce durumları düz bir dille adlandırın. Birçok özellik yalnızca "loading" ve "done" den daha fazlasına ihtiyaç duyar:
Sonra durumu değiştirebilecek olayları listeleyin: tap’ler, form gönderimi, pull-to-refresh, geri navigasyon, retry ve "kullanıcı bir alanı düzenledi". Bunu önceden yapmak tahminleri engeller.
Özellik için tek bir durum yaklaşımı seçin ve ona sadık kalın. Amaç "en iyi pattern" değil; tutarlı diff’lerdir.
Küçük bir ekran için basit bir controller (ChangeNotifier veya ValueNotifier gibi) sıklıkla yeterlidir. Mantığı tek bir yerde tutun:
Kodu eklemeden önce durum geçişlerini düz cümlelerle yazın. Örnek bir login ekranı için:
"Kullanıcı Sign in'e dokunduğunda: Loading olarak ayarla. E-posta geçersizse: Partial input’ta kal ve satır içi mesaj göster. Parola hatalıysa: Error ile mesaj göster ve Retry etkinleştir. Başarılıysa: Success olarak ayarla ve Home’a navigasyon yap."
Ardından bu cümlelere uyan minimal Dart kodunu üretin. İncelemeler basit kalır çünkü diff’i yazılı kurallarla karşılaştırabilirsiniz.
Doğrulamayı açık yapın. Girdiler geçersiz olduğunda ne olacağını karar verin:
Bu cevaplar yazıldığında UI temiz kalır ve durum kodu küçük kalır.
İyi navigasyon küçük bir harita olarak başlar, rotalar yığını olarak değil. Her kullanıcı hikayesi için dört anı yazın: kullanıcı nereden girer, en olası sonraki adım nedir, nasıl iptal eder ve "geri" ne anlama gelir (önceki ekrana mı dönülür yoksa güvenli ana duruma mı?).
Basit bir rota haritası genelde yeniden çalışmaya neden olan soruları yanıtlamalıdır:
Sonra ekranlar arasında hangi parametrelerin geçeceğini tanımlayın. Açık olun: ID’ler (productId, orderId), filtreler (tarih aralığı, durum), ve taslak veri (kısmi doldurulmuş form). Bunu atlarsanız bağlamı global singleton’lara tıkarsınız veya ekranları bağlam "bulmak" için yeniden inşa edersiniz.
Deep linkler bugün dağıtıma hazır olmasa bile önemlidir. Kullanıcı akışının ortasına inen bir kullanıcı geldiğinde ne olacağına karar verin: eksik veriyi yükleyebiliyor musunuz yoksa güvenli bir giriş ekranına mı yönlendiriyorsunuz?
Ayrıca hangi ekranların sonuç döndürmesi gerektiğini belirleyin. Örnek: "Adres Seç" ekranı bir addressId döndürür ve checkout ekranı tam yenileme yapmadan güncellenir. Döndürülen şekli küçük ve tipli tutun ki değişiklikler kolayca gözden geçirilebilsin.
Kodlamadan önce uç vakaları çağırın: kaydedilmemiş değişiklikler (onay diyaloğu göster), kimlik gerekli (login sonrası durumu devam ettir), eksik veya silinmiş veri (hata göster ve çıkış yolu sun).
Hızlı yineleme yaparken gerçek risk "yanlış UI" değil, "gözden geçirilmesi imkansız UI"dır. Bir ekip arkadaşı neyin değiştiğini, neden değiştiğini ve neyin sabit kaldığını söyleyemiyorsa, her sonraki yineleme yavaşlar.
Yardımcı bir kural: önce arayüzleri kilitleyin, sonra iç detayların hareket etmesine izin verin. Public widget prop’larını (girdi) sabitleyin, küçük UI modelleri ve rota argümanlarını sabitleyin. Bunlar adlandırılıp tiplenince widget ağacını şekillendirirken uygulamanın geri kalanını kırmadan yeniden düzenleyebilirsiniz.
Kod üretmeden önce diff-dostu bir plan isteyin. Hangi dosyaların değişeceğini ve hangilerinin dokunulmaması gerektiğini söyleyen bir plan, incelemeleri odaklı tutar ve istem dışı refaktörleri engeller.
Diff’leri küçük tutan desenler:
Hikaye: "Alışveriş yapan kullanıcı, checkout’tan gönderim adresini düzenleyebilsin." Önce rota argümanlarını kilitleyin: CheckoutArgs(cartId, shippingAddressId) sabit kalır. Sonra ekran içinde yineleme yapın. Düzen oturduktan sonra AddressForm, AddressSummary ve SaveBar olarak ayırın.
Durum yönetimi değişirse (ör. doğrulama widget’tan CheckoutController'a taşınırsa), inceleme okunaklı kalır: UI dosyaları çoğunlukla render değişikliğini gösterirken controller mantık değişikliğini tek bir yerde gösterir.
Hızlanmanın en hızlı yolunu yavaşlatan şey asistanın her şeyi birden değiştirmesini istemektir. Eğer bir commit düzeni, durumu ve navigasyonu birden değiştiriyorsa, inceleyenler neyin hataya neden olduğunu anlayamaz ve geri alma zorlaşır.
Daha güvenli bir alışkanlık: yinelemede bir amaç olsun: önce widget ağacını şekillendir, sonra durumu bağla, sonra navigasyonu bağla.
Oluşan hızlı sorunlardan biri üretilen kodun her sayfada yeni bir desen icat etmesidir. Biri Provider kullanırken, diğeri setState, üçüncü bir sayfa özel bir controller sınıfı tanıtıyorsa uygulama hızla tutarsızlaşır. Küçük bir pattern seti seçin ve ona sadık kalın.
Başka bir hata async işleri doğrudan build() içinde çalıştırmaktır. Hızlı demo için görünüşte iyi olabilir ama rebuild’lerde tekrarlanan çağrılara, flicker’a ve izlenmesi zor hatalara neden olur. Çağrıyı initState(), bir view model veya özel bir controller’a taşıyın; build() yalnızca render etsin.
İsimlendirme sessiz bir tuzaktır. Derlenen ama Widget1, data2 veya temp gibi adlar okuyanı zorlayan kod gelecekteki refaktörleri ağrılı kılar. Net isimler asistanın sonraki değişiklikleri daha iyi üretmesine de yardımcı olur çünkü niyet belli olur.
Kötü sonuçları önleyen gardrail’lar:
build() içinde ağ veya DB çağrısı yokGörsel bir hata düzeltmek için bir buton hizasını düzeltirken başka Container, Padding, Align, SizedBox ekleyerek ağacı okunamaz hale getirmek klasik bir hatadır.
Bir buton yanlış hizalanmışsa önce sarıcıları kaldırmayı, tek bir üst düzen widget’ı kullanmayı veya kendi kısıtlamalarıyla küçük bir widget çıkarmayı deneyin.
Örnek: toplam fiyatın yüklenirken zıpladığı bir checkout ekranı. Asistan fiyat satırını "stabilize etmek" için daha fazla sarıcı ekleyebilir. Daha temiz çözüm, satır yapısını değiştirmeden basit bir yükleme yer tutucusu ile alan ayırmaktır.
Commit etmeden önce iki dakikalık bir geçiş yapın: kullanıcı değerini kontrol edin ve sürpriz regresyonlardan korunun. Amaç mükemmellik değil; bu iterasyonun gözden geçirilmesi, test edilmesi ve geri alınmasının kolay olduğunu doğrulamaktır.
Kullanıcı hikayesini bir kez okuyun, sonra çalışan uygulama (veya basit bir widget testi) üzerinde aşağıyı doğrulayın:
Kısa bir gerçeklik kontrolü: yeni bir Order detay ekranı eklediyseniz, (1) listeden açılabildiğini, (2) loading spinner gösterdiğini, (3) hatayı simüle edip görebildiğinizi, (4) boş siparişi görebildiğinizi ve (5) geri bastığınızda listeye garip sıçramalar olmadan döndüğünüzü doğrulayabilmelisiniz.
Workflow’unuz snapshot/rollback destekliyorsa, daha büyük UI değişiklikleri öncesi snapshot alın. Koder.ai gibi bazı platformlar bunu destekler ve ana dalı riske atmadan daha hızlı yinelemenize yardımcı olabilir.
Kullanıcı hikayesi: "Alışveriş yapan kullanıcı öğeleri göz atabilsin, detay sayfasını açabilsin, öğeyi favorilere kaydedebilsin ve sonra favorilerimi görüntüleyebilsin." Amaç kelimelerden ekranlara üç küçük, gözden geçirilebilir adımda ilerlemek.
İterasyon 1: sadece tarama (browse) listesine odaklanın. Gerçek veriye bağlı olmayan ama render edebilen bir widget ağacı oluşturun: Scaffold ile AppBar, yer tutucu satırlardan oluşan ListView, loading ve empty durumları için net UI. Durumu basit tutun: loading (CircularProgressIndicator), empty (kısa mesaj + belki Try again butonu), ready (liste gösterilir).
İterasyon 2: detay ekranı ve navigasyonu ekleyin. Açıkça belirtin: onTap bir rota push eder ve küçük bir parametre objesi geçirir (ör. item id, title). Detay sayfasını başta salt okunur olarak bırakın: başlık, açıklama yer tutucusu ve Favorite aksiyon butonu. Amaç hikayeyle eşleşmek: liste -> detay -> geri, ekstra akış yok.
İterasyon 3: favorilere kaydetme durum güncellemelerini ve UI geri bildirimlerini ekleyin. Favoriler için tek bir doğruluk kaynağı (memory içinde bile olsa) ekleyin ve bunu her iki ekrana bağlayın. Favorite’a dokununca ikon hemen güncellensin ve küçük bir onay (SnackBar gibi) gösterilsin. Sonra aynı state’i okuyan bir Favorites ekranı ekleyin ve boş durumu yönetin.
Gözden geçirilebilir bir diff tipik olarak şöyle görünür:
browse_list_screen.dart: widget ağacı + loading/empty/ready UIitem_details_screen.dart: UI düzeni ve navigation param kabulüfavorites_store.dart: minimal state tutucu ve güncelleme metotlarıapp_routes.dart: rotalar ve tipli navigasyon yardımcılarıfavorites_screen.dart: durumu okur ve empty/list UI gösterirEğer herhangi bir dosya "her şeyin olduğu yer" haline gelirse, bir sonraki adıma geçmeden önce onu bölün. Küçük, net isimli dosyalar sonraki yinelemeyi hızlı ve güvenli kılar.
Eğer iş akışı yalnızca siz "odakta"yken çalışıyorsa, ekran değiştirince veya bir ekip arkadaşı özniteliklerine dokunduğunda bozulur. Döngüyü bir alışkanlık haline getirin: yazın ve değişiklik boyutu etrafında gardrail’lar koyun.
Her yineleme aynı girdilerle başlaması ve aynı tür çıktı üretmesi için takımca tek bir şablon kullanın. Kısa ama spesifik tutun:
Bu, asistanın özellik ortasında yeni pattern’ler icat etme şansını azaltır.
Her iterasyonu sınırlandırmak için inceleme sırasında uygulanabilir bir tanım seçin. Örnek kurallar:
Kötü bir adımı hızlı geri alabilmeniz için kontrol noktaları ekleyin. En azından commit’leri etiketleyin veya büyük refaktörlerden önce lokal checkpoint’ler saklayın. Workflow’unuz snapshot/rollback destekliyorsa bunları agresif kullanın.
Eğer sohbet tabanlı bir iş akışıyla uçtan uca Flutter uygulamaları üretebilen bir sistem istiyorsanız, Koder.ai planning mode’u planı ve beklenen dosya değişikliklerini gözden geçirmenize yardımcı olur.
Küçük, test edilebilir bir UI spesifikasyonu kullanın. 3–6 satırlık bir açıklama yazın ve şu konuları kapsayın:
Sonra yalnızca o dilimi (çoğunlukla bir ekran + 1–2 widget) oluşturun.
Hikayeyi dört bölüme dönüştürün:
Kabul kriterini hızlıca tanımlayamıyorsanız, hikaye temiz bir UI diff’i için hâlâ çok belirsizdir.
Önce yalnızca bir widget ağacı taslağı (isimler + hiyerarşi + her parçanın ne gösterdiği) üretin. Kod değil.
Sonra bir bileşen sorumluluk dökümü isteyin (hangi widget neyi yönetir).
Bunların ardından stateless iskeleti açık girişlerle (değerler + callback’ler) oluşturun; stil ve düzeni ayrı bir pass’te ekleyin.
Birdefa hangi amacı hedeflediğinizi sınırlandırın: her iterasyonda tek bir niyet. Örnek akış:
Tek bir commit’te düzen, durum ve rotalar birden değişirse, inceleyenler bir hatanın kaynağını bulamaz ve geri alma zorlaşır.
Widget’ları “aptal” tutun: render yapmalı, iş kurallarına karar vermemeli.
Pratik bir varsayılan:
Ayrıca build() içinde async çağrılardan kaçının—rebuild’lerde tekrar çalışır ve sorun çıkarır.
Ekranlarda kodlamadan önce durumları ve geçişlerini düz İngilizce ile yazın. Örnek desen:
Sonra bu durumlar arasında geçiş yapan olayları listeleyin (refresh, retry, submit, edit). Bu sayede kod yazıldıktan sonra davranışı yazılı kurallarla kolayca karşılaştırabilirsiniz.
Hikaye için küçük bir “akış haritası” yazın:
Ayrıca ekranlar arasında neyin taşınacağını kilitleyin: ID’ler (productId, orderId), filtreler, taslak veriler. Aksi halde bağlam globallerde saklanır veya ekranlar yeniden inşa edilir.
Özellik-öncelikli klasör yapısı, değişikliklerin dağılmasını engeller. Örnek:
lib/features/<feature>/screens/lib/features/<feature>/widgets/lib/features/<feature>/state/lib/features/<feature>/routes.dartHer iterasyonu tek bir feature klasörüne odaklayın ve başka yerlerde rastgele refaktörlerden kaçının.
Basit bir kural: arayüzleri sabitleyin, iç detayları değil.
İnceleyenler, layout değişse bile giriş/çıkışların sabit kaldığını görmeyi öncelikle önemser.
Kısa bir kontrol yapın:
Eğer workflow’ınız destekliyorsa (snapshot/rollback gibi), büyük düzenleme öncesi bir snapshot alın.