📱 Materi & Praktikum Flutter: Struktur Kontrol
Project: Aplikasi Penilaian Siswa
🎯 Tujuan Pembelajaran
Setelah mempelajari materi ini, mahasiswa mampu:
Memahami & menerapkan
if,switch,loop,break,continue, nested loopMengimplementasikan struktur kontrol dalam aplikasi Flutter nyata
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.
// 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).
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.
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.
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.
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:
break | continue |
|---|---|
| Menghentikan seluruh loop | Melewati 1 iterasi saja |
1.6 Nested Loop — Loop di Dalam Loop
Konteks nyata: Membuat tabel nilai (siswa × mata pelajaran).
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):
| Fitur | Struktur Kontrol |
|---|---|
| Input nilai & tentukan status kelulusan | if |
| Tampilkan grade A-E | switch |
| Hitung total & rata-rata seluruh siswa | loop |
| Stop input bila nilai di luar 0-100 | break |
| Skip siswa tanpa nama saat analisis | continue |
| Tampilkan tabel siswa × mapel | nested loop |
📁 Struktur Project
lib/
├── main.dart
├── models/
│ └── siswa.dart
├── services/
│ └── penilaian_service.dart ← LOGIKA STRUKTUR KONTROL
└── screens/
├── home_screen.dart
└── hasil_screen.dart1️⃣ models/siswa.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
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
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
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
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)
| No | Skenario Uji | Struktur Kontrol Diuji | Hasil yang Diharapkan |
|---|---|---|---|
| 1 | Input Budi: 80, 75, 90 | if, switch | Grade B, Status LULUS |
| 2 | Input Sari: 85, 88, 92 | if | LULUS, ranking 1 |
| 3 | Input nilai 150 | if validasi | Snackbar error |
| 4 | Hapus nama, klik Analisis | continue (di service) | Siswa dilewati |
| 5 | Tambah siswa nilai 200 lalu Analisis | break (di service) | Perhitungan berhenti, output peringatan |
| 6 | Tambah 3 siswa, lihat ranking | nested loop (sort) | Urutan benar |
✏️ Tugas Pengembangan (Mandiri)
Modifikasi
switch: Ubah grade menjadi skalaA+, A, A-, B+...(min. 6 case).Tambah
while: Buat menu "input cepat" yang terus meminta nilai sampai user menekan tombol "Selesai".Nested loop ekspor: Buat fitur cetak seluruh rapor dalam format teks (setiap siswa × mapel).
breakdengan label: Hentikan outer loop saat menemukan siswa dengan nilai di bawah 50.
📖 RANGKUMAN KONSEP
| Struktur | Kapan Dipakai | Contoh di Project |
|---|---|---|
if | 2+ jalur keputusan | Status kelulusan, validasi input |
switch | Banyak pilihan diskrit | Grade A-E |
for / for-in | Jumlah iterasi diketahui | Hitung total nilai |
while | Iterasi sampai kondisi | (Tugas) input berulang |
break | Hentikan seluruh loop | Stop saat data invalid |
continue | Lewati 1 iterasi | Skip siswa tanpa nama |
| Nested loop | 2 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! 🚀







Tidak ada komentar:
Posting Komentar