formtextfieldinputMenengah4 mnt baca

TextField & TextEditingController

Input teks: membaca nilai, membersihkan, dan mengontrol cursor.

Kenapa Perlu Controller?

TextField punya dua cara membaca input: onChanged (callback setiap ketikan) dan TextEditingController. Callback cukup untuk kasus sederhana, tapi controller memberi kendali penuh dari kode: membaca nilai kapan saja, mengisi nilai secara program, memindahkan cursor, sampai mendengarkan perubahan tanpa rebuild.

dart
final _namaController = TextEditingController();

TextField(
  controller: _namaController,
  onChanged: (nilai) => print(nilai), // opsional, untuk reaksi tiap ketikan
)

ElevatedButton(
  onPressed: () {
    print('Nama: ${_namaController.text}');
    _namaController.clear(); // kosongkan dari kode
  },
  child: const Text('Cetak & Bersihkan'),
)

Nilai Awal dan Manipulasi Teks

Beri nilai awal lewat konstruktor, dan ubah teks kapan saja dari kode:

dart
final _kotaController = TextEditingController(text: 'Bandung');

// nanti, misalnya tombol "Isi Otomatis":
_kotaController.text = 'Jakarta'; // cursor otomatis pindah ke akhir

Edge case yang sering mengejutkan: meng-assign .text selalu memindahkan cursor ke akhir teks. Kalau kamu perlu menjaga posisi cursor (misalnya saat memformat input nomor HP sambil mengetik), set value lengkap dengan selection:

dart
_nomorController.value = TextEditingValue(
  text: teksTerformat,
  selection: TextSelection.collapsed(offset: teksTerformat.length),
);

Mendengarkan Perubahan Tanpa Rebuild

addListener berguna saat perubahan teks harus memicu logika, bukan tampilan (misalnya mengaktifkan tombol hanya jika input valid), tanpa me-rebuild widget lewat setState:

dart
@override
void initState() {
  super.initState();
  _namaController.addListener(() {
    final valid = _namaController.text.trim().length >= 3;
    _tombolAktif.value = valid; // ValueNotifier, tanpa setState
  });
}

Jangan lupa removeListener kalau listener-nya fungsi bernama, atau biarkan ikut terbuang saat controller di-dispose (listener anonim aman ikut ter-dispose bersama controller).

Contoh Nyata: Pencarian Live

dart
final _cariController = TextEditingController();
final _hasil = ValueNotifier<List<String>>([]);

@override
void initState() {
  super.initState();
  _cariController.addListener(() {
    final q = _cariController.text.toLowerCase();
    _hasil.value = _semuaMenu
        .where((m) => m.toLowerCase().contains(q))
        .toList();
  });
}

// di build:
Column(children: [
  TextField(
    controller: _cariController,
    decoration: const InputDecoration(
      labelText: 'Cari menu',
      prefixIcon: Icon(Icons.search),
    ),
  ),
  ValueListenableBuilder<List<String>>(
    valueListenable: _hasil,
    builder: (context, hasil, _) => Column(
      children: hasil.map((m) => ListTile(title: Text(m))).toList(),
    ),
  ),
])

Catatan teknis: Satu controller untuk satu field saja. Memakai controller yang sama di dua TextField membuat keduanya berbagi teks dan cursor, hasilnya perilaku aneh yang sulit di-debug.

Kapan TIDAK Perlu Controller

  • Hanya butuh nilai saat tombol ditekan dan tidak perlu mengubah teks dari kode: onChanged + variabel lokal cukup, lebih sedikit boilerplate.
  • Field di dalam Form yang nilainya diambil lewat onSaved: biarkan FormState yang mengelola.
  • Input sekali pakai yang langsung di-submit (misalnya kolom komentar cepat): pakai onSubmitted saja.

Wajib Dispose

Controller menahan listener dan koneksi ke text input system. Selalu dispose:

dart
@override
void dispose() {
  _namaController.dispose();
  super.dispose();
}

Pengecualian: controller yang dibuat oleh TextFormField internal (tanpa kamu pass controller sendiri) diurus oleh Flutter, tidak perlu kamu dispose.

Tantangan

Input nama + sapa

Buat TextField nama + tombol 'Sapa'. Saat ditekan, tampilkan SnackBar 'Halo, [nama]!' dan kosongkan field.