Kembali ke Laporan

Laprak 4 — Navigasi dan Passing Data (Flutter)

Aplikasi Mobile · Praktikum 4

Saskia Alifah 2411531002 Aplikasi Mobile Flutter Navigator & Passing Data
Daftar Isi

Praktikum ini bertujuan supaya mahasiswa mampu mengelola perpindahan antar halaman (layar) pada aplikasi Flutter. Secara lebih rinci, setelah menyelesaikan praktikum ini mahasiswa diharapkan mampu menerapkan Navigator.push untuk berpindah ke layar baru, menggunakan Navigator.pop untuk menutup layar setelah suatu aksi selesai, serta mengirim data bertipe String dan int antar layar melalui Constructor.

Konsep tersebut diterapkan pada studi kasus aplikasi Expense Tracker yang terdiri dari halaman Dashboard, halaman Detail Transaksi, dan halaman form Catat Transaksi. Pada bagian latihan, mahasiswa menambahkan input Tanggal Transaksi pada form menggunakan showDatePicker lengkap dengan validasi wajib diisi.

Navigator adalah widget pengelola rute (halaman) pada Flutter yang bekerja seperti tumpukan kartu (stack). Setiap halaman dipandang sebagai sebuah route yang ditumpuk satu di atas yang lain, dan halaman yang tampil di layar selalu halaman yang berada di tumpukan paling atas. Prinsip kerjanya adalah LIFO (Last In, First Out): halaman yang terakhir dibuka menjadi halaman yang pertama ditutup.

Navigator.push (maju) meletakkan halaman baru di tumpukan teratas sehingga halaman lama tidak dihapus, hanya tertutup di bawahnya. Method ini membutuhkan context dan sebuah route, umumnya MaterialPageRoute yang properti builder-nya mengembalikan widget halaman tujuan. Karena halaman lama masih ada di dalam stack, AppBar pada halaman baru otomatis menampilkan tombol panah kiri (back).

Navigator.pop (mundur) menghapus atau menutup halaman teratas sehingga tampilan kembali ke halaman di bawahnya. Method ini juga dapat membawa nilai balik melalui Navigator.pop(context, nilai), namun pada praktikum ini pop hanya dipakai untuk menutup layar.

Constructor & Passing Data: untuk mengirim data dari Halaman A ke Halaman B, Halaman B harus menyiapkan variabel dan constructor. Variabel final berfungsi sebagai penampung data (misalnya title), sedangkan kata kunci required menandai bahwa data tersebut wajib diisi saat berpindah layar. Variabel dibuat final karena StatelessWidget bersifat immutable, sehingga nilainya tidak boleh berubah setelah widget dibuat.

showDatePicker adalah fungsi bawaan Flutter yang menampilkan dialog kalender. Fungsi ini mengembalikan Future<DateTime?>, sehingga pemanggilannya menggunakan async/await, dan hasilnya bernilai null apabila pengguna menutup dialog tanpa memilih tanggal.

  • Laptop / Komputer dengan Flutter SDK dan Dart terinstall
  • Visual Studio Code (Code Editor) dengan ekstensi Flutter & Dart
  • Chrome Browser / Android Emulator sebagai target device untuk menjalankan aplikasi
  • Modul Praktikum Aplikasi Mobile — Praktikum IV: Navigasi & Passing Data

Praktikum ini terdiri dari empat file yang dibuat di dalam folder lib, yaitu main.dart sebagai pintu masuk aplikasi, transaction_detail_screen.dart sebagai halaman penerima data, dashboard_screen.dart sebagai halaman utama yang mengirim data dan membuka form, serta add_transaction_screen.dart sebagai form yang menutup dirinya sendiri dengan Navigator.pop. Setiap file dikerjakan berurutan beserta penjelasan kodenya.

D.1 main.dart
1Membuat File main.dart dan Mengatur MaterialApp

Langkah pertama adalah membuat file main.dart di dalam folder lib. File ini menjalankan aplikasi, memanggil MyApp, dan menetapkan DashboardScreen sebagai halaman pertama yang dimuat.

