pembaruan wind_speed_screen.dart
tampilan halaman kecepatan angin memperbarui tampilan dan period grafik dari harian, mingguan dan bulanan
This commit is contained in:
parent
410c51722b
commit
2d1ccdb8b2
|
|
@ -17,21 +17,27 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
|
|||
(event, emit) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
|
||||
// 1. Ambil semua history
|
||||
final history = await _repository.getSensorHistory(
|
||||
'anemometer/history', (json) => MyWindSpeed.fromJson(json));
|
||||
|
||||
// 2. Petakan ke 24 jam (Harian)
|
||||
final List<double> dailyGraph = _mapHistoryToDaily(history);
|
||||
|
||||
emit(state.copyWith(
|
||||
dailySpeeds: dailyGraph,
|
||||
isLoading: false,
|
||||
));
|
||||
|
||||
// 2. Monitoring Real-time
|
||||
await emit.forEach<MyWindSpeed>(
|
||||
_repository.getSensorStream(
|
||||
'anemometer/realtime', (json) => MyWindSpeed.fromJson(json)),
|
||||
onData: (data) {
|
||||
// Mengambil list lama dan menambah data baru untuk grafik
|
||||
final updatedSpeeds = List<double>.from(state.dailySpeeds)
|
||||
..add(data.speed);
|
||||
// Batasi jumlah data di grafik (misal cuma simpan 20 data terakhir agar tidak berat)
|
||||
if (updatedSpeeds.length > 20) {
|
||||
updatedSpeeds.removeAt(0);
|
||||
}
|
||||
|
||||
return state.copyWith(
|
||||
currentSpeed: data.speed,
|
||||
dailySpeeds: updatedSpeeds, // Update list grafik
|
||||
isLoading: false,
|
||||
);
|
||||
},
|
||||
|
|
@ -41,13 +47,78 @@ class WindSpeedBloc extends Bloc<WindSpeedEvent, WindSpeedState> {
|
|||
transformer: restartable(),
|
||||
);
|
||||
|
||||
on<WindSpeedPeriodChanged>((event, emit) {
|
||||
on<WindSpeedPeriodChanged>((event, emit) async {
|
||||
emit(state.copyWith(selectedPeriod: event.period));
|
||||
// Ambil ulang history dari database
|
||||
final history = await _repository.getSensorHistory(
|
||||
'anemometer/history', (json) => MyWindSpeed.fromJson(json));
|
||||
List<double> updatedGraph;
|
||||
|
||||
// Pilih mapper berdasarkan pilihan user
|
||||
if (event.period == "Minggu Ini") {
|
||||
updatedGraph = _mapHistoryToWeekly(history);
|
||||
} else if (event.period == "Bulan Ini") {
|
||||
updatedGraph = _mapHistoryToMonthly(history);
|
||||
} else {
|
||||
updatedGraph = _mapHistoryToDaily(history);
|
||||
}
|
||||
|
||||
emit(state.copyWith(
|
||||
dailySpeeds: updatedGraph,
|
||||
isLoading: false,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
return super.close();
|
||||
}
|
||||
Future<void> close() => super.close();
|
||||
}
|
||||
|
||||
List<double> _mapHistoryToDaily(List<MyWindSpeed> history) {
|
||||
// Buat list 24 angka nol (representasi jam 00:00 - 23:00)
|
||||
List<double> slots = List.generate(24, (_) => 0.0);
|
||||
|
||||
for (var item in history) {
|
||||
// Ubah timestamp UTC ke jam lokal
|
||||
DateTime date = item.timestamp;
|
||||
|
||||
// Jika data ini adalah data hari ini, masukkan ke slot jamnya
|
||||
if (date.day == DateTime.now().day) {
|
||||
slots[date.hour] = item.speed;
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
// Mapper Mingguan (7 Slot: Senin - Minggu)
|
||||
List<double> _mapHistoryToWeekly(List<MyWindSpeed> history) {
|
||||
List<double> slots = List.generate(7, (_) => 0.0);
|
||||
final now = DateTime.now();
|
||||
|
||||
// Mencari awal minggu ini (Senin)
|
||||
DateTime startOfWeek = now.subtract(Duration(days: now.weekday - 1));
|
||||
startOfWeek = DateTime(startOfWeek.year, startOfWeek.month, startOfWeek.day);
|
||||
|
||||
for (var item in history) {
|
||||
if (item.timestamp.isAfter(startOfWeek)) {
|
||||
// index 0 = Senin, dst.
|
||||
slots[item.timestamp.weekday - 1] = item.speed;
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
// Mapper Bulanan (Sesuai jumlah hari di bulan ini)
|
||||
List<double> _mapHistoryToMonthly(List<MyWindSpeed> history) {
|
||||
final now = DateTime.now();
|
||||
final daysInMonth = DateTime(now.year, now.month + 1, 0).day;
|
||||
List<double> slots = List.generate(daysInMonth, (_) => 0.0);
|
||||
|
||||
for (var item in history) {
|
||||
if (item.timestamp.month == now.month && item.timestamp.year == now.year) {
|
||||
// index 0 = Tanggal 1, dst.
|
||||
slots[item.timestamp.day - 1] = item.speed;
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,57 @@
|
|||
/// === Tombol Filter Jam/Hari/Minggu === ///
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../blocs/wind_speed_bloc.dart';
|
||||
|
||||
class PeriodSelector extends StatelessWidget {
|
||||
const PeriodSelector({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Kita ambil state saat ini untuk tahu periode mana yang aktif
|
||||
final selectedPeriod =
|
||||
context.select((WindSpeedBloc bloc) => bloc.state.selectedPeriod);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildTab(context, "Hari Ini", selectedPeriod),
|
||||
_buildTab(context, "Minggu Ini", selectedPeriod),
|
||||
_buildTab(context, "Bulan Ini", selectedPeriod),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTab(BuildContext context, String label, String current) {
|
||||
bool isActive = label == current;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// Kirim event ke Bloc saat tombol diklik
|
||||
context.read<WindSpeedBloc>().add(WindSpeedPeriodChanged(label));
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive ? Colors.blue : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: isActive ? Colors.white : Colors.grey,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_chart/fl_chart.dart';
|
||||
// import 'package:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../blocs/wind_speed_bloc.dart';
|
||||
import './widgets/wind_speed_chart_widget.dart';
|
||||
import 'widgets/period_selector.dart';
|
||||
|
||||
class WindSpeedScreen extends StatelessWidget {
|
||||
const WindSpeedScreen({super.key});
|
||||
|
|
@ -37,10 +38,13 @@ class WindSpeedScreen extends StatelessWidget {
|
|||
_buildMainSpeedDisplay(state.currentSpeed),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
const Text("Tren Real-time",
|
||||
const Text("Tren Kecepatan",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 15),
|
||||
const PeriodSelector(),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
WindSpeedChartWidget(dailySpeeds: state.dailySpeeds),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
|
|
|||
|
|
@ -38,4 +38,28 @@ class FirebaseMonitoringRepo implements MonitoringRepository {
|
|||
final data = snapshot.value as Map<dynamic, dynamic>? ?? {};
|
||||
return mapper(data);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<T>> getSensorHistory<T>(
|
||||
String path,
|
||||
T Function(Map<dynamic, dynamic> json) mapper,
|
||||
) async {
|
||||
try {
|
||||
// Ambil data dari path history
|
||||
final snapshot = await _db.ref(path).get();
|
||||
|
||||
if (snapshot.exists && snapshot.value is Map) {
|
||||
final Map<dynamic, dynamic> data = snapshot.value as Map;
|
||||
|
||||
// Karena RTDB push() menghasilkan Map dengan ID unik,
|
||||
// kita ambil values-nya saja dan ubah jadi List objek
|
||||
return data.values.map((item) {
|
||||
return mapper(item as Map<dynamic, dynamic>);
|
||||
}).toList();
|
||||
}
|
||||
return [];
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ class MyWindSpeed {
|
|||
required this.timestamp,
|
||||
});
|
||||
|
||||
static final empty = MyWindSpeed(
|
||||
speed: 0.0,
|
||||
pulse: 0,
|
||||
timestamp: DateTime.fromMillisecondsSinceEpoch(0),
|
||||
);
|
||||
|
||||
factory MyWindSpeed.fromJson(Map<dynamic, dynamic> json) {
|
||||
return MyWindSpeed(
|
||||
speed: (json['speed'] ?? 0).toDouble(),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import 'models/models.dart';
|
||||
|
||||
abstract class MonitoringRepository {
|
||||
// fungsi untuk ambil data berkali/streaming
|
||||
Stream<T> getSensorStream<T>(
|
||||
String path,
|
||||
T Function(Map<dynamic, dynamic> json) mapper,
|
||||
);
|
||||
|
||||
// fungsi untuk ambil sensor sekali
|
||||
Future<T> getSensorSnapshot<T>(
|
||||
String path,
|
||||
T Function(Map<dynamic, dynamic> json) mapper,
|
||||
);
|
||||
|
||||
// Fungsi untuk ambil riwayat (List)
|
||||
Future<List<T>> getSensorHistory<T>(
|
||||
String path,
|
||||
T Function(Map<dynamic, dynamic> json) mapper,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue