1. Introduction
A. Latar Belakang
Widget merupakan unit dasar pembentuk antarmuka pada kerangka kerja Flutter, sehingga setiap elemen visual yang tampil di layar, baik berupa teks, tombol, maupun tata letak, dibangun dari susunan widget yang saling bersarang. Widget pada Flutter terbagi menjadi dua jenis, yaitu StatelessWidget yang tampilannya tetap setelah dirender dan StatefulWidget yang tampilannya dapat berubah melalui pemanggilan setState sebagai respons terhadap interaksi pengguna atau perubahan data. Pemahaman terhadap perbedaan kedua jenis widget tersebut menjadi dasar dalam menentukan struktur aplikasi, karena kesalahan memilih jenis widget dapat menyebabkan data yang seharusnya berubah tidak tampil secara langsung pada layar.
Setelah struktur dasar aplikasi terbentuk melalui widget, tahap selanjutnya adalah menyusun dan menata widget-widget tersebut melalui proses layouting dan styling. Widget Row dan Column digunakan untuk mengatur posisi elemen secara horizontal dan vertikal, widget Container digunakan untuk membungkus elemen dengan pengaturan padding, margin, dan warna, sementara widget ListTile digunakan untuk menyusun baris daftar secara instan melalui bagian leading, title, dan trailing. Pengaturan properti seperti mainAxisAlignment dan crossAxisAlignment menentukan bagaimana jarak dan perataan antarelemen terbentuk, sehingga tampilan aplikasi menjadi terstruktur dan mudah dibaca oleh pengguna.
Pemahaman terhadap konsep widget beserta prinsip layouting dan styling menjadi penting karena kesalahan penyusunan struktur dapat menyebabkan tampilan tidak proporsional, elemen saling tumpang tindih, atau tampilan meluap (overflow) saat data yang ditampilkan bertambah panjang, seperti pada daftar transaksi yang jumlahnya tidak tetap. Kesalahan tersebut umumnya baru terlihat ketika aplikasi dijalankan pada perangkat dengan ukuran layar yang berbeda, sehingga pemahaman konsep sejak tahap perancangan struktur widget menjadi penentu kestabilan tampilan aplikasi secara keseluruhan. Berdasarkan hal tersebut, dilakukan praktikum pengenalan widget beserta penerapan layouting dan styling pada aplikasi Flutter, mencakup pembuatan widget statis dan dinamis, penyusunan tata letak menggunakan Row, Column, dan ListTile, serta penerapan interaktivitas melalui setState.
B. Rumusan Masalah
Berdasarkan latar belakang, dan modul praktikum yang telah disediakan, berikut adalah rumusan masalah pengerjaan praktikum:
- Bagaimana perbedaan penerapan StatelessWidget dan StatefulWidget dalam membangun struktur aplikasi Flutter?
- Bagaimana widget MaterialApp dan Scaffold membentuk kerangka dasar aplikasi Flutter?
- Bagaimana fungsi setState diterapkan untuk membuat tampilan antarmuka bersifat interaktif?
- Bagaimana widget Row dan Column digunakan untuk mengatur tata letak elemen secara horizontal dan vertikal?
- Bagaimana widget ListTile digunakan untuk menyusun baris daftar transaksi secara terstruktur?
- Bagaimana widget SingleChildScrollView mengatasi tampilan yang meluap ketika daftar data bertambah panjang?
C. Tujuan
Berdasarkan rumusan masalah yang telah dijabarkan, berikut adalah tujuan pelaksanaan praktikum:
- Menerapkan StatelessWidget dan StatefulWidget dalam membangun struktur aplikasi Flutter.
- Menerapkan widget MaterialApp dan Scaffold dalam membentuk kerangka dasar aplikasi Flutter.
- Menerapkan fungsi setState untuk membuat tampilan antarmuka bersifat interaktif.
- Menerapkan penggunaan widget Row dan Column dalam mengatur tata letak elemen secara horizontal dan vertikal.
- Menerapkan penggunaan widget ListTile dalam menyusun baris daftar transaksi secara terstruktur.
- Menerapkanpenerapan widget SingleChildScrollView dalam mengatasi tampilan yang meluap ketika daftar data bertambah panjang.
2. Methodology
1. Praktikum 1
A. Membuat Widget Sapaan (Stateless)
- Dibuat kelas GreetingWidget sebagai turunan StatelessWidget.
- Pada method build, dikembalikan widget Row dengan properti mainAxisAlignment bernilai MainAxisAlignment.spaceBetween, berisi dua elemen pada properti children.
- Elemen pertama berupa widget Column dengan properti crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun dua widget Text bertuliskan "Halo, Budi!" dan "Selamat datang kembali" secara vertikal.
- Elemen kedua berupa widget CircleAvatar yang properti child-nya diisi widget Icon menggunakan ikon Icons.person.

