✅ Artikel Lengkap: Aplikasi Absensi + Pengingat Jadwal dengan Flutter
📌 Pendahuluan
Di era digital, efisiensi adalah kunci. Bayangkan jika siswa atau karyawan harus mencatat absensi manual dan mengingat jadwal pelajaran atau meeting tanpa pengingat. Ribet, kan?
Nah, solusinya adalah aplikasi absensi berbasis digital yang terintegrasi dengan pengingat jadwal. Dengan satu aplikasi ini, kita bisa:
✔ Memantau kehadiran secara real-time.
✔ Menyimpan data absensi di cloud.
✔ Memberi pengingat jadwal otomatis agar tidak ada yang terlewat.
Dan asiknya, kita akan membuat ini menggunakan Flutter—framework UI yang multiplatform (Android, iOS, Web, bahkan Desktop).
💡 Manfaat Aplikasi Ini
Kenapa aplikasi ini penting? Berikut manfaatnya:
✅ Untuk Siswa/Guru:
-
Siswa tidak lagi lupa pelajaran karena ada notifikasi pengingat.
-
Guru bisa mengecek absensi dengan cepat.
✅ Untuk Perusahaan:
-
Monitoring kehadiran karyawan lebih efisien.
-
Pengingat meeting otomatis tanpa harus pakai chat manual.
✅ Untuk Developer:
-
Belajar konsep CRUD (Create, Read, Update, Delete) dengan API.
-
Memahami notifikasi lokal & jadwal berbasis timezone.
🛠 Fitur Utama Aplikasi
✔ CRUD Absensi menggunakan MockAPI.io sebagai backend.
✔ Notifikasi pengingat jadwal pelajaran setiap harinya.
✔ Tampilan daftar jadwal dengan highlight jika sedang berlangsung.
✔ Form tambah/edit data absensi langsung dari aplikasi.
📸 Tampilan Aplikasi
Berikut tampilan dari aplikasi yang kita buat:
📥 Persiapan Proyek Flutter
Sebelum menulis kode, pastikan kamu sudah:
-
Install Flutter SDK versi terbaru.
-
Buat proyek baru:
-
Edit pubspec.yaml untuk menambahkan dependency:
Lalu jalankan:
🧩 Kode Lengkap Aplikasi (main.dart)
Berikut adalah kode lengkapnya:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
tz.initializeTimeZones();
await initializeDateFormatting('id_ID', null);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Absensi + Jadwal Pengingat',
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final String apiUrl = "https://68a522582a3deed2960c7b4d.mockapi.io/absen";
List items = [];
final TextEditingController nameController = TextEditingController();
final TextEditingController noAbsenController = TextEditingController();
final TextEditingController statusController = TextEditingController();
final TextEditingController keteranganController = TextEditingController();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
@override
void initState() {
super.initState();
fetchData();
initNotifications();
scheduleTodayNotifications();
}
Future<void> initNotifications() async {
const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher');
const InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
}
Future<void> showNotification(String title, String body) async {
const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails(
'jadwal_channel',
'Jadwal Pelajaran',
importance: Importance.max,
priority: Priority.high,
);
const NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(0, title, body, platformChannelSpecifics);
}
Future<void> scheduleTodayNotifications() async {
String hari = DateFormat('EEEE', 'id_ID').format(DateTime.now());
if (jadwalPelajaran.containsKey(hari)) {
for (var pelajaran in jadwalPelajaran[hari]!) {
final startTime = pelajaran['start']!;
final now = DateTime.now();
final startParts = startTime.split(":");
final notifTime = DateTime(now.year, now.month, now.day, int.parse(startParts[0]), int.parse(startParts[1]));
if (notifTime.isAfter(now)) {
await flutterLocalNotificationsPlugin.zonedSchedule(
notifTime.millisecondsSinceEpoch ~/ 1000,
'Pengingat Pelajaran',
'${pelajaran['mapel']} dimulai sekarang!',
tz.TZDateTime.from(notifTime, tz.local),
const NotificationDetails(android: AndroidNotificationDetails('jadwal_channel', 'Jadwal Pelajaran')),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
matchDateTimeComponents: DateTimeComponents.time,
);
}
}
}
}
Future<void> fetchData() async {
final response = await http.get(Uri.parse(apiUrl));
if (response.statusCode == 200) {
setState(() {
items = json.decode(response.body);
});
}
}
Future<void> createData(String noAbsen, String name, String status, String keterangan) async {
String tanggal = DateFormat('dd MMMM yyyy', 'id_ID').format(DateTime.now());
await http.post(
Uri.parse(apiUrl),
headers: {"Content-Type": "application/json"},
body: json.encode({
"noAbsen": noAbsen,
"name": name,
"status": status,
"keterangan": keterangan,
"tanggal": tanggal,
}),
);
fetchData();
}
Future<void> updateData(String id, String noAbsen, String name, String status, String keterangan) async {
await http.put(
Uri.parse("$apiUrl/$id"),
headers: {"Content-Type": "application/json"},
body: json.encode({
"noAbsen": noAbsen,
"name": name,
"status": status,
"keterangan": keterangan,
}),
);
fetchData();
}
Future<void> deleteData(String id) async {
await http.delete(Uri.parse("$apiUrl/$id"));
fetchData();
}
void showForm({String? id, String? noAbsen, String? name, String? status, String? keterangan}) {
if (id != null) {
noAbsenController.text = noAbsen ?? "";
nameController.text = name ?? "";
statusController.text = status ?? "";
keteranganController.text = keterangan ?? "";
} else {
noAbsenController.clear();
nameController.clear();
statusController.clear();
keteranganController.clear();
}
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(id == null ? "Tambah Absen" : "Edit Absen"),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: noAbsenController,
decoration: const InputDecoration(labelText: "No Absen"),
keyboardType: TextInputType.number,
),
TextField(
controller: nameController,
decoration: const InputDecoration(labelText: "Nama"),
),
TextField(
controller: statusController,
decoration: const InputDecoration(labelText: "Status (Hadir/Izin/Sakit/Terlambat)"),
),
TextField(
controller: keteranganController,
decoration: const InputDecoration(labelText: "Keterangan"),
),
],
),
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text("Batal")),
ElevatedButton(
onPressed: () {
if (id == null) {
createData(noAbsenController.text, nameController.text, statusController.text, keteranganController.text);
} else {
updateData(id, noAbsenController.text, nameController.text, statusController.text, keteranganController.text);
}
Navigator.pop(context);
},
child: const Text("Simpan"),
),
],
),
);
}
@override
Widget build(BuildContext context) {
String hari = DateFormat('EEEE', 'id_ID').format(DateTime.now());
List<Map<String, String>> jadwalHariIni = jadwalPelajaran[hari] ?? [];
String nowTime = DateFormat('HH:mm').format(DateTime.now());
return Scaffold(
appBar: AppBar(title: const Text("Absensi + Pengingat Jadwal")),
body: Column(
children: [
Container(
color: Colors.blue.shade50,
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Hari ini: $hari", style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 5),
...jadwalHariIni.map((pelajaran) {
bool isNow = nowTime.compareTo(pelajaran['start']!) >= 0 && nowTime.compareTo(pelajaran['end']!) <= 0;
return Text(
"${pelajaran['mapel']} (${pelajaran['start']} - ${pelajaran['end']})",
style: TextStyle(color: isNow ? Colors.green : Colors.black, fontWeight: isNow ? FontWeight.bold : FontWeight.normal),
);
}).toList(),
],
),
),
Expanded(
child: items.isEmpty
? const Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Card(
child: ListTile(
title: Text("${item["noAbsen"]}. ${item["name"]}"),
subtitle: Text("${item["status"]} | ${item["keterangan"]} | ${item["tanggal"]}"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.orange),
onPressed: () => showForm(
id: item["id"],
noAbsen: item["noAbsen"],
name: item["name"],
status: item["status"],
keterangan: item["keterangan"],
),
),
IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () => deleteData(item["id"]),
),
],
),
),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () => showForm(),
child: const Icon(Icons.add),
),
);
}
}
final Map<String, List<Map<String, String>>> jadwalPelajaran = {
"Senin": [
{"mapel": "KK ProgTeksMul", "start": "07:15", "end": "08:35"},
{"mapel": "B.Ingg", "start": "09:10", "end": "10:30"},
{"mapel": "KK ProgWebBSdt", "start": "10:35", "end": "11:45"},
],
"Selasa": [
{"mapel": "Pend.Agama", "start": "07:15", "end": "08:35"},
{"mapel": "Pend.Panc", "start": "08:40", "end": "09:20"},
{"mapel": "BP/BK", "start": "09:30", "end": "10:00"},
{"mapel": "MP Pem.Gim", "start": "10:15", "end": "11:45"},
],
"Rabu": [
{"mapel": "KK ProgMbl", "start": "07:15", "end": "09:30"},
{"mapel": "B.Sun", "start": "09:35", "end": "11:45"},
],
"Kamis": [
{"mapel": "B.Ingg", "start": "07:15", "end": "08:35"},
{"mapel": "KIK", "start": "08:40", "end": "10:00"},
{"mapel": "B.Indo", "start": "10:15", "end": "11:45"},
],
"Jumat": [
{"mapel": "Mtk", "start": "07:15", "end": "08:35"},
{"mapel": "KK ProgWebBSdt", "start": "08:40", "end": "10:45"},
],
};
🔗 Link Demo Aplikasi
Kamu bisa coba langsung di browser menggunakan Zapp.run di link berikut:
[https://z95006929510.zapp.page/#/]
🚀 Cara Menjalankan Aplikasi
-
Clone proyek atau buat sendiri pakai kode di atas.
-
Jalankan perintah:
-
Untuk web:
📌 Kesimpulan & Ide Pengembangan
Dengan aplikasi ini, kita berhasil membuat:
✅ CRUD absensi (Create, Read, Update, Delete)
✅ Pengingat jadwal berbasis notifikasi lokal
✅ Tampilan jadwal dengan highlight pelajaran yang sedang berlangsung
Ide pengembangan berikutnya:
-
Tambah login & autentikasi untuk keamanan data.
-
Simpan data ke Firebase agar real-time.
-
Integrasi calendar view untuk jadwal mingguan.
Penutup
Dengan tutorial ini, kamu sudah punya aplikasi yang powerful untuk kebutuhan sekolah, kampus, atau bahkan perusahaan. Tinggal modifikasi sesuai kebutuhan.
🔥 Selamat mencoba dan happy coding! 🔥


Komentar
Posting Komentar