Aksi: Tulis Kode main.dart Lengkap dengan Tema Merah
SS VS Code main.dart dengan MyApp, MaterialApp, tema merah, dan home DashboardScreen
Gambar D.1a – Kode main.dart: MaterialApp dengan tema Colors.red dan home DashboardScreen
Penjelasan Kode

Baris import 'dashboard_screen.dart'; menghubungkan main.dart dengan file dashboard, sementara void main() { runApp(const MyApp()); } adalah titik masuk yang menjalankan widget MyApp. Widget ini berupa StatelessWidget karena tidak memiliki data yang berubah.

  • debugShowCheckedModeBanner: false — menyembunyikan pita "DEBUG" di pojok kanan atas layar.
  • title: 'Expense Tracker - Modul 4' — judul aplikasi yang dikenali sistem.
  • theme: ThemeData(primarySwatch: Colors.red) — mengatur warna utama aplikasi menjadi merah sesuai slide Modul 4.
  • home: const DashboardScreen() — halaman pertama yang tampil ketika aplikasi dijalankan.
Pada versi Flutter yang memakai Material 3, primarySwatch tidak selalu mewarnai AppBar secara otomatis. Karena itu, setiap AppBar pada praktikum ini juga diberi backgroundColor: Colors.red dan foregroundColor: Colors.white agar warnanya konsisten.
Lanjut ke File Berikutnya
D.2 transaction_detail_screen.dart
1Menyiapkan Variabel Final dan Constructor Penerima Data

File transaction_detail_screen.dart dibuat sebagai halaman tujuan. Sesuai teori, halaman tujuan harus menyiapkan variabel penampung data dan constructor yang mewajibkan data tersebut diisi.

Aksi: Deklarasikan Empat Variabel final dan Constructor dengan required
SS VS Code class TransactionDetailScreen dengan variabel final dan constructor required
Gambar D.2a – Class TransactionDetailScreen dengan variabel final title, amount, category, date dan constructor required
Penjelasan Kode

Class TransactionDetailScreen extends StatelessWidget memiliki empat variabel final String, yaitu title, amount, category, dan date. Keempatnya bertipe final karena StatelessWidget tidak boleh mengubah datanya setelah dibuat.

  • const TransactionDetailScreen({...}) — constructor bernama parameter (named parameter) yang bersifat const.
  • super.key — meneruskan key widget ke class induk.
  • required this.title (dan tiga lainnya) — memasukkan nilai parameter langsung ke variabel milik class, sekaligus mewajibkan halaman pengirim mengisinya. Bila ada yang terlewat, kode akan error saat dikompilasi.
2Membangun Tampilan Halaman Detail pada build()

Data yang sudah diterima kemudian ditampilkan pada method build() menggunakan Scaffold, Column, dan beberapa widget Text.

Aksi: Tulis Method build() dan Helper _buildDetailRow()
SS VS Code method build TransactionDetailScreen dan _buildDetailRow
Gambar D.2b – Method build() yang menampilkan ikon, judul, nominal, dan baris detail, serta helper _buildDetailRow()
Penjelasan Kode

AppBar berjudul "Detail Transaksi" diberi warna merah dan teks putih. Isi halaman dibungkus Padding(all: 24) dan Column dengan crossAxisAlignment: CrossAxisAlignment.center agar semua elemen berada di tengah.

  • Icon(Icons.receipt_long, size: 80, color: Colors.redAccent) — ikon struk berukuran besar sebagai penanda halaman.
  • Text(title, ...) berukuran 24 tebal dan Text(amount, ...) berukuran 32 tebal berwarna merah — inilah tempat data hasil passing data ditampilkan.
  • Divider(height: 40, thickness: 2) — garis pemisah antara ringkasan dan rincian.
  • _buildDetailRow('Kategori', category) dan _buildDetailRow('Tanggal', date) — memakai data kiriman, sedangkan _buildDetailRow('Status', 'Berhasil') berisi teks tetap.

Method _buildDetailRow(String label, String value) adalah widget pembantu berupa Row dengan mainAxisAlignment: MainAxisAlignment.spaceBetween: label abu-abu di kiri dan nilai tebal di kanan, sehingga pembuatan tiga baris detail tidak perlu menulis kode berulang.

