InputDecoration
Percantik input: label, hint, icon, border, dan pesan error.
Kenapa InputDecoration Penting?
Input polos (kotak abu-abu tanpa label) membuat user menebak-nebak: field apa ini, formatnya bagaimana, dan kenapa error. InputDecoration menjawab semuanya dalam satu tempat: label, contoh format, ikon, sampai pesan error.
TextField(
decoration: InputDecoration(
labelText: 'Email',
hintText: '[email protected]',
prefixIcon: const Icon(Icons.email),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () => _emailController.clear(),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
fillColor: Colors.grey[100],
),
)Label vs Hint: Jangan Tertukar
- labelText: mengambang ke atas saat field fokus atau terisi, jadi selalu terlihat. Ini identitas field.
- hintText: contoh format, hilang begitu user mengetik. Ini bantuan sementara.
Aturan aksesibilitasnya sederhana: jangan pakai hint sebagai pengganti label untuk field wajib. Saat user mengetik, hint hilang dan mereka lupa sedang mengisi field apa. Screen reader pun lebih mengandalkan label.
Untuk teks bantuan yang tidak boleh hilang, pakai helperText (misalnya "Minimal 8 karakter, campur huruf dan angka").
Border untuk Setiap State
Field punya beberapa kondisi visual, dan tiap kondisi bisa punya border sendiri:
InputDecoration(
labelText: 'Password',
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Colors.grey),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Colors.deepPurple, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Colors.red),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Colors.red, width: 2),
),
)Daripada mengulang di setiap field, set sekali secara global:
MaterialApp(
theme: ThemeData(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
filled: true,
fillColor: Colors.grey[100],
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
),
)Catatan teknis:
errorTextyang di-set manual akan menimpa pesan error darivalidatorpadaTextFormField. Pilih satu: validasi otomatis via validator, atau error manual via errorText untuk kasus seperti error dari server.
Contoh Nyata: Field Password dengan Toggle
bool _sembunyi = true; // di State
TextField(
obscureText: _sembunyi,
decoration: InputDecoration(
labelText: 'Kata Sandi',
hintText: 'Minimal 8 karakter',
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(_sembunyi ? Icons.visibility : Icons.visibility_off),
onPressed: () => setState(() => _sembunyi = !_sembunyi),
),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
),
)Edge Case yang Sering Terjadi
- Layout melompat saat error muncul: pesan error menambah tinggi field sehingga form "melompat". Atasi dengan memberi ruang tetap: bungkus field dalam
SizedBox/Containerdengan tinggi cukup, atau seterrorStyledengan tinggi baris kecil. - Suffix icon menutupi teks panjang: beri
contentPaddingkanan yang cukup agar teks tidak tertutup ikon. counterText:maxLengthotomatis menampilkan counter "3/16". Kalau tidak mau, setcounterText: ''(bukan menghapus maxLength, supaya batas tetap jalan).- Dense untuk form padat:
isDense: true+contentPaddingkecil membuat form admin/data-entry lebih ringkas tanpa custom widget.
Kapan JANGAN Berlebihan
Dekorasi yang konsisten lebih penting daripada dekorasi yang ramai. Hindari: warna border berbeda-beda tiap field, terlalu banyak ikon dekoratif yang tidak punya fungsi, dan radius border yang tidak konsisten. Satu inputDecorationTheme global biasanya mengalahkan 20 dekorasi manual yang berbeda-beda.
Tantangan
Field password rapi
Buat TextField password dengan labelText 'Kata Sandi', prefixIcon lock, suffixIcon visibility yang toggle obscureText, dan border rounded 12.