Claude Code untuk iterasi UI Flutter: loop praktis untuk mengubah user story menjadi widget tree, state, dan navigasi sambil menjaga perubahan modular dan mudah direview.

Pekerjaan UI Flutter yang cepat sering kali dimulai dengan baik. Anda mengubah sedikit layout, menambahkan tombol, memindahkan sebuah field, dan layar jadi lebih baik dengan cepat. Masalah muncul setelah beberapa putaran, ketika kecepatan berubah menjadi tumpukan perubahan yang tak seorang pun mau review.
Tim biasanya menemui kegagalan yang sama:
Salah satu penyebab besar adalah pendekatan "satu prompt besar": jelaskan seluruh fitur, minta satu set layar penuh, dan terima keluaran besar. Asisten mencoba membantu, tapi menyentuh terlalu banyak bagian kode sekaligus. Itu membuat perubahan berantakan, sulit direview, dan berisiko di-merge.
Loop yang dapat diulang memperbaiki ini dengan memaksa kejelasan dan membatasi radius dampak. Alih-alih "bangun fitur," lakukan ini berulang: pilih satu user story, hasilkan irisan UI terkecil yang membuktikannya, tambahkan hanya state yang dibutuhkan untuk irisan itu, lalu hubungkan navigasi untuk satu jalur. Setiap putaran tetap cukup kecil untuk direview, dan kesalahan mudah dibalik.
Tujuannya adalah alur kerja praktis untuk mengubah user story menjadi layar konkret, penanganan state, dan alur navigasi tanpa kehilangan kendali. Jika dilakukan dengan baik, Anda akan berakhir dengan potongan UI modular, diff yang lebih kecil, dan lebih sedikit kejutan saat persyaratan berubah.
User story ditulis untuk manusia, bukan untuk widget tree. Sebelum Anda menghasilkan apa pun, ubah story menjadi spesifikasi UI kecil yang menggambarkan perilaku yang terlihat. "Selesai" harus dapat diuji: apa yang pengguna lihat, ketuk, dan konfirmasi, bukan apakah desain "terlihat modern."
Cara sederhana untuk menjaga cakupan konkret adalah membagi story menjadi empat bagian:
Jika story masih terasa kabur, jawab pertanyaan ini dengan bahasa biasa:
Tambahkan constraint sejak awal karena itu memandu setiap pilihan layout: dasar tema (warna, spasi, tipografi), responsivitas (prioritaskan ponsel potret dulu, lalu lebar tablet), dan minimum aksesibilitas seperti ukuran target ketuk, skala teks yang dapat dibaca, dan label bermakna untuk ikon.
Akhirnya, putuskan apa yang stabil vs fleksibel agar Anda tidak mengocok basis kode. Item stabil adalah hal-hal yang bergantung oleh fitur lain, seperti nama rute, model data, dan API yang ada. Item fleksibel lebih aman untuk diiterasi, seperti struktur layout, microcopy, dan komposisi widget tepatnya.
Contoh: "Sebagai pengguna, saya bisa menyimpan item ke Favorit dari layar detail." Spesifikasi UI yang bisa dibangun bisa berupa:
Itu cukup untuk membangun, mereview, dan mengiterasi tanpa menebak-nebak.
Diff kecil bukan soal bekerja lebih lambat. Mereka membuat setiap perubahan UI mudah direview, mudah dibatalkan, dan sulit untuk merusak. Aturan paling sederhana: satu layar atau satu interaksi per iterasi.
Pilih irisan yang ketat sebelum memulai. "Tambahkan empty state ke layar Orders" adalah irisan yang baik. "Ubah seluruh flow Orders" bukan. Usahakan diff yang bisa dipahami rekan dalam satu menit.
Struktur folder yang stabil juga membantu menjaga perubahan tetap terbungkus. Layout sederhana berfokus-fitur mencegah Anda menyebarkan widget dan rute ke seluruh aplikasi:
lib/
features/
orders/
screens/
widgets/
state/
routes.dart
Jaga widget tetap kecil dan terkomposisi. Ketika widget punya input dan output yang jelas, Anda bisa mengubah layout tanpa menyentuh logika state, dan mengubah state tanpa menulis ulang UI. Utamakan widget yang menerima nilai sederhana dan callback, bukan state global.
Loop yang tetap bisa direview:
Tetapkan aturan keras: setiap perubahan harus mudah dibalik atau diisolasi. Hindari refactor sambil lalu saat mengiterasi sebuah layar. Jika Anda melihat masalah yang tidak terkait, catat dan perbaiki dalam commit terpisah.
Jika alat Anda mendukung snapshot dan rollback, gunakan setiap irisan sebagai titik snapshot. Beberapa platform vibe-coding seperti Koder.ai menyertakan snapshot dan rollback, yang bisa membuat eksperimen lebih aman ketika Anda mencoba perubahan UI berani.
Satu kebiasaan lagi yang menenangkan iterasi awal: pilih menambahkan widget baru daripada mengedit yang shared. Komponen bersama adalah tempat perubahan kecil berubah menjadi diff besar.
Pekerjaan UI cepat tetap aman ketika Anda memisahkan berpikir dari mengetik. Mulai dengan mendapatkan rencana widget tree yang jelas sebelum menghasilkan kode.
Mintalah outline widget tree saja. Anda menginginkan nama widget, hierarki, dan apa yang ditampilkan tiap bagian. Tidak ada kode dulu. Di sini Anda akan menangkap state yang hilang, layar kosong, dan pilihan layout yang aneh saat semuanya masih murah untuk diubah.
Minta breakdown komponen dengan tanggung jawab. Jaga setiap widget fokus: satu widget merender header, yang lain merender list, yang lain menangani empty/error UI. Jika sesuatu butuh state nanti, catat sekarang tapi jangan implementasikan.
Hasilkan scaffold layar dan widget stateless. Mulai dengan satu file layar dengan konten placeholder dan TODO yang jelas. Jaga input eksplisit (constructor params) sehingga Anda bisa memasang state nyata nanti tanpa menulis ulang tree.
Lakukan pass terpisah untuk styling dan detail layout: spasi, tipografi, theming, dan responsivitas. Perlakukan styling sebagai diff tersendiri agar review tetap sederhana.
Letakkan constraint di depan agar asisten tidak mengarang UI yang tidak bisa Anda kirimkan:
Contoh konkret: user story adalah "As a user, I can review my saved items and remove one." Minta widget tree yang mencakup app bar, list dengan baris item, dan empty state. Lalu minta breakdown seperti SavedItemsScreen, SavedItemTile, EmptySavedItems. Hanya setelah itu, hasilkan scaffold dengan widget stateless dan data palsu, dan akhirnya tambahkan styling (divider, padding, dan tombol remove yang jelas) di pass terpisah.
Iterasi UI runtuh ketika setiap widget mulai mengambil keputusan. Jaga widget tree tetap dumb: ia harus membaca state dan merender, bukan mengandung aturan bisnis.
Mulailah dengan menamai state dalam kata-kata biasa. Kebanyakan fitur butuh lebih dari "loading" dan "done":
Lalu daftarkan event yang bisa mengubah state: tap, submit form, pull-to-refresh, back navigation, retry, dan "user mengedit field." Menulis ini di depan mencegah tebak-tebakan nanti.
Pilih satu pendekatan state untuk fitur dan tetap konsisten. Tujuannya bukan "pola terbaik," melainkan diff yang konsisten.
Untuk layar kecil, controller sederhana (seperti ChangeNotifier atau ValueNotifier) sering cukup. Letakkan logika di satu tempat:
Sebelum menulis kode, tulis transisi state dalam bahasa biasa. Contoh untuk layar login:
"Saat user mengetuk Sign in: set Loading. Jika email tidak valid: tetap di Partial input dan tunjukkan pesan inline. Jika password salah: set Error dengan pesan dan aktifkan Retry. Jika sukses: set Success dan navigasi ke Home."
Lalu hasilkan kode Dart minimal yang cocok dengan kalimat-kalimat itu. Review tetap sederhana karena Anda dapat membandingkan diff dengan aturan tertulis.
Jadikan validasi eksplisit. Putuskan apa yang terjadi ketika input tidak valid:
Saat jawaban-jawaban ini tertulis, UI Anda tetap bersih dan kode state tetap kecil.
Navigasi yang baik dimulai sebagai peta kecil, bukan tumpukan rute. Untuk setiap user story, tulis empat momen: dari mana pengguna masuk, langkah paling mungkin berikutnya, bagaimana mereka membatalkan, dan apa arti "back" (kembali ke layar sebelumnya, atau kembali ke home aman).
Peta rute sederhana harus menjawab pertanyaan yang biasanya menyebabkan rework:
Lalu tentukan parameter yang dikirim antar layar. Jelas: ID (productId, orderId), filter (rentang tanggal, status), dan draft data (form yang belum lengkap). Jika Anda melewatkan ini, Anda akan berakhir menyimpan state di singletons global atau membangun ulang layar untuk "mencari" konteks.
Deep link penting meski tidak Anda rilis hari pertama. Putuskan apa yang terjadi ketika pengguna mendarat di tengah flow: bisakah Anda memuat data yang hilang, atau harus mengarahkan ke layar masuk yang aman?
Juga putuskan layar mana yang harus mengembalikan hasil. Contoh: layar "Select Address" mengembalikan addressId, dan layar checkout memperbarui tanpa refresh penuh. Jaga bentuk hasil kecil dan bertipe agar perubahan tetap mudah direview.
Sebelum menulis kode, sebutkan edge case: perubahan yang belum disimpan (tunjukkan dialog konfirmasi), auth diperlukan (jeda dan lanjutkan setelah login), dan data yang hilang atau dihapus (tunjukkan error dan jalur keluar yang jelas).
Saat Anda iterasi cepat, risiko sebenarnya bukan "UI salah." Melainkan UI yang tidak bisa direview. Jika rekan tidak bisa tahu apa yang berubah, kenapa berubah, dan apa yang tetap stabil, setiap iterasi berikutnya melambat.
Aturan yang membantu: kunci interface dulu, lalu izinkan internals bergerak. Stabilkan props public widget (input), model UI kecil, dan argumen rute. Setelah itu dinamai dan bertipe, Anda bisa merombak widget tree tanpa merusak bagian lain aplikasi.
Minta rencana yang ramah-diff sebelum menghasilkan kode. Anda ingin rencana yang menyebut file mana yang akan berubah dan mana yang harus tetap utuh. Itu menjaga review fokus dan mencegah refactor tidak sengaja mengubah perilaku.
Pola yang menjaga diff kecil:
Misal user story: "As a shopper, I can edit my shipping address from checkout." Kunci argumen rute dulu: CheckoutArgs(cartId, shippingAddressId) tetap stabil. Lalu iterasi di dalam layar. Saat layout stabil, bagi menjadi AddressForm, AddressSummary, dan SaveBar.
Jika penanganan state berubah (mis. validasi pindah dari widget ke CheckoutController), review tetap terbaca: file UI sebagian besar hanya berubah rendering, sementara controller menunjukkan perubahan logika di satu tempat.
Cara tercepat memperlambat adalah meminta asisten mengubah semuanya sekaligus. Jika satu commit menyentuh layout, state, dan navigasi, reviewer tidak bisa tahu apa yang merusak, dan rollback jadi sulit.
Kebiasaan yang lebih aman adalah satu intent per iterasi: bentuk widget tree, lalu wiring state, lalu hubungkan navigasi.
Satu masalah umum adalah membiarkan kode yang digenerate menciptakan pola baru di setiap layar. Jika satu halaman menggunakan Provider, halaman berikutnya setState, dan ketiga memperkenalkan controller custom, aplikasi menjadi tidak konsisten cepat. Pilih sekumpulan pola kecil dan patuhi.
Kesalahan lain menempatkan pekerjaan async langsung di build(). Mungkin terlihat oke pada demo cepat, tapi memicu panggilan berulang pada rebuild, flicker, dan bug sulit dilacak. Pindahkan panggilan ke initState(), view model, atau controller khusus, dan jaga build() fokus pada rendering.
Penamaan adalah jebakan sunyi. Kode yang dapat dikompilasi tapi terbaca seperti Widget1, data2, atau temp membuat refactor masa depan menyakitkan. Nama yang jelas juga membantu asisten menghasilkan perubahan lanjutan karena maksud menjadi jelas.
Pengaman yang mencegah hasil terburuk:
build()Perbaikan visual klasik adalah menambahkan lagi Container, Padding, Align, dan SizedBox sampai terlihat benar. Setelah beberapa pass, tree menjadi tidak terbaca.
Jika sebuah tombol salah posisi, coba hapus wrapper, gunakan satu widget layout induk, atau ekstrak widget kecil dengan constraint-nya sendiri.
Contoh: layar checkout di mana total harga melonjak saat loading. Asisten mungkin membungkus row harga dengan lebih banyak widget untuk "menstabilkannya." Perbaikan yang lebih bersih adalah menyediakan ruang dengan placeholder loading sederhana sambil menjaga struktur row tetap sama.
Sebelum commit, lakukan pemeriksaan dua menit yang memeriksa nilai pengguna dan melindungi Anda dari regresi mengejutkan. Tujuannya bukan kesempurnaan. Melainkan memastikan iterasi ini mudah direview, mudah diuji, dan mudah dibatalkan.
Baca user story sekali, lalu verifikasi item ini terhadap app berjalan (atau setidaknya terhadap widget test sederhana):
Pemeriksaan realitas singkat: jika Anda menambahkan layar Order details baru, Anda harus bisa (1) membukanya dari daftar, (2) melihat spinner loading, (3) mensimulasikan error, (4) melihat order kosong, dan (5) tekan back untuk kembali ke daftar tanpa loncatan aneh.
Jika workflow Anda mendukung snapshot dan rollback, ambil snapshot sebelum perubahan UI besar. Beberapa platform seperti Koder.ai mendukung ini, dan bisa membantu iterasi lebih cepat tanpa mempertaruhkan branch utama.
User story: "As a shopper, I can browse items, open a details page, save an item to favorites, and later view my favorites." Tujuannya adalah bergerak dari kata-kata ke layar dalam tiga langkah kecil yang bisa direview.
Iterasi 1: fokus hanya pada layar browse list. Buat widget tree cukup untuk render tapi belum terikat data nyata: Scaffold dengan AppBar, ListView berisi baris placeholder, dan UI jelas untuk state loading dan empty. Jaga state sederhana: loading (tampilkan CircularProgressIndicator), empty (tampilkan pesan singkat dan tombol Try again), dan ready (tampilkan list).
Iterasi 2: tambahkan layar detail dan navigasi. Jaga eksplisit: onTap push rute dan mengirim objek parameter kecil (mis. id item, title). Mulai halaman detail sebagai read-only dengan title, placeholder deskripsi, dan tombol Favorite. Tujuannya mencocokkan story: list -> details -> back, tanpa alur tambahan.
Iterasi 3: perkenalkan update state favorites dan umpan balik UI. Tambahkan single source of truth untuk favorites (meski masih in-memory), dan hubungkan ke kedua layar. Ketuk Favorite memperbarui ikon segera dan menunjukkan konfirmasi kecil (mis. SnackBar). Lalu tambahkan layar Favorites yang membaca state yang sama dan menangani empty state.
Diff yang dapat direview biasanya terlihat seperti ini:
browse_list_screen.dart: widget tree plus UI loading/empty/readyitem_details_screen.dart: layout UI dan menerima param navigasifavorites_store.dart: penampung state minimal dan method updateapp_routes.dart: rute dan helper navigasi bertipefavorites_screen.dart: membaca state dan menampilkan empty/list UIJika satu file menjadi "tempat di mana semuanya terjadi," pisahkan sebelum melangkah. File kecil dengan nama jelas membuat iterasi berikutnya cepat dan aman.
Jika workflow hanya bekerja ketika Anda "sedang fokus," itu akan runtuh ketika Anda berganti layar atau rekan menyentuh fitur. Jadikan loop sebuah kebiasaan dengan menuliskannya dan memberi pengaman pada ukuran perubahan.
Gunakan satu template tim sehingga setiap iterasi dimulai dengan input yang sama dan menghasilkan keluaran jenis yang sama. Buat singkat tapi spesifik:
Ini mengurangi kemungkinan asisten mengarang pola baru di tengah fitur.
Pilih definisi kecil yang mudah ditegakkan di review kode. Misalnya, batasi setiap iterasi ke jumlah file tertentu, dan pisahkan refactor UI dari perubahan perilaku.
Sekumpulan aturan sederhana:
Tambahkan checkpoint agar Anda bisa membatalkan langkah buruk dengan cepat. Minimal, tag commit atau simpan checkpoint lokal sebelum refactor besar. Jika workflow mendukung snapshot/rollback, gunakan secara agresif.
Jika Anda ingin alur berbasis chat yang bisa menghasilkan dan menyempurnakan aplikasi Flutter end-to-end, Koder.ai memiliki mode perencanaan yang membantu meninjau rencana dan perubahan file yang diharapkan sebelum menerapkannya.
Gunakan spesifikasi UI kecil yang dapat diuji terlebih dahulu. Tulis 3–6 baris yang mencakup:
Lalu bangun hanya slice itu (seringkali satu layar + 1–2 widget).
Ubah user story menjadi empat bagian:
Jika Anda tidak bisa menjelaskan acceptance check dengan cepat, story itu masih terlalu kabur untuk menghasilkan diff UI yang bersih.
Mulai dengan menghasilkan hanya outline widget tree (nama + hierarki + apa yang ditampilkan tiap bagian). Tanpa kode.
Lalu minta rincian tanggung jawab komponen (apa yang dipegang tiap widget).
Baru setelah itu, hasilkan scaffold stateless dengan input eksplisit (nilai + callback), dan lakukan styling di tahap terpisah.
Anggap ini aturan keras: satu niat per iterasi.
Jika satu commit mengubah layout, state, dan rute sekaligus, reviewer tidak akan tahu apa yang menyebabkan bug dan rollback jadi berantakan.
Buat widget tetap “bodoh”: mereka harus render state, bukan mengeksekusi aturan bisnis.
Default praktis:
Jangan meletakkan panggilan async di build()—itu menyebabkan pemanggilan berulang saat rebuild.
Definisikan state dan transisinya dengan bahasa biasa sebelum menulis kode.
Contoh pola:
Lalu daftarkan event yang memindahkan antar state (refresh, retry, submit, edit). Kode jadi lebih mudah dibandingkan dengan aturan tertulis itu.
Tulis peta kecil alur untuk story:
Gunakan struktur folder berfokus-fitur agar perubahan tetap terkandung. Contoh:
lib/features/<feature>/screens/lib/features/<feature>/widgets/lib/features/<feature>/state/lib/features/<feature>/routes.dartFokuskan setiap iterasi pada satu folder fitur dan hindari refactor sambil lalu di tempat lain.
Aturan sederhana: stabilkan interface, bukan internals.
Reviewer paling peduli bahwa input/output tetap stabil walau layout berubah.
Lakukan pemeriksaan singkat 2 menit:
Jika workflow mendukung snapshot/rollback, ambil snapshot sebelum refactor layout besar agar mudah mengembalikan keadaan.
Juga kunci apa yang melintas antar layar (ID, filter, draft) agar tidak menyimpan konteks di global.