Lanjut ke File Berikutnya
D.3 dashboard_screen.dart
1Membuat Struktur Dashboard dan Tombol FAB dengan Navigator.push

File dashboard_screen.dart adalah halaman utama. Pada langkah ini dibuat kerangka Scaffold beserta tombol mengambang (FAB) yang berpindah ke halaman form menggunakan Navigator.push.

Aksi: Tulis Import, AppBar, dan FloatingActionButton
SS VS Code import, class DashboardScreen, AppBar, dan FloatingActionButton dengan Navigator.push
Gambar D.3a – Kerangka DashboardScreen dengan AppBar dan FloatingActionButton yang memanggil Navigator.push ke AddTransactionScreen
Penjelasan Kode

Dua baris import, yaitu add_transaction_screen.dart dan transaction_detail_screen.dart, dibutuhkan agar class AddTransactionScreen dan TransactionDetailScreen dapat dipanggil dari file ini.

  • AppBar(title: const Text('Dashboard Keuangan')) — judul halaman, berwarna latar merah dengan teks putih.
  • FloatingActionButton — tombol bulat merah berikon Icons.add di pojok kanan bawah.
  • //h
  • Navigator.push(context, MaterialPageRoute(builder: (context) => const AddTransactionScreen())) — meletakkan halaman form di atas tumpukan. context menunjukkan posisi widget di tree, sedangkan MaterialPageRoute membungkus halaman tujuan beserta animasi transisinya.
2Menyusun Body: Sapaan, Kartu Saldo, dan Judul Daftar

Bagian body berupa SingleChildScrollView agar halaman dapat digulir bila isinya melebihi layar.

Aksi: Tulis Body dan Method _buildBalanceCard()
SS VS Code body SingleChildScrollView dan method _buildBalanceCard
Gambar D.3b – Body Dashboard dan method _buildBalanceCard() untuk kartu Saldo Utama
Penjelasan Kode

Column dengan crossAxisAlignment: CrossAxisAlignment.start menata elemen rata kiri: teks 'Halo, Saskia' berukuran 20 tebal, kartu saldo, judul 'Transaksi Terakhir', lalu daftar transaksi. SizedBox(height: ...) dipakai sebagai jarak antar elemen.

  • _buildBalanceCard() — mengembalikan Card dengan elevation: 4, warna Colors.redAccent, dan sudut membulat BorderRadius.circular(16).
  • Isinya Text('Saldo Utama') berwarna putih transparan (Colors.white70) dan Text('Rp 5.000.000') berukuran 28 tebal putih.
  • _buildTransactionList(context) — context sengaja dikirim sebagai argumen karena method ini berada di luar build() tetapi memerlukan context untuk Navigator.push.
3Menambahkan onTap dengan Navigator.push dan Passing Data pada ListTile Pertama

Item transaksi dibuat dengan ListTile di dalam Card. Fungsi onTap ditambahkan agar ketika baris diketuk, aplikasi berpindah ke halaman detail sambil membawa data baris tersebut.

Aksi: Tulis ListTile "Makan Siang" beserta onTap
SS VS Code ListTile Makan Siang dengan onTap Navigator.push TransactionDetailScreen
Gambar D.3c – ListTile "Makan Siang" dengan onTap yang mengirim title, amount, category, dan date ke TransactionDetailScreen
Penjelasan Kode

ListTile tersusun dari leading berupa CircleAvatar oranye dengan ikon Icons.fastfood, title "Makan Siang", subtitle "Hari ini", dan trailing "- Rp 50.000" berwarna merah tebal.

Di dalam onTap, Navigator.push dipanggil dengan MaterialPageRoute yang builder-nya membuat TransactionDetailScreen dan mengisi constructor-nya: title: 'Makan Siang', amount: '- Rp 50.000', category: 'Makanan', dan date: '13 September 2026'. Inilah inti passing data via constructor: nilai diisikan saat objek halaman tujuan dibuat. Kata kunci const dapat dipakai karena semua nilainya konstanta.

4Menambahkan ListTile Kedua dan Pemisah