B. Membuat Kartu Saldo (Stateful)
- Dibuat kelas BalanceCard sebagai turunan StatefulWidget, dengan method createState yang mengembalikan objek _BalanceCardState.
- Dibuat kelas _BalanceCardState sebagai turunan State<BalanceCard>, dengan variabel _isObscure bertipe bool bernilai awal false dan variabel _balance bertipe String bernilai tetap "Rp 5.000.000".
- Method build pada kelas _BalanceCardState didefinisikan untuk mengembalikan tampilan kartu saldo, dilanjutkan pada langkah berikutnya.

C. Menerapkan Interaktivitas (setState)
- Pada method build, dikembalikan widget Card dengan properti color bernilai Colors.blueAccent, dan properti child diisi widget Row dengan mainAxisAlignment bernilai MainAxisAlignment.spaceBetween, berisi dua elemen pada properti children.
- Elemen pertama berupa widget Text yang isinya ditentukan melalui ekspresi ternary, menampilkan teks "Rp *********" apabila nilai _isObscure bernilai true, atau menampilkan nilai _balance apabila _isObscure bernilai false.
- Elemen kedua berupa widget IconButton yang ikonnya juga ditentukan melalui ekspresi ternary, menampilkan ikon Icons.visibility_off apabila _isObscure bernilai true, atau ikon Icons.visibility apabila _isObscure bernilai false. Pada properti onPressed, dipanggil setState untuk membalik nilai _isObscure melalui operator negasi, sehingga tampilan saldo dan ikon diperbarui setiap tombol ditekan.

D. Full Code dan Tampilan layar di Web
- Dibuat fungsi main yang memanggil runApp dengan argumen objek MyApp.
- Dibuat kelas MyApp sebagai turunan StatelessWidget. Pada method build, dikembalikan widget MaterialApp dengan properti debugShowCheckedModeBanner bernilai false, title bernilai "Expense Tracker", theme berupa ThemeData dengan primarySwatch bernilai Colors.blue, serta home diisi widget DashboardScreen.
- Dibuat kelas DashboardScreen sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Scaffold dengan appBar berupa AppBar bertuliskan "Praktikum 1: Widgets" dan backgroundColor bernilai Colors.blue.
- Bagian body diisi widget Padding dengan padding sebesar 16 piksel pada seluruh sisi, dan child-nya berupa widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun widget GreetingWidget, jarak vertikal melalui SizedBox setinggi 20 piksel, dan widget BalanceCardWidget secara berurutan dari atas ke bawah.
- Dibuat kelas GreetingWidget sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Row yang berisi widget CircleAvatar berukuran radius 24, backgroundColor bernilai Colors.blueAccent, dan child berupa Icon Icons.person berukuran 30 piksel berwarna putih.
- Setelah jarak horizontal melalui SizedBox selebar 12 piksel, ditampilkan widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun dua widget Text secara vertikal, yaitu "Halo, Budi" dengan fontSize 20 dan fontWeight tebal, serta "Selamat datang kembali!" dengan fontSize 14 berwarna abu-abu.
- Dibuat kelas BalanceCardWidget sebagai turunan StatefulWidget, dengan method createState yang mengembalikan objek _BalanceCardWidgetState.
- Dibuat kelas _BalanceCardWidgetState sebagai turunan State<BalanceCardWidget>, dengan variabel _isBalanceVisible bertipe bool bernilai awal true.
- Didefinisikan function _toggleVisibility yang memanggil setState untuk membalik nilai _isBalanceVisible melalui operator negasi.
- Pada method build, dikembalikan widget Card dengan elevation bernilai 4, shape berupa RoundedRectangleBorder dengan borderRadius melingkar sebesar 16 piksel, dan color bernilai Colors.blueAccent.
- Bagian child diisi widget Padding dengan padding sebesar 20 piksel pada seluruh sisi, dan child-nya berupa widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start.
- Elemen pertama pada Column berupa widget Row dengan mainAxisAlignment bernilai MainAxisAlignment.spaceBetween, menyusun teks "Saldo Utama" berwarna putih transparan dan widget IconButton yang ikonnya ditentukan melalui ekspresi ternary, menampilkan Icons.visibility apabila _isBalanceVisible bernilai true atau Icons.visibility_off apabila bernilai false, dengan onPressed memanggil function _toggleVisibility.
- Setelah jarak vertikal melalui SizedBox setinggi 8 piksel, elemen kedua pada Column berupa widget Text yang isinya ditentukan melalui ekspresi ternary, menampilkan "Rp 5.000.000" apabila _isBalanceVisible bernilai true atau "Rp *********" apabila bernilai false, dengan fontSize 28, fontWeight tebal, dan warna putih.


