๐Ÿ“ฑ

Laporan Praktikum Aplikasi Mobile โ€“ Flutter

Pengenalan Widgets, State Management & Layouting
Studi Kasus: Expense Tracker & Dashboard Finansial

Praktikum Aplikasi Mobile

Disusun Oleh
Ahmad Zahran
NIM: 2411532004
Dosen Pengampu & Instansi
Nurfiah, S.ST. M.Kom
Departemen Informatika ยท Universitas Andalas (2026)

I. Pendahuluan

Pengembangan aplikasi mobile modern menuntut pembuatan antarmuka pengguna (User Interface) yang responsif, terstruktur, dan interaktif. Framework Flutter, yang dikembangkan oleh Google dan menggunakan bahasa pemrograman Dart, hadir sebagai solusi lintas platform (cross-platform) yang efisien. Dalam pengembangan frontend aplikasi mobile, pemahaman terhadap hierarki widget dan tata letak (layouting) menjadi fondasi utama bagi seorang pengembang. Aplikasi dibangun dengan cara menyusun komponen-komponen widget secara bertingkat, baik yang bersifat statis maupun dinamis.

Praktikum ini berfokus pada dua konsep dasar utama Flutter:

  • Jenis widget (Stateless dan Stateful Widget) beserta manajemen state dasar.
  • Teknik layouting dan styling.

Pada materi dasar, dipelajari perbedaan antara komponen UI yang tidak berubah dan komponen UI yang dapat berubah merespons interaksi pengguna. Selanjutnya, dipelajari teknik penyusunan elemen UI secara horizontal (Row), vertikal (Column), pembungkusan area dekoratif (Container, Card), baris informasi instan (ListTile), hingga penanganan area yang dapat digulirkan menggunakan SingleChildScrollView.

Hubungan materi ini dengan pemrograman Dart/Flutter terletak pada penerapan konsep Object-Oriented Programming (OOP), karena hampir seluruh elemen UI di Flutter merupakan objek turunan dari kelas Widget. Pemanggilan metode seperti setState() adalah bentuk manipulasi state internal objek Dart yang secara langsung memicu pembaruan antarmuka (re-render).

Materi ini penting bagi mahasiswa Informatika karena memberikan pemahaman mendasar tentang arsitektur antarmuka aplikasi โ€” tidak hanya merancang tampilan menarik, tetapi juga mengelola alur data antarmuka, mengoptimalkan tata letak agar adaptif terhadap berbagai ukuran layar, serta menerapkan clean code pada komponen UI yang terstruktur.

II. Tujuan Praktikum

1

Memahami kerangka dasar aplikasi Flutter seperti MaterialApp dan Scaffold.

2

Membedakan penggunaan serta karakteristik StatelessWidget dan StatefulWidget.

3

Menerapkan fungsi setState() untuk membuat antarmuka pengguna yang interaktif dan dinamis.

4

Menerapkan widget Row untuk menata deretan tombol aksi secara horizontal.

5

Menerapkan widget Column untuk menyusun daftar transaksi secara vertikal.

6

Mengatur tata letak (padding, margin) serta styling (color, border radius, elevation) menggunakan Container dan Card.

7

Menggunakan widget ListTile untuk membuat baris daftar riwayat transaksi yang rapi dan terstruktur.

8

Menerapkan SingleChildScrollView untuk mencegah masalah overflow saat konten melebihi batas layar.

9

Menyelesaikan tugas dan latihan modul berupa penyesuaian styling tema warna, penambahan teks riwayat rekening, dan penambahan data transaksi.

III. Langkah Praktikum

Langkah-langkah disusun mengikuti urutan kegiatan pada Modul I dan Modul II. Kode program mengacu pada implementasi nyata file main.dart yang memuat seluruh langkah praktikum beserta tugas latihan modul.

3.1 Praktikum 1 โ€” Widget Sapaan (Stateless Widget)

Langkah: Membuat header halaman statis untuk menampilkan sapaan nama pengguna serta gambar avatar profil. Elemen disatukan secara horizontal (Row) dan vertikal (Column).

Kode Program: Widget Sapaan
// 3. STATELESS WIDGET (Sapaan Pengguna) class GreetingWidget extends StatelessWidget { const GreetingWidget({super.key}); @override Widget build(BuildContext context) { return const Row( children: [ CircleAvatar( radius: 24, backgroundColor: Colors.blueAccent, child: Icon(Icons.person, size: 30, color: Colors.white), ), SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Halo, Budi', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), Text( 'Selamat datang kembali!', style: TextStyle(fontSize: 14, color: Colors.grey), ), ], ), ], ); } }

Penjelasan Kode:

