Setelah menyelesaikan praktikum ini, mahasiswa diharapkan mampu:
TextEditingControllerDropdownButtonFormFieldBerbeda 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 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:
String? Function(String?) yang menerima nilai field saat ini dan memeriksa apakah nilai tersebut memenuhi aturan yang ditetapkanBuat 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.
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'.
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.
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.
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.
lib/add_transaction_screen.dart
lib/main.dart
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 / Method | Fungsi |
|---|---|
| GlobalKey<FormState> | Mengakses dan mengontrol state Form dari luar widget, mis. pada onPressed |
| TextEditingController | Mengambil dan mengelola nilai inputan dari TextFormField |
| TextFormField | Menerima input teks dengan validasi terintegrasi melalui properti validator |
| DropdownButtonFormField | Menampilkan 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 |
| SnackBar | Menampilkan pesan singkat sebagai hasil dari proses simpan |
Input Tanggal Transaksi - ditambahkan satu input baru sebelum tombol Simpan untuk mengisi Tanggal Transaksi, dikerjakan melalui empat langkah berikut.
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.
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().
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.
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.
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.
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.
Berdasarkan praktikum yang telah dilakukan, dapat disimpulkan bahwa:
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.DropdownButtonFormField memudahkan pembuatan pilihan tetap tanpa perlu widget tambahan untuk validasinya, karena sudah terintegrasi dengan Form.validator efektif mencegah data yang tidak sesuai format tersimpan ke sistem, dengan pesan error yang otomatis tampil di bawah field.GlobalKey<FormState> memungkinkan seluruh validator pada satu Form dijalankan sekaligus melalui satu pemanggilan validate(), termasuk validator pada field yang ditambahkan belakangan seperti tanggal.showDatePicker adalah cara praktis menangkap input tanggal tanpa perlu pengguna mengetik manual, sekaligus menjaga format data tetap konsisten dan terhindar dari salah ketik.