E. Tugas 1: Styling
- Pada widget Card di dalam kelas _BalanceCardWidgetState, properti color diubah nilainya dari Colors.blueAccent menjadi Colors.teal, sehingga warna latar kartu saldo berubah dari biru menjadi hijau emerald.
- Berikut adalah kode dan tampilan layar pada web:


F. Tugas 2: Modifikasi UI
- Di bagian bawah widget Text nominal saldo pada Column di dalam kartu, ditambahkan widget Text baru bertuliskan "No. Rekening: 1234-5678", dengan fontSize 14 dan warna putih transparan (Colors.white70).

2. Praktikum 2
A. Menyusun Tombol Aksi (Row)
- Dibuat kelas ActionButtons sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Row dengan properti mainAxisAlignment bernilai MainAxisAlignment.spaceEvenly, sehingga elemen pada properti children diberi jarak kosong yang sama rata di antara dan di sekeliling setiap elemen.
- Pada properti children, dipanggil function _buildButton sebanyak tiga kali, masing-masing dengan argumen ikon, label, dan warna berbeda, yaitu Icons.arrow_downward berlabel "Masuk" berwarna Colors.green, Icons.arrow_upward berlabel "Keluar" berwarna Colors.red, dan Icons.swap_horiz berlabel "Transfer" berwarna Colors.blue. Function _buildButton didefinisikan secara terpisah.

B. Menyusun Riwayat (Column + ListTile)
- Dibuat widget Column dengan properti crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun widget Text bertuliskan "Transaksi Terakhir" beserta dua widget ListTile secara vertikal dari atas ke bawah.
- ListTile pertama memiliki properti leading diisi Icon Icons.fastfood, properti title diisi Text bertuliskan "Makan Siang", dan properti trailing diisi Text bertuliskan "- Rp 50.000".
- ListTile kedua memiliki properti leading diisi Icon Icons.work, properti title diisi Text bertuliskan "Gaji Bulanan", dan properti trailing diisi Text bertuliskan "+ Rp 5.000.000".

C. Integrasi Dashboard (Scrollable)
- Pada method build, dikembalikan widget Scaffold dengan appBar berupa AppBar bertuliskan "Praktikum 2".
- Bagian body diisi widget SingleChildScrollView agar seluruh isi dashboard dapat digulir secara vertikal, child-nya berupa widget Padding dengan padding sebesar 16 piksel pada seluruh sisi.
- Child dari Padding berupa widget Column yang menyusun empat komponen secara berurutan dari atas ke bawah, yaitu GreetingWidget, BalanceCardWidget, ActionButtonsWidget, dan RecentTransactionsWidget.