Elemen / Syntax Fungsi / Peranan
class GreetingWidget extends StatelessWidget Widget immutable, tidak memiliki state dinamis.
const GreetingWidget({super.key}) Constructor konstan.
build(BuildContext context) Method utama yang mengembalikan struktur widget.
Row Layout horizontal.
CircleAvatar Lingkaran avatar berisi ikon Icons.person, ukuran 30, latar Colors.blueAccent.
SizedBox(width: 12) Jarak horizontal 12px antar komponen.
Column Layout vertikal dengan CrossAxisAlignment.start (rata kiri).
Text Menampilkan "Halo, Budi" (bold, 20) dan "Selamat datang kembali!" (abu-abu).

Hasil Praktikum: Tampil baris paling atas halaman berisi lingkaran avatar biru dengan ikon orang, diikuti dua baris teks sapaan di sebelah kanannya.

Hasil Praktikum 1 - Widget Sapaan
Gambar 3.1: Tampilan Widget Sapaan Statis

Analisis: Step ini melatih pembuatan komponen antarmuka statis. StatelessWidget dipilih karena sapaan pengguna tidak memerlukan perubahan data real-time, sehingga lebih efisien dari sisi konsumsi memori.

3.2 Praktikum 1 โ€” Kartu Saldo & Interaktivitas (Stateful Widget & setState)

Langkah: Membuat kartu saldo interaktif menggunakan StatefulWidget yang dapat menyembunyikan/menampilkan nominal saldo serta menampilkan nomor rekening saat ikon mata ditekan.

Kode Program: Kartu Saldo dengan Toggle
// 4. STATEFUL WIDGET (Kartu Saldo dengan Toggle) class BalanceCardWidget extends StatefulWidget { const BalanceCardWidget({super.key}); @override State<BalanceCardWidget> createState() => _BalanceCardWidgetState(); } class _BalanceCardWidgetState extends State<BalanceCardWidget> { bool _isBalanceVisible = true; void _toggleVisibility() { setState(() { _isBalanceVisible = !_isBalanceVisible; }); } @override Widget build(BuildContext context) { return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), color: Colors.teal, // Hasil Tugas 1 Praktikum 1 (Emerald/Teal) child: Padding( padding: const EdgeInsets.all(20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Saldo Utama', style: TextStyle(fontSize: 16, color: Colors.white70), ), IconButton( icon: Icon( _isBalanceVisible ? Icons.visibility : Icons.visibility_off, color: Colors.white, ), onPressed: _toggleVisibility, ), ], ), const SizedBox(height: 8), Text( _isBalanceVisible ? 'Rp 5.000.000' : 'Rp *********', style: const TextStyle( fontSize: 28, fontWeight: FontWeight.bold, color: Colors.white, ), ), const SizedBox(height: 12), // Hasil Tugas 2 Praktikum 1 (Nomor Rekening) const Text( 'No. Rekening: 1234-5678', style: TextStyle(fontSize: 14, color: Colors.white70), ), ], ), ), ); } }

Penjelasan Kode:

Elemen / Syntax Fungsi / Peranan
class BalanceCardWidget extends StatefulWidget Widget dinamis yang membutuhkan pemeliharaan state.
createState() Mengembalikan instance _BalanceCardWidgetState.
bool _isBalanceVisible = true Variabel state pelacak status tampil/sensor saldo.
_toggleVisibility() Memanggil setState() untuk membalik nilai _isBalanceVisible.
Card Latar Colors.teal (Tugas 1 Praktikum 1), sudut melengkung 16px, elevation 4.
IconButton Tombol ikon mata dengan event onPressed โ†’ _toggleVisibility.
Ternary Operator Memilih ikon (visibility/visibility_off) dan teks nominal (angka/sensor) secara kondisional.
Text('No. Rekening: 1234-5678') Teks statis tambahan (Tugas 2 Praktikum 1).

Hasil Praktikum: Kartu berwarna hijau teal dengan tulisan "Saldo Utama", tombol ikon mata di kanan atas, nilai saldo "Rp 5.000.000" (atau "Rp *********" saat disensor), dan teks "No. Rekening: 1234-5678". Tampilan berganti instan saat ikon mata diklik.

Hasil Praktikum 1 - Kartu Saldo Interaktif
Gambar 3.2: Tampilan Kartu Saldo Interaktif (StatefulWidget)

Analisis: Mendemonstrasikan manajemen state lokal Flutter. Saat setState() dipanggil, Flutter menandai widget untuk dirender ulang sehingga tampilan mencerminkan perubahan _isBalanceVisible secara interaktif. Perubahan warna ke teal dan penambahan nomor rekening membuktikan penerapan instruksi tugas modul.