Satu item transaksi lagi ditambahkan untuk membuktikan bahwa halaman detail menyesuaikan isinya dengan baris yang diketuk.

Aksi: Tulis Divider dan ListTile "Gaji Bulanan"
SS VS Code Divider dan ListTile Gaji Bulanan dengan onTap Navigator.push
Gambar D.3d – Divider dan ListTile "Gaji Bulanan" dengan data pemasukan yang dikirim ke halaman detail
Penjelasan Kode

Divider(height: 1) memberi garis tipis di antara dua item. ListTile kedua berisi ikon Icons.attach_money dengan latar hijau, judul "Gaji Bulanan", subjudul "1 September 2026", dan nominal "+ Rp 5.000.000" berwarna hijau tebal.

Data yang dikirim berbeda: title: 'Gaji Bulanan', amount: '+ Rp 5.000.000', category: 'Pemasukan', dan date: '1 September 2026'. Karena kelas tujuannya sama tetapi datanya berbeda, halaman detail akan menampilkan isi yang sesuai dengan baris yang diketuk.

Pengujian: Dashboard dan Navigator.push dengan Passing Data
Percobaan: Jalankan Aplikasi dan Ketuk "Makan Siang"
SS hasil running Dashboard dan halaman Detail Transaksi Makan Siang
Gambar D.3e – Tampilan Dashboard Keuangan dan halaman Detail Transaksi "Makan Siang" setelah baris diketuk

Aplikasi dijalankan dan Dashboard tampil dengan kartu saldo serta dua transaksi. Setelah baris "Makan Siang" diketuk, halaman Detail Transaksi terbuka dengan judul, nominal, kategori, dan tanggal yang sesuai dengan data baris. Tombol panah kiri muncul otomatis di AppBar karena halaman dibuka dengan Navigator.push, dan menekannya mengeluarkan halaman detail dari stack sehingga Dashboard tampil kembali.

Pengujian: Data Berbeda pada Baris Lain
Percobaan: Ketuk "Gaji Bulanan"
SS hasil halaman Detail Transaksi Gaji Bulanan
Gambar D.3f – Halaman Detail Transaksi "Gaji Bulanan" yang menampilkan nominal pemasukan berbeda dari baris pertama

Halaman detail yang sama kini menampilkan "Gaji Bulanan", "+ Rp 5.000.000", kategori "Pemasukan", dan tanggal "1 September 2026". Ini membuktikan data dikirim per baris melalui constructor, bukan ditulis tetap di halaman detail.

Ketuk pada kedua baris transaksi berhasil membuka halaman detail dengan data yang sesuai, menandakan Navigator.push dan passing data via constructor pada dashboard_screen.dart telah berfungsi dengan benar.
Lanjut ke File Berikutnya
D.4 add_transaction_screen.dart
1Membuat StatefulWidget, GlobalKey, dan Controller

File add_transaction_screen.dart (hasil Modul 3) adalah form pencatatan transaksi. Form dibuat sebagai StatefulWidget karena mengelola state berupa inputan pengguna.

Aksi: Tulis Class AddTransactionScreen dan State-nya
SS VS Code AddTransactionScreen StatefulWidget dengan formKey dan controller
Gambar D.4a – Class AddTransactionScreen dan _AddTransactionScreenState dengan _formKey, _titleController, dan _amountController
Penjelasan Kode

createState() menghubungkan widget dengan class State _AddTransactionScreenState. Di dalamnya, GlobalKey<FormState> bernama _formKey dipakai untuk memicu validasi seluruh field sekaligus, sedangkan _titleController dan _amountController bertipe TextEditingController untuk mengelola teks inputan Judul dan Nominal.

2Membuat Tampilan Form: Field Judul dan Nominal

Body form dibungkus Padding, Form, dan Column yang berisi dua TextFormField beserta validator.

Aksi: Tulis AppBar, Form, dan Dua TextFormField
SS VS Code Scaffold, Form, TextFormField Judul dan Nominal dengan validator
Gambar D.4b – Scaffold "Catat Transaksi" dengan Form berisi field Judul dan Nominal beserta validator
Penjelasan Kode

