Laprak 3 - Input Widgets & Forms

Praktikum Aplikasi Mobile Pertemuan 3 Zahran Azaria Anvaya - 2411531005

I. Tujuan Praktikum

Setelah menyelesaikan praktikum ini, mahasiswa diharapkan mampu:

  • Menangkap teks dari pengguna melalui TextEditingController
  • Menerapkan widget dropdown statis menggunakan DropdownButtonFormField
  • Menerapkan validasi input untuk mencegah data kosong atau format yang salah

II. Dasar Teori

Berbeda dengan tampilan statis, sebuah formulir input harus mampu menangkap data dari pengguna, memastikan data tersebut benar formatnya, lalu menyimpan atau memprosesnya. Untuk itu, Flutter menyediakan tiga komponen inti yang saling terhubung dan bekerja sama membentuk satu alur input yang utuh:

Form & GlobalKey Wadah utama untuk memvalidasi banyak input sekaligus.
TextFormField Kolom teks yang mendukung validasi terintegrasi.
TextController Pengendali untuk membaca teks yang diketik pengguna.

Form adalah widget pembungkus yang mengelompokkan beberapa field input menjadi satu kesatuan yang bisa divalidasi bersamaan. Agar Form ini bisa "diperintah" dari luar — misalnya dipanggil saat tombol Simpan ditekan — dibutuhkan GlobalKey<FormState> sebagai identitas unik yang menghubungkan kode di luar Form dengan state internal Form tersebut. Dengan key ini, method seperti validate() bisa dipanggil kapan saja untuk memeriksa seluruh field di dalamnya secara serentak.

TextFormField adalah versi TextField yang sudah terintegrasi dengan sistem validasi milik Form. Setiap TextFormField memiliki properti validator berupa fungsi yang dijalankan otomatis saat validate() dipanggil pada Form induknya.

TextEditingController bertugas menjembatani apa yang diketik pengguna di layar dengan kode Dart di baliknya — nilai yang diketik bisa dibaca lewat controller.text, dan bisa pula diisi secara terprogram (seperti yang nanti dipakai untuk mengisi tanggal hasil date picker pada bagian Latihan).

Validasi berfungsi mencegah masuknya data yang tidak sesuai format sebelum disimpan ke dalam sistem. Cara kerjanya sederhana namun konsisten di seluruh field:

  • Validator - sebuah fungsi bertipe String? Function(String?) yang menerima nilai field saat ini dan memeriksa apakah nilai tersebut memenuhi aturan yang ditetapkan
  • Return null - menandakan data yang dimasukkan sudah valid, sehingga tidak ada pesan error yang ditampilkan
  • Return String - menandakan data tidak valid; teks yang dikembalikan akan langsung muncul sebagai pesan error berwarna merah di bawah field terkait

III. Alat dan Bahan

  • Laptop / Komputer
  • Flutter SDK
  • Code Editor: VS Code atau Android Studio
  • Emulator / Device

IV. Langkah - Langkah Praktikum

1 Membuat File Baru

Buat file dart baru dengan nama add_transaction_screen.dart di dalam folder lib. File ini akan berisi halaman formulir "Catat Transaksi" yang terpisah dari main.dart, sehingga struktur project tetap rapi dan mudah dikembangkan.

Membuat file add_transaction_screen.dart
Gambar 1 - Membuat File add_transaction_screen.dart
2 Persiapan State & Controller

Digunakan StatefulWidget karena isi formulir (teks dan dropdown) akan terus berubah saat pengguna mengetik atau memilih opsi. Tiga hal disiapkan di awal class state: _formKey sebagai kunci untuk memicu validasi pada seluruh Form, dua TextEditingController untuk membaca teks pada kolom Judul Transaksi dan Nominal, serta variabel _selectedCategory yang menyimpan pilihan dropdown yang sedang aktif, dengan nilai awal 'Makanan'.

Persiapan State dan Controller
Gambar 2 - Persiapan State & Controller
3 Membuat Kolom Input (TextFormField)

Seluruh kolom dibungkus dengan widget Form dan dihubungkan dengan key: _formKey, sehingga seluruh field di dalamnya dapat divalidasi bersamaan lewat key tersebut. Kolom Judul Transaksi hanya diperiksa apakah kosong atau tidak, sementara kolom Nominal diberi dua lapis pemeriksaan: wajib diisi, dan isinya harus bisa diubah menjadi angka lewat int.tryParse(). Properti keyboardType: TextInputType.number juga diset khusus pada kolom nominal agar keyboard yang muncul otomatis berupa numpad, bukan keyboard huruf.