D. Full Code dan Tampilan Layar pada Web
- Dibuat fungsi main yang memanggil runApp dengan argumen objek MyApp.
- Dibuat kelas MyApp sebagai turunan StatelessWidget. Pada method build, dikembalikan widget MaterialApp dengan properti debugShowCheckedModeBanner bernilai false, title bernilai "Expense Tracker", theme berupa ThemeData dengan primarySwatch bernilai Colors.blue, serta home diisi widget DashboardScreen.
- Dibuat kelas DashboardScreen sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Scaffold dengan appBar berupa AppBar bertuliskan "Praktikum 2: Layouting" dan backgroundColor bernilai Colors.blue.
- Bagian body diisi widget SingleChildScrollView agar seluruh isi dashboard dapat digulir secara vertikal, child-nya berupa widget Padding dengan padding sebesar 16 piksel pada seluruh sisi, dan child dari Padding tersebut berupa widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun widget GreetingWidget, BalancedCardWidget, ActionButtonsWidget, dan RecentTransactionWidget secara berurutan dari atas ke bawah, masing-masing dipisahkan jarak vertikal 20 piksel melalui SizedBox.
- Dibuat kelas GreetingWidget sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Row yang berisi widget CircleAvatar berukuran radius 24, backgroundColor bernilai Colors.blueAccent, dan child berupa Icon Icons.person berukuran 30 piksel berwarna putih.
- Setelah jarak horizontal melalui SizedBox selebar 12 piksel, ditampilkan widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun dua widget Text secara vertikal, yaitu "Halo Budi" dengan fontSize 20 dan fontWeight tebal, serta "Selamat Datang Kembali!" dengan fontSize 14 berwarna abu-abu.
- Dibuat kelas BalancedCardWidget sebagai turunan StatefulWidget, dengan method createState yang mengembalikan objek _BalancedCardWidgetState.
- Dibuat kelas _BalancedCardWidgetState sebagai turunan State<BalancedCardWidget>, dengan variabel _isBalanceVisible bertipe bool bernilai awal true.
- Didefinisikan function _toggleVisibility yang memanggil setState untuk membalik nilai _isBalanceVisible melalui operator negasi.
- Pada method build, dikembalikan widget Card dengan elevation bernilai 4, shape berupa RoundedRectangleBorder dengan borderRadius melingkar sebesar 16 piksel, dan color bernilai Colors.blueAccent.
- Bagian child diisi widget Padding dengan padding sebesar 20 piksel pada seluruh sisi, dan child-nya berupa widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start.
- Elemen pertama pada Column berupa widget Row dengan mainAxisAlignment bernilai MainAxisAlignment.spaceBetween, menyusun teks "Saldo Utama" berwarna putih transparan dan widget IconButton yang ikonnya ditentukan melalui ekspresi ternary, menampilkan Icons.visibility apabila _isBalanceVisible bernilai true atau Icons.visibility_off apabila bernilai false, dengan onPressed memanggil function _toggleVisibility.
- Setelah jarak vertikal melalui SizedBox setinggi 8 piksel, elemen kedua pada Column berupa widget Text yang isinya ditentukan melalui ekspresi ternary, menampilkan "Rp 5.000.000" apabila _isBalanceVisible bernilai true atau "Rp *********" apabila bernilai false, dengan fontSize 28, fontWeight tebal, dan warna putih.
- Dibuat kelas ActionButtonsWidget sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Row dengan mainAxisAlignment bernilai MainAxisAlignment.spaceEvenly, menyusun tiga pemanggilan function _buildActionButton dengan argumen ikon, label, dan warna berbeda, yaitu Icons.arrow_downward berlabel "Pemasukan" berwarna Colors.green, Icons.arrow_upward berlabel "Pengeluaran" berwarna Colors.red, dan Icons.swap_horiz berlabel "Transfer" berwarna Colors.blue.
- Didefinisikan function _buildActionButton dengan parameter icon bertipe IconData, label bertipe String, dan color bertipe Color, mengembalikan widget Column berisi widget Container dengan padding 12 piksel pada seluruh sisi, decoration berupa BoxDecoration dengan color dari parameter color pada opasitas 0,2 dan borderRadius melingkar sebesar 12 piksel, serta child berupa Icon dari parameter icon berwarna sesuai parameter color berukuran 28 piksel.
- Setelah jarak vertikal melalui SizedBox setinggi 8 piksel, ditampilkan widget Text dari parameter label dengan fontWeight sedang.
- Dibuat kelas RecentTransactionWidget sebagai turunan StatelessWidget. Pada method build, dikembalikan widget Column dengan crossAxisAlignment bernilai CrossAxisAlignment.start, menyusun widget Text bertuliskan "Transaksi Terakhir" dengan fontSize 18 dan fontWeight tebal, diikuti jarak vertikal 12 piksel melalui SizedBox.
- Setelah itu, ditampilkan widget Card dengan elevation bernilai 2 dan shape berupa RoundedRectangleBorder dengan borderRadius melingkar sebesar 12 piksel, child-nya berupa widget Column yang menyusun tiga widget ListTile dengan widget Divider setinggi 1 piksel sebagai pemisah antarbaris.
- ListTile pertama memiliki leading berupa CircleAvatar berwarna Colors.redAccent dengan child Icon Icons.fastfood berwarna putih, title bertuliskan "Makan Siang", subtitle bertuliskan "13 Sep 2026", dan trailing bertuliskan "- Rp 50.000" berwarna merah dan tebal.
- ListTile kedua memiliki leading berupa CircleAvatar berwarna Colors.green dengan child Icon Icons.attach_money berwarna putih, title bertuliskan "Gaji Bulanan", subtitle bertuliskan "01 Sep 2026", dan trailing bertuliskan "+Rp 5.000.000" berwarna hijau dan tebal.
- ListTile ketiga memiliki leading berupa CircleAvatar berwarna Colors.blueAccent dengan child Icon Icons.directions_car berwarna putih, title bertuliskan "Isi Bensin", subtitle bertuliskan "10 Sep 2026", dan trailing bertuliskan "- Rp 150.000" berwarna merah dan tebal.


