Input Widgets dan Basic Form
Teori
Basic Form
Basic Form adalah wadah atau kerangka kerja (framework) di Flutter yang berfungsi untuk mengelola dan memvalidasi sekumpulan widget input (seperti TextField, Checkbox, Dropdown, dll.) secara kolektif.
Tugas utamanya adalah menyediakan tampilan inputan, memastikan bahwa inputan tersebut sesuai dengan aturan atau format yang ditetapkan (validasi), dan selanjutnya mengambil nilainya setelah proses pengecekan selesai dilakukan.
TextField
TextField adalah widget dasar yang digunakan untuk menerima masukan teks dari keyboard pengguna. Widget ini sangat fleksibel dan sering digunakan untuk membuat form login, kolom pencarian, atau input data sederhana.
Fitur TextField
-
Menerima input dari keyboard.
-
Memiliki properti lengkap untuk gaya (`style`), dekorasi (`decoration`), dan jenis inputan (`keyboardType`).
-
Dapat mengelola teks menggunakan
TextEditingControlleruntuk mengambil, memodifikasi, atau mendengarkan perubahan teks.
Contoh TextField
TextField(
decoration: const InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Misalnya masnoer',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person)),
controller: _textEditingController, // Untuk mengelola teks
keyboardType: TextInputType.text,
onChanged: (text) { // Event listener saat teks berubah
print('Sedang mengetik teks : ,$text');
},
),
TextFormField
TextFormField adalah versi yang lebih canggih dan lengkap dari TextField. Widget ini dirancang khusus untuk bekerja di dalam widget Form dan secara otomatis terintegrasi dengan logika validasi serta manajemen state form.
Fitur TextFormField
-
Menerima input teks dari keyboard.
-
Memiliki properti
validatoryang berfungsi untuk memeriksa apakah input sudah sesuai dengan aturan yang ditentukan (misalnya, tidak boleh kosong, harus berupa angka, dll.). -
Menampilkan pesan error secara otomatis di bawah field jika validasi gagal.
-
Berinteraksi dengan
FormStateuntuk melakukan validasi secara kolektif menggunakan metodevalidate().
Contoh TextFormField dengan Validator
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: "Nama : ", border: OutlineInputBorder()),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan nama anda'; // Pesan error jika gagal validasi
}
return null; // Null berarti validasi berhasil
},
),
GlobalKey dan FormState
GlobalKey adalah objek unik yang digunakan untuk mengidentifikasi dan mengakses State dari suatu widget secara global, artinya kita dapat mengakses widget tersebut dari mana saja dalam aplikasi.
FormState
-
FormStateadalah kelas yang mengelola status dari widgetForm, termasuk status validasi setiap inputan (TextFormField) di dalamnya. -
Dengan menetapkan `GlobalKey` pada widget
Form, kita dapat mengakses `FormState` tersebut (misalnya, melalui_formKey.currentState) dan memanggil metode-metode penting sepertivalidate()atausave()dari luar widget Form, biasanya dari tombol `onPressed` padaElevatedButton.
Metode `validate()`
Metode validate() adalah fungsi kunci yang terdapat pada FormState. Fungsi ini digunakan untuk menjalankan validasi pada semua TextFormField yang ada di dalam widget Form secara bersamaan.
Proses Kerja `validate()`:
-
Ketika Anda memanggil
_formKey.currentState!.validate(), Flutter akan mulai mengecek setiapTextFormFieldyang terikat padaFormtersebut. -
Fungsi
validatoryang telah didefinisikan pada setiapTextFormFieldakan dijalankan. -
Jika ada satu saja fungsi
validatoryang mengembalikan nilaiString(yaitu pesan error), maka:- Proses validasi akan berhenti.
- Metode
validate()akan mengembalikan nilaifalse. - Pesan error tersebut akan ditampilkan secara otomatis di bawah
TextFormFieldyang gagal.
-
Jika semua fungsi
validatormengembalikan nilainull(artinya tidak ada error), maka metodevalidate()akan mengembalikan nilaitrue, dan Anda dapat melanjutkan untuk mengambil atau menyimpan data.
Langkah Langkah Praktikum
Basic Form TextField
Form dasar adalah cara penting untuk mengambil masukan (input) dari pengguna. Di Flutter, untuk mengelola input ini dan memastikan tampilan (UI) diperbarui, kita harus menggunakan Stateful Widget.
Widget yang digunakan di sini memiliki peran spesifik:
-
`TextField`: Widget untuk menerima input teks dari keyboard.
-
`TextEditingController`: Sebuah objek yang berfungsi sebagai jembatan untuk mengambil, mengontrol, atau mengubah teks yang ada di dalam `TextField`.
-
`ElevatedButton`: Tombol yang event `onPressed`-nya akan kita hubungkan untuk memproses data yang telah diinput.
Berikut adalah langkah langkah penerapannya:
1. Buat file Dart baru dengan nama `form_textfield.dart` di dalam folder `lib`.
2. Buat tampilan basic form dengan menggunakan Widget `TextField` untuk inputan dan `ElevatedButton` untuk memberikan event listener. Struktur kode akan berada di dalam `StatefulWidget` untuk memungkinkan variabel input berubah dan memperbarui tampilan menggunakan `setState()`.
3. Berikut adalah kode yang menunjukkan bagian penting dari form tersebut:
Penjelasan Kode
-
MyApp(Stateless Widget): Berfungsi sebagai wadah utama aplikasi (root widget) dan menentukan properti desain material (MaterialApp). -
MyForm(Stateful Widget): Ini adalah kelas yang menampung form. Meskipun formnya belum memiliki data yang berubah saat ini, form harus dibuat sebagaiStatefulWidgetkarena form dirancang untuk memproses input yang akan mengubah keadaan UI di masa mendatang. -
_MyFormState: Kelas state yang membangun tampilan UI (buildmethod). -
PaddingdanColumn: Digunakan untuk mengatur tata letak.Paddingmemberi jarak konten dari tepi, danColumnmenyusun widget secara vertikal. -
TextField: Digunakan untuk menerima input. Propertidecorationmemberikan tampilan label, placeholder (hint), dan ikon yang rapi. -
ElevatedButton: Tombol yang diatur warna latarnya (amber) dan warna teksnya (hitam) melalui propertistyle. PropertionPressed: () {}saat ini kosong, yang berarti tombol akan tampil tetapi belum melakukan aksi apa pun saat ditekan.
Output
Penjelasan Tampilan
Setelah kode di atas dijalankan (di-run), Anda akan melihat tampilan form yang terdiri dari:
-
`AppBar`: Menampilkan judul "Basic Form".
-
`Padding`: Memberi ruang di sekeliling konten form agar tidak menempel di tepi layar.
-
`Column`: Menyusun widget secara vertikal.
-
`TextField`: Kotak input teks dengan dekorasi yang lengkap (`labelText`, `hintText`, `border`, dan `prefixIcon`).
-
`ElevatedButton`: Tombol berwarna kuning (amber) dengan teks "Tampilkan mama". Saat ini tombol tersebut belum memiliki fungsi karena properti `onPressed` masih kosong.
4. Implementasi Controller dan Dispose Pada class _MyFormState tambahkan kode program berikut :
TextEditingController _textEditingController = TextEditingController(); // Line 1
@override
void dispose() { // Line 2
_textEditingController.dispose(); // Line 3
super.dispose(); // Line 4
}
Penjelasan Kode
-
Line 1: Merupakan membuat sebuah variable dari
TextEditingController. Controller ini berfungsi untuk mengambil inputan dari user. -
Line 2 – 4: Merupakan method yang digunakan untuk membersihan teks inputan. Metode
dispose()dipanggil saat widget dihapus untuk mencegah kebocoran memori.
5. Menambahkan Properti Interaktif pada TextField untuk mengaitkan TextField dengan TextEditingController yang sudah dibuat pada Langkah 3, sehingga inputan dari pengguna dapat diambil dan diproses. Selain itu, kita menambahkan fitur interaktif seperti penyesuaian keyboard dan event listener saat teks berubah.
Tambahkan properti berikut pada Widget TextField di dalam metode build():
TextField(
decoration: const InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Misalnya Muhammad Hafiz',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person)),
controller: _textEditingController, // Line 7
keyboardType: TextInputType.text, // Line 8
onChanged: (text) { // Line 9 – 11
print('Sedang mengetik teks : ,$text');
},
),
Kode Lengkap:
Penjelasan Kode
-
controller: _textEditingControllerProperti ini menambahkan controller (`_textEditingController`). Ini adalah langkah krusial yang menghubungkan
TextFielddi tampilan dengan objek controller di kode Dart, memungkinkan kita untuk membaca atau memanipulasi teks input. -
keyboardType: TextInputType.textProperti ini menambahkan property jenis inputan yang boleh diinputkan oleh user. Dengan menyetelnya ke `TextInputType.text`, aplikasi memberi tahu sistem operasi untuk menampilkan keyboard standar yang dioptimalkan untuk input teks umum.
-
onChanged: (text) { ... }Ini adalah method yang digunakan untuk event listener ketika pengguna sedang menginputkan teks. Fungsi ini akan dieksekusi secara real-time (setiap kali teks berubah). Dalam contoh ini, teks input akan dicetak ke konsol setiap kali ada perubahan karakter.
6. Menambahkan Aksi pada ElevatedButton bertujuan untuk mendefinisikan apa yang terjadi ketika pengguna menekan tombol "Tampilkan mama". Logika yang ditambahkan akan mengambil data dari `TextField` dan menampilkannya sebagai pesan singkat menggunakan widget `SnackBar`.
Tambahkan kode berikut ke dalam properti `onPressed` pada widget ElevatedButton:
onPressed: () { // Line 1
String inputText = _textEditingController.text; // Line 2
ScaffoldMessenger.of(context).showSnackBar( // Line 3
SnackBar(content: Text('Nama anda adalah , $inputText')) // Line 4
); // Line 6
},
Kode Lengkap:
Penjelasan Kode
-
String inputText = _textEditingController.text;Baris ini membuat variable lokal bernama `inputText`. Fungsi utamanya adalah menampung inputan teks yang saat ini ada di dalam
TextField. Nilai tersebut diakses melalui properti `.text` dari `_textEditingController` (yang telah dihubungkan pada Langkah 4). Baris ini dieksekusi tepat pada saat tombol ditekan. -
ScaffoldMessenger.of(context).showSnackBar(...)Kode ini berfungsi untuk menampilkan inputan teks kepada pengguna menggunakan widget SnackBar. SnackBar adalah notifikasi non-modal yang muncul sebentar di bagian bawah layar. Prosesnya meliputi:
- `ScaffoldMessenger.of(context)`: Mengakses objek yang bertanggung jawab mengelola SnackBar untuk layar saat ini.
- `.showSnackBar()`: Memanggil fungsi untuk menampilkan SnackBar baru.
- `SnackBar(content: Text(...))`: Membuat konten SnackBar yang berisi pesan "Nama anda adalah ," diikuti oleh nilai yang ditampung dalam variabel `inputText`.
Setelah Langkah 6 selesai, pastikan untuk menjalankan Langkah 7, yaitu Simpan kemudian RUN, untuk melihat interaksi form yang sudah lengkap.
7. Simpan dan Jalankan (Final Output)
Penjelasan Output
-
Input Terproses: Saat tombol ditekan, aplikasi menggunakan `_textEditingController.text` untuk mengambil teks yang Anda masukkan.
-
Notifikasi SnackBar: Hasil dari kode di `onPressed` adalah munculnya SnackBar di bagian bawah layar. SnackBar ini berfungsi sebagai feedback instan kepada pengguna, menampilkan pesan konfirmasi: "Nama anda adalah , [Teks yang Anda Inputkan]".
-
Demonstrasi Interaktivitas: Output ini menunjukkan bahwa Anda telah berhasil membangun form interaktif yang mampu mengelola state (input teks) dan merespons event (tombol ditekan) menggunakan prinsip-prinsip dasar Flutter (`StatefulWidget`, `TextEditingController`, dan event listener).
Basic Form TextFormField (Dengan Validasi)
Untuk form yang membutuhkan validasi data yang ketat (seperti memastikan bidang tidak kosong atau email berformat benar), Flutter menyediakan widget khusus: `TextFormField`. Widget ini bekerja secara sinergis dengan widget `Form` dan objek `GlobalKey` untuk memproses validasi secara kolektif.
Widget yang digunakan di sini memiliki peran spesifik:
-
`TextFormField`: Versi
TextFieldyang dilengkapi properti `validator` untuk melakukan pengecekan data. -
`Form`: Sebuah wadah (container) yang mengelompokkan `TextFormField` dan memungkinkan validasi semua bidang secara serentak melalui fungsi `validate()`.
-
`GlobalKey
` : Kunci unik yang digunakan untuk mengakses state dari widget `Form`, terutama untuk memanggil metode `validate()` saat tombol diklik. -
`TextEditingController`: Digunakan untuk mengambil nilai input dari setiap `TextFormField`.
-
`ElevatedButton`: Tombol yang memicu proses validasi dan pengiriman data.
Berikut adalah langkah langkah penerapannya:
1. Buat file Dart baru dengan nama form-textformfield.dart di dalam folder lib.
2. Buat tampilan dasar dengan 2x `TextFormField` dan 1x `ElevatedButton` di dalam StatefulWidget (MyFormText).
3. Tambahkan pada class _MyFormTextState kode berikut:
final _formKey = GlobalKey(); // Line 1
final _nameController = TextEditingController(); // Line 2
final _emailController = TextEditingController(); // Line 3
@override
void dispose() { // Line 5 – 10
_nameController.dispose();
_emailController.dispose();
super.dispose();
}
void _submitForm(){ // Line 12 – 21: Logika Validasi dan Submit
if (_formKey.currentState!.validate()) { // Memanggil validasi
String name = _nameController.text;
String email = _emailController.text;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Validasi $name, $email Berhasil')) // Menampilkan hasil
);
}
}
Penjelasan Kode
-
Line 1 (`GlobalKey`): Membuat variable `GlobalKey
`. Ini adalah kunci unik yang memungkinkan kita mengakses status (state) dari widget Formyang akan kita buat, khususnya untuk memanggil fungsi validasi. -
Line 2 – 3 (`TextEditingController`): Membuat controller untuk masing-masing `TextFormField` (Nama dan Email), berfungsi untuk mengambil nilai input.
-
Line 5 – 10 (`dispose()`): Method yang berfungsi untuk menghapus controller (`_nameController` dan `_emailController`) dari memori saat widget dihapus, mencegah kebocoran memori.
-
Line 12 – 21 (`_submitForm()`): Method yang digunakan ketika tombol diklik. Di dalamnya terdapat:
- `_formKey.currentState!.validate()`: Perintah untuk menjalankan semua fungsi `validator` di seluruh `TextFormField` yang terbungkus dalam `Form`. Jika semua valid, akan mengembalikan `true`.
- Jika validasi berhasil, data nama dan email diambil dan ditampilkan melalui `SnackBar`.
Kode Lengkap:
4. Pada widget tampilan bungkus Column menggunakan widget `Form` dan tambahkan `key: _formKey`. Ini mengaitkan `GlobalKey` dengan form.
5. Ubah kode program pada `TextFormField` menjadi seperti kode program berikut ini (menambahkan `controller` dan `validator`).
// Modifikasi kode di dalam method build:
Widget build(BuildContext context) {
return Form(
key: _formKey, // Menghubungkan GlobalKey
child: Column(
children: [
const SizedBox(height: 10),
// TextFormField untuk Nama
TextFormField( // (1)
controller: _nameController, // Menghubungkan controller
decoration: const InputDecoration(
labelText: "Nama : ", border: OutlineInputBorder()),
validator: (value){ // Properti Validator
if (value == null || value.isEmpty) {
return 'Masukkan nama anda'; // Pesan error jika kosong
}
return null; // Tidak ada error
},
),
const SizedBox(height: 10),
// TextFormField untuk Email
TextFormField( // (2)
controller: _emailController, // Menghubungkan controller
decoration: const InputDecoration(
labelText: "Email : ", border: OutlineInputBorder()),
validator: (value){ // Properti Validator
if (value == null || value.isEmpty) {
return 'Masukkan email anda ';
}
if(!value.contains('@')){ // Pengecekan format email
return 'Email tidak valid';
}
return null;
},
),
// ... (SizedBox dan ElevatedButton)
],
),
);
}
Penjelasan Validasi (`validator` Property)
-
`validator: (value) { ... }`: Properti yang menerima fungsi yang akan dieksekusi saat `_formKey.currentState!.validate()` dipanggil. Input teks saat ini akan dikirim sebagai variabel `value`.
-
Jika validasi gagal (misalnya, `value.isEmpty` atau email tidak mengandung `@`), fungsi harus mengembalikan String berisi pesan error. Pesan ini akan otomatis ditampilkan di bawah bidang input.
-
Jika validasi berhasil, fungsi harus mengembalikan `null`.
6. Selanjutnya tambahkan method `_submitForm` pada method `onPressed` di `ElevatedButton` seperti kode program berikut.
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm, // Memanggil method validasi & submit
child: const Text('Submit'))
)
Dengan menautkan `onPressed: _submitForm`, tombol sekarang akan memicu seluruh proses validasi form yang telah didefinisikan pada Langkah 3.
Kode Lengkap:
7. Simpan dan RUN. Sekarang ketika tombol Submit diklik, dua skenario akan terjadi:
-
Validasi Gagal (Form Kosong): Jika Anda mengklik tombol tanpa mengisi input, fungsi `validator` akan berjalan, mengembalikan pesan error String, dan form akan menampilkan pesan tersebut di bawah bidang input.
-
Validasi Berhasil: Jika semua bidang terisi dengan format yang benar, `_submitForm` akan mengeksekusi kode di dalam blok `if` (berhasil), dan `SnackBar` akan muncul menampilkan data yang telah divalidasi.
Output menunjukkan pesan berhasil validasi muncul di bawah `TextFormField` saat kondisi `validator` terpenuhi.
Kesimpulan Akhir: Membandingkan TextField dan TextFormField
Meskipun keduanya berfungsi untuk menerima input teks dari pengguna, `TextField` dan `TextFormField` memiliki peran yang sangat berbeda dalam hal kompleksitas form, manajemen state, dan validasi data:
TextField: Solusi Input Dasar
-
Fungsi Utama: Digunakan untuk input tunggal atau sederhana di mana validasi data tidak diperlukan atau diatasi dengan logika kustom di luar widget itu sendiri.
-
Pengelolaan Data: Membutuhkan `TextEditingController` untuk mengambil input dan mengelola pembersihan memori melalui `dispose()`. Aksi dipicu menggunakan event listener seperti `onChanged` atau `onPressed` pada tombol terkait.
-
Validasi: Tidak memiliki properti `validator` bawaan. Validasi harus dilakukan secara manual di dalam fungsi `onPressed` atau event lainnya.
TextFormField: Solusi Form Interaktif dengan Validasi
-
Fungsi Utama: Dirancang untuk membangun form multi-bidang yang kompleks yang membutuhkan validasi data terstruktur (misalnya, Required, format Email, atau panjang minimum).
-
Integritas Form: Bekerja secara sinergis dengan widget `Form` dan `GlobalKey
`. `GlobalKey` memungkinkan kontrol terpusat atas seluruh form, memastikan semua bidang divalidasi dengan satu perintah (`validate()`). -
Validasi: Menggunakan properti `validator`. Validator secara otomatis menampilkan pesan error String di bawah bidang input jika validasi gagal, dan hanya mengizinkan pemrosesan data ketika mengembalikan `null`.
Ringkasan Pengambilan Keputusan
Pilih `TextField` jika Anda hanya membutuhkan kotak input sederhana tanpa validasi error yang terlihat di UI. Pilih `TextFormField` jika Anda membangun form yang memerlukan integritas data tinggi, membutuhkan validasi yang terlihat oleh pengguna, dan ingin mengelola status validasi seluruh form secara kolektif menggunakan `Form` dan `GlobalKey`.
Dalam kedua kasus, manajemen memori melalui `TextEditingController` dan implementasi `dispose()` adalah praktik wajib untuk mencegah memory leaks dan menjaga efisiensi aplikasi.
Link Tugas bisa diakses melalui link : https://github.com/muhammadhafiz27/Dart-Kalkulator-Sederhana.git