3.3 Praktikum 2 โ€” Tombol Aksi (Row Widget)

Langkah: Membuat deretan tombol aksi cepat ("Pemasukan", "Pengeluaran", "Transfer") secara horizontal menggunakan Row dan helper function untuk reusabilitas kode.

Kode Program: Widget Tombol Aksi
// 5. WIDGET TOMBOL AKSI class ActionButtonsWidget extends StatelessWidget { const ActionButtonsWidget({super.key}); @override Widget build(BuildContext context) { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildActionButton(Icons.arrow_downward, 'Pemasukan', Colors.green), _buildActionButton(Icons.arrow_upward, 'Pengeluaran', Colors.red), _buildActionButton(Icons.swap_horiz, 'Transfer', Colors.blue), ], ); } Widget _buildActionButton(IconData icon, String label, Color color) { return Column( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Icon(icon, color: color, size: 28), ), const SizedBox(height: 8), Text(label, style: const TextStyle(fontWeight: FontWeight.w500)), ], ); } }

Penjelasan Kode & Analisis:

  • Row + mainAxisAlignment: MainAxisAlignment.spaceEvenly: Membagi sisa ruang kosong secara rata di antara dan di sekitar widget anak.
  • _buildActionButton(...): Helper method yang mengembalikan Column, menerima IconData, String label, dan Color color. Ini adalah praktik Dart yang baik untuk menghindari duplikasi kode (DRY).
  • Container: Membungkus ikon dengan latar transparan color.withOpacity(0.2), sudut tumpul 12px, dan padding 12px.

Hasil Praktikum: Tiga tombol aksi sejajar: Pemasukan (ikon hijau), Pengeluaran (ikon merah), Transfer (ikon biru), lengkap dengan label di bawah masing-masing ikon.

Hasil Praktikum 2 - Tombol Aksi Row
Gambar 3.3: Tampilan Deretan Tombol Aksi Horizontal
3.4 Praktikum 2 โ€” Riwayat Transaksi (Column & ListTile)

Langkah: Menyusun daftar riwayat transaksi vertikal menggunakan Column, Card, ListTile, dan Divider. Mencakup Tugas 1 & 2 Praktikum 2 (penyesuaian warna indikator transaksi dan penambahan data transaksi fiktif baru).

Kode Program: Widget Daftar Transaksi
// 6. WIDGET DAFTAR TRANSAKSI class RecentTransactionsWidget extends StatelessWidget { const RecentTransactionsWidget({super.key}); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Transaksi Terakhir', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Column( children: [ // Item 1 (Pengeluaran - Merah) ListTile( leading: const CircleAvatar( backgroundColor: Colors.redAccent, child: Icon(Icons.fastfood, color: Colors.white), ), title: const Text('Makan Siang'), subtitle: const Text('13 Sep 2026'), trailing: const Text( '- Rp 50.000', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), ), ), const Divider(height: 1), // Item 2 (Pemasukan - Hijau) ListTile( leading: const CircleAvatar( backgroundColor: Colors.green, child: Icon(Icons.attach_money, color: Colors.white), ), title: const Text('Gaji Bulanan'), subtitle: const Text('01 Sep 2026'), trailing: const Text( '+ Rp 5.000.000', style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold), ), ), const Divider(height: 1), // Item 3 (Pengeluaran - Merah) ListTile( leading: const CircleAvatar( backgroundColor: Colors.blueAccent, child: Icon(Icons.directions_car, color: Colors.white), ), title: const Text('Isi Bensin'), subtitle: const Text('10 Sep 2026'), trailing: const Text( '- Rp 150.000', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), ), ), const Divider(height: 1), // TUGAS 2 PRAKTIKUM 2: Transaksi Fiktif Baru 1 (Pemasukan - Hijau) ListTile( leading: const CircleAvatar( backgroundColor: Colors.orange, child: Icon(Icons.laptop, color: Colors.white), ), title: const Text('Bonus Freelance'), subtitle: const Text('14 Sep 2026'), trailing: const Text( '+ Rp 1.200.000', style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold), ), ), const Divider(height: 1), // TUGAS 2 PRAKTIKUM 2: Transaksi Fiktif Baru 2 (Pengeluaran - Merah) ListTile( leading: const CircleAvatar( backgroundColor: Colors.purple, child: Icon(Icons.shopping_bag, color: Colors.white), ), title: const Text('Belanja Bulanan'), subtitle: const Text('15 Sep 2026'), trailing: const Text( '- Rp 350.000', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold), ), ), ], ), ), ], ); } }

