StatelessWidget
Widget tanpa state untuk tampilan statis: kapan dan cara memakainya.
Analogi: Stempel Karet
StatelessWidget itu seperti stempel karet. Kamu tekan ke tinta (parameter), cap ke kertas, hasilnya selalu sama untuk tinta yang sama. Stempel tidak "ingat" berapa kali ia dipakai, tidak menyimpan sisa tinta di dalamnya. Kalau mau hasil berbeda, kamu ganti tintanya (parent memberi parameter baru) lalu cap ulang.
Sebaliknya StatefulWidget itu seperti kalkulator: ia mengingat angka yang sedang tampil dan berubah setiap kamu menekan tombol. Modul ini tentang si stempel: widget yang tampilannya murni ditentukan oleh parameter yang diterimanya.
Kenapa Dipisah Jadi Stateless dan Stateful?
Kenapa Flutter tidak membuat satu jenis widget saja yang bisa menyimpan state? Karena pemisahan ini memberi dua keuntungan besar:
- Keterbacaan: melihat
StatelessWidget, kamu langsung tahu "widget ini tidak akan berubah sendiri". Tidak perlu menelusuri logika untuk memastikan. - Performa: framework tahu widget ini tidak punya state internal, jadi ia tidak perlu menyiapkan mekanisme pelacakan perubahan untuknya. Widget statis juga lebih mudah diberi
const, yang berarti dilewati saat rebuild.
Aturan praktis yang dipakai developer profesional: mulai selalu dari Stateless. Naik ke Stateful hanya saat ada data yang benar-benar berubah karena interaksi user (counter, form input, toggle).
Contoh 1: Widget Label Sederhana
Contoh paling murni: widget yang hanya menampilkan teks dengan gaya tertentu, tanpa data yang berubah:
class JudulHalaman extends StatelessWidget {
final String judul;
const JudulHalaman({super.key, required this.judul});
@override
Widget build(BuildContext context) {
return Text(
judul,
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
);
}
}Perhatikan tiga ciri khasnya: extends StatelessWidget, semua field final, dan constructor const. Tiga hal ini hampir selalu muncul bersamaan.
Contoh 2: Kartu Menu yang Dipakai Berulang
Sekarang contoh yang realistis: kartu item menu untuk aplikasi kasir. Widget ini menerima data lewat parameter, tidak menyimpan apa pun sendiri, dan dipakai puluhan kali di dalam daftar:
class KartuMenu extends StatelessWidget {
final String nama;
final int harga;
final bool tersedia;
const KartuMenu({
super.key,
required this.nama,
required this.harga,
this.tersedia = true,
});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: Icon(
Icons.restaurant_menu,
color: tersedia ? Colors.teal : Colors.grey,
),
title: Text(nama),
subtitle: Text(tersedia ? 'Rp$harga' : 'Stok habis'),
trailing: tersedia
? const Icon(Icons.add_circle_outline)
: const Icon(Icons.block, color: Colors.red),
),
);
}
}Pemakaiannya di dalam daftar:
ListView(
children: const [
KartuMenu(nama: 'Kopi Tubruk', harga: 8000),
KartuMenu(nama: 'Teh Manis', harga: 5000),
KartuMenu(nama: 'Indomie Goreng', harga: 10000, tersedia: false),
],
)Karena semua field final dan constructor const, seluruh daftar ini murah: Flutter membangunnya sekali dan tidak pernah membangun ulang sendiri. Kalau stok berubah, parent yang membangun ulang daftar dengan parameter baru (nanti, dengan state management yang proper).
Catatan teknis: Parameter
tersediapunya nilai defaulttrue, jadi pemakai boleh tidak mengisinya. Ini pola umum untuk parameter opsional. Parameterrequireddipakai untuk data yang tidak masuk akal kalau kosong, sepertinamadanharga.
Memakai Widget Buatanmu
const JudulHalaman(judul: 'Beranda')Karena judul final dan constructor const, widget ini murah dan tidak pernah rebuild sendiri. Letakkan widget buatanmu di file terpisah (misal lib/widgets/judul_halaman.dart) begitu project mulai membesar, supaya main.dart tetap ramping.
Kesalahan Umum Pemula
Salah: mencoba mengubah data di dalam StatelessWidget.
// BURUK: field tidak final, diubah di dalam build
class Penghitung extends StatelessWidget {
int angka = 0; // tidak final!
Penghitung({super.key});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
angka++; // mengubah field... tapi UI tidak pernah update!
},
child: Text('Klik: $angka'),
);
}
}Ini tidak akan pernah bekerja: menekan tombol mengubah variabel angka, tapi Flutter tidak tahu ia harus menggambar ulang, karena StatelessWidget tidak punya mekanisme setState. Teks tetap menampilkan 0 selamanya. Lebih parah lagi, widget bisa dibangun ulang kapan saja oleh framework dan nilaimu hilang tanpa jejak.
Benar: kalau datanya berubah karena interaksi, pakai StatefulWidget.
// BAIK: state disimpan di State, diubah lewat setState
class Penghitung extends StatefulWidget {
const Penghitung({super.key});
@override
State<Penghitung> createState() => _PenghitungState();
}
class _PenghitungState extends State<Penghitung> {
int angka = 0;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
setState(() {
angka++; // setState memberi tahu Flutter untuk build ulang
});
},
child: Text('Klik: $angka'),
);
}
}setState adalah caranya bilang ke Flutter: "dataku berubah, tolong panggil build lagi". Inilah garis pembatas yang jelas: data statis dari parent pakai Stateless, data yang berubah karena user pakai Stateful. Kalau kamu ragu widget-mu butuh yang mana, tanyakan: "apakah tampilannya bisa berubah tanpa parent memberi data baru?" Kalau ya, itu Stateful.
Tantangan
Kartu profil statis
Buat StatelessWidget bernama KartuProfil dengan parameter nama dan pekerjaan, yang menampilkan keduanya dalam sebuah Card + ListTile.