Form(key: _formKey) menghubungkan form dengan _formKey, dan crossAxisAlignment: CrossAxisAlignment.stretch membuat elemen melebar penuh. Field Judul memakai controller: _titleController, sedangkan field Nominal ditambah keyboardType: TextInputType.number agar yang muncul keyboard angka.

Keduanya memakai InputDecoration berupa labelText dan OutlineInputBorder(), serta validator: (value) => value!.isEmpty ? "Tidak boleh kosong" : null. Validator mengembalikan pesan error bila field kosong dan null bila valid.

3Menutup Layar Otomatis dengan Navigator.pop

Form diharapkan tertutup otomatis dan kembali ke Dashboard setelah tombol Simpan ditekan. Hal ini dilakukan dengan Navigator.pop pada onPressed tombol.

Aksi: Tulis ElevatedButton Simpan dengan SnackBar dan Navigator.pop
SS VS Code ElevatedButton Simpan dengan validate, showSnackBar, dan Navigator.pop
Gambar D.4c – ElevatedButton "Simpan" dengan validasi form, SnackBar, dan Navigator.pop(context)
Penjelasan Kode

ElevatedButton.styleFrom(...) mengatur warna latar merah, teks putih, dan padding vertikal 16. Logika utamanya ada di onPressed:

  • if (_formKey.currentState!.validate()) — menjalankan seluruh validator; blok di dalamnya hanya berjalan bila semua field valid.
  • ScaffoldMessenger.of(context).showSnackBar(...) — menampilkan notifikasi hijau "Transaksi Berhasil Disimpan!".
  • Navigator.pop(context) — mengeluarkan AddTransactionScreen dari tumpukan, sehingga Dashboard yang ada di bawahnya tampil kembali. Karena tidak ada nilai kedua, pop di sini hanya menutup layar tanpa membawa data balik.
Pengujian: Simpan dengan Field Kosong
Percobaan: Buka Form dari FAB, Tekan Simpan Tanpa Mengisi
SS form Catat Transaksi dengan pesan Tidak boleh kosong
Gambar D.4d – Form Catat Transaksi yang menampilkan pesan "Tidak boleh kosong" dan tetap terbuka

Tombol "+" pada Dashboard membuka form Catat Transaksi lewat Navigator.push. Saat tombol Simpan ditekan dalam keadaan kosong, validate() bernilai false sehingga pesan error muncul dan Navigator.pop tidak dijalankan; form tetap terbuka.

Pengujian: Simpan dengan Data Lengkap
Percobaan: Isi Judul dan Nominal, Tekan Simpan
SS Dashboard setelah form tertutup dan SnackBar berhasil muncul
Gambar D.4e – Form tertutup otomatis dan Dashboard tampil kembali dengan SnackBar "Transaksi Berhasil Disimpan!"

Setelah kedua field diisi dan tombol Simpan ditekan, SnackBar hijau muncul dan form langsung tertutup, kembali ke Dashboard. Ini menunjukkan Navigator.pop bekerja sesuai teori.

Form berhasil menolak input kosong dan menutup dirinya otomatis setelah data valid disimpan, menandakan Navigator.pop pada add_transaction_screen.dart berfungsi dengan benar.
Latihan — Input Tanggal Transaksi

Menambahkan satu input baru sebelum tombol Simpan untuk mengisi Tanggal Transaksi. Dapat memakai TextFormField biasa (format teks DD/MM/YYYY), tombol plus memakai fungsi showDatePicker, dan wajib diberi validasi wajib diisi.

E.1 add_transaction_screen.dart
1Menambahkan Controller Tanggal dan Method dispose()

Perubahan hanya dilakukan pada add_transaction_screen.dart. Langkah pertama adalah menyiapkan controller khusus untuk menampung teks tanggal.

Aksi: Tambahkan _dateController dan Method dispose()
SS VS Code deklarasi _dateController dan method dispose
Gambar E.1a – Deklarasi _dateController dan method dispose() yang membersihkan tiga controller
Penjelasan Kode

