✦ MEMBUAT APLIKASI KAS KELAS MENGGUNAKAN FLUTTER ✦

 



🌿 01. Pendahuluan

Di tengah perkembangan teknologi yang semakin pesat, berbagai aktivitas sehari-hari mulai beralih ke sistem digital yang lebih praktis dan efisien. Salah satunya adalah pengelolaan uang kas kelas yang biasanya masih dilakukan secara manual.

Melalui proyek ini, saya mencoba membuat sebuah Aplikasi Kas Kelas menggunakan Flutter, yaitu aplikasi sederhana yang dirancang untuk membantu pencatatan pemasukan, pengeluaran, dan saldo keuangan kelas.

Proyek ini menjadi salah satu pengalaman saya dalam mempelajari dunia pemrograman, khususnya dalam mengembangkan aplikasi menggunakan Flutter dan bahasa pemrograman Dart.

Bagi saya, setiap baris kode bukan hanya sekadar perintah, melainkan bagian dari proses menciptakan sesuatu yang bermanfaat.

💻 02. Mengenal Teknologi yang Digunakan

Dalam pembuatan aplikasi ini, terdapat beberapa teknologi yang digunakan.

✧ Flutter
Framework untuk membangun aplikasi dengan tampilan yang modern dan responsif.

✧ Dart
Bahasa pemrograman yang digunakan untuk mengembangkan aplikasi Flutter.

✧ Visual Studio Code
Editor kode yang digunakan untuk menulis dan mengelola program.

Dengan menggabungkan teknologi tersebut, aplikasi kas kelas dapat dibuat dengan tampilan sederhana yang mudah dipahami oleh pengguna.

🎯 03. Tujuan Pembuatan Aplikasi

Aplikasi kas kelas ini dibuat untuk membantu pengelolaan keuangan kelas agar lebih teratur dan mudah dipantau.

Beberapa tujuan pembuatannya adalah:

  • Mempermudah pencatatan uang kas.

  • Mengetahui jumlah pemasukan dan pengeluaran.

  • Menghitung saldo kas secara otomatis.

  • Menampilkan informasi keuangan dalam satu halaman.

  • Menerapkan ilmu pemrograman Flutter ke dalam proyek nyata.

🛠️ 04. Kode Program Aplikasi Kas Kelas

Berikut merupakan contoh kode program Flutter untuk membuat aplikasi kas kelas sederhana.

Aplikasi ini memiliki fitur pencatatan pemasukan dan pengeluaran, perhitungan saldo, serta riwayat transaksi.

Langkah 1 — Membuat Project Flutter

Buka terminal Visual Studio Code, kemudian jalankan perintah berikut:

flutter create aplikasi_kas_kelas

Masuk ke folder project:

cd aplikasi_kas_kelas

Jalankan project menggunakan perintah:

flutter run

Langkah 2 — Memasukkan Kode Program

Buka file lib/main.dart, hapus kode sebelumnya, kemudian ganti dengan kode berikut.

import 'package:flutter/material.dart';

void main() {
  runApp(const KasKelasApp());
}

class KasKelasApp extends StatelessWidget {
  const KasKelasApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Kas Kelas',
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2563EB),
        ),
        scaffoldBackgroundColor: const Color(0xFFF4F7FC),
        fontFamily: 'Roboto',
      ),
      home: const KasKelasPage(),
    );
  }
}

class Transaksi {
  final String nama;
  final String keterangan;
  final int jumlah;
  final DateTime tanggal;
  final bool pemasukan;

  Transaksi({
    required this.nama,
    required this.keterangan,
    required this.jumlah,
    required this.tanggal,
    required this.pemasukan,
  });
}

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

  @override
  State<KasKelasPage> createState() => _KasKelasPageState();
}

class _KasKelasPageState extends State<KasKelasPage> {
  final List<Transaksi> transaksiList = [];

  int get totalPemasukan {
    return transaksiList
        .where((t) => t.pemasukan)
        .fold(0, (total, t) => total + t.jumlah);
  }

  int get totalPengeluaran {
    return transaksiList
        .where((t) => !t.pemasukan)
        .fold(0, (total, t) => total + t.jumlah);
  }

  int get saldo => totalPemasukan - totalPengeluaran;

  String formatRupiah(int angka) {
    final nilai = angka.abs().toString();
    final hasil = nilai.replaceAllMapped(
      RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
      (match) => '${match[1]}.',
    );

    return '${angka < 0 ? '-' : ''}Rp$hasil';
  }

  String formatTanggal(DateTime tanggal) {
    final hari = tanggal.day.toString().padLeft(2, '0');
    final bulan = tanggal.month.toString().padLeft(2, '0');
    final tahun = tanggal.year;

    return '$hari/$bulan/$tahun';
  }