Membuat Kolom Input TextFormField
Gambar 3 - Kolom Input TextFormField
4 Dropdown & Tombol Simpan

Digunakan DropdownButtonFormField untuk pilihan kategori. Widget ini menerima daftar DropdownMenuItem yang dihasilkan dari method .map() terhadap list kategori, dan setiap kali pengguna memilih opsi baru, onChanged dipanggil untuk memperbarui _selectedCategory lewat setState(). Di bawahnya, ElevatedButton "Simpan Transaksi" memanggil _formKey.currentState!.validate() — method ini secara otomatis menjalankan seluruh validator pada tiap field di dalam Form, dan hanya mengembalikan true jika semuanya lolos, sehingga SnackBar konfirmasi baru muncul ketika data benar-benar valid.

Dropdown dan Tombol Simpan
Gambar 4 - Dropdown Kategori & Tombol Simpan
5 Hasil Akhir: Mockup Formulir

Tampilan akhir halaman "Catat Transaksi" yang menampilkan kolom Judul Transaksi, Nominal (lengkap dengan pesan error merah saat format salah), Kategori (dropdown), dan tombol Simpan Transaksi. Seluruh field ini dibungkus dalam SingleChildScrollView agar tampilan tidak terpotong ketika keyboard muncul di layar.

Hasil Akhir Mockup Formulir
Gambar 5 - Hasil Akhir Mockup Formulir Catat Transaksi Saat Berhasil
Hasil Akhir Mockup Formulir
Gambar 6 - Hasil Akhir Mockup Formulir Catat Transaksi Saat Gagal beserta Validasi

V. Source Code Lengkap

lib/add_transaction_screen.dart

Hasil Akhir Mockup Formulir
Gambar 7 - Kode Lengkap AddTransactionScreen

lib/main.dart

Hasil Akhir Mockup Formulir
Gambar 8 - Kode Lengkap main.dart

VI. Hasil & Pembahasan

Praktikum ini berhasil membangun formulir "Catat Transaksi" yang menangkap tiga jenis input sekaligus: teks bebas (judul), teks berformat angka (nominal), dan pilihan tetap (kategori). Validasi berjalan sesuai aturan yang ditetapkan — kolom judul tidak boleh kosong, dan kolom nominal wajib diisi serta harus berupa angka. Ketika seluruh input valid, _formKey.currentState!.validate() mengembalikan true dan SnackBar sukses ditampilkan berisi ringkasan data yang baru saja "disimpan".

Pendekatan menggabungkan GlobalKey<FormState> dengan validator per field terbukti efisien: tidak perlu menulis pengecekan manual satu per satu untuk setiap kolom sebelum tombol ditekan, karena Flutter sudah menyediakan mekanisme terpadu untuk itu melalui Form.

Widget / MethodFungsi
GlobalKey<FormState>Mengakses dan mengontrol state Form dari luar widget, mis. pada onPressed
TextEditingControllerMengambil dan mengelola nilai inputan dari TextFormField
TextFormFieldMenerima input teks dengan validasi terintegrasi melalui properti validator
DropdownButtonFormFieldMenampilkan daftar pilihan tetap dan menyimpan pilihan terpilih ke state
validate()Menjalankan validasi seluruh TextFormField di dalam Form sekaligus
setState()Memberitahu Flutter untuk rebuild widget setelah state berubah
SnackBarMenampilkan pesan singkat sebagai hasil dari proses simpan

VII. Latihan / Tugas

Input Tanggal Transaksi - ditambahkan satu input baru sebelum tombol Simpan untuk mengisi Tanggal Transaksi, dikerjakan melalui empat langkah berikut.

1 Menambahkan Controller Tanggal & Memperbarui dispose()

Ditambahkan satu TextEditingController baru bernama _dateController, sejajar dengan _titleController dan _amountController yang sudah ada, khusus untuk menampung teks tanggal yang nantinya diisi otomatis lewat date picker. Method dispose() juga diperbarui agar ikut membersihkan _dateController saat halaman ditutup, mencegah memory leak.

