Episode ini membangun fondasi tampilan Flutter: perbedaan StatelessWidget dan StatefulWidget, layout dasar dengan Row, Column, Stack, dan Container, pengaturan spacing, alignment, dan constraints, serta widget composition dan builder patterns untuk membangun UI yang reusable.

Project pertama kalian sudah berjalan di episode 3. Sekarang waktunya memahami bahan dasar pembentuk UI Flutter: widget. Hampir semua hal di Flutter adalah widget — teks, tombol, jarak, bahkan layout itu sendiri. Episode 4 membedah empat hal mendasar: kapan memakai StatelessWidget dan StatefulWidget, layout dasar dengan Row, Column, Stack, dan Container, cara mengatur spacing, alignment, dan constraints, serta widget composition dan builder patterns untuk membangun UI yang bersih dan reusable.
Flutter membagi widget menjadi dua keluarga:
class JudulStatis extends StatelessWidget {
const JudulStatis({super.key});
@override
Widget build(BuildContext context) {
return const Text('Ini tidak berubah');
}
}
class TombolBerubah extends StatefulWidget {
const TombolBerubah({super.key});
@override
State<TombolBerubah> createState() => _TombolBerubahState();
}
class _TombolBerubahState extends State<TombolBerubah> {
bool _ditekan = false;
@override
Widget build(BuildContext context) {
return Text(_ditekan ? 'Ditekan' : 'Belum');
}
}Aturan praktis: mulai dengan StatelessWidget. Ubah ke StatefulWidget hanya ketika ada data yang harus berubah dan memicu build ulang — keputusan yang menekan biaya rebuild sejak awal.
Dua widget layout paling sering dipakai:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Baris pertama'),
SizedBox(height: 8),
Text('Baris kedua'),
],
)SizedBox(height: 8) menyisipkan jarak vertikal antar teks. crossAxisAlignment: CrossAxisAlignment.start menyejajarkan children ke kiri pada sumbu silang.
Stack(
children: [
Container(
width: 200,
height: 200,
color: Colors.blue,
),
Positioned(
left: 16,
top: 16,
child: Container(
padding: EdgeInsets.all(8),
color: Colors.white,
child: const Text('Badge'),
),
),
],
)Positioned menempatkan child pada posisi relatif terhadap Stack. EdgeInsets.all(8) menambahkan padding seragam di dalam Container, dan kombinasi keduanya adalah pola umum untuk badge overlay.
Di dalam Row atau Column, SizedBox memberi jarak tetap, sedangkan Spacer menyerap sisa ruang:
Row(
children: [
const Text('Kiri'),
Spacer(),
const Text('Kanan'),
],
)Spacer tanpa parameter memakai flex default sehingga mendorong Text('Kanan') ke ujung kanan. Ini pola cepat untuk menyusun baris dua sisi.
Setiap widget menerima constraints dari parent dan memberi ukuran hasil ke parent. Memahami alur ini membantu men-debug layout aneh:
mainAxisAlignment — posisi children di sepanjang sumbu utama.crossAxisAlignment — posisi children di sepanjang sumbu silang.MainAxisAlignment.spaceEvenly membagi ruang kosong secara merata di antara dan di sekitar children, sedangkan spaceBetween memusatkan pembagian di tengah sehingga ujung-ujungnya menempel ke tepi.
Flutter mendorong komposisi: bangun widget kompleks dari widget sederhana, bukan menurunkan subclass. Kebiasaan yang harus ditanam sejak awal adalah memecah build besar menjadi widget-widget kecil yang reusable, seperti contoh berikut:
class AvatarCard extends StatelessWidget {
const AvatarCard({super.key, required this.nama});
final String nama;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: const CircleAvatar(child: Icon(Icons.person)),
title: Text(nama),
),
);
}
}AvatarCard mengemas Card, ListTile, dan CircleAvatar menjadi satu widget bernama. Parameter required this.nama membuat widget ini terasa seperti API yang jelas — pola yang akan kalian pakai terus sepanjang series.
Beberapa widget memakai builder function alih-alih children tetap, misalnya ListView.builder untuk daftar panjang:
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item $index'));
},
)ListView.builder hanya membangun item yang terlihat di layar — efisien untuk daftar ratusan atau ribuan entri, dan menjadi dasar yang akan kita pakai saat membangun aplikasi nyata.
Inti yang harus dibawa pulang:
SizedBox memberi jarak tetap; Spacer menyerap ruang sisa.ListView.builder mem-build item secara lazy untuk daftar panjang.Di episode 5 selanjutnya kita membahas input, forms, dan interaction — handling user input dengan TextField, buttons, dan gestures, form validation dan controllers, navigation dan routing sederhana, serta penggunaan SnackBar, dialogs, dan modal bottom sheets. Kalian mulai membuat aplikasi yang benar-benar interaktif.