TKK_E32231446/lib/screens/graph_screen.dart

351 lines
8.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:fl_chart/fl_chart.dart';
class GraphScreen extends StatefulWidget {
const GraphScreen({super.key});
@override
State<GraphScreen> createState() => _GraphScreenState();
}
class _GraphScreenState extends State<GraphScreen> {
final DatabaseReference historyRef =
FirebaseDatabase.instance.ref("hydroponic/history");
List<FlSpot> phSpots = [];
List<FlSpot> ppmSpots = [];
double latestPh = 0;
double latestPpm = 0;
String latestTime = "-";
@override
void initState() {
super.initState();
loadGraph();
}
void loadGraph() {
historyRef.onValue.listen((event) {
if (!event.snapshot.exists) return;
final data =
Map<dynamic, dynamic>.from(event.snapshot.value as Map);
List<MapEntry<dynamic, dynamic>> entries =
data.entries.toList();
entries.sort(
(a, b) => int.parse(a.key.toString())
.compareTo(int.parse(b.key.toString())),
);
// Ambil 20 data terakhir
if (entries.length > 20) {
entries = entries.sublist(entries.length - 20);
}
List<FlSpot> tempPh = [];
List<FlSpot> tempPpm = [];
for (int i = 0; i < entries.length; i++) {
final item =
Map<dynamic, dynamic>.from(entries[i].value);
final ph =
double.tryParse(item["ph"].toString()) ?? 0;
final ppm =
double.tryParse(item["ppm"].toString()) ?? 0;
tempPh.add(
FlSpot(i.toDouble(), ph),
);
tempPpm.add(
FlSpot(i.toDouble(), ppm),
);
}
final latestItem =
Map<dynamic, dynamic>.from(entries.last.value);
setState(() {
phSpots = tempPh;
ppmSpots = tempPpm;
latestPh =
double.tryParse(latestItem["ph"].toString()) ?? 0;
latestPpm =
double.tryParse(latestItem["ppm"].toString()) ?? 0;
latestTime =
latestItem["waktu"]?.toString() ?? "-";
});
});
}
Widget buildChart(
String title,
List<FlSpot> spots,
bool isPhChart,
) {
if (spots.isEmpty) {
return const SizedBox();
}
double minY = 0;
double maxY = isPhChart ? 9 : 1200;
return Card(
elevation: 4,
margin: const EdgeInsets.all(12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
SizedBox(
height: 350,
child: LineChart(
LineChartData(
minY: minY,
maxY: maxY,
lineTouchData: LineTouchData(
enabled: true,
),
gridData: FlGridData(
show: true,
drawVerticalLine: false,
),
borderData: FlBorderData(
show: true,
),
titlesData: FlTitlesData(
topTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: false,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 40,
interval: isPhChart ? 1 : 200,
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
interval: 5,
getTitlesWidget:
(value, meta) {
return Padding(
padding:
const EdgeInsets.only(
top: 8,
),
child: Text(
value.toInt().toString(),
style:
const TextStyle(
fontSize: 10,
),
),
);
},
),
),
),
lineBarsData: [
LineChartBarData(
spots: spots,
isCurved: true,
barWidth: 4,
color: isPhChart
? Colors.green
: Colors.blue,
dotData: FlDotData(
show: true,
),
belowBarData: BarAreaData(
show: true,
color: (isPhChart
? Colors.green
: Colors.blue)
.withOpacity(0.15),
),
),
],
),
),
),
],
),
),
);
}
Widget infoCard(
String title,
String value,
IconData icon,
) {
return Expanded(
child: Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(
icon,
size: 32,
),
const SizedBox(height: 8),
Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
value,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade100,
appBar: AppBar(
title: const Text("Grafik Monitoring"),
centerTitle: true,
backgroundColor: Colors.green,
),
body: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: 10),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: [
infoCard(
"pH Terbaru",
latestPh.toStringAsFixed(2),
Icons.science,
),
const SizedBox(width: 10),
infoCard(
"PPM Terbaru",
latestPpm.toStringAsFixed(0),
Icons.water_drop,
),
],
),
),
Card(
margin: const EdgeInsets.all(12),
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(14),
child: Row(
children: [
const Icon(
Icons.access_time,
color: Colors.green,
),
const SizedBox(width: 10),
Expanded(
child: Text(
"Update Terakhir : $latestTime",
style: const TextStyle(
fontWeight: FontWeight.w500,
),
),
),
],
),
),
),
buildChart(
"Grafik pH (20 Data Terakhir)",
phSpots,
true,
),
buildChart(
"Grafik PPM (20 Data Terakhir)",
ppmSpots,
false,
),
const SizedBox(height: 20),
],
),
),
);
}
}