Praktikum Mobile Application 2

Implementasi Flutter Widgets, State Management, Layouting & Styling

Dokumentasi praktikum Flutter yang berfokus pada penggunaan widget, state management dasar, layouting, styling, serta pengembangan dashboard sederhana menggunakan Flutter.

Flutter
Halo, Budi
Saldo Utama
Rp 5.000.000
No. Rekening: 1234-5678
Pemasukan
Pengeluaran
Transfer
Transaksi Terakhir

Ringkasan Praktikum Mobile Application

Praktikum ini membahas pengembangan antarmuka aplikasi menggunakan Flutter melalui implementasi widget, state management dasar, layouting, dan styling. Implementasi dilakukan dengan membangun dashboard sederhana bertema Expense Tracker yang memiliki informasi pengguna, saldo, tombol aksi, serta daftar transaksi.

Flutter Widgets

Memahami penggunaan StatelessWidget, StatefulWidget, State, Scaffold, AppBar, Card, CircleAvatar, dan IconButton.

State Management

Mengimplementasikan perubahan tampilan saldo menggunakan StatefulWidget, State, dan setState().

Layouting

Menggunakan Row, Column, Container, Card, dan ListTile untuk membangun struktur dashboard.

Transaction Dashboard

Membuat daftar transaksi dengan nominal pemasukan dan pengeluaran serta kemampuan scrolling menggunakan SingleChildScrollView.

1. Konsep Widget & State Management

Praktikum ini berfokus pada pemahaman dasar widget dan state management pada Flutter. Widget menjadi fondasi utama dalam membangun antarmuka, sementara state digunakan untuk mengelola data yang dapat berubah selama aplikasi berjalan.

StatelessWidget

Digunakan pada komponen yang tidak memiliki perubahan state. Contoh: MyApp, GreetingWidget, dan DashboardScreen.

StatefulWidget

Digunakan pada BalanceCardWidget karena visibilitas saldo dapat berubah.

State
bool _isBalanceVisible = true;

Variabel tersebut menentukan apakah saldo ditampilkan atau disembunyikan.

setState()
void _toggleVisibility() {
  setState(() {
    _isBalanceVisible = !_isBalanceVisible;
  });
}

Fungsi setState() digunakan untuk memperbarui tampilan ketika state berubah.

Text(
  _isBalanceVisible
      ? 'Rp 5.000.000'
      : 'Rp *********',
)
Hasil

Implementasi berhasil menampilkan dashboard dengan komponen greeting dan kartu saldo yang memiliki interaksi untuk menampilkan atau menyembunyikan nominal saldo.

2. Implementasi Dashboard

Dashboard dibuat dengan komponen utama yang mudah dibaca dan memiliki struktur yang rapi. Implementasi ini mencakup element greeting, saldocard, action buttons, serta recent transactions.

Greeting

Halo, Budi
Selamat datang kembali!

Menggunakan CircleAvatar, Row, Column, dan Text.

Balance Card

Saldo Utama
Rp 5.000.000
Tombol visibility ditambahkan pada kartu saldo.

Pada hasil pengembangan, kartu saldo menggunakan Colors.teal dan memiliki informasi No. Rekening: 1234-5678.

Action Buttons

Terdapat tombol aksi untuk:
- Pemasukan
- Pengeluaran
- Transfer

Recent Transactions

Bagian ini menampilkan daftar transaksi terakhir dengan kategori pemasukan dan pengeluaran.

Hasil

Dashboard berhasil dibuat dengan struktur yang konsisten, menampilkan greeting, saldo, tombol aksi, dan daftar transaksi yang rapi.

3. Layouting & Styling

Pada praktikum ini, layouting dan styling digunakan untuk membangun tampilan dashboard yang rapi, modern, dan mudah dipahami.

Row

Digunakan untuk menyusun widget secara horizontal.

Column

Digunakan untuk menyusun widget secara vertikal.

Container

Digunakan untuk styling tombol aksi.

Card

Digunakan sebagai container untuk saldo dan transaksi.

ListTile

Digunakan untuk setiap item transaksi. Setiap ListTile memiliki leading, title, subtitle, dan trailing.

SingleChildScrollView

Digunakan agar dashboard dapat di-scroll secara vertikal.

Hasil

