Setiap kali saya memulai proyek aplikasi baru, selalu ada momen yang sama: layar kosong, folder kosong, dan pertanyaan klasik — dari mana mulai?
Setelah mengerjakan lebih dari belasan proyek Flutter — dari aplikasi utilitas kecil hingga platform dengan puluhan ribu pengguna aktif — saya akhirnya menemukan alur kerja yang konsisten dan bisa diandalkan. Bukan alur kerja yang sempurna, tapi alur kerja yang bekerja: menghasilkan kode yang bisa dimaintain, jadwal yang bisa diprediksi, dan APK yang siap dipublish tanpa kejutan di menit-menit terakhir.
Artikel ini adalah dokumentasi jujur dari alur kerja tersebut — termasuk keputusan yang kadang kontraintuitif dan kesalahan yang sudah saya bayar mahal.
Fase 0: Desain Keputusan Sebelum Menyentuh Kode
Ini fase yang paling sering dilewatkan developer yang baru mulai. Langsung buka VS Code, flutter create, dan mulai nulis widget. Dua minggu kemudian, refactor besar-besaran karena arsitektur awal tidak mendukung fitur yang ternyata dibutuhkan.
Saya belajar hal ini dengan cara yang menyakitkan.
Sekarang, sebelum baris kode pertama ditulis, saya selalu menjawab tiga pertanyaan ini terlebih dahulu:
1. Seberapa kompleks state aplikasi ini?
Kalau aplikasi hanya punya beberapa layar dengan data yang tidak saling bergantung, setState sudah lebih dari cukup. Tapi kalau ada data yang perlu dibagi antar widget yang tidak punya hubungan parent-child langsung, saya butuh solusi state management yang lebih terstruktur. Pilihan saya: Riverpod untuk proyek menengah ke atas, Provider untuk proyek kecil yang tim-nya sudah familiar.
2. Apakah aplikasi ini perlu berjalan offline?
Kalau ya, arsitektur data harus dirancang dari awal dengan asumsi koneksi tidak selalu ada. Ini bukan sesuatu yang bisa ditambahkan belakangan tanpa refactor besar. Saya biasanya menggunakan kombinasi Hive (untuk data lokal ringan) atau SQLite via Drift (untuk data relasional kompleks).
3. Seberapa penting performa di low-end device?
Kalau target pengguna adalah segmen menengah ke bawah Indonesia, saya langsung menerapkan aturan ketat dari awal: tidak ada package besar yang bisa digantikan implementasi custom, semua ListView wajib pakai builder, tidak ada gambar PNG yang bisa digantikan WebP atau CustomPainter.
Fase 1: Struktur Folder yang Tidak Akan Menyesal Anda Kemudian
Flutter tidak memaksakan struktur folder tertentu, dan itu sekaligus kelebihan dan jebakan. Tanpa struktur yang disengaja, proyek besar akan berakhir dengan ratusan file di folder lib/ yang tidak bisa dibaca siapapun — termasuk diri sendiri tiga bulan kemudian.
Struktur yang saya gunakan konsisten di semua proyek:
lib/
├── core/
│ ├── constants/ # Warna, ukuran, string konstan
│ ├── themes/ # ThemeData dan konfigurasi visual
│ ├── utils/ # Helper function, extension
│ └── errors/ # Custom exception class
├── data/
│ ├── models/ # Data class / entity
│ ├── repositories/ # Abstraksi akses data
│ └── sources/
│ ├── local/ # Hive, SQLite, SharedPreferences
│ └── remote/ # HTTP client, API service
├── presentation/
│ ├── pages/ # Layar utama (satu folder per fitur)
│ ├── widgets/ # Widget yang dipakai di banyak tempat
│ └── providers/ # State management
└── main.dart
Pemisahan antara data/ dan presentation/ bukan sekadar estetika. Ini memungkinkan saya mengganti sumber data (misalnya dari API ke lokal) tanpa menyentuh satu baris pun di layer presentation. Dan sebaliknya, saya bisa mengubah tampilan tanpa khawatir merusak logika pengambilan data.
Fase 2: Bangun Fondasi Dulu, Fitur Kemudian
Sebelum membangun fitur pertama, ada infrastruktur yang harus ada:
Router. Saya menggunakan GoRouter untuk semua proyek sekarang. Konfigurasi path yang deklaratif, dukungan deep linking otomatis, dan penanganan redirect yang bersih untuk alur autentikasi. Setup awal membutuhkan 30 menit, tapi menghemat waktu berhari-hari di kemudian hari.
Theme yang komprehensif. Semua warna, ukuran font, dan style komponen didefinisikan di ThemeData sebelum membangun satu layar pun. Kalau klien atau diri sendiri memutuskan mengganti palet warna di tengah proyek, perubahan hanya perlu dilakukan di satu tempat.
Error handling yang konsisten. Setiap operasi async punya kemungkinan gagal. Saya mendefinisikan custom exception class dan error widget yang konsisten di seluruh aplikasi sejak awal, bukan tambal sulam di akhir.
Fase 3: Pengembangan Fitur — Satu Vertikal per Waktu
Ini adalah prinsip yang paling mengubah cara kerja saya: selesaikan satu fitur secara vertikal sebelum pindah ke fitur berikutnya.
Artinya: untuk fitur "daftar artikel", saya menyelesaikan model datanya, repository-nya, provider-nya, dan tampilan layarnya — sampai bisa berjalan end-to-end — sebelum menyentuh fitur "detail artikel". Bukan mengerjakan semua model dulu, lalu semua repository, lalu semua tampilan.
Keuntungan praktisnya besar sekali. Setiap hari kerja menghasilkan sesuatu yang bisa dilihat dan dicoba. Bug yang muncul terlokalisir di fitur yang sedang dikerjakan. Dan kalau mendadak ada perubahan prioritas — yang hampir selalu terjadi — fitur yang sudah selesai benar-benar sudah selesai, tidak setengah-setengah.
Fase 4: Testing — Serius, Jangan Dilewat
Saya tidak akan berpura-pura bahwa saya selalu menulis unit test untuk setiap fungsi. Tidak. Tapi ada bagian kode yang tanpa test sama sekali, saya tidak akan berani deploy.
Prioritas testing saya:
Unit test wajib untuk: Logika bisnis yang tidak trivial (kalkulasi, transformasi data, validasi), repository class yang menangani data persistence, dan utility function yang dipakai di banyak tempat.
Widget test untuk: Komponen UI yang kompleks dengan banyak state (form dengan validasi, widget yang berubah berdasarkan kondisi).
Integration test untuk: Happy path dari fitur utama. Minimal satu test per user journey kritis.
Flutter menyediakan tooling testing yang sangat baik. Tidak ada alasan untuk tidak menggunakannya sama sekali.
Fase 5: Pre-Release Checklist
Ini daftar yang saya jalankan sebelum setiap rilis, tanpa kecuali:
- Versi
versionCodedanversionNamedibuild.gradlesudah diincrement - Semua API key production sudah diset (tidak ada kunci development yang ikut ke build release)
- ProGuard aktif dan sudah diuji tidak membreak fitur apapun
- APK diuji di minimal tiga perangkat: low-end (RAM 1 GB), mid-range, dan emulator API terbaru
- Deep link diuji dari luar aplikasi
- Skenario offline diuji (matikan koneksi, buka aplikasi, gunakan fitur offline)
- Screenshot Play Store sudah diupdate kalau ada perubahan UI signifikan
Daftar ini terasa berlebihan sampai Anda pernah rilis dengan kunci API development yang terbuka ke publik. Saya pernah. Tidak perlu pengalaman yang sama.
Penutup
Tidak ada alur kerja yang satu ukuran cocok untuk semua. Apa yang saya tulis di sini adalah hasil dari iterasi panjang — beberapa bagian lahir dari frustrasi proyek yang berantakan, beberapa dari membaca kode developer lain yang jauh lebih berpengalaman.
Yang paling penting bukan mengikuti alur kerja ini persis kata per kata, tapi memiliki alur kerja yang disengaja. Keputusan yang dibuat secara sadar selalu menghasilkan kode yang lebih baik dari keputusan yang dibuat karena kebiasaan atau karena tidak sempat berpikir.
Dan itu berlaku untuk pengembangan aplikasi maupun hal lain dalam hidup.
Belum ada komentar. Jadilah yang pertama menulis.
Tulis Komentar