Input Widgets dan Basic Form

Card image

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 TextEditingController untuk 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 validator yang 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 FormState untuk melakukan validasi secara kolektif menggunakan metode validate().

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

  • FormState adalah kelas yang mengelola status dari widget Form, 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 seperti validate() atau save() dari luar widget Form, biasanya dari tombol `onPressed` pada ElevatedButton.


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()`:

  1. Ketika Anda memanggil _formKey.currentState!.validate(), Flutter akan mulai mengecek setiap TextFormField yang terikat pada Form tersebut.

  2. Fungsi validator yang telah didefinisikan pada setiap TextFormField akan dijalankan.

  3. Jika ada satu saja fungsi validator yang mengembalikan nilai String (yaitu pesan error), maka:

    • Proses validasi akan berhenti.
    • Metode validate() akan mengembalikan nilai false.
    • Pesan error tersebut akan ditampilkan secara otomatis di bawah TextFormField yang gagal.
  4. Jika semua fungsi validator mengembalikan nilai null (artinya tidak ada error), maka metode validate() akan mengembalikan nilai true, 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:

Gambar membuat file form_textfield.dart

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 sebagai StatefulWidget karena form dirancang untuk memproses input yang akan mengubah keadaan UI di masa mendatang.

  • _MyFormState: Kelas state yang membangun tampilan UI (build method).

  • Padding dan Column: Digunakan untuk mengatur tata letak. Padding memberi jarak konten dari tepi, dan Column menyusun widget secara vertikal.

  • TextField: Digunakan untuk menerima input. Properti decoration memberikan tampilan label, placeholder (hint), dan ikon yang rapi.

  • ElevatedButton: Tombol yang diatur warna latarnya (amber) dan warna teksnya (hitam) melalui properti style. Properti onPressed: () {} saat ini kosong, yang berarti tombol akan tampil tetapi belum melakukan aksi apa pun saat ditekan.

Output

Gambar membuat file form_textfield.dart

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:

Gambar membuat file form_textfield.dart

Penjelasan Kode

  • controller: _textEditingController

    Properti ini menambahkan controller (`_textEditingController`). Ini adalah langkah krusial yang menghubungkan TextField di tampilan dengan objek controller di kode Dart, memungkinkan kita untuk membaca atau memanipulasi teks input.

  • keyboardType: TextInputType.text

    Properti 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:

Gambar membuat file form_textfield.dart

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:

    1. `ScaffoldMessenger.of(context)`: Mengakses objek yang bertanggung jawab mengelola SnackBar untuk layar saat ini.
    2. `.showSnackBar()`: Memanggil fungsi untuk menampilkan SnackBar baru.
    3. `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)

Gambar membuat file form_textfield.dart

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 TextField yang 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).

Gambar membuat file form_textfield.dart

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 Form yang 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:

Gambar membuat file form_textfield.dart

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:

Gambar membuat file form_textfield.dart

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.

Tampilan form TextFormField dengan pesan validasi berhasil

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

Setup Environment, Dart Dasar dan OOP Dart

Card image Read More

Setup Flutter Development, Stateless & Statefull Widget, Basic Widgets

Card image Read More