PRAKTIKUM 1: Input Widgets dan Basic Form pada Flutter
NIM: 2411533019 - Adila Bintang Fauzi
Pengembangan aplikasi mobile modern menuntut pengelolaan interaksi pengguna yang responsif, terstruktur, dan
tervalidasi dengan baik. Pada framework Flutter, penanganan input dari pengguna dilakukan melalui komponen
formulir dasar (Basic Form) dan Input Widgets, seperti TextField, TextFormField,
CheckBox, Switch, Dropdown, Radio,
DatePicker, hingga komponen antarmuka dialog pendukung seperti BottomSheet dan
SnackBar. Komponen-komponen ini berfungsi menangkap data, menampilkan field input, memvalidasi
format data secara akurat, serta mengambil nilai inputan untuk diproses pada tingkat logika aplikasi.
Dalam praktik pengembangannya, Flutter menyediakan dua komponen utama untuk menangani input teks, yaitu
TextField dan TextFormField. TextField digunakan untuk penangkapan teks
sederhana secara langsung dari keyboard dengan pengelolaan state mandiri via
TextEditingController, di mana validasi dan pesan kesalahan harus ditangani secara manual.
Sebaliknya, TextFormField merupakan widget pembungkus TextField yang terintegrasi
langsung dengan logika Form. Komponen ini dilengkapi dengan properti bawaan validator untuk
memeriksa aturan input secara otomatis dan menampilkan pesan kesalahan di bawah field jika data tidak sesuai,
serta dapat dikontrol secara terpusat menggunakan FormState.
Pengelolaan formulir yang efisien di Flutter ditopang oleh beberapa komponen kunci.
TextEditingController digunakan untuk mengontrol dan membaca teks pada input field, yang mana
wajib dibersihkan melalui metode dispose() untuk mencegah kebocoran memori (memory leak). Untuk
mengidentifikasi dan mengakses status dari Form dari luar widget, digunakan
GlobalKey<FormState>. Status ini menyediakan fungsi validate() yang
mengeksekusi seluruh validator TextFormField di dalam Form secara kolektif—menghasilkan nilai
true jika seluruh data valid atau false jika terdapat kesalahan. Selain itu,
pembaruan antarmuka saat terjadi perubahan state ditangani oleh metode setState(), sedangkan
optimasi memori dan performa reloading aplikasi dicapai dengan menerapkan kata kunci const pada
widget konstan saat kompilasi.
TextEditingController.TextFormField, FormState, dan
GlobalKey.dispose()), serta
peremajaan antarmuka menggunakan setState().
Berikut merupakan kode program pembuatan form dasar menggunakan TextField untuk mengambil input
nama dan menampilkannya pada notifikasi SnackBar:
// NIM: 2411533019 - Adila Bintang Fauzi
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Basic Form')),
body: const MyForm(),
),
);
}
}
class MyForm extends StatefulWidget {
const MyForm({super.key});
@override
State<MyForm> createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
final TextEditingController _textEditingController = TextEditingController();
@override
void dispose() {
_textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Masukkan nama anda :'),
const SizedBox(height: 10),
TextField(
controller: _textEditingController,
keyboardType: TextInputType.text,
decoration: const InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Misalnya masnoer',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
onChanged: (text) {
print('Sedang mengetik teks: $text');
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
String inputText = _textEditingController.text;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Nama anda adalah: $inputText')),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber,
foregroundColor: Colors.black,
),
child: const Text('Tampilkan nama'),
),
],
),
);
}
}
Penjelasan Kode & Alur Kerja:
_textEditingController dibuat untuk
menangkap dan memantau isi input field secara langsung. Pada method dispose(), controller
dibersihkan untuk mencegah kebocoran memori (memory leak).decoration: InputDecoration(...) dengan
OutlineInputBorder() dan ikon Icons.person agar tampilan input lebih komunikatif.
Event onChanged digunakan untuk memantau setiap karakter yang diketik oleh pengguna melalui
konsol terminal.ElevatedButton ditekan, aplikasi mengambil nilai
teks melalui properti _textEditingController.text lalu menampilkannya sebagai pesan notifikasi
melayang via ScaffoldMessenger.of(context).showSnackBar().
Tampilan form saat input nama "lala" dimasukkan dan tombol "Tampilkan mama" ditekan, memunculkan SnackBar di bagian bawah:
Berikut merupakan kode program terstruktur menggunakan widget Form,
GlobalKey<FormState>, serta mekanisme validasi input Nama dan Email:
// NIM: 2411533019 - Adila Bintang Fauzi
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("Basic Form TextFormField")),
body: const MyFormText(),
),
);
}
}
class MyFormText extends StatefulWidget {
const MyFormText({super.key});
@override
State<MyFormText> createState() => _MyFormTextState();
}
class _MyFormTextState extends State<MyFormText> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
super.dispose();
}
void _submitForm() {
if (_formKey.currentState!.validate()) {
String name = _nameController.text;
String email = _emailController.text;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Validasi $name, $email Berhasil')),
);
}
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: "Nama",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan nama anda';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: "Email",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan email anda';
}
if (!value.contains('@')) {
return 'Email tidak valid';
}
return null;
},
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
),
],
),
),
);
}
}
Penjelasan Validasi & Alur Program:
_formKey digunakan sebagai
identitas unik widget Form, memungkinkan akses method validate() dari luar pohon
widget.TextFormField memiliki parameter callback
validator:
value == null || value.isEmpty). Jika kosong,
mengembalikan string error 'Masukkan nama anda'.!value.contains('@') untuk memastikan adanya tanda '@'. Jika tidak ada, mengembalikan pesan
error 'Email tidak valid'._formKey.currentState!.validate() dieksekusi. Apabila seluruh field valid (mengembalikan
true), maka SnackBar berhasil ditampilkan. Sebaliknya jika ada field yang salah,
pesan error otomatis dicetak merah di bawah masing-masing kolom input.
Tampilan saat nama "zaki" dan email "zaki@gmail.com" berhasil lolos validasi, memicu SnackBar notifikasi sukses:
Tugas Kelas B: Form Pendaftaran Pengguna
Membuat aplikasi Form Pendaftaran Pengguna terstruktur menggunakan Flutter dengan spesifikasi:
main.dart dan
user_registration_form.dart.@'.StatefulWidget, manajemen controller, serta pemanggilan method
_submitForm() saat tombol submit ditekan.Source Code Program:
1. main.dart:
import 'package:flutter/material.dart';
import 'user_registration_form.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Form Pendaftaran Pengguna',
home: const UserRegistrationForm(),
);
}
}
2. user_registration_form.dart:
import 'package:flutter/material.dart';
class UserRegistrationForm extends StatefulWidget {
const UserRegistrationForm({super.key});
@override
State<UserRegistrationForm> createState() => _UserRegistrationFormState();
}
class _UserRegistrationFormState extends State<UserRegistrationForm> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
void _submitForm() {
if (_formKey.currentState!.validate()) {
String name = _nameController.text;
String email = _emailController.text;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Pendaftaran $name ($email) Berhasil!')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Form Pendaftaran Pengguna"),
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 10),
// Input Nama
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: "Nama : ",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan nama anda';
}
return null;
},
),
const SizedBox(height: 10),
// Input Email
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: "Email : ",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan email anda';
}
if (!value.contains('@')) {
return 'Email tidak valid (harus mengandung @)';
}
return null;
},
),
const SizedBox(height: 10),
// Input Password
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: "Password : ",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan password anda';
}
return null;
},
),
const SizedBox(height: 10),
// Input Confirm Password
TextFormField(
controller: _confirmPasswordController,
obscureText: true,
decoration: const InputDecoration(
labelText: "Confirm Password : ",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Konfirmasi password anda';
}
if (value != _passwordController.text) {
return 'Password dan Confirm Password tidak sama';
}
return null;
},
),
const SizedBox(height: 20),
// Tombol Submit
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
),
],
),
),
),
),
);
}
}
Pembahasan Logika Kode Program Tugas:
main.dart bertindak murni sebagai entry-point
(menginisialisasi MaterialApp) dan memanggil widget terpisah UserRegistrationForm.
Hal ini menjaga kode tetap bersih, mudah didebug, dan siap diskalakan.Column formulir dengan
SingleChildScrollView mencegah terjadinya pesan error RenderFlex overflowed by X
pixels ketika keyboard virtual smartphone muncul dan memperkecil ruang layar.obscureText: true pada field
password dan confirm password untuk menyamarkan karakter input menjadi titik-titik demi menjaga privasi
pengguna.value != _passwordController.text). Apabila string
kedua input tidak identik, sistem menolak submit dan memunculkan error peringatan.
Tampilan antarmuka Form Pendaftaran Pengguna yang diisi lengkap beserta notifikasi SnackBar "Pendaftaran zaki (zaki@gmail.com) Berhasil!":
Berdasarkan praktikum yang telah dilaksanakan mengenai Input Widgets dan Basic Form pada Flutter, dapat ditarik beberapa kesimpulan penting sebagai berikut:
TextField sangat efektif untuk
penanganan input teks sederhana dan independen. Sementara TextFormField merupakan solusi ideal
untuk formulir terstruktur karena langsung terintegrasi dengan widget Form dan menyediakan
parameter validator bawaan untuk validasi deklaratif.
GlobalKey<FormState> memungkinkan kontrol terpusat terhadap seluruh form. Cukup dengan
satu pemanggilan method _formKey.currentState!.validate(), seluruh validator di dalam form akan
serempak dieksekusi tanpa perlu validasi manual berulang kali.
TextEditingController wajib di-dispose secara eksplisit di dalam lifecycle method
dispose() pada StatefulWidget untuk memastikan resource memori perangkat dilepas
secara bersih saat widget tidak lagi digunakan.
obscureText: true memberikan keamanan esensial untuk input kata sandi, sedangkan penggunaan
SingleChildScrollView mencegah terjadinya bottom overflow error saat keyboard virtual
perangkat lunak aktif.
main.dart dan user_registration_form.dart) membuat
struktur aplikasi lebih rapi, modular, mudah dibaca, serta memudahkan perawatan (maintainability)
pada proyek skala besar.