Dashboard berhasil menampilkan greeting, balance card, action buttons, transaction list, dan dapat di-scroll ketika konten bertambah.

4. Pengembangan Fitur

Selama praktikum, beberapa perubahan dilakukan untuk meningkatkan kualitas tampilan dan fungsi dashboard.

Perubahan Styling Balance Card

Warna awal:

Colors.blueAccent

Kemudian dikembangkan menjadi:

Colors.teal
Penambahan Nomor Rekening
No. Rekening: 1234-5678
Transaction Styling

Pengeluaran menggunakan warna merah.
Pemasukan menggunakan warna hijau.

Penambahan Data Transaksi

Ditambahkan beberapa transaksi seperti:
- Belanja Bulanan — 15 Sep 2026 — - Rp 300.000
- Freelance Design — 18 Sep 2026 — + Rp 750.000

Hasil

Implementasi dashboard mengalami peningkatan visual dan fungsionalitas dengan tampilan saldo lebih modern serta data transaksi yang lebih bervariasi.

5. Daftar Transaksi

Transaksi Tanggal Nominal Jenis
Makan Siang 13 Sep 2026 - Rp 50.000 Pengeluaran
Gaji Bulanan 01 Sep 2026 + Rp 5.000.000 Pemasukan
Isi Bensin 10 Sep 2026 - Rp 150.000 Pengeluaran
Belanja Bulanan 15 Sep 2026 - Rp 300.000 Pengeluaran
Freelance Design 18 Sep 2026 + Rp 750.000 Pemasukan

6. Source Code

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(
      title: 'Expense Tracker',
      debugShowCheckedModeBanner: false,
      home: const DashboardScreen(),
    );
  }
}

class DashboardScreen extends StatefulWidget {
  const DashboardScreen({super.key});

  @override
  State<DashboardScreen> createState() => _DashboardScreenState();
}

class _DashboardScreenState extends State<DashboardScreen> {
  bool _isBalanceVisible = true;

  void _toggleVisibility() {
    setState(() {
      _isBalanceVisible = !_isBalanceVisible;
    });
  }

  final List<Map<String, dynamic>> transactions = [
    {'title': 'Makan Siang', 'date': '13 Sep 2026', 'amount': -50000, 'type': 'Pengeluaran'},
    {'title': 'Gaji Bulanan', 'date': '01 Sep 2026', 'amount': 5000000, 'type': 'Pemasukan'},
    {'title': 'Isi Bensin', 'date': '10 Sep 2026', 'amount': -150000, 'type': 'Pengeluaran'},
    {'title': 'Belanja Bulanan', 'date': '15 Sep 2026', 'amount': -300000, 'type': 'Pengeluaran'},
    {'title': 'Freelance Design', 'date': '18 Sep 2026', 'amount': 750000, 'type': 'Pemasukan'},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F7FF),
      appBar: AppBar(
        title: const Text('Expense Tracker'),
        backgroundColor: Colors.teal,
        foregroundColor: Colors.white,
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              GreetingWidget(),
              const SizedBox(height: 20),
              BalanceCardWidget(
                isBalanceVisible: _isBalanceVisible,
                onToggle: _toggleVisibility,
              ),
              const SizedBox(height: 20),
              ActionButtonsWidget(),
              const SizedBox(height: 25),
              RecentTransactionsWidget(transactions: transactions),
            ],
          ),
        ),
      ),
    );
  }
}

class GreetingWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        const CircleAvatar(
          radius: 24,
          backgroundColor: Color(0xFFB3E5FC),
          child: Icon(Icons.person, color: Colors.teal),
        ),
        const SizedBox(width: 12),
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: const [
            Text(
              'Halo, Budi',
              style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
            ),
            Text(
              'Selamat datang kembali!',
              style: TextStyle(color: Colors.grey),
            ),
          ],
        ),
      ],
    );
  }
}

class BalanceCardWidget extends StatelessWidget {
  final bool isBalanceVisible;
  final VoidCallback onToggle;

