Aplikasi Jadwal Pelajaran Sederhana

Thumbnail Praktikum Flutter

๐Ÿ“ฑ Praktikum Flutter : Aplikasi Jadwal Pelajaran Sederhana

๐ŸŽฏ CRUD + setState + Local Storage (SharedPreferences)

๐Ÿ“… 07 April 2026 | ✏️ Wahana Belajar dan Berbagi

✅ Berikut contoh kode praktikum Flutter yang sederhana namun powerful. Boleh menggunakan code program ini, boleh juga cari referensi lain asalkan tentang fitur: CRUD sederhana (Tambah, Edit, Hapus) + setState + Local Storage.

๐Ÿ‘‰ Cocok untuk pembelajaran mandiri hari ini ๐Ÿ’ป๐Ÿ“ฑ

๐Ÿ’ก Kenapa Kode Ini Disarankan?

  • Mudah Dipahami Pemula – Struktur kode rapi, tidak terlalu kompleks.
  • CRUD Lengkap – Tambah, Edit, Hapus semua pakai setState (sesuai permintaan).
  • Penyimpanan Permanen – Pakai SharedPreferences, data tidak hilang saat aplikasi ditutup.
  • UI Fresh & Modern – Warna card dinamis, gradient appbar, modal bottom sheet.
  • Search & Filter Hari – Fitur pencarian dan filter jadwal berdasarkan hari.
  • Responsif Light/Dark Mode – Menyesuaikan tema sistem pengguna.
  • Siap Running di Zapp.run atau VS Code – Tanpa ribet setup database.
lib/main.dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Jadwal Pelajaran',
      theme: ThemeData(primarySwatch: Colors.indigo, useMaterial3: true),
      darkTheme: ThemeData(brightness: Brightness.dark, useMaterial3: true),
      themeMode: ThemeMode.system,
      home: JadwalPage(),
    );
  }
}

// Model Data
class JadwalModel {
  String id, mataPelajaran, hari, jam, ruang;
  int warnaIndeks;
  JadwalModel({required this.id, required this.mataPelajaran, required this.hari, required this.jam, required this.ruang, this.warnaIndeks = 0});
  Map<String,dynamic> toJson() => {'id':id,'mataPelajaran':mataPelajaran,'hari':hari,'jam':jam,'ruang':ruang,'warnaIndeks':warnaIndeks};
  factory JadwalModel.fromJson(Map<String,dynamic> json) => JadwalModel(id:json['id'],mataPelajaran:json['mataPelajaran'],hari:json['hari'],jam:json['jam'],ruang:json['ruang'],warnaIndeks:json['warnaIndeks']);
}

// Halaman Utama dengan CRUD + setState
class JadwalPage extends StatefulWidget {
  @override
  _JadwalPageState createState() => _JadwalPageState();
}

class _JadwalPageState extends State<JadwalPage> {
  List<JadwalModel> jadwalList = [];
  List<JadwalModel> filteredList = [];
  String searchQuery = '';
  String filterHari = 'Semua';
  final List<String> daftarHari = ['Semua','Senin','Selasa','Rabu','Kamis','Jumat','Sabtu'];

  @override
  void initState() { super.initState(); _loadData(); }

  // Simpan ke Local Storage
  Future<void> _saveData() async {
    final prefs = await SharedPreferences.getInstance();
    List<String> jsonList = jadwalList.map((j) => jsonEncode(j.toJson())).toList();
    await prefs.setStringList('jadwal_list', jsonList);
  }

  // Load dari Local Storage
  Future<void> _loadData() async {
    final prefs = await SharedPreferences.getInstance();
    List<String>? jsonList = prefs.getStringList('jadwal_list');
    if (jsonList != null) {
      setState(() { jadwalList = jsonList.map((j) => JadwalModel.fromJson(jsonDecode(j))).toList(); _filterData(); });
    } else {
      // Data dummy awal
      setState(() {
        jadwalList = [JadwalModel(id: DateTime.now().toString(), mataPelajaran: 'Flutter UI', hari: 'Selasa', jam: '09:00-11:00', ruang: 'Lab 1')];
        _saveData(); _filterData();
      });
    }
  }

  void _filterData() => setState(() {
    filteredList = jadwalList.where((j) =>
        (filterHari == 'Semua' || j.hari == filterHari) &&
        j.mataPelajaran.toLowerCase().contains(searchQuery.toLowerCase())
    ).toList();
  });

  // CRUD OPERATIONS
  void tambahData(JadwalModel j) { setState(() { jadwalList.add(j); _saveData(); _filterData(); }); }
  void editData(int idx, JadwalModel j) { setState(() { jadwalList[idx] = j; _saveData(); _filterData(); }); }
  void hapusData(int idx) { setState(() { jadwalList.removeAt(idx); _saveData(); _filterData(); }); }

  // Dialog Input (Tambah/Edit)
  void tampilDialog({JadwalModel? jadwalLama, int? index}) {
    bool isEdit = jadwalLama != null;
    TextEditingController mapelC = TextEditingController(text: jadwalLama?.mataPelajaran ?? '');
    TextEditingController jamC = TextEditingController(text: jadwalLama?.jam ?? '');
    TextEditingController ruangC = TextEditingController(text: jadwalLama?.ruang ?? '');
    String selectedHari = jadwalLama?.hari ?? daftarHari[1];

    showModalBottomSheet(context: context, isScrollControlled: true, builder: (_) {
      return StatefulBuilder(builder: (ctx, setStateBottom) {
        return Padding(padding: EdgeInsets.only(bottom: MediaQuery.of(ctx).viewInsets.bottom, left: 20, right: 20, top: 20),
          child: Column(mainAxisSize: MainAxisSize.min, children: [
            Text(isEdit ? 'Edit Jadwal' : 'Tambah Jadwal', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
            TextField(controller: mapelC, decoration: InputDecoration(labelText: 'Mata Pelajaran', prefixIcon: Icon(Icons.book))),
            DropdownButtonFormField(value: selectedHari, items: daftarHari.skip(1).map((h) => DropdownMenuItem(value: h, child: Text(h))).toList(),
              onChanged: (v) => setStateBottom(() => selectedHari = v!)),
            TextField(controller: jamC, decoration: InputDecoration(labelText: 'Jam', prefixIcon: Icon(Icons.access_time))),
            TextField(controller: ruangC, decoration: InputDecoration(labelText: 'Ruang', prefixIcon: Icon(Icons.location_on))),
            SizedBox(height: 20),
            Row(children: [
              Expanded(child: OutlinedButton(onPressed: () => Navigator.pop(ctx), child: Text('Batal'))),
              SizedBox(width: 10),
              Expanded(child: ElevatedButton(onPressed: () {
                if (mapelC.text.isNotEmpty) {
                  if (isEdit) editData(index!, JadwalModel(id: jadwalLama!.id, mataPelajaran: mapelC.text, hari: selectedHari, jam: jamC.text, ruang: ruangC.text));
                  else tambahData(JadwalModel(id: DateTime.now().toString(), mataPelajaran: mapelC.text, hari: selectedHari, jam: jamC.text, ruang: ruangC.text));
                  Navigator.pop(ctx);
                }
              }, child: Text(isEdit ? 'Update' : 'Simpan')))
            ]),
            SizedBox(height: 20)
          ]),
        );
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Jadwal Pelajaran'), centerTitle: true, flexibleSpace: Container(decoration: BoxDecoration(gradient: LinearGradient(colors: [Colors.indigo, Colors.blue])))),
      body: Column(children: [
        Padding(padding: EdgeInsets.all(12), child: TextField(onChanged: (v) => setState(() { searchQuery = v; _filterData(); }), decoration: InputDecoration(hintText: 'Cari jadwal...', prefixIcon: Icon(Icons.search), border: OutlineInputBorder(borderRadius: BorderRadius.circular(30))))),
        SizedBox(height: 40, child: ListView.builder(scrollDirection: Axis.horizontal, padding: EdgeInsets.symmetric(horizontal: 12), itemCount: daftarHari.length, itemBuilder: (_, i) => Padding(padding: EdgeInsets.only(right: 8), child: FilterChip(label: Text(daftarHari[i]), selected: filterHari == daftarHari[i], onSelected: (_) => setState(() { filterHari = daftarHari[i]; _filterData(); }))))),
        Expanded(child: filteredList.isEmpty ? Center(child: Text('Belum ada jadwal')) : ListView.builder(itemCount: filteredList.length, itemBuilder: (_, i) {
          var j = filteredList[i];
          int originalIdx = jadwalList.indexOf(j);
          return Card(margin: EdgeInsets.all(8), child: ListTile(
            leading: CircleAvatar(child: Text(j.mataPelajaran[0])),
            title: Text(j.mataPelajaran, style: TextStyle(fontWeight: FontWeight.bold)),
            subtitle: Text('${j.hari} | ${j.jam} | ${j.ruang}'),
            trailing: Row(mainAxisSize: MainAxisSize.min, children: [
              IconButton(icon: Icon(Icons.edit, color: Colors.blue), onPressed: () => tampilDialog(jadwalLama: j, index: originalIdx)),
              IconButton(icon: Icon(Icons.delete, color: Colors.red), onPressed: () => hapusData(originalIdx))
            ])
          ));
        }))
      ]),
      floatingActionButton: FloatingActionButton(onPressed: () => tampilDialog(), child: Icon(Icons.add)),
    );
  }
}
      

๐ŸŽฏ Target Praktikum Siswa

✅ Level Wajib

  • Jalankan aplikasi
  • Tambah data jadwal
  • Edit data jadwal
  • Hapus data jadwal
  • Data tetap tersimpan setelah ditutup

๐Ÿš€ Level Pengembangan

  • ➕ Tambah field "Guru Pengajar"
  • ๐ŸŽจ Warna berbeda tiap mata pelajaran
  • ⏰ Notifikasi pengingat
  • ๐Ÿ“ค Export jadwal ke PDF

๐Ÿงช Tugas Praktikum

Kirimkan ke siswa:

  1. Jalankan aplikasi yang diberikan
  2. Pastikan fitur berjalan: Tambah ✅ Edit ✅ Hapus ✅ Local Storage ✅
  3. Modifikasi: Tambahkan keterangan "Guru Pengajar"
  4. Percantik tampilan (ganti warna, gradient, atau font)
  5. Screenshot: Tampilan utama, saat tambah data, saat edit
  6. Upload ke Google Drive, kirim link tugas

๐Ÿ“Œ Catatan Penting

Kode ini sengaja dibuat agar:

  • ✅ Tidak terlalu kompleks – mudah dipahami pemula
  • ✅ Mudah dimodifikasi – struktur jelas, pisah model dan widget
  • ✅ Minim error – menggunakan state management setState standar Flutter
  • ✅ Penyimpanan permanen dengan SharedPreferences – tanpa database rumit

⚠️ Untuk menjalankan, pastikan tambahkan dependency di pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.2
๐Ÿ“˜ Facebook ๐Ÿฆ Twitter ๐Ÿ“ง Email ๐Ÿ“ฑ WhatsApp

Copyright © 2026 WAHANA BELAJAR DAN BERBAGI | Powered by Blogger

Design by FlexiThemes | Blogger Theme by NewBloggerThemes.com

Komentar

Postingan populer dari blog ini

Kunci Jawaban Soal Pilihan Ganda Flutter UI (State Management)

๐ŸŽฎ Review Aplikasi Flutter: Pixel Quest To-Do