MODUL IV: Navigasi & Passing Data (Navigator, Konsep Stack, dan Constructor)
NIM: 2411533019 - Adila Bintang Fauzi
Navigator.push untuk berpindah dari satu layar ke layar baru.Navigator.pop untuk menutup layar dan kembali ke halaman sebelumnya setelah aksi selesai.String, int) antar layar menggunakan Constructor.showDatePicker untuk memilih tanggal transaksi serta menambahkan validasi input.Navigator.push menumpuk halaman baru di paling atas, sedangkan Navigator.pop menghapus halaman teratas untuk kembali ke halaman sebelumnya.
final dan dideklarasikan pada constructor menggunakan kata kunci required.
Form dengan GlobalKey<FormState> untuk memvalidasi input teks, serta memanfaatkan fungsi showDatePicker bawaan Flutter untuk memilih tanggal secara interaktif.
Proyek disusun ke dalam 4 file terpisah di dalam folder lib/:
lib/main.dart – Inisialisasi dan konfigurasi utama aplikasi.lib/dashboard_screen.dart – Halaman utama yang menampilkan saldo dan daftar transaksi.lib/add_transaction_screen.dart – Form pencatatan transaksi baru (termasuk input tanggal dan validasi).lib/transaction_detail_screen.dart – Halaman detail untuk menampilkan informasi transaksi yang dipilih.lib/main.dartMenjalankan aplikasi dan mengatur tema warna utama menjadi merah.
import 'package:flutter/material.dart';
import 'dashboard_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 4',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: const DashboardScreen(),
);
}
}
lib/dashboard_screen.dartMenampilkan antarmuka utama. Tombol FAB bernavigasi ke halaman form AddTransactionScreen, dan item transaksi bernavigasi ke TransactionDetailScreen sambil membawa data transaksi (passing data).
import 'package:flutter/material.dart';
import 'add_transaction_screen.dart';
import 'transaction_detail_screen.dart';
class DashboardScreen extends StatelessWidget {
const DashboardScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dashboard Keuangan'),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
child: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const AddTransactionScreen()),
);
},
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Halo, Budi',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
_buildBalanceCard(),
const SizedBox(height: 20),
const Text(
'Transaksi Terakhir',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
_buildTransactionList(context),
],
),
),
);
}
Widget _buildBalanceCard() {
return Card(
elevation: 4,
color: Colors.redAccent,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: const Padding(
padding: EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Saldo Utama', style: TextStyle(fontSize: 16, color: Colors.white70)),
SizedBox(height: 8),
Text(
'Rp 5.000.000',
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, color: Colors.white),
),
],
),
),
);
}
Widget _buildTransactionList(BuildContext context) {
return Card(
elevation: 2,
child: Column(
children: [
ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.orange,
child: Icon(Icons.fastfood, color: Colors.white),
),
title: const Text('Makan Siang'),
subtitle: const Text('Hari ini'),
trailing: const Text(
'- Rp 50.000',
style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TransactionDetailScreen(
title: 'Makan Siang',
amount: '- Rp 50.000',
category: 'Makanan',
date: '13 September 2026',
),
),
);
},
),
const Divider(height: 1),
ListTile(
leading: const CircleAvatar(
backgroundColor: Colors.green,
child: Icon(Icons.attach_money, color: Colors.white),
),
title: const Text('Gaji Bulanan'),
subtitle: const Text('1 September 2026'),
trailing: const Text(
'+ Rp 5.000.000',
style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TransactionDetailScreen(
title: 'Gaji Bulanan',
amount: '+ Rp 5.000.000',
category: 'Pemasukan',
date: '1 September 2026',
),
),
);
},
),
],
),
);
}
}
lib/add_transaction_screen.dartMenyediakan form pencatatan transaksi. Sesuai dengan instruksi Latihan 1.5, ditambahkan input tanggal menggunakan showDatePicker dengan format DD/MM/YYYY dan dilengkapi validator required.
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();
@override
void dispose() {
_titleController.dispose();
_amountController.dispose();
_dateController.dispose();
super.dispose();
}
// Fungsi untuk menampilkan date picker dialog
Future<void> _selectDate(BuildContext context) async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null) {
setState(() {
_dateController.text =
"${pickedDate.day.toString().padLeft(2, '0')}/${pickedDate.month.toString().padLeft(2, '0')}/${pickedDate.year}";
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Catat Transaksi"),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: _titleController,
decoration: const InputDecoration(
labelText: "Judul",
border: OutlineInputBorder(),
),
validator: (value) =>
value == null || value.isEmpty ? "Tidak boleh kosong" : null,
),
const SizedBox(height: 16),
TextFormField(
controller: _amountController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: "Nominal",
border: OutlineInputBorder(),
),
validator: (value) =>
value == null || value.isEmpty ? "Tidak boleh kosong" : null,
),
const SizedBox(height: 16),
// Input Tanggal Transaksi (Tugas Latihan)
TextFormField(
controller: _dateController,
readOnly: true,
onTap: () => _selectDate(context),
decoration: InputDecoration(
labelText: "Tanggal Transaksi (DD/MM/YYYY)",
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: const Icon(Icons.calendar_today),
onPressed: () => _selectDate(context),
),
),
validator: (value) =>
value == null || value.isEmpty ? "Tanggal wajib diisi!" : null,
),
const SizedBox(height: 32),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
),
onPressed: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Transaksi Berhasil Disimpan!'),
backgroundColor: Colors.green,
),
);
Navigator.pop(context); // Kembali ke Dashboard
}
},
child: const Text("Simpan", style: TextStyle(fontSize: 16)),
),
],
),
),
),
);
}
}
Tampilan dialog pemilih tanggal (showDatePicker) dan pesan error saat formulir coba disimpan tanpa mengisi kolom tanggal:
1. Dialog Pemilihan Tanggal (DatePicker)
2. Validasi Input Tanggal Wajib Diisi
3. Form Catat Transaksi Terisi Lengkap Beserta Tanggal Transaksi
lib/transaction_detail_screen.dartMenerima parameter data (title, amount, category, date) via constructor dan menginstasikannya ke tampilan antarmuka.
import 'package:flutter/material.dart';
class TransactionDetailScreen extends StatelessWidget {
final String title;
final String amount;
final String category;
final String date;
const TransactionDetailScreen({
super.key,
required this.title,
required this.amount,
required this.category,
required this.date,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Detail Transaksi"),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Icons.receipt_long, size: 80, color: Colors.redAccent),
const SizedBox(height: 24),
Text(title, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Text(amount, style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.red)),
const Divider(height: 40, thickness: 2),
_buildDetailRow('Kategori', category),
const SizedBox(height: 16),
_buildDetailRow('Tanggal', date),
const SizedBox(height: 16),
_buildDetailRow('Status', 'Berhasil'),
],
),
),
);
}
Widget _buildDetailRow(String label, String value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontSize: 16, color: Colors.grey)),
Text(value, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
],
);
}
}
Hasil penerimaan data dari halaman Dashboard menuju Halaman Detail melalui Constructor:
4. Antarmuka Detail Transaksi yang Menerima Parameter Data
Navigator.push & Navigator.pop):
AddTransactionScreen. Setelah form divalidasi dan disimpan, perintah Navigator.pop(context) mematikan/menutup layar form dan membawa pengguna kembali ke DashboardScreen.ListTile membuka layar TransactionDetailScreen.DashboardScreen melalui parameter constructor ke TransactionDetailScreen dan ditampilkan secara dinamis.TextFormField khusus tanggal dengan fitur readOnly: true.showDatePicker.DD/MM/YYYY.
Pada praktikum ini, telah berhasil dipelajari dan diimplementasikan konsep dasar navigasi di Flutter menggunakan Navigator.push dan Navigator.pop. Selain itu, mekanisme pengiriman data antar layar melalui constructor berhasil diterapkan secara baik. Penambahan komponen showDatePicker dan mekanisme validasi form melengkapi fitur pencatatan transaksi sesuai dengan tugas latihan yang diberikan.