Stateful Widgets
Di dalam Flutter terdapat dua tipe widget yaitu stateless dan stateful widget. Stateless widget adalah widget statis atau tidak akan berubah, sedangkan stateful widget adalah widget yang dinamis atau dapat berubah.

Gambar di sebelah kiri adalah contoh stateless widgets, karena pada aplikasi tersebut, semua widget bersifat statis. Sedangkan pada gambar sebelah kanan, angka 0 akan berubah menjadi 1 jika tombol ditekan, artinya widget tersebut adalah stateful.
Dice
Kita akan membuat sebuah aplikasi dengan stateful widget seperti berikut ini.

Aplikasi diawali dengan menampilkan gambar dua buah dadu 1 titik. Saat salah satu dadu ditekan, maka kedua dadu akan menampilkan gambar dadu dengan jumlah titik secara acak.
New Project
Buat sebuah proyek Flutter baru dan sesuaikan kodenya seperti berikut ini
Buat folder images dan letakkan file gambar ini ke dalam folder images. Kemudian tambahkan folder images ke assets pada file pubspec.yaml.
Class DicePage
Pada baris 20 kita membuat objek DicePage namun kita belum membuat class DicePage. Karena gambar dadu pada aplikasi akan berubah saat ditekan, maka kita harus membuat class DicePage yang extend class StatefulWidget.
Buat class DicePage seperti dibawah ini. Untuk mempercepat penulisan kode, kita bisa menuliskan stlu pilih Flutter Stateful Widget dan tekan enter. Kemudian ubah MyWidget menjadi DicePage.
class DicePage extends StatefulWidget {
const DicePage({Key? key}) : super(key: key);
@override
State<DicePage> createState() => _DicePageState();
}
class _DicePageState extends State<DicePage> {
@override
Widget build(BuildContext context) {
}
}
Semua widget yang diletakkan pada baris ke 36 sifatnya adalah stateful, widget-widget tersebut akan dirender ulang setiap kali ada perubahan state.
Design
Kita akan membuat widget gambar dadu sebanyak 2 buah yang posisinya berada di tengah layar. Untuk itu kita memerlukan widget Center dan Row.
class _DicePageState extends State<DicePage> {
@override
Widget build(BuildContext context) {
return Center(
child: Row(
children: [
Image.asset('images/dice1.png'),
Image.asset('images/dice1.png'),
],
),
);
}
}
Karena gambar dadu terlalu besar, makan akan terjadi exception yang memberi tahu bahwa widget terlalu besar dan melewati batas layar.

Untuk memperbaiki itu kita bisa menggunakan widget Expanded. Widget ini akan menyesuaikan ukuran gambar dadu dan memenuhi ruang yang tersedia.
Expanded(child: Image.asset('images/dice1.png')),
Expanded(child: Image.asset('images/dice1.png')),

Untuk membuat jarak antar gambar dadu kita bisa menggunakan widget Container atau Padding dan mengatur properti paddingnya.
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Image.asset('images/dice1.png'),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Image.asset('images/dice1.png'),
),
),

Coding
Untuk membuat gambar dadu bisa ditekan, kita harus bungkus gambar dadu kedalam widget tombol. Widget yang kita gunakan adalah TextButton. Widget TextButton memiliki method onPressed yang digunakan untuk mengeksekusi kode program saat tombol ditekan.
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextButton(
onPressed: () {},
child: Image.asset('images/dice1.png'),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextButton(
onPressed: () {},
child: Image.asset('images/dice1.png'),
),
),
),
Berikutnya kita akan membuat dua buah variabel untuk menampung nilai masing-masing dadu dan sebuah fungsi untuk mengacak nilai dari 1 sampai 6.
class _DicePageState extends State<DicePage> {
var leftDice = 1;
var rightDice = 1;
void randomDice() {
leftDice = Random().nextInt(6) + 1;
rightDice = Random().nextInt(6) + 1;
}
@override
Widget build(BuildContext context) {
Karena kita menggunakan class Random maka kita perlu mengimpor library dart:math.
Ubah kode untuk menampilkan gambar dadu agar bisa berubah sesuai dengan nilai variabel leftDice dan rightDice.
menjadi
Image.asset('images/dice$leftDice.png') # dadu kiri
Image.asset('images/dice$rightDice.png') # dadu kanan
Pada stateful widget, Flutter akan merender ulang widget-widget saat ada perubahan state. Untuk mengubah state, kita gunakan fungsi setState() didalam method onPressed di TextButton. State yang kita ubah adalah mengubah data di variabel leftDice dan rightDice secara acak menggunakan fungsi randomDice().
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextButton(
onPressed: () {
setState(() {
randomDice();
});
},
child: Image.asset('images/dice$leftDice.png'),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: TextButton(
onPressed: () {
setState(() {
randomDice();
});
},
child: Image.asset('images/dice$rightDice.png'),
),
),
),
Karena ada state yang diubah, maka jalankan aplikasi dengan menekan tombol Restart bukan Hot Reload.

Final Code
Berikut ini adalah kode lengkap dari main.dart
Simple Calculator
Kita akan membuat kalkulator sederhana seperti berikut ini

Chalange
- Tambahkan tombol untuk tambah, kurang, kali dan bagi.
- Validasi input agar pengguna hanya bisa memasukkan angka.