import 'dart:async'; import 'package:flutter/material.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'dashboard.dart'; import 'history.dart'; import '../widgets/fade_in_up.dart'; import '../widgets/whatsapp_notification.dart'; import 'akun.dart'; class NotifikasiPage extends StatefulWidget { const NotifikasiPage({super.key}); @override State createState() => _NotifikasiPageState(); } class _NotifikasiPageState extends State { final db = FirebaseDatabase.instance.ref(); List> notifications = []; // 🔥 REALTIME NEW NOTIFICATION POPUP STATE StreamSubscription? _notifChildSubscription; StreamSubscription? _notifSubscription; int _pageOpenTime = DateTime.now().millisecondsSinceEpoch; @override void initState() { super.initState(); // Jalankan listener notifikasi WhatsApp global WhatsAppNotificationService().startListening(); listenToNotifications(); listenToNewNotifPush(); } @override void dispose() { _notifChildSubscription?.cancel(); _notifSubscription?.cancel(); super.dispose(); } void listenToNewNotifPush() { _pageOpenTime = DateTime.now().millisecondsSinceEpoch; // Memantau penambahan notifikasi baru secara realtime untuk memicu popup modal dialog _notifChildSubscription = db.child("notifikasi") .orderByChild("timestamp") .startAt(_pageOpenTime) .onChildAdded .listen((event) { final data = event.snapshot.value as Map?; if (data != null && mounted) { final timestamp = data['timestamp']; if (timestamp != null && timestamp > _pageOpenTime) { _tampilkanPopUpHasil( data["title"] ?? "Notifikasi Baru", data["message"] ?? "", ); } } }); } void _tampilkanPopUpHasil(String title, String message) { bool isBahaya = title.toLowerCase().contains("bahaya"); bool isAman = title.toLowerCase().contains("aman"); showDialog( context: context, barrierDismissible: false, builder: (context) => Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), ), elevation: 10, backgroundColor: Colors.white, child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), border: Border.all( color: isBahaya ? Colors.red.withOpacity(0.4) : (isAman ? Colors.green.withOpacity(0.4) : Colors.orange.withOpacity(0.4)), width: 2, ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Icon Indikator Menarik Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isBahaya ? Colors.red.withOpacity(0.1) : (isAman ? Colors.green.withOpacity(0.1) : Colors.orange.withOpacity(0.1)), shape: BoxShape.circle, ), child: Icon( isBahaya ? Icons.warning_amber_rounded : (isAman ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded), color: isBahaya ? Colors.red : (isAman ? Colors.green : Colors.orange), size: 50, ), ), const SizedBox(height: 20), // Title Text( title, textAlign: TextAlign.center, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: isBahaya ? Colors.red : (isAman ? Colors.green : const Color(0xFF1565C0)), ), ), const SizedBox(height: 12), // Message Text( message, textAlign: TextAlign.center, style: const TextStyle( fontSize: 15, color: Colors.black87, height: 1.4, ), ), const SizedBox(height: 24), // Tombol OK SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.pop(context), style: ElevatedButton.styleFrom( backgroundColor: isBahaya ? Colors.red : (isAman ? Colors.green : const Color(0xFF1565C0)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), padding: const EdgeInsets.symmetric(vertical: 14), ), child: const Text( "Selesai", style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ), ); } void _tampilkanDetailNotif(Map notif) { String title = notif["title"] ?? "Info Notifikasi"; String message = notif["message"] ?? ""; String time = notif["time"] ?? "--:--"; bool isBahaya = title.toLowerCase().contains("bahaya"); bool isAman = title.toLowerCase().contains("aman"); showDialog( context: context, builder: (context) => Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(24), ), elevation: 10, backgroundColor: Colors.white, child: Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), border: Border.all( color: isBahaya ? Colors.red.withOpacity(0.4) : (isAman ? Colors.green.withOpacity(0.4) : Colors.orange.withOpacity(0.4)), width: 2, ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Icon Indikator Menarik Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isBahaya ? Colors.red.withOpacity(0.1) : (isAman ? Colors.green.withOpacity(0.1) : Colors.orange.withOpacity(0.1)), shape: BoxShape.circle, ), child: Icon( isBahaya ? Icons.warning_amber_rounded : (isAman ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded), color: isBahaya ? Colors.red : (isAman ? Colors.green : Colors.orange), size: 50, ), ), const SizedBox(height: 20), // Title Text( title, textAlign: TextAlign.center, style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: isBahaya ? Colors.red : (isAman ? Colors.green : const Color(0xFF1565C0)), ), ), const SizedBox(height: 8), // Time Text( "Waktu: $time", style: const TextStyle( fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 16), // Message Text( message, textAlign: TextAlign.center, style: const TextStyle( fontSize: 15, color: Colors.black87, height: 1.4, ), ), const SizedBox(height: 16), // Tips / Info Tambahan Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isBahaya ? Colors.red.withOpacity(0.05) : const Color(0xFF1565C0).withOpacity(0.05), borderRadius: BorderRadius.circular(12), ), child: Text( isBahaya ? "⚠️ Rekomendasi: Segera buang makanan tersebut dan jangan dikonsumsi demi kesehatan Anda." : "✅ Informasi: Makanan tergolong aman untuk dikonsumsi.", textAlign: TextAlign.center, style: TextStyle( fontSize: 13, color: isBahaya ? Colors.red.shade700 : const Color(0xFF1565C0), fontStyle: FontStyle.italic, ), ), ), const SizedBox(height: 24), // Tombol Tutup SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.pop(context), style: ElevatedButton.styleFrom( backgroundColor: isBahaya ? Colors.red : (isAman ? Colors.green : const Color(0xFF1565C0)), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), padding: const EdgeInsets.symmetric(vertical: 14), ), child: const Text( "Tutup", style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ), ); } void listenToNotifications() { // Memantau folder 'notifikasi' dan mengurutkan berdasarkan timestamp terbaru _notifSubscription?.cancel(); _notifSubscription = db.child("notifikasi").orderByChild("timestamp").onValue.listen((event) { final data = event.snapshot.value as Map?; if (data != null) { List> tempList = []; data.forEach((key, value) { // Mengambil data dan menyimpan key-nya untuk fitur hapus Map notifWithKey = Map.from(value); notifWithKey['key'] = key; tempList.add(notifWithKey); }); setState(() { // Dibalik agar notifikasi paling baru muncul di paling atas notifications = tempList.reversed.toList(); }); } else { setState(() { notifications = []; }); } }); } // Fungsi untuk menghapus semua notifikasi sekaligus void hapusSemua() { db.child("notifikasi").remove(); } // Fungsi untuk menghapus satu notifikasi (geser/swipe) void hapusSatu(String key) { db.child("notifikasi").child(key).remove(); } @override Widget build(BuildContext context) { return Scaffold( body: Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( colors: [ Colors.white, Color(0xFFE3F2FD), // Light blue Color(0xFF42A5F5), // Medium blue Color(0xFF1565C0), // Dark blue ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header Baru (Gaya Dashboard) Center( child: Padding( padding: const EdgeInsets.fromLTRB(20, 10, 20, 20), child: FadeInUp( delay: 50, child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const Text( "Notifikasi", textAlign: TextAlign.center, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFF1565C0), ), ), Text( "System alerts & info", textAlign: TextAlign.center, style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: const Color(0xFF1565C0).withOpacity(0.7), ), ), ], ), ), ), ), // Header status jumlah notifikasi if (notifications.isNotEmpty) FadeInUp( delay: 200, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 12), color: const Color(0xFF1565C0).withOpacity(0.08), child: Center( child: Text( "Terdeteksi ${notifications.length} peringatan sistem", style: const TextStyle(color: Colors.black54, fontSize: 13), ), ), ), ), Expanded( child: FadeInUp( delay: 400, child: notifications.isEmpty ? _buildEmptyState() : ListView.builder( padding: const EdgeInsets.all(20), itemCount: notifications.length, itemBuilder: (context, index) { final notif = notifications[index]; return Dismissible( key: Key(notif['key']), direction: DismissDirection.endToStart, onDismissed: (direction) => hapusSatu(notif['key']), background: Container( alignment: Alignment.centerRight, padding: const EdgeInsets.only(right: 20), margin: const EdgeInsets.only(bottom: 15), decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(20), ), child: const Icon(Icons.delete, color: Colors.white), ), child: _buildNotifCard(notif), ); }, ), ), ), ], ), ), ), // Bottom Navigation Bar agar navigasi antar halaman sinkron bottomNavigationBar: BottomNavigationBar( currentIndex: 2, // Highlight icon Notif selectedItemColor: const Color(0xFF1565C0), unselectedItemColor: Colors.grey, type: BottomNavigationBarType.fixed, onTap: (index) { if (index == 0) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const DashboardPage()), ); } else if (index == 1) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const RiwayatPage()), ); } else if (index == 3) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => const AkunPage()), ); } }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "Home"), BottomNavigationBarItem(icon: Icon(Icons.history), label: "History"), BottomNavigationBarItem(icon: Icon(Icons.notifications), label: "Notif"), BottomNavigationBarItem(icon: Icon(Icons.person), label: "Akun"), ], ), ); } Widget _buildNotifCard(Map notif) { bool isBahaya = notif["title"].toString().toLowerCase().contains("bahaya"); return Container( margin: const EdgeInsets.only(bottom: 15), padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all( color: isBahaya ? Colors.red.withOpacity(0.3) : Colors.transparent, width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Icon indikator bulat (Clickable) GestureDetector( onTap: () => _tampilkanDetailNotif(notif), child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: isBahaya ? Colors.red.withOpacity(0.1) : Colors.green.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( isBahaya ? Icons.warning_rounded : Icons.info_outline, color: isBahaya ? Colors.red : const Color(0xFF1565C0), size: 24, ), ), ), const SizedBox(width: 15), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( notif["title"] ?? "Pemberitahuan", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 15, color: isBahaya ? Colors.red : Colors.black87, ), ), Text( notif["time"] ?? "--:--", style: const TextStyle(fontSize: 11, color: Colors.grey), ), ], ), const SizedBox(height: 6), Text( notif["message"] ?? "", style: const TextStyle( fontSize: 14, color: Colors.black54, height: 1.4, ), ), ], ), ), ], ), ); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.notifications_off_outlined, size: 80, color: Colors.white70, ), const SizedBox(height: 15), const Text( "Belum ada pemberitahuan baru", style: TextStyle(color: Colors.white), ), ], ), ); } }