final _dateController = TextEditingController(); menampung teks tanggal yang muncul di field. Method dispose() ditambahkan untuk memanggil dispose() pada _titleController, _amountController, dan _dateController, lalu super.dispose(), sehingga resource dibersihkan saat halaman ditutup dan tidak terjadi kebocoran memori.

2Membuat Fungsi _pickDate() dengan showDatePicker

Fungsi terpisah dibuat untuk menampilkan kalender dan mengisi controller dengan tanggal terpilih.

Aksi: Tulis Method _pickDate()
SS VS Code method _pickDate dengan showDatePicker dan format DD/MM/YYYY
Gambar E.1b – Method _pickDate() yang memanggil showDatePicker dan memformat hasilnya menjadi DD/MM/YYYY
Penjelasan Kode
  • Future<void> _pickDate() async — fungsi asinkron karena menunggu pengguna memilih tanggal.
  • final DateTime? picked = await showDatePicker(context: context, initialDate: DateTime.now(), firstDate: DateTime(2000), lastDate: DateTime(2100)); — menampilkan kalender yang terbuka di tanggal hari ini dengan rentang pilihan tahun 2000 sampai 2100. Hasilnya DateTime? yang bisa null.
  • if (picked != null) — memastikan pengguna benar-benar memilih tanggal, bukan menutup dialog.
  • picked.day.toString().padLeft(2, '0') — mengubah angka menjadi teks dua digit (misalnya 5 menjadi 05), begitu pula untuk bulan; tahun langsung diubah ke teks.
  • setState(() { _dateController.text = '$day/$month/$year'; }) — mengisi field dengan format DD/MM/YYYY dan memperbarui tampilan.
3Menambahkan TextFormField Tanggal Sebelum Tombol Simpan

Field tanggal diletakkan setelah field Nominal dan sebelum tombol Simpan, sesuai perintah tugas.

Aksi: Tulis TextFormField Tanggal Transaksi dengan Ikon Kalender
SS VS Code TextFormField Tanggal Transaksi dengan readOnly, onTap, dan suffixIcon
Gambar E.1c – TextFormField "Tanggal Transaksi" dengan readOnly, onTap: _pickDate, dan ikon kalender
Penjelasan Kode
  • controller: _dateController — menghubungkan field dengan controller tanggal.
  • readOnly: true — field tidak bisa diketik manual sehingga format selalu konsisten dan bebas salah ketik.
  • onTap: _pickDate — mengetuk field membuka kalender.
  • labelText: "Tanggal Transaksi" dan hintText: "DD/MM/YYYY" — label dan contoh format.
  • suffixIcon: IconButton(icon: const Icon(Icons.calendar_today), onPressed: _pickDate) — tombol ikon kalender di sisi kanan yang juga memanggil _pickDate.
4Menambahkan Validasi Wajib Diisi

Field tanggal juga diberi validator agar form tidak bisa disimpan bila tanggal belum dipilih.

Aksi: Tambahkan Properti validator pada Field Tanggal
SS VS Code validator pada TextFormField tanggal
Gambar E.1d – Validator "Tanggal wajib diisi" pada field Tanggal Transaksi
Penjelasan Kode

validator: (value) => value == null || value.isEmpty ? "Tanggal wajib diisi" : null mengembalikan pesan error bila nilai null atau kosong dan null bila sudah terisi. Karena field ini berada di dalam Form yang sama, _formKey.currentState!.validate() pada tombol Simpan otomatis ikut memeriksanya, dan Navigator.pop hanya berjalan bila field tanggal terisi.

5Menjalankan dan Menguji Input Tanggal

Aplikasi dijalankan untuk memeriksa tiga skenario: kalender muncul, tanggal masuk ke field dengan format benar, dan validasi menolak field kosong.

Aksi: Jalankan Aplikasi dan Buka Form Catat Transaksi
SS form Catat Transaksi dengan field Tanggal Transaksi baru
Gambar E.1e – Form Catat Transaksi dengan field Tanggal Transaksi di antara Nominal dan tombol Simpan
Penjelasan Kode

Field Tanggal Transaksi dengan ikon kalender kini berada tepat sebelum tombol Simpan.

Pengujian: Memilih Tanggal
Percobaan: Ketuk Field atau Ikon Kalender
SS dialog showDatePicker dan field tanggal terisi
Gambar E.1f – Dialog kalender showDatePicker dan field terisi tanggal terpilih dalam format DD/MM/YYYY

Mengetuk field atau ikon kalender memunculkan dialog kalender. Setelah tanggal dipilih dan dikonfirmasi, field terisi otomatis dengan format DD/MM/YYYY.

Pengujian: Tanggal Dikosongkan
Percobaan: Isi Judul dan Nominal, Tekan Simpan Tanpa Tanggal
SS pesan Tanggal wajib diisi pada form
Gambar E.1g – Pesan "Tanggal wajib diisi" muncul dan form tidak tertutup

Saat tanggal belum dipilih, muncul pesan "Tanggal wajib diisi" dan form tidak tertutup, karena validate() bernilai false.

Pengujian: Semua Field Terisi
Percobaan: Isi Semua Field, Tekan Simpan
SS Dashboard setelah menyimpan dengan tanggal terisi
Gambar E.1h – Form tertutup dan Dashboard tampil kembali dengan SnackBar berhasil setelah semua field terisi

Bila Judul, Nominal, dan Tanggal terisi, SnackBar muncul dan form tertutup lewat Navigator.pop.

Ketiga skenario berjalan sesuai ekspektasi: kalender showDatePicker terbuka, tanggal terformat DD/MM/YYYY, dan validasi menolak tanggal kosong, menandakan tugas input tanggal telah berfungsi dengan baik.

Melalui Praktikum 4 ini, implementasi navigasi antar halaman dan pengiriman data pada Flutter berhasil diselesaikan pada aplikasi Expense Tracker yang terdiri dari tiga halaman: Dashboard, Detail Transaksi, dan Catat Transaksi.

Pada dashboard_screen.dart, Navigator.push dengan MaterialPageRoute terbukti membuka halaman baru di atas tumpukan, baik dari tombol FAB menuju form maupun dari ListTile menuju halaman detail. Pada halaman detail, data title, amount, category, dan date berhasil dikirim lewat constructor berparameter required dan tampil sesuai baris yang diketuk. Tombol panah kiri pada AppBar muncul otomatis karena halaman sebelumnya masih ada di stack.

Pada add_transaction_screen.dart, Navigator.pop(context) berhasil menutup form dan mengembalikan tampilan ke Dashboard setelah data valid disimpan, sedangkan input kosong tertahan oleh validasi. Pada bagian latihan, field Tanggal Transaksi berhasil dibuat dengan showDatePicker: tanggal terformat DD/MM/YYYY, field bersifat readOnly sehingga aman dari salah ketik, dan validasi wajib diisi bekerja bersama validator Judul dan Nominal.

Perlu dicatat bahwa Navigator.pop(context) pada form belum membawa data balik ke Dashboard, sehingga transaksi dan tanggal yang diinput belum muncul di daftar. Hal ini sesuai lingkup modul, dan dapat dikembangkan dengan Navigator.pop(context, data).

Berdasarkan seluruh tahapan praktikum yang telah dilaksanakan, diperoleh beberapa kesimpulan penting:

  1. Navigator mengelola halaman dengan konsep tumpukan (stack): Navigator.push menaruh halaman baru di atas, sedangkan Navigator.pop menutup halaman teratas untuk kembali ke halaman sebelumnya.
  2. MaterialPageRoute dengan properti builder menjadi cara standar membuat route, dan halaman yang dibuka dengan push otomatis memiliki tombol back di AppBar.
  3. Passing data via constructor dilakukan dengan menyiapkan variabel final dan parameter required pada halaman tujuan, lalu mengisinya saat halaman dibuat, sehingga satu halaman detail dapat menampilkan data yang berbeda-beda.
  4. Validasi Form menentukan kapan Navigator.pop dijalankan, sehingga form hanya tertutup setelah seluruh field valid.
  5. showDatePicker dengan readOnly: true dan validator memungkinkan input tanggal terformat konsisten (DD/MM/YYYY) tanpa kesalahan ketik dan tetap wajib diisi.

Seluruh source code praktikum ini dapat diakses pada repository Github berikut: