import 'package:flutter/material.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; import 'dart:async'; import 'package:intl/date_symbol_data_local.dart'; import 'riwayat_screen.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await initializeDateFormatting('id_ID', null); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Health Monitor', debugShowCheckedModeBanner: false, theme: ThemeData.light().copyWith( scaffoldBackgroundColor: const Color(0xFFF8F9FA), ), home: const DashboardScreen(), ); } } class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { int _currentIndex = 0; int bpm = 0; int spo2 = 0; double temperature = 0.0; int systolic = 0; int diastolic = 0; String lastUpdate = "Menunggu data..."; String statusMessage = "Menghubungkan ke server..."; // URL untuk mengambil data terbaru final String fetchUrl = "http://10.105.126.239/iot/api.php?action=latest"; Timer? timer; Future fetchData() async { setState(() => statusMessage = "Mengambil data..."); print("🔄 Fetching from: $fetchUrl"); // Debug Console try { final response = await http .get(Uri.parse(fetchUrl), headers: {'Cache-Control': 'no-cache'}) .timeout(const Duration(seconds: 10)); print("📡 Status Code: ${response.statusCode}"); if (response.statusCode == 200) { final data = json.decode(response.body); print("📦 Response Body: ${response.body}"); // Debug if (data['status'] == 'success') { setState(() { bpm = data['bpm'] ?? 0; spo2 = data['spo2'] ?? 0; temperature = (data['temperature'] ?? 0.0).toDouble(); systolic = data['systolic'] ?? 120; diastolic = data['diastolic'] ?? 80; lastUpdate = data['time'] ?? "Baru saja"; statusMessage = "✅ Data berhasil diambil"; }); } else { setState( () => statusMessage = "❌ ${data['message'] ?? 'Error dari server'}", ); } } else { setState( () => statusMessage = "❌ Server error (${response.statusCode})", ); } } catch (e) { print("❌ Error: $e"); setState(() => statusMessage = "❌ Tidak bisa terhubung ke laptop"); } } @override void initState() { super.initState(); fetchData(); timer = Timer.periodic(const Duration(seconds: 4), (timer) { fetchData(); }); } @override void dispose() { timer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final List screens = [ SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Halo, User! 👋", style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold), ), const Text( "Berikut adalah hasil kesehatan Anda.", style: TextStyle(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 8), Text( statusMessage, style: const TextStyle(fontSize: 14, color: Colors.grey), ), const SizedBox(height: 24), // Layout 2x2 Cards Column( children: [ Row( children: [ Expanded( child: _buildCard( Icons.favorite, Colors.red, "Detak Jantung", bpm.toString(), "BPM", bpm >= 60 && bpm <= 100, ), ), const SizedBox(width: 12), Expanded( child: _buildCard( Icons.air, Colors.teal, "SpO2", spo2.toString(), "%", spo2 >= 95 && spo2 <= 100, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: _buildCard( Icons.water_drop, Colors.purple, "Tekanan Darah", "$systolic/$diastolic", "mmHg", systolic < 140 && diastolic < 90, ), ), const SizedBox(width: 12), Expanded( child: _buildCard( Icons.thermostat, Colors.blue, "Suhu Tubuh", temperature.toStringAsFixed(1), "°C", temperature >= 36.0 && temperature <= 37.5, ), ), ], ), ], ), const SizedBox(height: 24), // Ringkasan Hasil Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.1), blurRadius: 10, ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Ringkasan Hasil", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), _buildSummary("Detak Jantung", "$bpm BPM", Colors.red), _buildSummary("SpO2", "$spo2%", Colors.teal), _buildSummary( "Suhu Tubuh", "${temperature.toStringAsFixed(1)} °C", Colors.blue, ), _buildSummary( "Tekanan Darah", "$systolic/$diastolic mmHg", Colors.purple, ), ], ), ), ], ), ), const RiwayatScreen(), ]; return Scaffold( backgroundColor: const Color(0xFFF8F9FA), appBar: _currentIndex == 0 ? AppBar( backgroundColor: Colors.white, elevation: 0, title: Row( children: const [ Icon(Icons.favorite, color: Colors.red, size: 28), SizedBox(width: 8), Text( "Health Monitor", style: TextStyle( color: Colors.black87, fontWeight: FontWeight.bold, ), ), ], ), ) : null, body: screens[_currentIndex], bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) { setState(() => _currentIndex = index); }, selectedItemColor: Colors.blue, unselectedItemColor: Colors.grey, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: "Beranda"), BottomNavigationBarItem(icon: Icon(Icons.history), label: "Riwayat"), ], ), ); } Widget _buildCard( IconData icon, Color color, String title, String value, String unit, bool isNormal, ) { final bool isSpo2 = title == "SpO2"; return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow(color: Colors.grey.withOpacity(0.08), blurRadius: 10), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( radius: 24, backgroundColor: color.withOpacity(0.1), child: isSpo2 ? const Text( "O₂", style: TextStyle( fontSize: 26, fontWeight: FontWeight.bold, color: Colors.teal, ), ) : Icon(icon, color: color, size: 24), ), const SizedBox(height: 12), Text( title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), const SizedBox(height: 12), Text( value, style: TextStyle( fontSize: 26, fontWeight: FontWeight.bold, color: color, ), ), Text(unit, style: const TextStyle(fontSize: 12)), const SizedBox(height: 12), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: isNormal ? Colors.green.withOpacity(0.1) : Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: Text( isNormal ? "Normal" : "Perlu Perhatian", style: TextStyle( color: isNormal ? Colors.green : Colors.orange, fontWeight: FontWeight.w500, fontSize: 11, ), textAlign: TextAlign.center, ), ), ], ), ); } Widget _buildSummary(String label, String value, Color color) { return Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.circle, size: 12, color: color), const SizedBox(width: 10), Text(label, style: const TextStyle(fontSize: 16)), ], ), Text( value, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ], ), ); } }