Form Input Transaksi pada Flutter
Implementasi form pencatatan transaksi menggunakan TextFormField, DropdownButtonFormField, validasi input, showDatePicker, dan SnackBar pada Flutter.
Input transaksi
Periksa field wajib
Pilih tanggal transaksi
Feedback penyimpanan
1. Tujuan Praktikum
Memahami pembuatan form transaksi di Flutter menggunakan StatefulWidget, Form, TextFormField, DropdownButtonFormField, controller, validasi, dan SnackBar. Pada tugas, form dikembangkan dengan input tanggal melalui showDatePicker.
Form Input
Mengelola judul transaksi, nominal, kategori, dan tanggal.
Validasi
Memeriksa input sebelum transaksi dikirim.
Date Picker
Memilih tanggal dan menampilkannya dalam format DD/MM/YYYY.
Teknologi
Flutter dengan StatefulWidget dan Material components.
2. Konsep yang Digunakan
StatefulWidget & setState()
Menjaga dan memperbarui kategori serta tanggal yang dipilih selama halaman digunakan.
Form & GlobalKey
Mengelompokkan input dan menjalankan validator melalui GlobalKey<FormState>.
Controller & Input
TextEditingController membaca teks; TextFormField dan DropdownButtonFormField menyediakan input.
showDatePicker
Membuka kalender dan mengembalikan tanggal yang dipilih.
SnackBar
Menampilkan feedback transaksi setelah seluruh input valid.
dispose()
Melepas controller ketika State tidak lagi digunakan.
3. Persiapan Project
Project menggunakan dua file utama di dalam folder lib.
lib/
├── main.dart
└── add_transaction_screen.dart
4. Langkah-Langkah Praktikum
01. Siapkan project
Buat aplikasi Flutter dan siapkan main.dart serta add_transaction_screen.dart.
02. Buat halaman form
Buat AddTransactionScreen sebagai StatefulWidget dan susun input di dalam Form.
03. Validasi dan simpan
Validasi field, pilih kategori dan tanggal, lalu tampilkan feedback SnackBar.
5. Implementasi Form
Form menggunakan GlobalKey<FormState> untuk mengoordinasikan input, sedangkan controller menyimpan teks judul, nominal, dan tanggal. SingleChildScrollView menjaga seluruh form tetap dapat diakses pada layar pendek.
Input transaksi
TextFormField menerima judul dan nominal. Nominal memakai keyboard angka dan diperiksa agar merupakan bilangan bulat yang valid.
Siklus widget
StatefulWidget dan setState() memperbarui pilihan kategori/tanggal. Controller dibersihkan melalui dispose().
6. Validasi Input
Judul
Wajib diisi sebelum transaksi dapat disimpan.
Nominal
Wajib diisi dan harus berupa angka bulat yang valid.
Tanggal
Tanggal transaksi wajib diisi sebelum form dinyatakan valid.
7. Implementasi Dropdown Kategori
DropdownButtonFormField menyediakan pilihan kategori transaksi. Kategori awal adalah Makanan; pilihan lain meliputi Transportasi, Hiburan, dan Lainnya. Perubahan pilihan disimpan menggunakan setState().
8. Input Tanggal Transaksi
Field Tanggal Transaksi diletakkan sebelum tombol Simpan Transaksi. Field bersifat read-only agar tanggal dipilih melalui kalender, dan wajib diisi.
9. Implementasi showDatePicker
showDatePicker menampilkan kalender dengan rentang tahun 2000 hingga 2100. Tanggal terpilih diformat menjadi DD/MM/YYYY, misalnya 15/09/2026.
Validasi tanggal
Jika tanggal belum dipilih, validator menampilkan pesan “Tanggal transaksi wajib diisi” dan pengiriman form dihentikan.
10. Source Code
Source code final Praktikum 3, termasuk tugas input tanggal.
main.dart
import 'package:flutter/material.dart';
import 'add_transaction_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Expense Tracker - Modul 3',
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: const AddTransactionScreen(),
);
}
}
add_transaction_screen.dart
import 'package:flutter/material.dart';
class AddTransactionScreen extends StatefulWidget {
const AddTransactionScreen({super.key});
@override
State<AddTransactionScreen> createState() => _AddTransactionScreenState();
}
class _AddTransactionScreenState extends State<AddTransactionScreen> {
final _formKey = GlobalKey<FormState>();
final _titleController = TextEditingController();
final _amountController = TextEditingController();
final _dateController = TextEditingController();
String _selectedCategory = 'Makanan';
final List<String> _categories = [
'Makanan',
'Transportasi',
'Hiburan',
'Lainnya',
];
Future<void> _selectDate() async {
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
final String day = pickedDate.day.toString().padLeft(2, '0');
final String month = pickedDate.month.toString().padLeft(2, '0');
final String year = pickedDate.year.toString();
setState(() {
_dateController.text = '$day/$month/$year';
});
}
}
void _saveTransaction() {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Tersimpan: ${_titleController.text} '
'(Rp ${_amountController.text}) - ${_dateController.text}',
),
backgroundColor: Colors.green,
),
);
}
}
@override
void dispose() {
_titleController.dispose();
_amountController.dispose();
_dateController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Catat Transaksi Baru'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _titleController,
decoration: const InputDecoration(
labelText: 'Judul Transaksi',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.title),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Judul transaksi tidak boleh kosong';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _amountController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'Nominal (Rp)',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.attach_money),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Nominal wajib diisi';
}
if (int.tryParse(value) == null) {
return 'Harus berupa angka bulat yang valid';
}
return null;
},
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
value: _selectedCategory,
decoration: const InputDecoration(
labelText: 'Kategori',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.category),
),
items: _categories.map((String category) {
return DropdownMenuItem<String>(
value: category,
child: Text(category),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue == null) return;
setState(() {
_selectedCategory = newValue;
});
},
),
const SizedBox(height: 16),
TextFormField(
controller: _dateController,
readOnly: true,
decoration: InputDecoration(
labelText: 'Tanggal Transaksi',
hintText: 'DD/MM/YYYY',
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.calendar_today),
suffixIcon: IconButton(
icon: const Icon(Icons.add),
onPressed: _selectDate,
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Tanggal transaksi wajib diisi';
}
return null;
},
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
onPressed: _saveTransaction,
child: const Text('Simpan Transaksi'),
),
),
],
),
),
),
);
}
}
11. Pengujian
Validasi judul dan nominal
Kosongkan judul atau nominal, lalu kirim form.
Hasil: pesan validasi muncul dan data tidak dikirim.
Validasi nominal angka
Masukkan nominal yang bukan angka bulat.
Hasil: input ditolak oleh validator.
Validasi tanggal
Kirim form tanpa memilih tanggal.
Hasil: “Tanggal transaksi wajib diisi”.
Simpan transaksi
Isi SPAGGETI, 10, Makanan, dan 15/09/2026.
Hasil: SnackBar menampilkan transaksi tersimpan.
12. Hasil Praktikum 3
Implementasi awal menggunakan tiga input: judul transaksi, nominal, dan kategori. Contoh yang diuji adalah CROISSANT, Rp 5, kategori Makanan. SnackBar berhasil menampilkan “Tersimpan: CROISSANT (Rp 5)”.
13. Hasil Tugas Praktikum 3
Tugas menambahkan Tanggal Transaksi sebagai input keempat dan mewajibkannya sebelum submit. Data SPAGGETI, Rp 10, Makanan, dan 15/09/2026 menghasilkan SnackBar “Tersimpan: SPAGGETI (Rp 10) - 15/09/2026”.
14. Kesimpulan
Praktikum berhasil membangun form pencatatan transaksi Flutter menggunakan StatefulWidget, Form, TextFormField, DropdownButtonFormField, GlobalKey<FormState>, TextEditingController, dan SnackBar.
Pada tugas, input tanggal ditambahkan menggunakan showDatePicker dan diformat menjadi DD/MM/YYYY. Validasi memastikan tanggal wajib dipilih sebelum transaksi tersimpan.
Hasil Akhir
Form transaksi divalidasi dan memberikan feedback berhasil dengan tanggal yang dipilih.
15. Dokumentasi Hasil
Hasil Praktikum 3
Implementasi awal form pencatatan transaksi menggunakan input judul, nominal, dan kategori serta validasi dan feedback menggunakan SnackBar.
Hasil Tugas Praktikum 3
Implementasi akhir setelah penambahan input Tanggal Transaksi menggunakan showDatePicker dan validasi wajib diisi.