  Future<void> tambahTransaksi() async {
    final namaController = TextEditingController();
    final jumlahController = TextEditingController();
    final keteranganController = TextEditingController();

    bool pemasukan = true;

    final hasil = await showDialog<Transaksi>(
      context: context,
      builder: (dialogContext) {
        return StatefulBuilder(
          builder: (context, setDialogState) {
            return AlertDialog(
              title: const Text('Tambah Transaksi'),
              content: SingleChildScrollView(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    TextField(
                      controller: namaController,
                      decoration: const InputDecoration(
                        labelText: 'Nama transaksi',
                        hintText: 'Contoh: Kas Mingguan',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 12),
                    TextField(
                      controller: jumlahController,
                      keyboardType: TextInputType.number,
                      decoration: const InputDecoration(
                        labelText: 'Jumlah uang',
                        prefixText: 'Rp ',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 12),
                    TextField(
                      controller: keteranganController,
                      decoration: const InputDecoration(
                        labelText: 'Keterangan',
                        hintText: 'Contoh: Kas minggu pertama',
                        border: OutlineInputBorder(),
                      ),
                    ),
                    const SizedBox(height: 12),
                    DropdownButtonFormField<bool>(
                      initialValue: pemasukan,
                      decoration: const InputDecoration(
                        labelText: 'Jenis transaksi',
                        border: OutlineInputBorder(),
                      ),
                      items: const [
                        DropdownMenuItem(
                          value: true,
                          child: Text('Pemasukan'),
                        ),
                        DropdownMenuItem(
                          value: false,
                          child: Text('Pengeluaran'),
                        ),
                      ],
                      onChanged: (value) {
                        if (value != null) {
                          setDialogState(() {
                            pemasukan = value;
                          });
                        }
                      },
                    ),
                  ],
                ),
              ),
              actions: [
                TextButton(
                  onPressed: () {
                    Navigator.pop(dialogContext);
                  },
                  child: const Text('Batal'),
                ),
                ElevatedButton(
                  onPressed: () {
                    final nama = namaController.text.trim();
                    final jumlah = int.tryParse(
                      jumlahController.text.trim(),
                    );

                    if (nama.isEmpty ||
                        jumlah == null ||
                        jumlah <= 0) {
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(
                          content: Text(
                            'Isi nama dan jumlah uang dengan benar.',
                          ),
                        ),
                      );
                      return;
                    }

                    Navigator.pop(
                      dialogContext,
                      Transaksi(
                        nama: nama,
                        keterangan:
                            keteranganController.text.trim().isEmpty
                                ? '-'
                                : keteranganController.text.trim(),
                        jumlah: jumlah,
                        tanggal: DateTime.now(),
                        pemasukan: pemasukan,
                      ),
                    );
                  },
                  child: const Text('Simpan'),
                ),
              ],
            );
          },
        );
      },
    );

    namaController.dispose();
    jumlahController.dispose();
    keteranganController.dispose();

    if (!mounted) return;

