458 lines
11 KiB
Dart
458 lines
11 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:firebase_database/firebase_database.dart';
|
|
|
|
class NotifikasiPage extends StatefulWidget {
|
|
const NotifikasiPage({super.key});
|
|
|
|
@override
|
|
State<NotifikasiPage> createState() => _NotifikasiPageState();
|
|
}
|
|
|
|
class _NotifikasiPageState extends State<NotifikasiPage> {
|
|
|
|
final dbRef = FirebaseDatabase.instance.ref("sensor");
|
|
|
|
double jarak = 0;
|
|
double berat1 = 0;
|
|
double berat2 = 0;
|
|
|
|
final statusRef = FirebaseDatabase.instance.ref("status");
|
|
|
|
int lastHeartbeat = 0;
|
|
DateTime lastHeartbeatTime = DateTime.now();
|
|
|
|
bool perangkatOffline = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
dbRef.onValue.listen((event) {
|
|
final data = event.snapshot.value as Map?;
|
|
|
|
if (data != null) {
|
|
|
|
double newJarak =
|
|
(data['jarak'] ?? 0).toDouble();
|
|
|
|
double newBerat1 =
|
|
(data['berat1'] ?? 0).toDouble();
|
|
|
|
double newBerat2 =
|
|
(data['berat2'] ?? 0).toDouble();
|
|
|
|
setState(() {
|
|
jarak = newJarak;
|
|
berat1 = newBerat1;
|
|
berat2 = newBerat2;
|
|
});
|
|
}
|
|
statusRef.child("heartbeat").onValue.listen((event) {
|
|
|
|
int heartbeat = 0;
|
|
|
|
if (event.snapshot.value != null) {
|
|
heartbeat = (event.snapshot.value as num).toInt();
|
|
}
|
|
|
|
if (heartbeat != lastHeartbeat) {
|
|
|
|
lastHeartbeat = heartbeat;
|
|
|
|
setState(() {
|
|
lastHeartbeatTime = DateTime.now();
|
|
perangkatOffline = false;
|
|
});
|
|
}
|
|
});
|
|
|
|
// cek setiap detik
|
|
Stream.periodic(
|
|
const Duration(seconds: 1),
|
|
).listen((_) {
|
|
|
|
int selisih = DateTime.now()
|
|
.difference(lastHeartbeatTime)
|
|
.inSeconds;
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
perangkatOffline = selisih >= 15;
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
|
|
// 🔥 HITUNG PERSEN PENAMPUNG
|
|
double persen = ((12 - jarak) / 8) * 100;
|
|
|
|
if (persen < 0) persen = 0;
|
|
if (persen > 100) persen = 100;
|
|
|
|
// 🔥 DETEKSI PAKAN BERHASIL
|
|
bool pakanBerhasil =
|
|
berat1 > 0 || berat2 > 0;
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFE0E0E0),
|
|
|
|
appBar: AppBar(
|
|
backgroundColor: const Color(0xFFFFC107),
|
|
elevation: 0,
|
|
title: const Text(
|
|
"Notifikasi",
|
|
style: TextStyle(
|
|
color: Colors.black,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
iconTheme: const IconThemeData(
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
|
|
body: ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
if (perangkatOffline)
|
|
Container(
|
|
margin: const EdgeInsets.only(bottom: 15),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(15),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.1),
|
|
blurRadius: 5,
|
|
)
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
|
|
const CircleAvatar(
|
|
backgroundColor: Colors.orange,
|
|
child: Icon(
|
|
Icons.wifi_off,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
const Text(
|
|
"Perangkat Offline",
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
"Jaringan lemah atau perangkat tidak terhubung.",
|
|
style: TextStyle(
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
|
|
],
|
|
),
|
|
),
|
|
|
|
],
|
|
),
|
|
),
|
|
|
|
// 🔔 PAKAN HAMPIR HABIS
|
|
// 🔔 PAKAN FULL
|
|
if (persen >= 100)
|
|
Container(
|
|
margin: const EdgeInsets.only(
|
|
bottom: 15,
|
|
),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(15),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black
|
|
.withOpacity(0.1),
|
|
blurRadius: 5,
|
|
)
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
|
|
const CircleAvatar(
|
|
backgroundColor: Colors.green,
|
|
child: Icon(
|
|
Icons.inventory,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
const Text(
|
|
"Pakan Full",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
"Wadah penampung pakan penuh.",
|
|
style: TextStyle(
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (persen <= 25)
|
|
Container(
|
|
margin: const EdgeInsets.only(
|
|
bottom: 15,
|
|
),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(15),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black
|
|
.withOpacity(0.1),
|
|
blurRadius: 5,
|
|
)
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
|
|
const CircleAvatar(
|
|
backgroundColor: Colors.red,
|
|
child: Icon(
|
|
Icons.warning,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
const Text(
|
|
"Pakan Hampir Habis",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
"Segera isi ulang wadah penampung pakan.",
|
|
style: TextStyle(
|
|
color:
|
|
Colors.grey[700],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 🔔 PAKAN HABIS
|
|
if (persen <= 0)
|
|
Container(
|
|
margin: const EdgeInsets.only(
|
|
bottom: 15,
|
|
),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(15),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black
|
|
.withOpacity(0.1),
|
|
blurRadius: 5,
|
|
)
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
|
|
const CircleAvatar(
|
|
backgroundColor: Colors.black,
|
|
child: Icon(
|
|
Icons.error,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
const Text(
|
|
"Pakan Habis",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
"Wadah penampung pakan sudah kosong.",
|
|
style: TextStyle(
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 🔔 PEMBERIAN PAKAN BERHASIL
|
|
if (pakanBerhasil)
|
|
Container(
|
|
margin: const EdgeInsets.only(
|
|
bottom: 15,
|
|
),
|
|
padding: const EdgeInsets.all(15),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(15),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black
|
|
.withOpacity(0.1),
|
|
blurRadius: 5,
|
|
)
|
|
],
|
|
),
|
|
child: Row(
|
|
children: [
|
|
|
|
const CircleAvatar(
|
|
backgroundColor:
|
|
Colors.green,
|
|
child: Icon(
|
|
Icons.check,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
const Text(
|
|
"Pemberian Pakan Berhasil",
|
|
style: TextStyle(
|
|
fontWeight:
|
|
FontWeight.bold,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 5),
|
|
|
|
Text(
|
|
"Pakan berhasil diberikan ke wadah pakan.",
|
|
style: TextStyle(
|
|
color:
|
|
Colors.grey[700],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 🔥 TIDAK ADA NOTIFIKASI
|
|
if (persen > 25 &&
|
|
!pakanBerhasil)
|
|
Center(
|
|
child: Padding(
|
|
padding:
|
|
const EdgeInsets.only(
|
|
top: 50,
|
|
),
|
|
child: Column(
|
|
children: [
|
|
|
|
Icon(
|
|
Icons.notifications_off,
|
|
size: 80,
|
|
color: Colors.grey[400],
|
|
),
|
|
|
|
const SizedBox(height: 10),
|
|
|
|
Text(
|
|
"Belum ada notifikasi",
|
|
style: TextStyle(
|
|
color: Colors.grey[600],
|
|
fontSize: 16,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |