Kembali ke Laporan

Laprak 3 — Input Widgets & Forms (Flutter)

Aplikasi Mobile · Praktikum III · Menangkap Data, Dropdown, dan Validasi

Saskia Alifah 2411531002 Aplikasi Mobile Flutter Form, Dropdown & Validasi
Daftar Isi

Praktikum ini bertujuan supaya mahasiswa mampu menangkap teks dari pengguna melalui TextEditingController, menerapkan widget dropdown statis menggunakan DropdownButtonFormField, serta membuat validasi input agar data kosong atau berformat salah tidak dapat tersimpan ke dalam sistem.

Konsep tersebut diterapkan pada studi kasus formulir pencatatan transaksi pada aplikasi Expense Tracker, lalu dikembangkan pada bagian latihan dengan menambahkan input tanggal transaksi yang memanfaatkan fungsi showDatePicker.

Form adalah widget pembungkus yang mengelompokkan banyak kolom input sehingga seluruhnya dapat divalidasi sekaligus. Form dikendalikan melalui GlobalKey<FormState>, yaitu key unik yang memungkinkan method validate() dipanggil dari luar widget Form, misalnya saat tombol ditekan.

TextFormField adalah kolom teks yang sudah terintegrasi dengan Form dan memiliki properti validator. Nilai yang diketik dibaca menggunakan TextEditingController, dan controller tersebut wajib dibersihkan lewat dispose() ketika halaman ditutup agar tidak terjadi kebocoran memori.

Validasi mencegah masuknya data yang tidak sesuai format sebelum disimpan. Fungsi validator memeriksa isi kolom: jika mengembalikan null berarti data valid, sedangkan jika mengembalikan String berarti data tidak valid dan pesan error berwarna merah ditampilkan di bawah kolom.

DropdownButtonFormField adalah widget pilihan berbentuk daftar turun yang juga terintegrasi dengan Form. Karena isi formulir (teks dan dropdown) terus berubah saat pengguna berinteraksi, halaman dibuat sebagai StatefulWidget dan setiap perubahan pilihan diumumkan lewat setState(). Untuk input tanggal, Flutter menyediakan showDatePicker, yaitu dialog kalender yang mengembalikan nilai DateTime pilihan pengguna.

  • Laptop / Komputer dengan Flutter SDK dan Dart terinstall
  • Visual Studio Code (Code Editor) dengan ekstensi Flutter & Dart
  • Chrome Browser / Android Emulator sebagai target device untuk menjalankan aplikasi
  • Modul Praktikum Aplikasi Mobile — Praktikum III: Input Widgets & Forms

Praktikum ini terdiri dari dua file di dalam folder lib, yaitu add_transaction_screen.dart yang berisi formulir pencatatan transaksi dan main.dart yang memanggil halaman tersebut. Setiap file dikerjakan berurutan, mulai dari struktur dasar hingga pengujian.

D.1 add_transaction_screen.dart
1Membuat File dan Struktur Dasar StatefulWidget

Buat file baru bernama add_transaction_screen.dart di dalam folder lib, lalu import package material dan deklarasikan kelas halaman.

Aksi: Buat File, Import Material, dan Kelas AddTransactionScreen
SS VS Code struktur dasar AddTransactionScreen StatefulWidget
Gambar D.1a – Import material.dart dan kelas AddTransactionScreen sebagai StatefulWidget
Penjelasan Kode

import 'package:flutter/material.dart'; memuat seluruh widget Material Design. Kelas AddTransactionScreen extends StatefulWidget dipilih karena isi formulir akan terus berubah saat pengguna mengetik dan memilih kategori, sedangkan createState() menghubungkannya dengan kelas _AddTransactionScreenState yang menampung seluruh state.

2Menyiapkan GlobalKey, Controller, dan State Dropdown

Di dalam _AddTransactionScreenState disiapkan key untuk memicu validasi, controller untuk judul dan nominal, serta variabel untuk dropdown kategori.

Aksi: Tambahkan _formKey, _titleController, _amountController, _selectedCategory, dan _categories
SS VS Code deklarasi formKey controller dan state dropdown
Gambar D.1b – Deklarasi _formKey, dua controller, _selectedCategory, dan daftar _categories
Penjelasan Kode

GlobalKey<FormState>() menjadi kunci untuk mengontrol Form dan memicu validasi. _titleController dan _amountController membaca teks yang diketik pengguna. _selectedCategory = 'Makanan' adalah nilai awal dropdown, sedangkan _categories berisi opsi Makanan, Transportasi, Hiburan, dan Lainnya.

3Membersihkan Controller dengan dispose()

Controller yang sudah tidak dipakai harus dilepaskan saat halaman ditutup.

Aksi: Buat Method dispose()
SS VS Code method dispose membersihkan controller
Gambar D.1c – Method dispose() yang membersihkan _titleController dan _amountController
Penjelasan Kode

dispose() dipanggil Flutter ketika halaman dihancurkan. Kedua controller dipanggil dispose()-nya satu per satu, lalu super.dispose() dijalankan untuk mencegah memory leak.

4Membangun Kerangka Scaffold, Form, dan Column

Tampilan dibangun di dalam method build() menggunakan Scaffold dengan AppBar dan body yang dapat digulir.

Aksi: Buat Scaffold, AppBar, SingleChildScrollView, Form, dan Column
SS VS Code kerangka Scaffold AppBar SingleChildScrollView Form Column
Gambar D.1d – Kerangka tampilan Scaffold berisi AppBar dan body SingleChildScrollView yang membungkus Form
Penjelasan Kode

AppBar berjudul "Catat Transaksi Baru" diberi warna Colors.deepPurple. SingleChildScrollView mencegah error layout saat keyboard muncul. Widget Form(key: _formKey) membungkus Column agar seluruh kolom di dalamnya terdaftar pada key yang sama, dan CrossAxisAlignment.stretch membuat anak-anaknya melebar penuh.

5TextFormField Judul Transaksi

Kolom pertama menerima judul transaksi dan tidak boleh dikosongkan.

Aksi: Isi TextFormField Judul beserta Validator
SS VS Code TextFormField judul transaksi dengan validator
Gambar D.1e – TextFormField Judul Transaksi dengan validator cek kosong
Penjelasan Kode

controller: _titleController menghubungkan kolom dengan controller, sedangkan InputDecoration mengatur label, garis tepi OutlineInputBorder, dan ikon Icons.title. Pada validator, jika value == null || value.isEmpty maka dikembalikan pesan "Judul transaksi tidak boleh kosong", selain itu dikembalikan null yang berarti valid.

6TextFormField Nominal dengan Keyboard Angka

Kolom kedua menerima nominal uang, sehingga keyboard dibuat khusus angka dan formatnya divalidasi.

Aksi: Isi TextFormField Nominal dengan keyboardType dan Validator
SS VS Code TextFormField nominal keyboardType number dan validator
Gambar D.1f – TextFormField Nominal dengan keyboardType number dan dua kondisi validator
Penjelasan Kode

keyboardType: TextInputType.number memunculkan keyboard angka. Validator memeriksa dua kondisi bertingkat: kolom kosong menghasilkan "Nominal wajib diisi", lalu int.tryParse(value) == null menghasilkan "Harus berupa angka bulat yang valid". int.tryParse mengembalikan null bila teks bukan bilangan bulat, misalnya berisi huruf atau tanda titik.

7DropdownButtonFormField Kategori

Pilihan kategori transaksi dibuat dengan dropdown statis yang isinya diambil dari daftar _categories.

Aksi: Buat DropdownButtonFormField dengan items dan onChanged
SS VS Code DropdownButtonFormField kategori items onChanged
Gambar D.1g – DropdownButtonFormField Kategori beserta items dan onChanged
Penjelasan Kode

value: _selectedCategory menampilkan pilihan aktif. Method .map() mengubah setiap string pada _categories menjadi DropdownMenuItem, lalu .toList() mengumpulkannya. Pada onChanged, setState() memperbarui _selectedCategory dengan nilai baru sehingga tampilan dropdown ikut berubah.

8Tombol Simpan dan Pemicu Validasi

Tombol Simpan menjalankan seluruh validator sekaligus dan menampilkan SnackBar jika data valid.

Aksi: Buat ElevatedButton Simpan Transaksi dengan validate() dan SnackBar
SS VS Code ElevatedButton simpan validate dan SnackBar
Gambar D.1h – ElevatedButton Simpan Transaksi dengan validate() dan SnackBar sukses
Penjelasan Kode

_formKey.currentState!.validate() menjalankan semua validator; hasilnya true hanya bila seluruhnya mengembalikan null. Jika lolos, ScaffoldMessenger.of(context).showSnackBar() menampilkan pesan "Tersimpan: [judul] (Rp [nominal])" berlatar hijau, dengan nilai diambil dari controller.text. Gaya tombol diatur lewat ElevatedButton.styleFrom.

D.2 main.dart
1Memanggil Halaman Form dari main()

File main.dart bawaan diubah agar langsung membuka halaman formulir.

Aksi: Tambahkan Import add_transaction_screen.dart dan Fungsi main()
SS VS Code import dan fungsi main pada main.dart
Gambar D.2a – Import add_transaction_screen.dart dan fungsi main() dengan runApp(const MyApp())
Penjelasan Kode

Baris import 'add_transaction_screen.dart'; memanggil file form yang dibuat sebelumnya. Fungsi main() adalah titik awal aplikasi dan runApp(const MyApp()) menjalankan widget root.

2Membuat MyApp dengan MaterialApp

Kelas MyApp menjadi root widget yang mengatur judul, tema, dan halaman awal.

Aksi: Buat MyApp dengan Tema deepPurple dan home AddTransactionScreen
SS VS Code kelas MyApp MaterialApp tema deepPurple
Gambar D.2b – MyApp mengembalikan MaterialApp dengan tema deepPurple dan home AddTransactionScreen
Penjelasan Kode

debugShowCheckedModeBanner: false menyembunyikan label debug, title berisi "Expense Tracker - Modul 3", dan primarySwatch: Colors.deepPurple menjadi warna tema. Properti home: const AddTransactionScreen() membuat aplikasi langsung membuka halaman form.

3Menjalankan dan Menguji Form

Aplikasi dijalankan lalu diuji dengan beberapa skenario: kolom dikosongkan, nominal diisi format salah, pemilihan kategori, dan seluruh input diisi benar.

Pengujian 1: Seluruh Kolom Dikosongkan
Percobaan: Tekan Tombol "Simpan Transaksi" Tanpa Mengisi Apa Pun
SS hasil running validasi kolom kosong
Gambar D.2c – Pesan error merah muncul di bawah kolom Judul dan Nominal
Pengujian 2: Nominal Bukan Angka Bulat
Percobaan: Isi Nominal dengan Format Salah (misal 50.000 atau huruf), lalu Simpan
SS hasil running validasi nominal bukan angka
Gambar D.2d – Pesan "Harus berupa angka bulat yang valid" pada kolom Nominal
Pengujian 3: Memilih Kategori Dropdown
Percobaan: Buka Dropdown Kategori dan Pilih Salah Satu Opsi
SS hasil running dropdown kategori
Gambar D.2e – Daftar opsi kategori pada dropdown dan nilai yang berubah setelah dipilih
Pengujian 4: Seluruh Input Terisi dengan Benar
Percobaan: Isi Judul, Nominal Angka Bulat, dan Kategori, lalu Simpan
SS hasil running validasi berhasil dan SnackBar muncul
Gambar D.2f – SnackBar hijau "Tersimpan: [judul] (Rp [nominal])"
Keempat skenario berjalan sesuai ekspektasi, menandakan validator, GlobalKey<FormState>, dan DropdownButtonFormField pada add_transaction_screen.dart berfungsi dengan benar.
Latihan — Menambah Input Tanggal Transaksi

Menambahkan satu input baru sebelum tombol Simpan untuk mengisi Tanggal Transaksi berformat DD/MM/YYYY, dengan tombol plus yang membuka showDatePicker serta validasi wajib diisi.

E.1 add_transaction_screen.dart
1Menambah _dateController

Kolom tanggal membutuhkan controller sendiri untuk menampung teks tanggal yang dipilih.

Aksi: Tambahkan _dateController dan Bersihkan di dispose()
SS VS Code deklarasi dateController dan dispose
Gambar E.1a – Deklarasi _dateController dan pemanggilan dispose()-nya
Penjelasan Kode

final _dateController = TextEditingController(); menyiapkan controller tanggal, dan _dateController.dispose() ditambahkan pada dispose() agar dibersihkan bersama controller lainnya.

2Membuat Fungsi _selectDate() dengan showDatePicker

Fungsi asynchronous dibuat untuk membuka kalender dan menuliskan tanggal pilihan ke kolom.

Aksi: Buat Future<void> _selectDate()
SS VS Code fungsi selectDate showDatePicker padLeft
Gambar E.1b – Fungsi _selectDate() dengan showDatePicker dan format DD/MM/YYYY
Penjelasan Kode

await showDatePicker(...) menampilkan kalender dengan initialDate: DateTime.now() serta rentang firstDate tahun 2000 sampai lastDate tahun 2100, dan menghasilkan DateTime?. Jika pickedDate != null, setState() mengisi _dateController.text dengan format DD/MM/YYYY; padLeft(2, '0') menambahkan angka nol di depan agar tanggal dan bulan selalu dua digit.

3TextFormField Tanggal dengan Tombol Plus

Kolom tanggal dibuat hanya-baca dan diisi lewat tombol plus yang memanggil _selectDate.

Aksi: Isi TextFormField Tanggal dengan readOnly, suffixIcon, dan Validator
SS VS Code TextFormField tanggal readOnly suffixIcon validator
Gambar E.1c – TextFormField Tanggal Transaksi dengan readOnly, tombol plus, dan validator wajib diisi
Penjelasan Kode

readOnly: true mencegah pengguna mengetik manual sehingga format selalu benar. prefixIcon memakai Icons.calendar_today, sedangkan suffixIcon berupa IconButton(icon: Icon(Icons.add), onPressed: _selectDate) sebagai tombol plus. Validator mengembalikan "Tanggal transaksi wajib diisi" bila kolom kosong. Kolom ini disisipkan sebelum tombol Simpan.

4Menampilkan Tanggal pada SnackBar

Pesan sukses diperbarui agar ikut menampilkan tanggal transaksi.

Aksi: Ubah Teks SnackBar
SS VS Code SnackBar menampilkan tanggal
Gambar E.1d – Teks SnackBar yang kini memuat judul, nominal, dan tanggal
Penjelasan Kode

Teks SnackBar digabung menjadi 'Tersimpan: ${{_titleController.text}} (Rp ${{_amountController.text}}) Tanggal: ${{_dateController.text}}', sehingga ketiga isian tampil bersama.

5Menjalankan dan Menguji Input Tanggal

Aplikasi dijalankan kembali untuk menguji validasi wajib diisi, tombol plus, dan penyimpanan lengkap.

Pengujian 1: Tanggal Dikosongkan
Percobaan: Isi Judul dan Nominal, Kosongkan Tanggal, lalu Simpan
SS hasil running validasi tanggal kosong
Gambar E.1e – Pesan "Tanggal transaksi wajib diisi" pada kolom Tanggal
Pengujian 2: Memilih Tanggal dengan Tombol Plus
Percobaan: Tekan Tombol Plus dan Pilih Tanggal pada Kalender
SS hasil running date picker dan tanggal terisi
Gambar E.1f – Kalender showDatePicker dan kolom Tanggal terisi format DD/MM/YYYY
Pengujian 3: Seluruh Input Terisi dengan Benar
Percobaan: Isi Semua Kolom termasuk Tanggal, lalu Simpan
SS hasil running SnackBar dengan tanggal
Gambar E.1g – SnackBar menampilkan judul, nominal, dan tanggal transaksi
Input tanggal berfungsi: validasi wajib diisi menolak kolom kosong, tombol plus membuka showDatePicker, dan tanggal terpilih tampil pada kolom serta SnackBar.

Melalui Praktikum III ini, formulir pencatatan transaksi berhasil dibuat lengkap dengan penangkapan teks, dropdown, dan validasi. TextEditingController terbukti mampu membaca judul dan nominal untuk ditampilkan kembali pada SnackBar, dan dispose() membersihkan seluruh controller.

GlobalKey<FormState> beserta validate() menjalankan semua validator sekaligus: kolom kosong dan nominal yang bukan bilangan bulat ditolak dengan pesan error merah, sedangkan input valid meloloskan proses simpan. DropdownButtonFormField dengan setState() memperbarui kategori terpilih pada tampilan.

Pada latihan, input tanggal berhasil ditambahkan: showDatePicker yang dipanggil dari tombol plus mengisi kolom berformat DD/MM/YYYY, readOnly menjaga format tetap konsisten, dan validator memastikan tanggal tidak dikosongkan.

Berdasarkan seluruh tahapan praktikum, diperoleh kesimpulan sebagai berikut:

  1. TextEditingController berfungsi membaca teks pengguna dan wajib di-dispose() untuk mencegah kebocoran memori.
  2. Form dengan GlobalKey<FormState> memungkinkan seluruh TextFormField divalidasi sekaligus dari satu titik lewat validate().
  3. Validator mengembalikan null untuk data valid dan String untuk pesan error, sehingga data kosong atau berformat salah dapat dicegah sebelum disimpan.
  4. DropdownButtonFormField dan setState() mempermudah pemilihan kategori dari opsi statis.
  5. showDatePicker memudahkan pengisian tanggal tanpa mengetik manual, sehingga format DD/MM/YYYY tetap konsisten.

Seluruh source code praktikum ini dapat diakses pada repository Github berikut: