import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import '../services/notification_service.dart'; class NotificationDebugScreen extends StatefulWidget { const NotificationDebugScreen({super.key}); @override State createState() => _NotificationDebugScreenState(); } class _NotificationDebugScreenState extends State { bool _isLoading = false; bool? _canScheduleExactNotifications; String? _lastStatus; List _pendingNotifications = []; @override void initState() { super.initState(); _loadPendingNotifications(); } Future _setStatus(String message) async { if (!mounted) return; setState(() { _lastStatus = message; }); } Future _loadPendingNotifications() async { setState(() { _isLoading = true; }); try { await NotificationService.instance.initialize(); final canScheduleExact = await NotificationService.instance.canScheduleExactNotifications(); final pending = await NotificationService.instance.pendingNotifications(); if (!mounted) return; setState(() { _pendingNotifications = pending; _canScheduleExactNotifications = canScheduleExact; _isLoading = false; }); } catch (e) { if (!mounted) return; setState(() { _isLoading = false; _lastStatus = 'Gagal memuat notifikasi tertunda: $e'; }); } } Future _sendSamplePanenPagi() async { await _runAction(() async { await NotificationService.instance.showPanenSummaryNotification( jenisPanen: 'pagi', kandangTotals: const { 'kandang 1': 15, 'kandang 2': 15, }, ); }, 'Notif panen pagi terkirim'); } Future _sendSamplePanenSore() async { await _runAction(() async { await NotificationService.instance.showPanenSummaryNotification( jenisPanen: 'sore', kandangTotals: const { 'kandang 1': 12, 'kandang 2': 8, }, ); }, 'Notif panen sore terkirim'); } Future _sendImmediateSimple() async { await _runAction(() async { await NotificationService.instance.showInstantNotification( title: 'Tes Notifikasi', body: 'Notifikasi lokal berhasil dikirim dari halaman debug.', payload: 'debug:immediate', ); }, 'Notif langsung terkirim'); } Future _scheduleThirtySecondNotification() async { await _runAction(() async { await NotificationService.instance.scheduleNotification( title: 'Tes Notifikasi Terjadwal', body: 'Notif ini dijadwalkan 30 detik sebelumnya. Coba minimize atau tutup app setelah dijadwalkan.', delay: const Duration(seconds: 30), prioritizeBackgroundDelivery: true, payload: 'debug:scheduled_30s', ); }, 'Notif 30 detik dijadwalkan'); await _loadPendingNotifications(); } Future _requestExactAlarmPermission() async { await _runAction(() async { final granted = await NotificationService.instance.requestExactAlarmsPermission(); if (!granted) { throw 'Izin exact alarm belum diberikan. Aktifkan dari settings aplikasi.'; } }, 'Izin exact alarm berhasil diminta'); await _loadPendingNotifications(); } Future _cancelAllNotifications() async { await _runAction(() async { await NotificationService.instance.cancelAll(); }, 'Semua notifikasi dibatalkan'); await _loadPendingNotifications(); } Future _runAction( Future Function() action, String successText) async { setState(() { _isLoading = true; }); try { await NotificationService.instance.initialize(); await action(); await _setStatus(successText); } catch (e) { await _setStatus('Error: $e'); } finally { if (mounted) { setState(() { _isLoading = false; }); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Debug Notifikasi'), backgroundColor: Colors.orange, actions: [ IconButton( onPressed: _isLoading ? null : _loadPendingNotifications, icon: const Icon(Icons.refresh), tooltip: 'Refresh', ), ], ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildInfoCard(), const SizedBox(height: 16), _buildActionCard(), const SizedBox(height: 16), _buildPendingCard(), if (_lastStatus != null) ...[ const SizedBox(height: 16), _buildStatusCard(), ], ], ), ), ); } Widget _buildInfoCard() { return Card( elevation: 2, color: Colors.blue.shade50, child: const Padding( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Cara kerja notifikasi', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text( '• Notifikasi langsung akan muncul saat app masih aktif atau berjalan di background.\n' '• Notifikasi terjadwal tetap bisa muncul walaupun app diminimize atau ditutup, selama notif sudah dijadwalkan sebelum app berhenti.\n' '• Kalau exact alarm tidak aktif, Android bisa menunda jadwal lokal. Gunakan tombol Aktifkan Exact Alarm agar notif 30 detik lebih presisi.\n' '• Jika yang diminta adalah notif dari event server saat app benar-benar mati total, itu perlu push notification dari backend/FCM.', style: TextStyle(fontSize: 13, height: 1.4), ), ], ), ), ); } Widget _buildActionCard() { return Card( elevation: 3, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Aksi Debug', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), ElevatedButton.icon( onPressed: _isLoading ? null : _sendSamplePanenPagi, icon: const Icon(Icons.wb_sunny), label: const Text('Simulasi Panen Pagi'), ), const SizedBox(height: 8), ElevatedButton.icon( onPressed: _isLoading ? null : _sendSamplePanenSore, icon: const Icon(Icons.wb_twilight), label: const Text('Simulasi Panen Sore'), ), const SizedBox(height: 8), OutlinedButton.icon( onPressed: _isLoading ? null : _sendImmediateSimple, icon: const Icon(Icons.notifications_active), label: const Text('Kirim Notifikasi Langsung'), ), const SizedBox(height: 8), OutlinedButton.icon( onPressed: _isLoading ? null : _scheduleThirtySecondNotification, icon: const Icon(Icons.schedule), label: const Text('Jadwalkan Notif 30 Detik'), ), const SizedBox(height: 8), OutlinedButton.icon( onPressed: _isLoading ? null : _requestExactAlarmPermission, icon: const Icon(Icons.alarm_on), label: const Text('Aktifkan Exact Alarm'), ), const SizedBox(height: 8), TextButton.icon( onPressed: _isLoading ? null : _cancelAllNotifications, icon: const Icon(Icons.cancel), label: const Text('Batalkan Semua Notifikasi'), ), ], ), ), ); } Widget _buildPendingCard() { return Card( elevation: 3, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Notifikasi Tertunda', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( 'Exact alarm: ${_canScheduleExactNotifications == null ? 'memuat...' : (_canScheduleExactNotifications! ? 'aktif' : 'belum aktif')}', style: TextStyle( fontSize: 13, color: _canScheduleExactNotifications == true ? Colors.green.shade700 : Colors.red.shade700, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 12), if (_pendingNotifications.isEmpty) const Text('Belum ada notifikasi terjadwal.') else ..._pendingNotifications.map( (notification) => Padding( padding: const EdgeInsets.only(bottom: 8), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(notification.title ?? '(tanpa judul)'), const SizedBox(height: 4), Text(notification.body ?? '(tanpa isi)'), const SizedBox(height: 4), Text( 'ID: ${notification.id}', style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ], ), ), ), ), ], ), ), ); } Widget _buildStatusCard() { final success = _lastStatus!.contains('terkirim') || _lastStatus!.contains('dijadwalkan') || _lastStatus!.contains('dibatalkan'); return Card( color: success ? Colors.green.shade50 : Colors.red.shade50, child: Padding( padding: const EdgeInsets.all(16), child: Text( _lastStatus!, style: TextStyle( fontWeight: FontWeight.w600, color: success ? Colors.green.shade800 : Colors.red.shade800, ), ), ), ); } }