📱 Materi & Praktikum Flutter: Struktur Kontrol Project | Aplikasi Penilaian Siswa


📱 Materi & Praktikum Flutter: Struktur Kontrol

Project: Aplikasi Penilaian Siswa


🎯 Tujuan Pembelajaran

Setelah mempelajari materi ini, mahasiswa mampu:

  1. Memahami & menerapkan if, switch, loop, break, continue, nested loop

  2. Mengimplementasikan struktur kontrol dalam aplikasi Flutter nyata

  3. Membangun Aplikasi Penilaian yang valid sesuai kebutuhan dunia nyata


📚 BAGIAN 1: MATERI (Dengan Konteks Nyata)

1.1 if — Pengambilan Keputusan

Konteks nyata: Guru menentukan status kelulusan berdasarkan nilai.

dart
// Logika kelulusan sekolah
int nilai = 78;
String status;

if (nilai >= 75) {
  status = "LULUS";
} else if (nilai >= 60) {
  status = "REMEDIAL";
} else {
  status = "TIDAK LULUS";
}

Analogi: Seperti satpam sekolah — hanya membuka pintu jika syarat terpenuhi.


1.2 switch — Pemilihan Berdasarkan Kategori

Konteks nyata: Konversi nilai angka ke huruf (A, B, C, D, E).

dart
int nilai = 85;
String grade;

switch (nilai ~/ 10) {   // pembagian bulat
  case 10:
  case 9:
    grade = "A";
    break;
  case 8:
    grade = "B";
    break;
  case 7:
    grade = "C";
    break;
  case 6:
    grade = "D";
    break;
  default:
    grade = "E";
}
// grade = "B"

Kapan pakai switch? Saat kondisi berupa pilihan diskrit (bukan rentang).


1.3 loop — Perulangan

Konteks nyata: Menghitung rata-rata nilai dari banyak siswa.

dart
List<int> nilaiSiswa = [80, 75, 90, 65, 85];

// for — tahu jumlah pastinya
int total = 0;
for (int i = 0; i < nilaiSiswa.length; i++) {
  total += nilaiSiswa[i];
}
double rataRata = total / nilaiSiswa.length; // 79.0

// for-in — lebih bersih untuk koleksi
int total2 = 0;
for (var n in nilaiSiswa) {
  total2 += n;
}

// while — kondisi dinamis
int attempt = 0;
while (attempt < 3) {
  print("Percobaan login ke-${attempt + 1}");
  attempt++;
}

1.4 break — Hentikan Paksa

Konteks nyata: Guru berhenti input nilai jika siswa sudah tidak hadir.

dart
List<int?> nilai = [80, 75, null, 90, 85];

for (var n in nilai) {
  if (n == null) {
    print("Data tidak lengkap, hentikan input.");
    break;   // keluar dari loop
  }
  print("Nilai: $n");
}
// Output: 80, 75, "hentikan input"

1.5 continue — Lewati Iterasi

Konteks nyata: Lewati siswa yang tidak punya nilai (null) saat menghitung rata-rata.

dart
List<int?> nilai = [80, 75, null, 90, null, 85];
int total = 0;
int count = 0;

for (var n in nilai) {
  if (n == null) continue;   // skip yang null
  total += n;
  count++;
}
print("Rata-rata: ${total / count}");  // (80+75+90+85)/4 = 82.5

Perbedaan kunci:

breakcontinue
Menghentikan seluruh loopMelewati 1 iterasi saja

1.6 Nested Loop — Loop di Dalam Loop

Konteks nyata: Membuat tabel nilai (siswa × mata pelajaran).

dart
List<String> siswa = ["Budi", "Sari", "Andi"];
List<String> mapel = ["Matematika", "IPA", "B. Inggris"];
Map<String, List<int>> nilai = {
  "Budi": [80, 75, 90],
  "Sari": [85, 88, 92],
  "Andi": [70, 65, 78],
};

for (var s in siswa) {
  print("=== $s ===");
  for (int j = 0; j < mapel.length; j++) {
    print("${mapel[j]}: ${nilai[s]![j]}");
  }
}