E. Tugas 1: Styling
- Pada ListTile "Makan Siang", trailing diisi widget Text bertuliskan "- Rp 50.000", dengan properti style berupa TextStyle yang color-nya bernilai Colors.red dan fontWeight-nya tebal, menandai transaksi tersebut sebagai pengeluaran.
- Pada ListTile "Gaji Bulanan", trailing diisi widget Text bertuliskan "+Rp 5.000.000", dengan properti style berupa TextStyle yang color-nya bernilai Colors.green dan fontWeight-nya tebal, menandai transaksi tersebut sebagai pemasukan.
- Pada ListTile "Isi Bensin", trailing diisi widget Text bertuliskan "- Rp 150.000", dengan properti style berupa TextStyle yang color-nya bernilai Colors.red dan fontWeight-nya tebal, menandai transaksi tersebut sebagai pengeluaran.
- Pada ListTile "Bayar Token Listrik", trailing diisi widget Text bertuliskan "- Rp 50.000", dengan properti style berupa TextStyle yang color-nya bernilai Colors.red dan fontWeight-nya tebal, menandai transaksi tersebut sebagai pengeluaran.
- Pada ListTile "Bayar Subscription Aplikasi", trailing diisi widget Text bertuliskan "- Rp 384.000", dengan properti style berupa TextStyle yang color-nya bernilai Colors.red dan fontWeight-nya tebal, menandai transaksi tersebut sebagai pengeluaran.

F. Tugas 2: Tambah Data
- Di bagian bawah ListTile "Isi Bensin" pada widget Column di dalam Card, ditambahkan widget ListTile baru dengan leading berupa CircleAvatar berwarna Colors.yellow dan child Icon Icons.electric_bolt berwarna merah, title bertuliskan "Bayar Token Listrik", subtitle bertuliskan "10 Sep 2026", dan trailing berupa Text bertuliskan "- Rp 50.000" dengan style berwarna Colors.red dan fontWeight tebal.
- Setelah itu, ditambahkan satu widget ListTile lagi dengan leading berupa CircleAvatar berwarna Colors.blueAccent dan child Icon Icons.phone_android berwarna putih, title bertuliskan "Bayar Subscription Aplikasi", subtitle bertuliskan "10 Sep 2026", dan trailing berupa Text bertuliskan "- Rp 384.000" dengan style berwarna Colors.red dan fontWeight tebal.

G. Hasil Pengerjaan Tugas 1 & Tugas 2


4. Conclusion
KESIMPULAN
Berdasarkan praktik yang telah dilakukan, dapat disimpulkan bahwa penyusunan antarmuka pada Flutter dimulai dari pemahaman konsep widget sebagai unit dasar pembentuk tampilan, yang terbagi menjadi StatelessWidget untuk tampilan statis seperti sapaan pengguna dan StatefulWidget untuk tampilan dinamis seperti kartu saldo yang dapat menampilkan atau menyembunyikan nominal melalui fungsi setState. Kerangka dasar aplikasi dibentuk melalui widget MaterialApp dan Scaffold, sementara interaktivitas antarmuka terjadi ketika perubahan pada variabel status memicu pembangunan ulang tampilan tanpa mengubah struktur kode secara keseluruhan.
Pada tahap berikutnya, penyusunan tata letak dilakukan melalui widget Row untuk menyusun tombol aksi secara horizontal, widget Column untuk menyusun sapaan, kartu saldo, dan daftar transaksi secara vertikal, serta widget ListTile untuk menampilkan setiap baris transaksi dengan bagian leading, title, dan trailing yang tersusun rapi. Penggunaan widget SingleChildScrollView turut memastikan tampilan tetap dapat digulir dan tidak meluap ketika jumlah transaksi pada daftar bertambah. Dengan demikian, praktikum ini menunjukkan bahwa pemahaman konsep widget serta prinsip layouting dan styling menjadi dasar yang saling berkaitan dalam membangun antarmuka aplikasi Flutter yang terstruktur dan responsif.