Praktikum ini membahas Input Widgets dan Basic Form pada Flutter. Tujuannya membuat form yang bisa menerima inputan dari keyboard lalu memvalidasi dan mengelola inputan tersebut.
ITujuan Praktikum
Setelah menyelesaikan praktikum ini, mahasiswa diharapkan mampu:
- ✓mampu membuat membuat basic form untuk menerima inputan dari keyboard dan mengelola inputan
- ✓Membuat beberapa input widgets
- ✓Membuat dan mengontrol inputan dari user
IIDasar Teori
Basic Form
Basic Form merupakan widget yang berfungsi sebagai inputan nilai seperti TextField, TextFormField,
CheckBox, Switch, Dropdown, Radio, Dialog, DatePicker, BottomSheet, Snackbar dan lain-lain. Basic
Form digunakan untuk validasi dan mengelola inputan dari berbagai field.
Form akan memberikan tampilan inputan kemudian inputan akan diperiksa apakah sudah sesuai
dengan aturan atau format yang ditetapkan, selanjtunya data inputan akan diambil nilainya setelah
proses pengecekan selesai dilakukan.
TextField
TextField adalah widget yang digunakan untuk memasukkan text oleh pengguna, widget ini biasanya
digunakan untuk membuat form inputan seperti form login, pencarian dll.
Fitur TextField:
Menerima input dari keyboard
Memiliki property yang lengkap style, decoration, dan jenis inputan
Dapat mengelola teks menggunakan TextEditingController
IIIAlat dan Bahan
IVLangkah Kerja Praktikum
Basic Form TextField
1.1 — Buat file dart baru dengan nama form-textfield.dart di dalam folder lib.
1.2 — Buat tampilan basic form menggunakan widget TextField untuk inputan dan ElevatedButton untuk memberikan event listener. Tuliskan kode program berikut:
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> {
@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,
),
const TextField(
decoration: InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Misalnya masnoer',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person)),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.amber, foregroundColor: Colors.black),
child: const Text('Tampilkan mama'))
],
),
);
}
}1.3 — Pada class _MyFormState tambahkan kode program berikut:
TextEditingController _textEditingController = TextEditingController();
@override
void dispose() {
_textEditingController.dispose();
super.dispose();
}Penjelasan:
Baris pertama membuat variabel dari
TextEditingController. Controller ini berfungsi untuk mengambil inputan dari user.Method
dispose()digunakan untuk membersihkan controller teks inputan ketika widget sudah tidak digunakan.
1.4 — Tambahkan property controller, keyboardType, dan onChanged pada widget TextField:
TextField(
decoration: InputDecoration(
labelText: 'Nama Lengkap',
hintText: 'Misalnya masnoer',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
controller: _textEditingController,
keyboardType: TextInputType.text,
onChanged: (text) {
print('Sedang mengetik teks: ,$text');
},
),Penjelasan:
controller: _textEditingControllermenghubungkan TextField dengan controller.keyboardType: TextInputType.textmenentukan jenis inputan yang boleh diinputkan oleh user.onChangedmerupakan event listener yang dijalankan ketika user sedang mengetikkan teks.Kata kunci
constpadaTextFielddihapus karena_textEditingControllerbukan nilai konstanta.
1.5 — Tambahkan kode program berikut pada onPressed di widget ElevatedButton:
onPressed: () {
String inputText = _textEditingController.text;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Nama anda adalah , $inputText')),
);
},Penjelasan:
Variabel
inputTextberfungsi untuk menampung inputan dari keyboard melalui_textEditingController.text.ScaffoldMessenger.of(context).showSnackBar(...)menampilkan inputan teks menggunakan widgetSnackBar.
1.6 — Simpan kemudian RUN. Ketikkan nama pada TextField (teks yang sedang diketik akan tercetak di console), lalu tekan tombol Tampilkan mama, maka nama akan tampil pada SnackBar.
Basic Form TextFormField
2.1 — Buat file baru di dalam folder lib dengan nama form-textformfield.dart.
2.2 — Buat form inputan menggunakan 2 buah widget TextFormField dan 1 buah ElevatedButton. Tuliskan kode program berikut:
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> {
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(
height: 10,
),
TextFormField(
decoration: const InputDecoration(
labelText: "Nama : ", border: OutlineInputBorder()),
),
const SizedBox(
height: 10,
),
TextFormField(
decoration: const InputDecoration(
labelText: "Email : ", border: OutlineInputBorder()),
),
const SizedBox(
height: 10,
),
SizedBox(
width: double.infinity,
child:
ElevatedButton(onPressed: () {}, child: const Text('Submit')))
],
);
}
}2.3 — Tambahkan pada class _MyFormTextState kode program berikut:
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')),
);
}
}Penjelasan:
_formKeymerupakan variabelGlobalKey<FormState>untuk mengakses state dari Form._nameControllerdan_emailControllermerupakan controller untuk TextFormField.Method
dispose()berfungsi untuk menghapus controller inputan name dan email.Method
_submitForm()digunakan ketika tombol diklik._formKey.currentState!.validate()menjalankan semua validator; jika semua valid maka SnackBar ditampilkan.
2.4 — Pada widget tampilan, bungkus menggunakan widget Form dan tambahkan key: _formKey. Tambahkan juga Padding agar form tidak menempel ke tepi layar:
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// ... TextFormField dan ElevatedButton
],
),
),
);2.5 — Ubah kode program pada kedua TextFormField dengan menambahkan controller dan validator:
TextFormField Nama:
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: "Nama : ",
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan nama anda';
}
return null;
},
),TextFormField 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';
}
return null;
},
),Penjelasan:
Validator nama mengembalikan pesan error jika inputan kosong.
Validator email mengembalikan pesan error jika inputan kosong atau tidak mengandung karakter
@.Jika validator mengembalikan
null, berarti inputan valid.
2.6 — Tambahkan method _submitForm pada onPressed di ElevatedButton:
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
),2.7 — Simpan dan RUN. Tekan tombol Submit saat inputan kosong, maka pesan error akan muncul di bawah TextFormField. Isi nama dan email yang benar lalu tekan Submit, maka SnackBar "Validasi ... Berhasil" akan tampil.
VLatihan dan Tugas
Buat 2 buah file dart di dalam folder lib, yaitu main.dart dan user_registration_form.dart.
Pada file main.dart, gunakan MaterialApp sebagai root dan set property home ke widget UserRegistrationForm:
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 const MaterialApp(
home: UserRegistrationForm(),
);
}
}Penjelasan:
import 'user_registration_form.dart';digunakan agar classUserRegistrationFormdari file lain dapat dipanggil dimain.dart.MaterialAppmenjadi root aplikasi dan propertyhomediisi dengan widgetUserRegistrationForm.
Pada file user_registration_form.dart, buat class UserRegistrationForm menggunakan StatefulWidget:
import 'package:flutter/material.dart';
class UserRegistrationForm extends StatefulWidget {
const UserRegistrationForm({super.key});
@override
State<UserRegistrationForm> createState() => _UserRegistrationFormState();
}Pada class _UserRegistrationFormState, buat variabel GlobalKey, 4 buah controller, dan method dispose():
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();
}Penjelasan:
_formKeydigunakan untuk mengaksesFormStatesehingga dapat memanggil methodvalidate().Masing-masing controller digunakan untuk mengambil nilai inputan nama, email, password, dan confirm password.
Method
dispose()membersihkan semua controller ketika widget tidak digunakan lagi.
Buat method _submitForm() yang akan dipanggil ketika tombol Submit ditekan:
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')),
);
}
}Penjelasan:
_formKey.currentState!.validate()menjalankan validator pada seluruh TextFormField.Jika semua inputan valid, nilai nama dan email diambil dari controller lalu ditampilkan melalui
SnackBar.
Buat method build() yang mengembalikan Scaffold, lalu bungkus inputan menggunakan widget Form dengan key: _formKey:
@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: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
// ... 4 TextFormField dan ElevatedButton
],
),
),
),
);
}Penjelasan:
Karena property
homepadaMaterialApplangsung diisiUserRegistrationForm, makaScaffolddanAppBardibuat di dalamUserRegistrationForm.
Tambahkan TextFormField untuk inputan Nama dengan validator tidak boleh kosong:
const SizedBox(height: 10),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: "Nama : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
keyboardType: TextInputType.text,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan nama anda';
}
return null;
},
),Tambahkan TextFormField untuk inputan Email dengan validator harus menggunakan format @:
const SizedBox(height: 10),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: "Email : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan email anda';
}
if (!value.contains('@')) {
return 'Email tidak valid';
}
return null;
},
),Penjelasan:
!value.contains('@')bernilaitruejika inputan email tidak mengandung karakter@, sehingga validator mengembalikan pesan'Email tidak valid'.
Tambahkan TextFormField untuk inputan Password:
const SizedBox(height: 10),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: "Password : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan password anda';
}
return null;
},
),Penjelasan:
obscureText: truedigunakan agar teks password yang diketik disembunyikan.
Tambahkan TextFormField untuk inputan Confirm Password dengan validator harus sama dengan password:
const SizedBox(height: 10),
TextFormField(
controller: _confirmPasswordController,
decoration: const InputDecoration(
labelText: "Confirm Password : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan confirm password anda';
}
if (value != _passwordController.text) {
return 'Password dan confirm password tidak sama';
}
return null;
},
),Penjelasan:
value != _passwordController.textmembandingkan inputan confirm password dengan inputan password. Jika tidak sama, validator mengembalikan pesan error.
Tambahkan ElevatedButton dengan onPressed yang memanggil _submitForm:
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
),Simpan dan RUN file main.dart
#1 Kode program lengkap 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 const MaterialApp(
home: UserRegistrationForm(),
);
}
}#2 Kode program lengkap 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: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(height: 10),
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: "Nama : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
keyboardType: TextInputType.text,
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(),
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan email anda';
}
if (!value.contains('@')) {
return 'Email tidak valid';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: "Password : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan password anda';
}
return null;
},
),
const SizedBox(height: 10),
TextFormField(
controller: _confirmPasswordController,
decoration: const InputDecoration(
labelText: "Confirm Password : ",
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Masukkan confirm password anda';
}
if (value != _passwordController.text) {
return 'Password dan confirm password tidak sama';
}
return null;
},
),
const SizedBox(height: 10),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submitForm,
child: const Text('Submit'),
),
),
],
),
),
),
);
}
}VIKesimpulan
Basic Form pada Flutter merupakan kumpulan widget inputan (seperti
TextFielddanTextFormField) yang digunakan untuk menerima, memvalidasi, dan mengelola inputan dari user. Inputan diperiksa terlebih dahulu apakah sesuai aturan, kemudian nilainya diambil setelah proses pengecekan selesai.TextFielddigunakan untuk menerima input teks dari keyboard. Nilai inputannya dapat diambil menggunakanTextEditingController, dan perubahan teks dapat dipantau menggunakan eventonChanged.TextFormFieldmerupakan versi lengkap dariTextFieldyang terintegrasi denganForm. Melalui propertyvalidator, inputan dapat diperiksa dan pesan error otomatis ditampilkan di bawah field jika validasi gagal.GlobalKey<FormState>digunakan untuk mengakses state dari widgetForm, sehingga methodvalidate()dapat dipanggil darionPressedpadaElevatedButton. Methodvalidate()mengembalikantruejika semua validator mengembalikannull, danfalsejika ada validator yang mengembalikan pesan error.Controller perlu dibersihkan menggunakan method
dispose()ketika widget sudah tidak digunakan agar tidak membebani memori.SnackBaryang dipanggil melaluiScaffoldMessenger.of(context).showSnackBar()dapat digunakan untuk menampilkan hasil inputan atau pesan berhasil kepada user.Penggunaan kata kunci
constpada widget yang nilainya tetap dapat meningkatkan performa karena widget tersebut tidak di-build ulang. Namun widget yang menggunakan nilai yang berubah (seperti controller) tidak boleh menggunakanconst.Pada tugas Kelas B, form pendaftaran pengguna berhasil dibuat dengan memisahkan kode menjadi dua file (
main.dartdanuser_registration_form.dart). Validasi berhasil diterapkan: semua inputan wajib diisi, email harus mengandung@, serta password dan confirm password harus sama. Jika semua valid, method_submitFormmenampilkan pesan pendaftaran berhasil.