Penjelasan Kode & Analisis:

  • ListTile: Terdiri dari 3 slot utama, yaitu leading (CircleAvatar berikon), title/subtitle (nama & tanggal transaksi), serta trailing (nominal). Mempermudah pembentukan UI daftar tanpa menyusun Row/Column secara manual.
  • Divider(height: 1): Garis tipis penyekat antar item transaksi.
  • Tugas 1 Praktikum 2: Warna teks trailing diatur โ€” merah untuk pengeluaran (-) dan hijau untuk pemasukan (+), sehingga langsung meningkatkan readability informasi.
  • Tugas 2 Praktikum 2: Dua item baru ditambahkan โ€” "Bonus Freelance" (+ Rp 1.200.000, indikator hijau) dan "Belanja Bulanan" (- Rp 350.000, indikator merah).

Hasil Praktikum: Daftar 5 item riwayat transaksi dalam satu kartu berbayangan, tiap baris memiliki ikon berwarna, nama, tanggal, dan nominal berwarna hijau/merah sesuai jenis transaksi.

Hasil Praktikum 2 - Riwayat Transaksi ListTile
Gambar 3.4: Tampilan Riwayat Transaksi Berbasis ListTile
3.5 Praktikum 2 โ€” Integrasi Dashboard (Scrollable) & Root App

Langkah: Mengintegrasikan seluruh widget (GreetingWidget, BalanceCardWidget, ActionButtonsWidget, RecentTransactionsWidget) ke dalam satu layar DashboardScreen yang dibungkus SingleChildScrollView, serta konfigurasi aplikasi utama MyApp.

Kode Program: Entry Point & Dashboard Screen (main.dart)
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } // 1. WIDGET UTAMA class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Expense Tracker', theme: ThemeData(primarySwatch: Colors.blue), home: const DashboardScreen(), ); } } // 2. HALAMAN UTAMA (Scaffold) class DashboardScreen extends StatelessWidget { const DashboardScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Praktikum 1: Widgets'), backgroundColor: Colors.blue, ), body: const SingleChildScrollView( child: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ GreetingWidget(), SizedBox(height: 20), BalanceCardWidget(), SizedBox(height: 20), ActionButtonsWidget(), SizedBox(height: 20), RecentTransactionsWidget(), ], ), ), ), ); } }

Penjelasan Kode & Analisis:

  • void main() => runApp(const MyApp()): Entry point aplikasi Flutter.
  • MaterialApp: Widget root yang menetapkan tema Material Design, judul aplikasi, dan halaman awal (home).
  • Scaffold: Menyediakan struktur dasar antarmuka dengan appBar dan area body.
  • SingleChildScrollView: Menyediakan kemampuan scrolling pada body. Membungkus Column di dalamnya mencegah masalah overflow pixels (garis hitam-kuning) ketika jumlah item transaksi bertambah atau saat aplikasi dijalankan pada perangkat berlayar kecil.
  • Padding(padding: EdgeInsets.all(16.0)): Memberikan jarak seragam 16px dari seluruh tepi layar.

Hasil Praktikum: Aplikasi berjalan utuh membentuk antarmuka Dashboard Expense Tracker; layar dapat digulirkan secara halus saat konten melebihi tinggi layar.

IV. Kesimpulan

Hasil & Kesimpulan Praktikum
  • Berdasarkan seluruh rangkaian kegiatan praktikum, pengembangan antarmuka aplikasi menggunakan Flutter berbasis Dart menawarkan fleksibilitas serta efisiensi tinggi melalui konsep everything is a widget.
  • Praktikum 1 berhasil menanamkan pemahaman mendasar mengenai perbedaan StatelessWidget (komponen statis) dan StatefulWidget (komponen interaktif). Fungsi setState() terbukti efektif sebagai mekanisme dasar untuk merender ulang tampilan saat terjadi perubahan state internal.
  • Praktikum 2 berhasil menerapkan kemampuan layouting dan styling secara nyata. Penggabungan Row, Column, Card, Container, dan ListTile memungkinkan pembangunan antarmuka Dashboard Expense Tracker yang rapi, informatif, dan responsif.
  • Pemanfaatan SingleChildScrollView berhasil mencegah kendala UI overflow saat komponen antarmuka melebihi batas layar perangkat.
  • Seluruh tugas latihan pada modul praktikum โ€” kustomisasi warna kartu saldo, penambahan informasi nomor rekening, pewarnaan status nominal transaksi, hingga penambahan data transaksi fiktif baru โ€” telah berhasil diimplementasikan dan diverifikasi di dalam kode program utama.
๐Ÿ™
Source Code Repository
Lihat seluruh kode program Aplikasi Mobile di GitHub (Branch Praktikum 2)
AhmadZahran1011 / AppMobile-AhmadZahran (praktikum2)