From 3ae23dfdef281e8f0605c5e58ce1e1c370d1b15e Mon Sep 17 00:00:00 2001 From: Muhammad Iqbal Date: Sun, 18 May 2025 23:50:06 +0700 Subject: [PATCH] update thermal logic --- lib/screens/dashboard/dashboard_screen.dart | 108 ++++++++++++++------ lib/screens/main_screen.dart | 4 +- lib/widgets/costum_header.dart | 6 +- lib/widgets/thermal_heatmap.dart | 40 +++++--- 4 files changed, 108 insertions(+), 50 deletions(-) diff --git a/lib/screens/dashboard/dashboard_screen.dart b/lib/screens/dashboard/dashboard_screen.dart index 869959d..377f38a 100644 --- a/lib/screens/dashboard/dashboard_screen.dart +++ b/lib/screens/dashboard/dashboard_screen.dart @@ -16,9 +16,9 @@ class _DashboardScreenState extends State { late DatabaseReference _thermalRef; Map sensorData = { - 'PIR': 'Memuat...', - 'Ultrasonik': 'Memuat...', - 'Status Pengusir': 'Memuat...', + 'PIR': '0', + 'Ultrasonik': '0', + 'Status Pengusir': '0', }; List thermalData = List.filled(64, 0.0); @@ -30,7 +30,6 @@ class _DashboardScreenState extends State { _sensorRef = FirebaseDatabase.instance.ref('sensor'); _thermalRef = FirebaseDatabase.instance.ref('thermal/temperatures'); - // Listener sensor data _sensorRef.onValue.listen((event) { final data = event.snapshot.value; if (data != null && data is Map) { @@ -46,11 +45,9 @@ class _DashboardScreenState extends State { } }); - // Listener thermal data _thermalRef.onValue.listen((event) { final data = event.snapshot.value; if (data != null) { - // Firebase RTDB terkadang mengirim List atau Map List temps = []; if (data is List) { temps = data.map((e) { @@ -58,7 +55,6 @@ class _DashboardScreenState extends State { return 0.0; }).toList(); } else if (data is Map) { - // Jika data disimpan sebagai Map index:string -> value temps = List.filled(64, 0); data.forEach((key, value) { int idx = int.tryParse(key) ?? -1; @@ -82,7 +78,7 @@ class _DashboardScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.grey[100], + backgroundColor: const Color.fromARGB(255, 242, 242, 242), appBar: CustomHeader( deviceName: 'HamaGuard', notificationCount: 5, @@ -94,39 +90,47 @@ class _DashboardScreenState extends State { }, ), body: SingleChildScrollView( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), child: Column( children: [ - // Thermal Sensor + // Thermal Sensor Card Card( - elevation: 4, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16)), + borderRadius: BorderRadius.circular(20), + ), + elevation: 2, + shadowColor: Colors.green.withOpacity(0.2), + color: Colors.white, child: Padding( - padding: const EdgeInsets.all(16.0), + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: const [ - Icon(Icons.thermostat_outlined, color: Colors.red), - SizedBox(width: 8), + Icon(Icons.thermostat_outlined, + color: Colors.deepOrangeAccent, size: 30), + SizedBox(width: 12), Text( 'Thermal Sensor', style: TextStyle( - fontWeight: FontWeight.bold, fontSize: 16), + fontWeight: FontWeight.w600, + fontSize: 20, + color: Colors.deepOrangeAccent, + ), ), ], ), - const SizedBox(height: 12), + const SizedBox(height: 16), ThermalHeatmap(temperatures: thermalData), ], ), ), ), - const SizedBox(height: 16), + const SizedBox(height: 24), - // PIR & Ultrasonik + // Sensor Cards row (PIR & Ultrasonik) Row( children: [ Expanded( @@ -134,28 +138,29 @@ class _DashboardScreenState extends State { title: 'PIR', value: sensorData['PIR'] ?? '-', icon: Icons.motion_photos_on_outlined, - color: Colors.orange, + color: Colors.orange.shade700, ), ), - const SizedBox(width: 12), + const SizedBox(width: 20), Expanded( child: SensorCard( - title: 'Ultrasonik', + title: 'Jarak', value: sensorData['Ultrasonik'] ?? '-', icon: Icons.straighten, - color: Colors.blue, + color: Colors.blue.shade700, ), ), ], ), - const SizedBox(height: 12), + const SizedBox(height: 20), // Status Pengusir SensorCard( title: 'Status Pengusir', value: sensorData['Status Pengusir'] ?? '-', icon: Icons.speaker, - color: Colors.green, + color: Colors.green.shade700, + isWide: true, ), ], ), @@ -169,6 +174,7 @@ class SensorCard extends StatelessWidget { final String value; final IconData icon; final Color color; + final bool isWide; const SensorCard({ super.key, @@ -176,20 +182,56 @@ class SensorCard extends StatelessWidget { required this.value, required this.icon, required this.color, + this.isWide = false, }); @override Widget build(BuildContext context) { return Card( - elevation: 3, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - child: ListTile( - leading: CircleAvatar( - backgroundColor: color.withAlpha(15), - child: Icon(icon, color: color), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + elevation: 2, // shadow tipis + shadowColor: color.withOpacity(0.1), // lebih transparan + color: Colors.white, + child: Container( + width: isWide ? double.infinity : null, + padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20), + child: Row( + children: [ + Container( + decoration: BoxDecoration( + color: color.withOpacity(0.15), + borderRadius: BorderRadius.circular(14), + ), + padding: const EdgeInsets.all(12), + child: Icon(icon, size: 30, color: color), + ), + const SizedBox(width: 20), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + color: color.withOpacity(0.9), + fontWeight: FontWeight.w600, + fontSize: 16, + ), + ), + const SizedBox(height: 6), + Text( + value, + style: TextStyle( + fontWeight: FontWeight.w400, + fontSize: 15, + color: Colors.grey[800], + ), + ), + ], + ), + ), + ], ), - title: Text(title), - subtitle: Text(value), ), ); } diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 851cf6a..2efc59a 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -70,7 +70,7 @@ class _MainScreenState extends State { child: FloatingActionButton( onPressed: () => _onItemTapped(0), // Dashboard / Monitoring di tengah backgroundColor: - _selectedIndex == 0 ? Colors.green[700] : Colors.green[400], + _selectedIndex == 0 ? const Color.fromRGBO(56, 142, 60, 1) : Colors.green[400], elevation: _selectedIndex == 0 ? 8 : 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(35), @@ -87,7 +87,7 @@ class _MainScreenState extends State { shape: const CircularNotchedRectangle(), notchMargin: 8, elevation: 8, - color: Colors.white, + color: const Color.fromARGB(255, 255, 255, 255), child: SizedBox( height: 60, child: Row( diff --git a/lib/widgets/costum_header.dart b/lib/widgets/costum_header.dart index 73f8260..095e7ce 100644 --- a/lib/widgets/costum_header.dart +++ b/lib/widgets/costum_header.dart @@ -69,7 +69,7 @@ class _CustomHeaderState extends State { style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: Color.fromARGB(255, 56, 142, 60), + color: Color.fromRGBO(56, 142, 60, 1), ), ), const SizedBox(height: 4), @@ -90,7 +90,7 @@ class _CustomHeaderState extends State { Padding( padding: const EdgeInsets.only(left: 8.0), child: Material( - color: const Color.fromARGB(255, 198, 215, 197), + color: const Color.fromARGB(255, 219, 230, 221), shape: const CircleBorder(), child: Stack( clipBehavior: Clip.none, @@ -98,7 +98,7 @@ class _CustomHeaderState extends State { IconButton( padding: const EdgeInsets.all(8), icon: const Icon(Icons.notifications_none, - color: Colors.black87, size: 28), + color: Color.fromARGB(221, 57, 35, 35), size: 28), onPressed: widget.onNotificationTap ?? () { ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/widgets/thermal_heatmap.dart b/lib/widgets/thermal_heatmap.dart index 45aa18f..be2bed6 100644 --- a/lib/widgets/thermal_heatmap.dart +++ b/lib/widgets/thermal_heatmap.dart @@ -8,11 +8,33 @@ class ThermalHeatmap extends StatelessWidget { @override Widget build(BuildContext context) { return Center( - child: SizedBox( - width: 300, - height: 300, - child: CustomPaint( - painter: _ThermalPainter(temperatures), + child: Container( + width: 310, + height: 310, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + border: Border.all( + color: Colors.deepOrangeAccent.withOpacity(0.6), + width: 3, + ), + boxShadow: [ + BoxShadow( + color: Colors.deepOrangeAccent.withOpacity(0.15), + blurRadius: 8, + spreadRadius: 1, + offset: Offset(0, 3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(18), + child: SizedBox( + width: 300, + height: 300, + child: CustomPaint( + painter: _ThermalPainter(temperatures), + ), + ), ), ), ); @@ -50,13 +72,7 @@ class _ThermalPainter extends CustomPainter { } } - final center = Offset(size.width / 2, size.height / 2); - final plusPaint = Paint() - ..color = Colors.white.withOpacity(0.95) - ..strokeWidth = 2 - ..strokeCap = StrokeCap.round; - canvas.drawLine(center - const Offset(10, 0), center + const Offset(10, 0), plusPaint); - canvas.drawLine(center - const Offset(0, 10), center + const Offset(0, 10), plusPaint); + // Garis putih di tengah dihilangkan (dihapus) } Color _getColorForTemp(double t) {