  const BalanceCardWidget({
    super.key,
    required this.isBalanceVisible,
    required this.onToggle,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.teal,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      elevation: 4,
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text('Saldo Utama', style: TextStyle(color: Colors.white, fontSize: 16)),
                IconButton(
                  onPressed: onToggle,
                  icon: Icon(
                    isBalanceVisible ? Icons.visibility : Icons.visibility_off,
                    color: Colors.white,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 10),
            Text(
              isBalanceVisible ? 'Rp 5.000.000' : 'Rp *********',
              style: const TextStyle(
                color: Colors.white,
                fontSize: 28,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 8),
            const Text(
              'No. Rekening: 1234-5678',
              style: TextStyle(color: Colors.white70),
            ),
          ],
        ),
      ),
    );
  }
}

class ActionButtonsWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 14),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(16),
            ),
            child: const Center(child: Text('Pemasukan')),
          ),
        ),
        const SizedBox(width: 10),
        Expanded(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 14),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(16),
            ),
            child: const Center(child: Text('Pengeluaran')),
          ),
        ),
        const SizedBox(width: 10),
        Expanded(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 14),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(16),
            ),
            child: const Center(child: Text('Transfer')),
          ),
        ),
      ],
    );
  }
}

class RecentTransactionsWidget extends StatelessWidget {
  final List<Map<String, dynamic>> transactions;

  const RecentTransactionsWidget({super.key, required this.transactions});

  @override
  Widget build(BuildContext context) {
    return Card(
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: transactions.map((item) {
            final amount = item['amount'] as int;
            final isIncome = amount > 0;

            return ListTile(
              leading: CircleAvatar(
                backgroundColor: isIncome ? Colors.green[100] : Colors.red[100],
                child: Icon(
                  isIncome ? Icons.arrow_downward : Icons.arrow_upward,
                  color: isIncome ? Colors.green : Colors.red,
                ),
              ),
              title: Text(item['title']),
              subtitle: Text(item['date']),
              trailing: Text(
                '${isIncome ? '+' : '-'} Rp ${amount.abs().toString()}',
                style: TextStyle(
                  color: isIncome ? Colors.green : Colors.red,
                  fontWeight: FontWeight.bold,
                ),
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

7. Pengujian

A. Pengujian Saldo Terlihat

Action: Aplikasi dijalankan dengan saldo dalam kondisi terlihat.

Expected: "Rp 5.000.000"

Status: Berhasil

B. Pengujian Saldo Disembunyikan

Action: Tekan icon visibility.

Expected: "Rp *********"

Status: Berhasil

C. Pengujian Daftar Transaksi

Pastikan seluruh transaksi tampil:

- Makan Siang

- Gaji Bulanan

- Isi Bensin

- Belanja Bulanan

- Freelance Design

Status: Berhasil

D. Pengujian Warna Transaksi

Pengeluaran: Merah

Pemasukan: Hijau

Status: Berhasil

E. Pengujian Scroll

Action: Scroll dashboard ke bawah.

Expected: Seluruh daftar transaksi dapat diakses tanpa overflow.

Status: Berhasil

8. Kesimpulan

Praktikum Mobile Application 2 berhasil menerapkan konsep dasar widget Flutter, state management, layouting, dan styling melalui pengembangan dashboard sederhana berbasis Expense Tracker.

StatelessWidget digunakan untuk komponen yang tidak membutuhkan perubahan state. StatefulWidget digunakan pada kartu saldo. setState() digunakan untuk mengubah visibilitas saldo. Row dan Column digunakan untuk layout. Container dan Card digunakan untuk styling. ListTile digunakan untuk daftar transaksi. SingleChildScrollView digunakan untuk scrolling. Warna merah dan hijau digunakan untuk membedakan pengeluaran dan pemasukan.

Hasil Akhir: Dashboard Expense Tracker berhasil dibuat dan seluruh komponen utama dapat ditampilkan serta digunakan sesuai dengan tujuan praktikum.

Hasil Akhir

Dashboard Expense Tracker berhasil dibuat dan seluruh komponen utama dapat ditampilkan serta digunakan sesuai dengan tujuan praktikum.

Dokumentasi Hasil

Karimah Irsyadiyah

Mahasiswa Informatika, Universitas Andalas

Profil Profesional

Karimah Irsyadiyah adalah mahasiswa Informatika di Universitas Andalas yang menekuni pengembangan aplikasi mobile menggunakan Flutter serta dokumentasi hasil praktikum secara rapi dan profesional.