    if (hasil != null) {
      setState(() {
        transaksiList.insert(0, hasil);
      });

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Transaksi berhasil ditambahkan!'),
        ),
      );
    }
  }

  Future<void> hapusTransaksi(int index) async {
    final setuju = await showDialog<bool>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('Hapus Transaksi'),
          content: const Text(
            'Apakah kamu yakin ingin menghapus transaksi ini?',
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context, false),
              child: const Text('Batal'),
            ),
            ElevatedButton(
              onPressed: () => Navigator.pop(context, true),
              child: const Text('Hapus'),
            ),
          ],
        );
      },
    );

    if (setuju == true && mounted) {
      setState(() {
        transaksiList.removeAt(index);
      });

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Transaksi berhasil dihapus.'),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color(0xFF2563EB),
        foregroundColor: Colors.white,
        title: const Row(
          children: [
            Icon(Icons.account_balance_wallet_rounded),
            SizedBox(width: 10),
            Text(
              'Kas Kelas',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
          ],
        ),
        actions: [
          IconButton(
            onPressed: tambahTransaksi,
            tooltip: 'Tambah transaksi',
            icon: const Icon(Icons.add_circle_outline),
          ),
        ],
      ),
      body: SafeArea(
        child: ListView(
          padding: const EdgeInsets.all(18),
          children: [
            const Text(
              'Halo, Bendahara! 👋',
              style: TextStyle(
                fontSize: 23,
                fontWeight: FontWeight.bold,
                color: Color(0xFF172554),
              ),
            ),
            const SizedBox(height: 6),
            const Text(
              'Kelola keuangan kelas dengan mudah.',
              style: TextStyle(
                color: Colors.blueGrey,
                fontSize: 14,
              ),
            ),
            const SizedBox(height: 22),

            // Kartu saldo
            Container(
              padding: const EdgeInsets.all(22),
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  colors: [
                    Color(0xFF2563EB),
                    Color(0xFF1D4ED8),
                  ],
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                ),
                borderRadius: BorderRadius.circular(22),
                boxShadow: [
                  BoxShadow(
                    color: Colors.blue.withValues(alpha: 0.18),
                    blurRadius: 16,
                    offset: const Offset(0, 8),
                  ),
                ],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Row(
                    children: [
                      Icon(
                        Icons.account_balance_wallet_outlined,
                        color: Colors.white70,
                      ),
                      SizedBox(width: 8),
                      Text(
                        'Total Saldo Kas',
                        style: TextStyle(
                          color: Colors.white70,
                          fontSize: 15,
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 14),
                  Text(
                    formatRupiah(saldo),
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 30,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 18),
                  const Divider(color: Colors.white30),
                  const SizedBox(height: 8),
                  const Row(
                    children: [
                      Icon(
                        Icons.check_circle_outline,
                        color: Colors.white70,
                        size: 18,
                      ),
                      SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          'Catat setiap transaksi dengan teliti.',
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 12,
                          ),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),

            const SizedBox(height: 20),

            // Ringkasan pemasukan dan pengeluaran
            Row(
              children: [
                Expanded(
                  child: kartuRingkasan(
                    judul: 'Pemasukan',
                    jumlah: totalPemasukan,
                    ikon: Icons.south_west_rounded,
                    warna: const Color(0xFF16A34A),
                  ),
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: kartuRingkasan(
                    judul: 'Pengeluaran',
                    jumlah: totalPengeluaran,
                    ikon: Icons.north_east_rounded,
                    warna: const Color(0xFFEA580C),
                  ),
                ),
              ],
            ),

            const SizedBox(height: 26),

            // Judul daftar transaksi
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                const Text(
                  'Riwayat Transaksi',
                  style: TextStyle(
                    fontSize: 19,
                    fontWeight: FontWeight.bold,
                    color: Color(0xFF172554),
                  ),
                ),
                Text(
                  '${transaksiList.length} transaksi',
                  style: const TextStyle(
                    color: Colors.blueGrey,
                    fontSize: 12,
                  ),
                ),
              ],
            ),

            const SizedBox(height: 12),

            if (transaksiList.isEmpty)
              Container(
                padding: const EdgeInsets.symmetric(
                  vertical: 35,
                  horizontal: 20,
                ),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(18),
                ),
                child: const Column(
                  children: [
                    Icon(
                      Icons.receipt_long_outlined,
                      size: 55,
                      color: Color(0xFF94A3B8),
                    ),
                    SizedBox(height: 12),
                    Text(
                      'Belum Ada Transaksi',
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    SizedBox(height: 6),
                    Text(
                      'Tekan tombol + untuk menambahkan '
                      'pemasukan atau pengeluaran.',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: Colors.blueGrey,
                        fontSize: 13,
                      ),
                    ),
                  ],
                ),
              )
            else
              ...transaksiList.asMap().entries.map((entry) {
                final index = entry.key;
                final transaksi = entry.value;

                return Card(
                  elevation: 0,
                  color: Colors.white,
                  margin: const EdgeInsets.only(bottom: 10),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(16),
                  ),
                  child: ListTile(
                    contentPadding: const EdgeInsets.symmetric(
                      horizontal: 14,
                      vertical: 7,
                    ),
                    leading: CircleAvatar(
                      backgroundColor: transaksi.pemasukan
                          ? const Color(0xFFDCFCE7)
                          : const Color(0xFFFFEDD5),
                      child: Icon(
                        transaksi.pemasukan
                            ? Icons.south_west_rounded
                            : Icons.north_east_rounded,
                        color: transaksi.pemasukan
                            ? const Color(0xFF16A34A)
                            : const Color(0xFFEA580C),
                      ),
                    ),
                    title: Text(
                      transaksi.nama,
                      style: const TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 14,
                      ),
                    ),
                    subtitle: Text(
                      '${transaksi.keterangan}\n'
                      '${formatTanggal(transaksi.tanggal)}',
                      style: const TextStyle(
                        fontSize: 12,
                        height: 1.6,
                        color: Colors.blueGrey,
                      ),
                    ),
                    isThreeLine: true,
                    trailing: SizedBox(
                      width: 105,
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.end,
                        children: [
                          Flexible(
                            child: FittedBox(
                              fit: BoxFit.scaleDown,
                              child: Text(
                                '${transaksi.pemasukan ? '+' : '-'}'
                                '${formatRupiah(transaksi.jumlah)}',
                                style: TextStyle(
                                  fontWeight: FontWeight.bold,
                                  fontSize: 13,
                                  color: transaksi.pemasukan
                                      ? const Color(0xFF16A34A)
                                      : const Color(0xFFEA580C),
                                ),
                              ),
                            ),
                          ),
                          IconButton(
                            visualDensity: VisualDensity.compact,
                            tooltip: 'Hapus transaksi',
                            onPressed: () => hapusTransaksi(index),
                            icon: const Icon(
                              Icons.delete_outline,
                              size: 20,
                              color: Colors.redAccent,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                );
              }),

            const SizedBox(height: 20),

            SizedBox(
              width: double.infinity,
              child: ElevatedButton.icon(
                onPressed: tambahTransaksi,
                icon: const Icon(Icons.add),
                label: const Text('Tambah Transaksi'),
                style: ElevatedButton.styleFrom(
                  backgroundColor: const Color(0xFF2563EB),
                  foregroundColor: Colors.white,
                  padding: const EdgeInsets.symmetric(vertical: 16),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(14),
                  ),
                ),
              ),
            ),

            const SizedBox(height: 20),

            const Center(
              child: Text(
                'Aplikasi Kas Kelas • Dibuat dengan Flutter',
                style: TextStyle(
                  fontSize: 11,
                  color: Colors.blueGrey,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget kartuRingkasan({
    required String judul,
    required int jumlah,
    required IconData ikon,
    required Color warna,
  }) {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(18),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.all(9),
            decoration: BoxDecoration(
              color: warna.withValues(alpha: 0.12),
              borderRadius: BorderRadius.circular(12),
            ),
            child: Icon(
              ikon,
              color: warna,
              size: 22,
            ),
          ),
          const SizedBox(height: 14),
          Text(
            judul,
            style: const TextStyle(
              fontSize: 13,
              color: Colors.blueGrey,
            ),
          ),
          const SizedBox(height: 6),
          FittedBox(
            fit: BoxFit.scaleDown,
            alignment: Alignment.centerLeft,
            child: Text(
              formatRupiah(jumlah),
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Color(0xFF172554),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Langkah 3 — Menjalankan Aplikasi

Setelah kode dimasukkan, simpan file main.dart, kemudian jalankan perintah berikut di terminal VS Code:

flutter run

Jika menggunakan emulator Android, pastikan emulator sudah aktif. Jika menggunakan HP Android, aktifkan USB debugging dan hubungkan perangkat ke komputer.

Catatan: Kode ini menggunakan withValues(alpha: 0.10), yang tersedia pada versi Flutter modern. Data transaksi masih disimpan sementara di memori aplikasi, sehingga belum tersimpan secara permanen ketika aplikasi ditutup.

📱 05. Hasil Aplikasi

Setelah program berhasil dijalankan, aplikasi akan menampilkan halaman utama dengan desain minimalis dan perpaduan warna hijau sage serta putih krem.

Berikut gambaran hasil yang akan ditampilkan berdasarkan kode program di atas.





🌸 06. Kesimpulan

Pembuatan aplikasi kas kelas menggunakan Flutter menjadi pengalaman yang bermanfaat dalam mempelajari pengembangan aplikasi mobile.

Melalui proyek ini, saya belajar membangun antarmuka, menggunakan komponen Flutter, mengolah data transaksi, serta membuat perhitungan saldo secara otomatis.

Meskipun masih sederhana, aplikasi ini dapat menjadi dasar untuk pengembangan fitur yang lebih lengkap, seperti penyimpanan data permanen, daftar pembayaran kas setiap siswa, laporan keuangan, dan ekspor laporan ke PDF.

Saya percaya bahwa setiap proyek adalah kesempatan untuk berkembang. Tidak harus dimulai dari sesuatu yang besar, karena karya yang bermanfaat sering kali berawal dari ide sederhana yang diwujudkan dengan kesungguhan.

"Satu baris kode adalah satu langkah kecil. Satu proyek adalah pengalaman baru. Dan setiap pengalaman membawa kita lebih dekat pada impian."

━━━━━━━━━━━━━━━━━━━━

♡ Terima Kasih Telah Berkunjung ♡

Terima kasih telah membaca dokumentasi proyek saya.

Semoga tulisan ini dapat memberikan inspirasi bagi teman-teman yang ingin mulai belajar Flutter dan membuat aplikasi sendiri.

Teruslah belajar, teruslah mencoba, dan jangan berhenti berkarya.

Dibuat oleh Soni Kurniawan yang berani memulai hari ini.

— Dokumentasi Proyek Pemrograman | Rekayasa Perangkat Lunak (RPL)

Komentar

Postingan populer dari blog ini

Praktikum Flutter Seri 2 — Struktur Project dan main.dart

💻 MEMBUAT "APLIKASI DATA SISWA" MENGGUNAKAN FLUTTER

"Dari Pengguna HP Menjadi Pembuat Aplikasi"