import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:async'; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp( options: const FirebaseOptions( databaseURL: 'https://rifky-ea47a-default-rtdb.firebaseio.com', apiKey: 'AIzaSyCtV4taj9jDIY31GtedIWmS9z5zP8JPmmY', // You'll need to add your API key here appId: '1:381205401183:web:2d37bb3d8cb686227115ea', // You need to get this from Firebase Console messagingSenderId: '381205401183', // You'll need to add your Sender ID here projectId: 'rifky-ea47a', // Your project ID ), ); await initializeService(); runApp(const MyApp()); } Future initializeService() async { final service = FlutterBackgroundService(); /// OPTIONAL, using custom notification channel id const AndroidNotificationChannel channel = AndroidNotificationChannel( 'my_foreground', // id 'MY FOREGROUND SERVICE', // title description: 'This channel is used for important notifications.', // description importance: Importance.low, // importance must be at low or higher level ); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); // TANPA .png const InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, ); await flutterLocalNotificationsPlugin.initialize(initializationSettings); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); await service.configure( androidConfiguration: AndroidConfiguration( // this will be executed when app is in foreground or background in separated isolate onStart: onStart, // auto start service autoStart: true, isForegroundMode: true, notificationChannelId: 'my_foreground', initialNotificationTitle: 'Jamur', initialNotificationContent: 'Jamur Sedang Berjalan', foregroundServiceNotificationId: 888, foregroundServiceTypes: [AndroidForegroundType.location], ), iosConfiguration: IosConfiguration( // auto start service autoStart: true, // this will be executed when app is in foreground in separated isolate onForeground: onStart, // you have to enable background fetch capability on xcode project onBackground: onIosBackground, ), ); } // to ensure this is executed // run app from xcode, then from xcode menu, select Simulate Background Fetch @pragma('vm:entry-point') Future onIosBackground(ServiceInstance service) async { WidgetsFlutterBinding.ensureInitialized(); DartPluginRegistrant.ensureInitialized(); SharedPreferences preferences = await SharedPreferences.getInstance(); await preferences.reload(); final log = preferences.getStringList('log') ?? []; log.add(DateTime.now().toIso8601String()); await preferences.setStringList('log', log); return true; } @pragma('vm:entry-point') void onStart(ServiceInstance service) async { DartPluginRegistrant.ensureInitialized(); await Firebase.initializeApp(); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final DatabaseReference _database = FirebaseDatabase.instance.ref(); double? lastTemp; double? lastHumidity; // Listener suhu _database.child('Temperature').onValue.listen((event) async { final data = event.snapshot.value; if (data != null) { final double temp = double.tryParse(data.toString()) ?? 0.0; if (temp >= 30 && (lastTemp == null || lastTemp! < 30)) { await showNotification( 'Suhu Tinggi!', 'Suhu sekarang $temp°C. Aktifkan kipas!', ); } lastTemp = temp; } }); // Listener kelembaban _database.child('Humidity').onValue.listen((event) async { final data = event.snapshot.value; if (data != null) { final double humidity = double.tryParse(data.toString()) ?? 0.0; if (humidity <= 70 && (lastHumidity == null || lastHumidity! > 70)) { await showNotification( 'Kelembaban Rendah!', 'Humidity sekarang $humidity%. Periksa kelembaban ruangan.', ); } lastHumidity = humidity; } }); // Background service commands if (service is AndroidServiceInstance) { service.on('setAsForeground').listen((event) { service.setAsForegroundService(); }); service.on('setAsBackground').listen((event) { service.setAsBackgroundService(); }); } service.on('stopService').listen((event) { service.stopSelf(); }); } Future showNotification(String title, String body) async { const androidDetails = AndroidNotificationDetails( 'temp_channel_id', 'Temperature Alerts', channelDescription: 'Notification for temperature threshold', importance: Importance.max, priority: Priority.high, showWhen: false, ); const platformDetails = NotificationDetails(android: androidDetails); await flutterLocalNotificationsPlugin.show( 0, title, body, platformDetails, ); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Monitoring App', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), useMaterial3: true, ), home: const MonitoringScreen(), ); } } class MonitoringScreen extends StatefulWidget { const MonitoringScreen({super.key}); @override State createState() => _MonitoringScreenState(); } class _MonitoringScreenState extends State { final DatabaseReference _database = FirebaseDatabase.instance.ref(); late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; Map _monitoringData = { 'Fan': 'OFF', 'Humidity': 0.0, 'Pump': 'OFF', 'Temperature': 0.0, }; @override void initState() { super.initState(); //_initializeNotifications(); // Inisialisasi notifikasi _setupRealtimeUpdates(); } // void _initializeNotifications() { // flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // const AndroidInitializationSettings initializationSettingsAndroid = // AndroidInitializationSettings('@mipmap/ic_launcher'); // const InitializationSettings initializationSettings = // InitializationSettings(android: initializationSettingsAndroid); // flutterLocalNotificationsPlugin.initialize(initializationSettings); // } // Future _showNotification(String title, String body) async { // const AndroidNotificationDetails androidPlatformChannelSpecifics = // AndroidNotificationDetails( // 'temp_channel_id', // 'Temperature Alerts', // importance: Importance.max, // priority: Priority.high, // showWhen: false, // ); // const NotificationDetails platformChannelSpecifics = // NotificationDetails(android: androidPlatformChannelSpecifics); // await flutterLocalNotificationsPlugin.show( // 0, // Notification ID // title, // body, // platformChannelSpecifics, // ); // } void _setupRealtimeUpdates() { _database.onValue.listen((DatabaseEvent event) { if (event.snapshot.value != null) { setState(() { _monitoringData = Map.from(event.snapshot.value as Map); }); } }); } void _toggleDevice(String device) { final currentStatus = _monitoringData[device] as String; final newStatus = currentStatus == 'ON' ? 'OFF' : 'ON'; _database.update({device: newStatus}); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Monitoring & Control Dashboard'), backgroundColor: Theme.of(context).colorScheme.inversePrimary, ), body: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Monitoring', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: _buildMonitoringCard( 'Temperature', '${_monitoringData['Temperature']}°C', Icons.thermostat, Colors.red, ), ), const SizedBox(width: 16), Expanded( child: _buildMonitoringCard( 'Humidity', '${_monitoringData['Humidity']}%', Icons.water_drop, Colors.blue, ), ), ], ), const SizedBox(height: 32), const Text( 'Control', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: _buildControlCard( 'Fan', _monitoringData['Fan'] ?? 'OFF', Icons.air, Colors.green, () => _toggleDevice('Fan'), ), ), const SizedBox(width: 16), Expanded( child: _buildControlCard( 'Pump', _monitoringData['Pump'] ?? 'OFF', Icons.water, Colors.orange, () => _toggleDevice('Pump'), ), ), ], ), ], ), ), ), ); } Widget _buildMonitoringCard( String title, String value, IconData icon, Color color) { return Card( elevation: 4, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Icon(icon, size: 40, color: color), const SizedBox(height: 8), Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( value, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: color, ), ), ], ), ), ); } Widget _buildControlCard(String title, String status, IconData icon, Color color, VoidCallback onTap) { return Card( elevation: 4, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Icon(icon, size: 40, color: color), const SizedBox(height: 8), Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: status == 'ON' ? Colors.green : Colors.red, borderRadius: BorderRadius.circular(20), ), child: Text( status, style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), ), const SizedBox(height: 8), Text( 'Tap to toggle', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ), ), ); } }