Kode Controller Tanggal
Gambar 9 - Penambahan Controller Tanggal dan Pembaruan dispose()
2 Membuat Fungsi _selectDate()

Dibuat method _selectDate() yang memanggil showDatePicker, dialog kalender bawaan Flutter, dengan rentang tahun yang dibuka lebar dari 2000 sampai 2100 agar transaksi lama maupun jauh ke depan tetap bisa dipilih. Fungsi ini bersifat async karena harus menunggu (await) hingga pengguna selesai memilih tanggal atau menutup dialog. Hasil pilihan berupa objek DateTime?, yang kemudian diformat manual menjadi string DD/MM/YYYY menggunakan padLeft(2, '0') agar tanggal dan bulan satu digit tetap tampil dua digit, lalu dimasukkan ke _dateController.text di dalam setState().

Kode Fungsi _selectDate
Gambar 10 - Fungsi _selectDate() dengan showDatePicker
3 Menambahkan Field Tanggal ke UI

Ditambahkan satu TextFormField baru sebelum tombol "Simpan Transaksi", diletakkan setelah dropdown Kategori. Properti readOnly: true membuat keyboard tidak muncul saat field disentuh, sehingga pengguna hanya bisa mengisi tanggal lewat date picker. Ikon "+" pada suffixIcon memanggil _selectDate() ketika ditekan untuk membuka kalender.

Kode TextFormField Tanggal
Gambar 11 - Field Tanggal Ditambahkan ke UI
4 Menambahkan Validasi Wajib Diisi

Sesuai instruksi tugas, field tanggal diberi validator agar ikut diperiksa saat _formKey.currentState!.validate() dipanggil. Jika pengguna menekan tombol Simpan tanpa memilih tanggal terlebih dahulu, _dateController.text masih kosong sehingga validator mengembalikan pesan "Tanggal transaksi wajib diisi", dan proses simpan otomatis dibatalkan sampai tanggal diisi.

Kode Validasi Tanggal
Gambar 12 - Validasi Wajib Diisi pada Field Tanggal
5 Menampilkan Tanggal pada SnackBar Sukses

Pesan pada SnackBar di tombol Simpan diperbarui agar ikut menampilkan tanggal yang dipilih, selain judul dan nominal yang sudah ada sebelumnya, sehingga ringkasan transaksi yang tersimpan lebih lengkap.

Kode SnackBar dengan Tanggal
Gambar 13 - SnackBar Menampilkan Tanggal Transaksi
✓ Hasil Akhir Latihan

Tampilan formulir setelah kolom Tanggal Transaksi ditambahkan. Date picker terbuka saat ikon "+" ditekan, tanggal terpilih otomatis muncul dalam format DD/MM/YYYY, pesan error "Tanggal transaksi wajib diisi" tampil berwarna merah jika kolom masih kosong, dan SnackBar sukses menampilkan judul, nominal, sekaligus tanggal transaksi.

Hasil Akhir Latihan Input Tanggal
Hasil Akhir Latihan Input Tanggal
Hasil Akhir Latihan Input Tanggal
Gambar 14 - Hasil Akhir Formulir dengan Input Tanggal

VIII. Kesimpulan

Berdasarkan praktikum yang telah dilakukan, dapat disimpulkan bahwa:

  1. TextEditingController memungkinkan pengambilan nilai input dari TextFormField kapan saja dibutuhkan, misalnya saat tombol Simpan ditekan, dan juga bisa diisi secara terprogram seperti pada kasus date picker.
  2. DropdownButtonFormField memudahkan pembuatan pilihan tetap tanpa perlu widget tambahan untuk validasinya, karena sudah terintegrasi dengan Form.
  3. Validasi melalui properti validator efektif mencegah data yang tidak sesuai format tersimpan ke sistem, dengan pesan error yang otomatis tampil di bawah field.
  4. GlobalKey<FormState> memungkinkan seluruh validator pada satu Form dijalankan sekaligus melalui satu pemanggilan validate(), termasuk validator pada field yang ditambahkan belakangan seperti tanggal.
  5. showDatePicker adalah cara praktis menangkap input tanggal tanpa perlu pengguna mengetik manual, sekaligus menjaga format data tetap konsisten dan terhindar dari salah ketik.
Semua langkah berhasil dijalankan dan output sesuai yang diharapkan.
Lihat Repository GitHub