Analogi: Menelusuri baris × kolom tabel — seperti membaca rapor per baris (siswa) lalu per kolom (mapel).


🛠️ BAGIAN 2: PRAKTIKUM — APLIKASI PENILAIAN

📋 Spesifikasi Aplikasi

Nama: SIAKNILAI — Sistem Input & Analisis Nilai Siswa

Fitur (mapping struktur kontrol):

FiturStruktur Kontrol
Input nilai & tentukan status kelulusanif
Tampilkan grade A-Eswitch
Hitung total & rata-rata seluruh siswaloop
Stop input bila nilai di luar 0-100break
Skip siswa tanpa nama saat analisiscontinue
Tampilkan tabel siswa × mapelnested loop

📁 Struktur Project

text
lib/
├── main.dart
├── models/
│   └── siswa.dart
├── services/
│   └── penilaian_service.dart    ← LOGIKA STRUKTUR KONTROL
└── screens/
    ├── home_screen.dart
    └── hasil_screen.dart

1️⃣ models/siswa.dart

dart
class Siswa {
  final String nama;
  final Map<String, int> nilaiMapel; // mapel -> nilai

  Siswa({required this.nama, required this.nilaiMapel});

  double get rataRata {
    int total = 0;
    for (var n in nilaiMapel.values) {
      total += n;
    }
    return nilaiMapel.isEmpty ? 0 : total / nilaiMapel.length;
  }

  String get status {
    if (rataRata >= 75) return "LULUS";
    else if (rataRata >= 60) return "REMEDIAL";
    else return "TIDAK LULUS";
  }

  String get grade {
    switch (rataRata ~/ 10) {
      case 10:
      case 9: return "A";
      case 8: return "B";
      case 7: return "C";
      case 6: return "D";
      default: return "E";
    }
  }
}

2️⃣ services/penilaian_service.dart — Inti Praktikum

dart
import '../models/siswa.dart';

class PenilaianService {
  // ===== LOOP + IF + BREAK: hitung rata-rata kelas, stop jika data invalid =====
  static Map<String, dynamic> hitungKelas(List<Siswa> daftar) {
    int total = 0;
    int jumlahNilai = 0;
    int jumlahSiswa = 0;

    for (var siswa in daftar) {
      // SKIP siswa tanpa nama (CONTINUE)
      if (siswa.nama.trim().isEmpty) {
        continue;
      }

      // VALIDASI: nilai harus 0-100, jika tidak → BREAK
      bool invalid = siswa.nilaiMapel.values.any((n) => n < 0 || n > 100);
      if (invalid) {
        print("⚠️ Data tidak valid pada ${siswa.nama}, hentikan perhitungan.");
        break;
      }

      // NESTED LOOP: jumlahkan setiap nilai mapel
      for (var nilai in siswa.nilaiMapel.values) {
        total += nilai;
        jumlahNilai++;
      }
      jumlahSiswa++;
    }

    double rataKelas = jumlahNilai == 0 ? 0 : total / jumlahNilai;

    return {
      "total": total,
      "jumlahNilai": jumlahNilai,
      "jumlahSiswa": jumlahSiswa,
      "rataKelas": rataKelas,
      "predikatKelas": _predikatKelas(rataKelas), // SWITCH
    };
  }

  // ===== SWITCH: predikat rata-rata kelas =====
  static String _predikatKelas(double rata) {
    if (rata >= 90) return "Sangat Baik";
    if (rata >= 75) return "Baik";
    if (rata >= 60) return "Cukup";
    return "Perlu Bimbingan";
  }

  // ===== NESTED LOOP: ranking siswa =====
  static List<Siswa> urutkanRanking(List<Siswa> daftar) {
    final hasil = List<Siswa>.from(daftar);
    // bubble sort sederhana
    for (int i = 0; i < hasil.length; i++) {
      for (int j = i + 1; j < hasil.length; j++) {
        if (hasil[j].rataRata > hasil[i].rataRata) {
          final tmp = hasil[i];
          hasil[i] = hasil[j];
          hasil[j] = tmp;
        }
      }
    }
    return hasil;
  }
}

3️⃣ screens/home_screen.dart

dart
import 'package:flutter/material.dart';
import '../models/siswa.dart';
import '../services/penilaian_service.dart';
import 'hasil_screen.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});
  
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final _namaCtrl = TextEditingController();
  final _matCtrl = TextEditingController();
  final _ipaCtrl = TextEditingController();
  final _bindoCtrl = TextEditingController();

  final List<Siswa> _daftarSiswa = [];

  void _tambahSiswa() {
    // VALIDASI dengan IF
    if (_namaCtrl.text.trim().isEmpty) {
      _snack("Nama tidak boleh kosong");
      return;
    }

    final mat = int.tryParse(_matCtrl.text) ?? -1;
    final ipa = int.tryParse(_ipaCtrl.text) ?? -1;
    final bindo = int.tryParse(_bindoCtrl.text) ?? -1;

    if (mat < 0 || mat > 100 || ipa < 0 || ipa > 100 || bindo < 0 || bindo > 100) {
      _snack("Nilai harus 0-100");
      return;
    }

    setState(() {
      _daftarSiswa.add(Siswa(
        nama: _namaCtrl.text.trim(),
        nilaiMapel: {"Matematika": mat, "IPA": ipa, "B. Indonesia": bindo},
      ));
    });

    _namaCtrl.clear();
    _matCtrl.clear();
    _ipaCtrl.clear();
    _bindoCtrl.clear();
  }

  void _snack(String msg) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("SIAKNILAI — Input Nilai")),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(
              controller: _namaCtrl,
              decoration: const InputDecoration(labelText: "Nama Siswa"),
            ),
            Row(
              children: [
                Expanded(child: _numField(_matCtrl, "Mat")),
                const SizedBox(width: 8),
                Expanded(child: _numField(_ipaCtrl, "IPA")),
                const SizedBox(width: 8),
                Expanded(child: _numField(_bindoCtrl, "B.Indo")),
              ],
            ),
            const SizedBox(height: 12),
            Row(
              children: [
                Expanded(
                  child: ElevatedButton(
                    onPressed: _tambahSiswa,
                    child: const Text("Tambah"),
                  ),
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green,
                    ),
                    onPressed: _daftarSiswa.isEmpty
                        ? null
                        : () {
                            final analisis =
                                PenilaianService.hitungKelas(_daftarSiswa);
                            final ranking =
                                PenilaianService.urutkanRanking(_daftarSiswa);
                            Navigator.push(
                              context,
                              MaterialPageRoute(
                                builder: (_) => HasilScreen(
                                  analisis: analisis,
                                  ranking: ranking,
                                ),
                              ),
                            );
                          },
                    child: const Text("Analisis"),
                  ),
                ),
              ],
            ),
            const SizedBox(height: 16),
            Expanded(
              child: ListView.builder(
                itemCount: _daftarSiswa.length,
                itemBuilder: (_, i) {
                  final s = _daftarSiswa[i];
                  return Card(
                    child: ListTile(
                      title: Text(s.nama),
                      subtitle: Text(
                        "Mat:${s.nilaiMapel['Matematika']}  "
                        "IPA:${s.nilaiMapel['IPA']}  "
                        "BIndo:${s.nilaiMapel['B. Indonesia']}",
                      ),
                      trailing: Chip(
                        label: Text("${s.grade} | ${s.status}"),
                        backgroundColor: s.status == "LULUS"
                            ? Colors.green.shade100
                            : s.status == "REMEDIAL"
                                ? Colors.orange.shade100
                                : Colors.red.shade100,
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _numField(TextEditingController c, String label) {
    return TextField(
      controller: c,
      keyboardType: TextInputType.number,
      decoration: InputDecoration(labelText: label),
    );
  }
}

4️⃣ screens/hasil_screen.dart

dart
import 'package:flutter/material.dart';
import '../models/siswa.dart';

class HasilScreen extends StatelessWidget {
  final Map<String, dynamic> analisis;
  final List<Siswa> ranking;

  const HasilScreen({
    super.key,
    required this.analisis,
    required this.ranking,
  });

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Hasil Analisis")),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Ringkasan kelas
            Card(
              color: Colors.blue.shade50,
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text("Rata-rata Kelas: ${analisis['rataKelas'].toStringAsFixed(2)}",
                        style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                    Text("Predikat: ${analisis['predikatKelas']}"),
                    Text("Jumlah Siswa Dianalisis: ${analisis['jumlahSiswa']}"),
                  ],
                ),
              ),
            ),
            const SizedBox(height: 16),
            const Text("🏆 Ranking Siswa",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
            const SizedBox(height: 8),
            Expanded(
              child: ListView.builder(
                itemCount: ranking.length,
                itemBuilder: (_, i) {
                  final s = ranking[i];
                  return ListTile(
                    leading: CircleAvatar(child: Text("${i + 1}")),
                    title: Text(s.nama),
                    subtitle: Text(
                      "Rata-rata: ${s.rataRata.toStringAsFixed(1)} | "
                      "Grade ${s.grade} | ${s.status}",
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

5️⃣ main.dart

dart
import 'package:flutter/material.dart';
import 'screens/home_screen.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SIAKNILAI',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      home: const HomeScreen(),
    );
  }
}

🧪 BAGIAN 3: LEMBAR PRAKTIKUM

🔬 Uji Coba Wajib (Isi tabel hasil)

NoSkenario UjiStruktur Kontrol DiujiHasil yang Diharapkan
1Input Budi: 80, 75, 90if, switchGrade B, Status LULUS
2Input Sari: 85, 88, 92ifLULUS, ranking 1
3Input nilai 150if validasiSnackbar error
4Hapus nama, klik Analisiscontinue (di service)Siswa dilewati
5Tambah siswa nilai 200 lalu Analisisbreak (di service)Perhitungan berhenti, output peringatan
6Tambah 3 siswa, lihat rankingnested loop (sort)Urutan benar

✏️ Tugas Pengembangan (Mandiri)

  1. Modifikasi switch: Ubah grade menjadi skala A+, A, A-, B+... (min. 6 case).

  2. Tambah while: Buat menu "input cepat" yang terus meminta nilai sampai user menekan tombol "Selesai".

  3. Nested loop ekspor: Buat fitur cetak seluruh rapor dalam format teks (setiap siswa × mapel).

  4. break dengan label: Hentikan outer loop saat menemukan siswa dengan nilai di bawah 50.


📖 RANGKUMAN KONSEP

StrukturKapan DipakaiContoh di Project
if2+ jalur keputusanStatus kelulusan, validasi input
switchBanyak pilihan diskritGrade A-E
for / for-inJumlah iterasi diketahuiHitung total nilai
whileIterasi sampai kondisi(Tugas) input berulang
breakHentikan seluruh loopStop saat data invalid
continueLewati 1 iterasiSkip siswa tanpa nama
Nested loop2 dimensi (baris×kolom)Ranking (sort), tabel nilai

💡 KESIMPULAN

Aplikasi SIAKNILAI membuktikan bahwa struktur kontrol bukan konsep abstrak — ia adalah jantung logika bisnis aplikasi nyata. Setiap tombol di aplikasi penilaian di baliknya ada if, switch, loop, break, continue, dan nested loop yang bekerja bersama.

Pesan kunci: Kuasai struktur kontrol = kuasai cara komputer "berpikir" menyelesaikan masalah nyata.


Aktifitas pengembangan info bisa tambahkan:

  • 🎥 Script video demo langkah praktikum

  • 📝 Soal kuis/asesmen (pilihan ganda + essay)

  • 🔌 Versi integrasi API (simpan ke backend)

  • 🎨 Desain UI lebih kompleks (grafik nilai dengan fl_chart)

Tinggal pilih beberapa atau semuanya, kita akan lanjutkan! 🚀

Share:

Tidak ada komentar:

Posting Komentar

Chat Admin

Chat via WhatsApp

Categories

Blogger Tricks

Blogger Themes