From 6fc5d105a55d7a604ee2ae9f16268ac612c2dd59 Mon Sep 17 00:00:00 2001 From: afanfahreza <158114832+afanfahreza@users.noreply.github.com> Date: Wed, 5 Aug 2026 15:31:12 +0700 Subject: [PATCH] first commit --- .vscode/settings.json | 2 + APK_MONITORING_FINAL.md | 382 ---- CHANGELOG.md | 67 - CHANGELOG_NOTIFIKASI.md | 127 ++ DOKUMENTASI.md | 114 -- GRAFIK_CHART_GUIDE.md | 244 +++ NOTIFICATION_FIX_README.md | 161 ++ PERBAIKAN_APK_FINAL.md | 296 --- PERBAIKAN_STATUS_MQTT.md | 235 --- android/app/build.gradle.kts | 14 + android/app/proguard-rules.pro | 69 + android/app/src/main/AndroidManifest.xml | 36 +- .../example/aplikasi_novil/MainActivity.kt | 29 +- .../res/mipmap-anydpi-v26/launcher_icon.xml | 5 + .../main/res/mipmap-hdpi/launcher_icon.png | Bin 0 -> 1628 bytes .../main/res/mipmap-mdpi/launcher_icon.png | Bin 0 -> 963 bytes .../main/res/mipmap-xhdpi/launcher_icon.png | Bin 0 -> 1948 bytes .../main/res/mipmap-xxhdpi/launcher_icon.png | Bin 0 -> 3079 bytes .../main/res/mipmap-xxxhdpi/launcher_icon.png | Bin 0 -> 3652 bytes android/app/src/main/res/values/colors.xml | 2 +- android/build.gradle.kts | 12 + android/gradle.properties | 14 + backend/CARA_PUSH.md | 112 ++ backend/push.ps1 | 42 + backend/server.js | 4 +- create_adaptive_icon.html | 185 ++ devtools_options.yaml | 3 + esp32_pengering_ikan_railway.ino | 242 ++- fix_icon.py | 131 ++ flutter_02.png | Bin 0 -> 92132 bytes flutter_command.txt | 1 + .../Icon-App-1024x1024@1x.png | Bin 16657 -> 16071 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 439 -> 392 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 822 -> 763 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1187 -> 1099 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 582 -> 532 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1175 -> 1099 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1740 -> 1664 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 822 -> 763 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1611 -> 1461 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2368 -> 2272 bytes .../AppIcon.appiconset/Icon-App-50x50@1x.png | Bin 1012 -> 929 bytes .../AppIcon.appiconset/Icon-App-50x50@2x.png | Bin 2008 -> 1910 bytes .../AppIcon.appiconset/Icon-App-57x57@1x.png | Bin 1143 -> 1067 bytes .../AppIcon.appiconset/Icon-App-57x57@2x.png | Bin 2089 -> 1996 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2368 -> 2272 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3538 -> 3414 bytes .../AppIcon.appiconset/Icon-App-72x72@1x.png | Bin 1628 -> 1485 bytes .../AppIcon.appiconset/Icon-App-72x72@2x.png | Bin 3079 -> 2850 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1633 -> 1459 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3469 -> 3101 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3643 -> 3336 bytes lib/main.dart | 10 +- lib/screens/chart_screen.dart | 740 ++++++++ lib/screens/dashboard_screen.dart | 1636 ----------------- lib/screens/dashboard_screen_with_api.dart | 327 +++- lib/services/api_service.dart | 4 +- lib/services/notification_manager.dart | 37 +- lib/widgets/temperature_chart.dart | 264 +++ lib/widgets/weight_chart.dart | 385 ++++ nixpacks.toml | 8 + pubspec.yaml | 20 +- web/favicon.png | Bin 917 -> 341 bytes web/icons/Icon-192.png | Bin 5292 -> 3652 bytes web/icons/Icon-512.png | Bin 8252 -> 9374 bytes web/icons/Icon-maskable-192.png | Bin 5594 -> 3652 bytes web/icons/Icon-maskable-512.png | Bin 20998 -> 9374 bytes web/manifest.json | 6 +- 68 files changed, 3028 insertions(+), 2938 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 APK_MONITORING_FINAL.md delete mode 100644 CHANGELOG.md create mode 100644 CHANGELOG_NOTIFIKASI.md delete mode 100644 DOKUMENTASI.md create mode 100644 GRAFIK_CHART_GUIDE.md create mode 100644 NOTIFICATION_FIX_README.md delete mode 100644 PERBAIKAN_APK_FINAL.md delete mode 100644 PERBAIKAN_STATUS_MQTT.md create mode 100644 android/app/proguard-rules.pro create mode 100644 android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml create mode 100644 android/app/src/main/res/mipmap-hdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-mdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png create mode 100644 android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png create mode 100644 backend/CARA_PUSH.md create mode 100644 backend/push.ps1 create mode 100644 create_adaptive_icon.html create mode 100644 devtools_options.yaml create mode 100644 fix_icon.py create mode 100644 flutter_02.png create mode 100644 flutter_command.txt create mode 100644 lib/screens/chart_screen.dart delete mode 100644 lib/screens/dashboard_screen.dart create mode 100644 lib/widgets/temperature_chart.dart create mode 100644 lib/widgets/weight_chart.dart create mode 100644 nixpacks.toml diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/APK_MONITORING_FINAL.md b/APK_MONITORING_FINAL.md deleted file mode 100644 index 147d2e2..0000000 --- a/APK_MONITORING_FINAL.md +++ /dev/null @@ -1,382 +0,0 @@ -# ๐Ÿ“ฑ APK Monitoring Pengering Ikan - FINAL VERSION - -## ๐ŸŽฏ Aplikasi Monitoring Only (Tanpa Kontrol) - ---- - -## โœ… Perubahan Terakhir - -### 1. **Hapus Lamp** ๐Ÿ”ฆ -- โŒ Lamp dihapus dari tampilan -- โœ… Hanya 3 device: **Heater, Fan, Exhaust** -- โœ… Grid 3 kolom (lebih rapi) - -### 2. **Hapus Semua Kontrol** ๐ŸŽฎ -- โŒ Tidak ada tombol toggle device -- โŒ Tidak ada floating action button (START/RESET) -- โœ… **Monitoring Only** - hanya menampilkan status -- โœ… Device cards tidak bisa diklik - -### 3. **Auto-Update Data** ๐Ÿ”„ -- โœ… Data update otomatis setiap **5 detik** -- โœ… Tidak perlu pull-to-refresh manual -- โœ… Polling dari Railway API -- โœ… Real-time via MQTT (jika tersedia) - -### 4. **Notifikasi Push** ๐Ÿ”” -- โœ… **Pengeringan Siap** (status READY) -- โœ… **Ikan Terdeteksi** (status BERJALAN) -- โœ… **Pengeringan Selesai** (status SELESAI) -- โœ… Notifikasi muncul di dalam & luar aplikasi -- โœ… Notifikasi tersimpan di history - ---- - -## ๐Ÿ“Š Tampilan Aplikasi - -### Status Card -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๐ŸŸฃ SCANNING โ”‚ -โ”‚ Menunggu deteksi ikan... โ”‚ -โ”‚ โ”‚ -โ”‚ Waktu Mulai: 10:30:45 โ”‚ -โ”‚ Durasi: 00:15:32 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Metrics -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๐ŸŒก๏ธ Suhu โ”‚ โ”‚ โš–๏ธ Berat โ”‚ -โ”‚ 25.9ยฐC โ”‚ โ”‚ 2183 g โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๐ŸŽฏ Target Berat โ”‚ -โ”‚ 628 g โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Device Status (3 Kolom) -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๐Ÿ”ฅ Heaterโ”‚ โ”‚ ๐Ÿ’จ Fan โ”‚ โ”‚ ๐ŸŒช๏ธ Exhaustโ”‚ -โ”‚ ON โ”‚ โ”‚ ON โ”‚ โ”‚ OFF โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - ---- - -## ๐Ÿ”” Notifikasi - -### Kapan Notifikasi Muncul? - -#### 1. **Pengeringan Dimulai** (READY) -``` -๐Ÿ”ต Pengeringan Dimulai -Proses pengeringan ikan telah dimulai -``` -- Trigger: Status berubah ke READY -- Muncul: Saat ESP32 mulai proses - -#### 2. **Ikan Terdeteksi** (BERJALAN) -``` -๐Ÿ”ต Ikan Terdeteksi -Berat ikan: 2183 gram -``` -- Trigger: Status berubah ke BERJALAN -- Muncul: Saat ikan diletakkan di timbangan - -#### 3. **Pengeringan Selesai** (SELESAI) -``` -๐ŸŸข Pengeringan Selesai -Target tercapai! Berat akhir: 628 gram -``` -- Trigger: Status berubah ke SELESAI -- Muncul: Saat berat mencapai target - -### Notifikasi di Luar Aplikasi -- โœ… Muncul di notification bar Android -- โœ… Dengan suara & vibration -- โœ… Bisa diklik untuk buka aplikasi -- โœ… Tersimpan di history notifikasi - ---- - -## ๐Ÿ”„ Auto-Update Mechanism - -### Polling Timer (5 Detik) -```dart -Timer.periodic(Duration(seconds: 5), (timer) { - // Fetch latest data dari API - _loadLatestData(); - - // Cek perubahan status - if (newStatus != previousStatus) { - // Trigger notifikasi - _notificationManager.notify...(); - } -}); -``` - -### Flow: -1. **Setiap 5 detik** โ†’ Fetch data dari Railway API -2. **Cek status** โ†’ Bandingkan dengan status sebelumnya -3. **Jika berubah** โ†’ Trigger notifikasi -4. **Update UI** โ†’ Tampilkan data terbaru - ---- - -## ๐Ÿ“ฑ APK Info - -- **Nama:** PengeringIkan.apk -- **Lokasi:** Desktop -- **Ukuran:** 20.65 MB -- **Build:** Release -- **Version:** 1.0.0 (Monitoring Only) - -### Fitur: -- โœ… Monitoring only (no control) -- โœ… Auto-update setiap 5 detik -- โœ… Push notifications -- โœ… Connection indicators -- โœ… 3 device cards (Heater, Fan, Exhaust) -- โœ… Status tracking -- โœ… Timer durasi -- โœ… Data dari Railway API - ---- - -## ๐Ÿš€ Cara Install & Test - -### 1. Uninstall APK Lama -``` -Settings โ†’ Apps โ†’ Pengering Ikan โ†’ Uninstall -``` - -### 2. Install APK Baru -``` -File Manager โ†’ Desktop โ†’ PengeringIkan.apk โ†’ Install -``` - -### 3. Izinkan Notifikasi -``` -Saat pertama buka, izinkan: -- โœ… Show notifications -- โœ… Sound -- โœ… Vibration -``` - -### 4. Test Auto-Update -- Buka aplikasi -- Tunggu 5 detik -- Data harus update otomatis -- Tidak perlu pull-to-refresh - -### 5. Test Notifikasi -- Ubah status di ESP32 (READY โ†’ BERJALAN โ†’ SELESAI) -- Notifikasi harus muncul otomatis -- Cek notification bar Android -- Cek history notifikasi di app - ---- - -## ๐ŸŽจ UI Changes - -### Before (4 Device Cards): -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Heater โ”‚ โ”‚ Fan โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Lamp โ”‚ โ”‚ Exhaust โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### After (3 Device Cards): -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Heater โ”‚ โ”‚ Fan โ”‚ โ”‚Exhaust โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### Device Card Styling: -- โœ… Compact design -- โœ… Icon dengan background color -- โœ… Status badge (ON/OFF) -- โœ… Tidak bisa diklik -- โœ… Responsive 3 kolom - ---- - -## ๐Ÿ” Troubleshooting - -### Notifikasi Tidak Muncul? - -**Cek 1: Permission** -``` -Settings โ†’ Apps โ†’ Pengering Ikan โ†’ Notifications -- Pastikan "Show notifications" ON -``` - -**Cek 2: Do Not Disturb** -``` -Settings โ†’ Sound โ†’ Do Not Disturb -- Pastikan OFF atau allow app notifications -``` - -**Cek 3: Battery Optimization** -``` -Settings โ†’ Battery โ†’ Battery Optimization -- Set "Pengering Ikan" to "Don't optimize" -``` - -**Cek 4: Status Berubah** -``` -- Notifikasi hanya muncul saat status berubah -- Cek ESP32 mengirim status baru -``` - -### Data Tidak Update? - -**Cek 1: Connection Icons** -- WiFi icon hijau? โ†’ MQTT OK -- Cloud icon hijau? โ†’ API OK - -**Cek 2: Backend Status** -``` -Browser: https://web-production-47eb.up.railway.app/ -Harus return: {"status":"OK",...} -``` - -**Cek 3: ESP32 Status** -- ESP32 harus nyala -- ESP32 harus connect WiFi -- ESP32 harus publish data - -**Cek 4: Polling Timer** -- Tunggu 5 detik -- Data harus update otomatis -- Cek timestamp data - ---- - -## ๐Ÿ“Š Status Flow - -``` -DISCONNECTED - โ†“ -CONNECTING - โ†“ -READY (๐Ÿ”” Notif: Pengeringan Dimulai) - โ†“ -SCANNING (Menunggu ikan) - โ†“ -BERJALAN (๐Ÿ”” Notif: Ikan Terdeteksi) - โ†“ -SELESAI (๐Ÿ”” Notif: Pengeringan Selesai) -``` - ---- - -## ๐ŸŽฏ Testing Checklist - -### Installation -- [ ] Uninstall APK lama -- [ ] Install APK baru -- [ ] Izinkan notifikasi -- [ ] Buka aplikasi - -### UI -- [ ] Hanya 3 device cards (Heater, Fan, Exhaust) -- [ ] Tidak ada Lamp -- [ ] Device cards tidak bisa diklik -- [ ] Tidak ada floating action button -- [ ] Connection icons terlihat - -### Auto-Update -- [ ] Data update setiap 5 detik -- [ ] Tidak perlu pull-to-refresh -- [ ] Timestamp berubah -- [ ] Status update otomatis - -### Notifikasi -- [ ] Notif muncul saat status READY -- [ ] Notif muncul saat status BERJALAN -- [ ] Notif muncul saat status SELESAI -- [ ] Notif muncul di notification bar -- [ ] Notif tersimpan di history -- [ ] Notif bisa diklik - -### Monitoring -- [ ] Suhu update real-time -- [ ] Berat update real-time -- [ ] Target terlihat -- [ ] Status berubah sesuai ESP32 -- [ ] Device status (ON/OFF) update -- [ ] Timer durasi jalan - ---- - -## ๐Ÿ“ Technical Details - -### Auto-Update -- **Interval:** 5 detik -- **Method:** Polling via API -- **Endpoint:** `/api/data/latest` -- **Fallback:** MQTT real-time - -### Notifikasi -- **Library:** flutter_local_notifications -- **Storage:** SharedPreferences -- **Trigger:** Status change detection -- **Types:** Info, Success, Warning, Error - -### Device Cards -- **Count:** 3 (Heater, Fan, Exhaust) -- **Layout:** GridView 3 columns -- **Aspect Ratio:** 0.9 -- **Interaction:** Display only (no tap) - ---- - -## ๐Ÿ”„ Update History - -### Version 1.0.0 - Monitoring Only (1 Juni 2026) -- โœ… Hapus Lamp dari tampilan -- โœ… Hapus semua kontrol (monitoring only) -- โœ… Auto-update data setiap 5 detik -- โœ… Notifikasi push untuk status -- โœ… Device cards 3 kolom -- โœ… Compact UI design -- โœ… No interaction (display only) - ---- - -## ๐Ÿ’ก Tips - -1. **Biarkan Aplikasi Berjalan** - - Auto-update bekerja di background - - Notifikasi tetap muncul meski app tertutup - -2. **Cek Notifikasi** - - Swipe down untuk lihat notification bar - - Tap notifikasi untuk buka app - -3. **Monitor Status** - - Perhatikan perubahan status - - Notifikasi muncul saat status berubah - -4. **Connection Indicators** - - WiFi icon = MQTT status - - Cloud icon = API status - - Hijau = Connected, Merah = Disconnected - ---- - -**Build Date:** 1 Juni 2026 -**Version:** 1.0.0 (Monitoring Only) -**Status:** โœ… PRODUCTION READY - -๐ŸŽ‰ **Aplikasi siap digunakan untuk monitoring!** diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 7b4c627..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,67 +0,0 @@ -# Changelog - Aplikasi Pengering Ikan - -## Version 1.1.0 (Build 2) - 2024-12-XX - -### ๐ŸŽ‰ Fitur Baru -- โœ… **Status Relay Real-time**: Aplikasi sekarang menampilkan status relay (Heater 1, Heater 2, Fan, Exhaust) secara real-time dari ESP32 -- โœ… **Exhaust Otomatis**: Exhaust akan otomatis menyala saat suhu mencapai 60ยฐC atau lebih -- โœ… **Cooling Down 20 Detik**: Setelah pengeringan selesai, sistem akan menjalankan cooling down selama 20 detik dengan exhaust menyala dan countdown di LCD -- โœ… **Auto Reset**: Setelah cooling down, sistem otomatis kembali ke mode "Ready to Dry" untuk batch berikutnya - -### ๐Ÿ”ง Perbaikan Backend -- โœ… **Parse Relay Status**: Backend sekarang membaca dan menyimpan status relay1, relay2, relay3, relay4 dari data MQTT ESP32 -- โœ… **Real-time Update**: Status relay di database dan API diupdate secara real-time - -### ๐Ÿ”ง Perbaikan ESP32 -- โœ… **Kirim Status Relay**: ESP32 sekarang mengirim status semua relay (relay1-4) dalam format JSON ke MQTT -- โœ… **Kontrol Exhaust Otomatis**: Exhaust dikendalikan berdasarkan suhu (ON jika >= 60ยฐC) -- โœ… **4 Relay Support**: Mendukung Heater 1 (GPIO 13), Heater 2 (GPIO 12), Fan (GPIO 14), Exhaust (GPIO 27) -- โœ… **Load Cell Error Handling**: Penanganan error yang lebih baik untuk load cell dengan timeout -- โœ… **LCD Status**: Tampilan LCD yang lebih informatif di setiap tahap proses - -### ๐Ÿ› Bug Fixes -- โœ… Fixed: Perhitungan berat load cell (dari rata-rata menjadi penjumlahan) -- โœ… Fixed: Crash saat inisialisasi HX711 (skip tare di setup, tare saat start pengeringan) -- โœ… Fixed: Status relay tidak update di aplikasi Flutter - -### ๐Ÿ“Š Format Data MQTT (ESP32 โ†’ Backend) -```json -{ - "suhu": 55.5, - "berat": 850, - "target": 600, - "relay1": true, // Heater 1 - "relay2": true, // Heater 2 - "relay3": true, // Fan - "relay4": false // Exhaust (ON jika suhu >= 60ยฐC) -} -``` - -### ๐ŸŽฏ Alur Pengeringan Baru -1. Ready to Dry โ†’ Tekan Button -2. Calibrating (Tare Load Cell) -3. Pengeringan Berjalan (Heater 1, 2, Fan ON | Exhaust ON jika suhu >= 60ยฐC) -4. Target Tercapai โ†’ "PENGERINGAN SELESAI" (5 detik) -5. Cooling Down dengan Exhaust ON (20 detik dengan countdown) -6. Auto Reset โ†’ Kembali ke "Ready to Dry" - ---- - -## Version 1.0.0 (Build 1) - 2024-XX-XX - -### ๐ŸŽ‰ Rilis Pertama -- โœ… Dashboard monitoring suhu dan berat real-time -- โœ… Grafik history suhu dan berat -- โœ… Kontrol relay (Heater, Fan, Exhaust) via MQTT -- โœ… Notifikasi pengeringan selesai -- โœ… Koneksi ke backend Railway dengan MySQL database -- โœ… Support MQTT broker HiveMQ - ---- - -## ๐Ÿ”ฎ Roadmap (Coming Soon) -- [ ] Push notification ke smartphone -- [ ] Export data ke Excel/CSV -- [ ] Multi-user dengan login -- [ ] Dashboard web version -- [ ] Schedule pengeringan otomatis diff --git a/CHANGELOG_NOTIFIKASI.md b/CHANGELOG_NOTIFIKASI.md new file mode 100644 index 0000000..06a4b6f --- /dev/null +++ b/CHANGELOG_NOTIFIKASI.md @@ -0,0 +1,127 @@ +# Changelog - Perbaikan Status dan Notifikasi + +## Tanggal: 25 Juni 2026 + +### Masalah yang Diperbaiki + +1. **Status Pengeringan Tetap Berjalan Meskipun Alat Terputus** + - Sebelumnya: Aplikasi tidak mendeteksi ketika ESP32 terputus saat pengeringan berlangsung + - Sekarang: Sistem mendeteksi timeout (10 detik tanpa data) dan memberikan warning + +2. **Notifikasi Terlalu Banyak** + - Sebelumnya: Notifikasi muncul untuk setiap perubahan status (dimulai, ikan terdeteksi, berjalan, selesai) + - Sekarang: Notifikasi hanya muncul untuk **Pengeringan Selesai** + +### Perubahan Detail + +#### 1. Dashboard Screen (dashboard_screen.dart) + +**Deteksi Timeout:** +- Menambahkan variabel `_lastDataReceived` untuk tracking waktu data terakhir +- Menambahkan `_timeoutCheckTimer` yang mengecek setiap 2 detik +- Jika tidak ada data > 10 detik saat pengeringan: + - Menampilkan pesan "Koneksi ke alat terputus" + - Relay UI dimatikan + - Status pengeringan tetap dipertahankan untuk konsistensi + +**Reset Status:** +- Ketika menerima status `READY` atau `CONNECTED`: + - Reset `modePengeringan` = false + - Reset `pengeringanSelesai` = false + - Reset `beratTersimpan` = false + +**Notifikasi:** +- Menghapus notifikasi untuk status `START` (pengeringan dimulai) +- Menghapus notifikasi untuk status `BERJALAN` (ikan terdeteksi) +- **Mempertahankan** notifikasi untuk status `SELESAI` (pengeringan selesai) + +#### 2. Dashboard Screen With API (dashboard_screen_with_api.dart) + +**Notifikasi:** +- Menghapus notifikasi untuk status `READY` (pengeringan dimulai) +- Menghapus notifikasi untuk status `BERJALAN` (ikan terdeteksi) +- **Mempertahankan** notifikasi untuk status `SELESAI` (pengeringan selesai) + +#### 3. Notification Service (notification_service.dart) + +**Method yang Dihapus:** +- `notifyPengeringanDimulai()` - pengeringan dimulai +- `notifyIkanTerdeteksi(double berat)` - ikan terdeteksi +- `notifyPengeringanBerjalan(double berat, double target)` - pengeringan berjalan + +**Method yang Dipertahankan:** +- `notifyPengeringanSelesai(double beratAkhir)` - pengeringan selesai โœ… +- `notifySuhuTinggi(double suhu)` - peringatan suhu tinggi +- `notifyKoneksiTerputus()` - koneksi terputus +- `notifyKoneksiTersambung()` - koneksi tersambung + +#### 4. Notification Manager (notification_manager.dart) + +**Method yang Dihapus:** +- `notifyPengeringanDimulai()` - pengeringan dimulai +- `notifyIkanTerdeteksi(double berat)` - ikan terdeteksi +- `notifyPengeringanBerjalan(double berat, double target)` - pengeringan berjalan + +**Method yang Dipertahankan:** +- `notifyPengeringanSelesai(double beratAkhir)` - pengeringan selesai โœ… + +### Cara Kerja Deteksi Timeout + +```dart +// Timer berjalan setiap 2 detik +_timeoutCheckTimer = Timer.periodic(const Duration(seconds: 2), (timer) { + // Cek apakah sudah > 10 detik tanpa data + if (_lastDataReceived != null && + DateTime.now().difference(_lastDataReceived!) > Duration(seconds: 10) && + modePengeringan) { + + // Tampilkan warning + lastStatusMessage = 'Koneksi ke alat terputus - Status mungkin tidak akurat'; + + // Matikan relay di UI + heater = false; + fanIn = false; + lamp = false; + exhaust = false; + + // Status pengeringan tetap dipertahankan + } +}); +``` + +### Testing + +**Skenario 1: Alat Terputus Saat Pengeringan** +1. Mulai pengeringan +2. Cabut koneksi WiFi ESP32 atau matikan alat +3. โœ… Setelah 10 detik, aplikasi menampilkan warning +4. โœ… Relay di UI otomatis OFF +5. โœ… Status pengeringan tetap ditampilkan + +**Skenario 2: Notifikasi** +1. Mulai pengeringan +2. โŒ TIDAK ada notifikasi "Pengeringan Dimulai" +3. Ikan terdeteksi +4. โŒ TIDAK ada notifikasi "Ikan Terdeteksi" +5. Pengeringan selesai +6. โœ… Muncul notifikasi "Pengeringan Selesai" + +### Catatan + +- Timeout 10 detik dipilih karena ESP32 mengirim data setiap 1 detik +- 10 detik memberikan buffer yang cukup untuk network latency +- Status pengeringan tidak direset otomatis untuk menghindari kehilangan informasi +- User harus manual reset dengan tombol RESET setelah koneksi pulih + +### Backward Compatibility + +Perubahan ini **tidak mempengaruhi**: +- Backend server +- ESP32 firmware +- MQTT communication +- Database structure +- API endpoints + +Perubahan **hanya di aplikasi Flutter** untuk: +- Deteksi timeout +- Filtering notifikasi diff --git a/DOKUMENTASI.md b/DOKUMENTASI.md deleted file mode 100644 index 038bf60..0000000 --- a/DOKUMENTASI.md +++ /dev/null @@ -1,114 +0,0 @@ -# ๐Ÿ“š Dokumentasi Aplikasi Pengering Ikan - -## ๐Ÿ“„ File Dokumentasi - -### 1. **README.md** (Dokumentasi Utama) -**Isi:** -- Overview aplikasi -- Arsitektur sistem -- Quick start guide -- Troubleshooting -- Tech stack - -**Untuk:** Developer & user baru - ---- - -### 2. **APK_MONITORING_FINAL.md** (Panduan Aplikasi) -**Isi:** -- Fitur aplikasi lengkap -- Cara install & test -- Penjelasan UI -- Notifikasi system -- Testing checklist - -**Untuk:** User yang install APK - ---- - -### 3. **PERBAIKAN_APK_FINAL.md** (Changelog) -**Isi:** -- Masalah yang diperbaiki -- Perubahan fitur -- Update history -- Technical details - -**Untuk:** Developer & maintenance - ---- - -### 4. **PERBAIKAN_STATUS_MQTT.md** (API Only) -**Isi:** -- Penjelasan kenapa MQTT dihapus -- Cara kerja API polling -- Status icon -- Troubleshooting koneksi - -**Untuk:** User yang bertanya tentang MQTT - ---- - -### 5. **backend/API_DOCUMENTATION.md** (API Reference) -**Isi:** -- Semua endpoint API -- Request/response format -- Error handling -- Testing dengan curl/Postman - -**Untuk:** Developer backend & integration - ---- - -## ๐Ÿ—‚๏ธ Struktur Folder - -``` -aplikasi_novil/ -โ”œโ”€โ”€ README.md โ† Baca ini dulu! -โ”œโ”€โ”€ APK_MONITORING_FINAL.md โ† Panduan install APK -โ”œโ”€โ”€ PERBAIKAN_APK_FINAL.md โ† Changelog -โ”œโ”€โ”€ PERBAIKAN_STATUS_MQTT.md โ† Penjelasan API only -โ”œโ”€โ”€ DOKUMENTASI.md โ† File ini -โ”‚ -โ”œโ”€โ”€ backend/ -โ”‚ โ”œโ”€โ”€ API_DOCUMENTATION.md โ† API reference -โ”‚ โ”œโ”€โ”€ server.js โ† Backend code -โ”‚ โ”œโ”€โ”€ database.js โ† Database config -โ”‚ โ””โ”€โ”€ ... -โ”‚ -โ”œโ”€โ”€ lib/ โ† Flutter app code -โ”‚ โ”œโ”€โ”€ screens/ โ† UI screens -โ”‚ โ”œโ”€โ”€ services/ โ† API & notification -โ”‚ โ””โ”€โ”€ models/ โ† Data models -โ”‚ -โ”œโ”€โ”€ esp32_pengering_ikan_railway.ino โ† ESP32 code -โ””โ”€โ”€ build-with-icon.ps1 โ† Build script -``` - ---- - -## ๐Ÿš€ Quick Links - -### Untuk User: -1. **Install APK** โ†’ Baca `APK_MONITORING_FINAL.md` -2. **Masalah koneksi** โ†’ Baca `PERBAIKAN_STATUS_MQTT.md` -3. **Overview** โ†’ Baca `README.md` - -### Untuk Developer: -1. **Setup project** โ†’ Baca `README.md` -2. **API integration** โ†’ Baca `backend/API_DOCUMENTATION.md` -3. **Changelog** โ†’ Baca `PERBAIKAN_APK_FINAL.md` - ---- - -## ๐Ÿ“ Catatan - -- Semua file MD lama sudah dihapus (36 files) -- Hanya tersisa 5 file penting -- Dokumentasi lebih rapi dan terorganisir -- Mudah dicari dan dipahami - ---- - -**Last Updated:** 1 Juni 2026 -**Total Docs:** 5 files -**Status:** โœ… Clean & Organized diff --git a/GRAFIK_CHART_GUIDE.md b/GRAFIK_CHART_GUIDE.md new file mode 100644 index 0000000..f4830b5 --- /dev/null +++ b/GRAFIK_CHART_GUIDE.md @@ -0,0 +1,244 @@ +# ๐Ÿ“Š Panduan Fitur Grafik Chart - Aplikasi Pengering Ikan + +## ๐ŸŽฏ Fitur yang Ditambahkan + +Aplikasi sekarang memiliki **sistem grafik chart yang lengkap** untuk monitoring data pengeringan ikan secara visual dan detail. + +--- + +## โœจ Komponen Grafik + +### 1. **Dashboard - Grafik Real-time** ๐Ÿ“ˆ +Tampilan utama dengan 2 grafik real-time yang menampilkan: + +#### **Grafik Suhu** +- Menampilkan perubahan suhu secara real-time +- Warna: Orange/Red gradient +- Range: 20-60ยฐC +- Update setiap detik saat menerima data dari ESP32 +- Menampilkan 20 data terakhir dengan scrolling otomatis + +#### **Grafik Berat** +- Menampilkan penurunan berat ikan secara real-time +- Warna: Blue/Cyan gradient +- Menampilkan garis target berat (hijau putus-putus) +- Tooltip menampilkan sisa berat menuju target +- Update setiap detik saat menerima data dari ESP32 + +**Fitur Grafik Dashboard:** +- ๐Ÿ”ด Live indicator - menandakan data real-time +- ๐Ÿ“ Dot marker pada titik terakhir +- ๐Ÿ’ง Gradient fill di bawah garis +- ๐Ÿ“ Grid lines untuk kemudahan pembacaan +- ๐ŸŽฏ Target line pada grafik berat +- ๐Ÿ‘† Touch tooltip - tap grafik untuk detail + +--- + +### 2. **Chart Screen - History Detail** ๐Ÿ“Š + +Layar khusus untuk melihat data history dari database dengan detail lengkap. + +#### **Cara Akses:** +Klik tab **"Grafik"** di bottom navigation (icon bar_chart) + +#### **Grafik yang Tersedia:** + +**A. Grafik Suhu History** +- Menampilkan history suhu dari database +- Timeline berupa index data (#1, #2, dst) +- Tooltip menampilkan suhu + timestamp +- Smooth curve line + +**B. Grafik Berat History** +- Menampilkan history berat dari database +- Garis target berat horizontal +- Tooltip menampilkan berat, target, dan timestamp +- Color coding untuk titik di atas/bawah target + +**C. Statistik Card** +Menampilkan analisis data: +- ๐Ÿ“Š Rata-rata Suhu +- โš–๏ธ Rata-rata Berat +- ๐Ÿ”บ Max Suhu +- ๐Ÿ”ป Min Suhu +- ๐Ÿ”บ Max Berat +- ๐Ÿ”ป Min Berat + +#### **Filter Data:** +Tekan icon filter (โ‹ฎ) di kanan atas untuk memilih: +- 50 Data Terakhir +- 100 Data Terakhir +- 200 Data Terakhir + +#### **Refresh Data:** +- Pull to refresh (tarik ke bawah) +- Atau tekan icon refresh di info card + +--- + +## ๐ŸŽจ Visual Features + +### **Color Scheme:** +- **Suhu:** Orange โ†’ Red (panas) +- **Berat:** Blue โ†’ Cyan (sejuk) +- **Target:** Green (tujuan) +- **Live:** Orange dot (status real-time) + +### **Gradient Effects:** +- Semua grafik menggunakan gradient fill +- Memberikan efek depth dan modern +- Smooth transitions + +### **Interactive Elements:** +- Touch tooltip - tap grafik untuk detail +- Smooth animations +- Responsive layout + +--- + +## ๐Ÿ”ง Cara Kerja + +### **Dashboard Real-time Chart:** +1. Data diterima dari ESP32 via MQTT setiap 1 detik +2. Data disimpan ke list `suhuHistory` dan `beratHistory` +3. List maksimal 20 data (auto scroll/shift) +4. Widget `TemperatureChart` dan `WeightChart` render data +5. Chart update otomatis setiap ada data baru + +### **Chart Screen History:** +1. Load data dari API endpoint `/api/data/history` +2. Parse data menjadi `List` +3. Convert ke `List` untuk grafik +4. Hitung statistik (avg, min, max) +5. Render dengan `fl_chart` LineChart + +--- + +## ๐Ÿ“ฑ User Experience + +### **Dashboard:** +- Scroll ke bawah untuk melihat grafik +- Grafik selalu update real-time saat pengeringan berjalan +- Compact layout untuk quick glance + +### **Chart Screen:** +- Full-screen view untuk analisis detail +- Filter untuk berbagai range data +- Statistik summary untuk insights +- Refresh untuk update data terbaru + +--- + +## ๐Ÿ› ๏ธ Teknologi + +### **Package:** +- `fl_chart: ^0.69.0` - Chart library Flutter terbaik +- Smooth animations +- Touch interactions +- Customizable + +### **Widget Structure:** + +``` +lib/ +โ”œโ”€โ”€ screens/ +โ”‚ โ”œโ”€โ”€ dashboard_screen.dart (Main screen dengan real-time charts) +โ”‚ โ””โ”€โ”€ chart_screen.dart (History charts screen) +โ””โ”€โ”€ widgets/ + โ”œโ”€โ”€ temperature_chart.dart (Reusable temperature chart widget) + โ””โ”€โ”€ weight_chart.dart (Reusable weight chart widget) +``` + +--- + +## ๐Ÿ“Š Data Flow + +### **Real-time:** +``` +ESP32 โ†’ MQTT โ†’ MqttService โ†’ DashboardScreen + โ†“ + suhuHistory/beratHistory + โ†“ + TemperatureChart/WeightChart +``` + +### **History:** +``` +Backend MySQL โ†’ API โ†’ ApiService โ†’ ChartScreen + โ†“ + List + โ†“ + fl_chart LineChart +``` + +--- + +## ๐ŸŽฏ Tips Penggunaan + +1. **Monitoring Real-time:** + - Lihat dashboard untuk monitoring cepat + - Grafik akan auto-scroll saat data penuh + - Tap grafik untuk detail nilai spesifik + +2. **Analisis History:** + - Buka Chart Screen untuk analisis mendalam + - Gunakan filter untuk berbagai rentang waktu + - Perhatikan statistik untuk insights + +3. **Performance:** + - Dashboard limit 20 data untuk performa optimal + - Chart Screen bisa load hingga 200 data + - Refresh jika data tidak update + +--- + +## ๐Ÿš€ Future Improvements + +Beberapa ide untuk pengembangan: + +1. **Export Data:** + - Export grafik sebagai image + - Export data ke CSV/Excel + +2. **Advanced Analytics:** + - Trend analysis + - Prediksi waktu selesai + - Efisiensi pengeringan + +3. **Comparison:** + - Bandingkan multiple sessions + - Overlay grafik + +4. **Zoom & Pan:** + - Zoom in/out grafik + - Pan untuk navigasi + +--- + +## โœ… Testing Checklist + +- [x] Grafik suhu tampil real-time +- [x] Grafik berat tampil real-time +- [x] Target line muncul di grafik berat +- [x] Tooltip berfungsi saat tap +- [x] Chart screen dapat load history +- [x] Filter data berfungsi +- [x] Statistik dihitung dengan benar +- [x] Refresh data berfungsi +- [x] Layout responsive +- [x] No memory leaks + +--- + +## ๐Ÿ“ž Support + +Jika ada pertanyaan atau bug: +1. Check console log untuk error messages +2. Pastikan backend API running +3. Pastikan MQTT connection aktif +4. Verify data di database + +--- + +**Selamat menggunakan fitur Grafik Chart! ๐ŸŽ‰** diff --git a/NOTIFICATION_FIX_README.md b/NOTIFICATION_FIX_README.md new file mode 100644 index 0000000..142c6b2 --- /dev/null +++ b/NOTIFICATION_FIX_README.md @@ -0,0 +1,161 @@ +# Fix Notifikasi untuk Release Build + +## Masalah +Notifikasi tidak muncul pada aplikasi release (APK yang sudah di-install), padahal berfungsi normal saat debug. + +## Penyebab +1. **ProGuard/R8 Code Shrinking**: Pada release build, Android secara otomatis menghapus kode yang dianggap tidak digunakan, termasuk komponen notifikasi plugin. +2. **Minify Enabled**: Setting minifyEnabled di build.gradle menyebabkan code shrinking aktif. + +## Solusi yang Diterapkan + +### 1. File `proguard-rules.pro` (BARU) +File ini berisi rules untuk mencegah ProGuard menghapus kode notifikasi: +- Keep Flutter plugin classes +- Keep flutter_local_notifications classes +- Keep Android notification components +- Keep MQTT client classes + +### 2. Update `build.gradle.kts` +Menambahkan konfigurasi: +```kotlin +isMinifyEnabled = false // Disable code shrinking +isShrinkResources = false // Disable resource shrinking +proguardFiles(...) // ProGuard rules (untuk jika diaktifkan nanti) +``` + +### 3. Update `MainActivity.kt` +Menambahkan pembuatan notification channel saat aplikasi start: +- Membuat channel "pengering_ikan_channel" +- Set importance ke HIGH +- Enable vibration dan lights + +## Cara Build Ulang Aplikasi + +### Option 1: Build APK (Tanpa minify - Recommended) +```bash +flutter clean +flutter pub get +flutter build apk --release +``` +APK akan ada di: `build/app/outputs/flutter-apk/app-release.apk` + +### Option 2: Build APK (Dengan minify + ProGuard) +Jika ingin APK lebih kecil, edit `build.gradle.kts`: +```kotlin +isMinifyEnabled = true +isShrinkResources = true +``` +Lalu build: +```bash +flutter clean +flutter pub get +flutter build apk --release +``` + +### Option 3: Build App Bundle (untuk Google Play) +```bash +flutter clean +flutter pub get +flutter build appbundle --release +``` +AAB akan ada di: `build/app/outputs/bundle/release/app-release.aab` + +## Testing Notifikasi + +### 1. Uninstall aplikasi lama +```bash +adb uninstall com.example.aplikasi_novil +``` + +### 2. Install APK baru +```bash +adb install build/app/outputs/flutter-apk/app-release.apk +``` + +### 3. Test notifikasi +- Buka aplikasi +- Pastikan permission notifikasi sudah di-allow (Android 13+) +- Jalankan proses pengeringan +- Tunggu hingga selesai +- Notifikasi harus muncul + +### 4. Check via Logcat +```bash +adb logcat | grep -i notification +``` + +## Verifikasi Permission + +### Check di Settings Android: +1. Buka **Settings > Apps > Pengering Ikan** +2. Pilih **Notifications** +3. Pastikan notifikasi **ENABLED** +4. Check channel "Pengering Ikan" harus ada + +### Check via ADB: +```bash +adb shell dumpsys notification_listener +adb shell cmd notification list +``` + +## Troubleshooting + +### Notifikasi masih tidak muncul? + +1. **Check permission di runtime:** + - Aplikasi harus request permission POST_NOTIFICATIONS untuk Android 13+ + - Sudah di-handle di `NotificationService.initialize()` + +2. **Check notification channel:** + - Channel harus dibuat di native (MainActivity.kt) โœ… + - Channel ID harus sama dengan yang di Dart โœ… + +3. **Check battery optimization:** + - Buka Settings > Battery > Battery Optimization + - Set aplikasi ke "Don't optimize" + +4. **Check Do Not Disturb:** + - Pastikan DND mode tidak aktif + - Atau allow aplikasi bypass DND + +5. **Test dengan notification sederhana:** + Tambahkan button test notifikasi di UI untuk memastikan sistem notifikasi bekerja. + +## Files yang Diubah/Ditambahkan + +### Baru: +- โœ… `android/app/proguard-rules.pro` + +### Diubah: +- โœ… `android/app/build.gradle.kts` +- โœ… `android/app/src/main/kotlin/com/example/aplikasi_novil/MainActivity.kt` + +### Tidak berubah (sudah benar): +- โœ… `android/app/src/main/AndroidManifest.xml` (permission sudah lengkap) +- โœ… `lib/services/notification_service.dart` (channel ID sudah benar) + +## Catatan Penting + +1. **Setiap kali build release**, pastikan: + - `flutter clean` dulu + - `flutter pub get` + - Uninstall APK lama sebelum install yang baru + +2. **minifyEnabled = false** akan membuat APK lebih besar (~10-20MB lebih besar) + - Tapi notifikasi dijamin bekerja + - Jika ingin APK lebih kecil, gunakan minify + ProGuard rules + +3. **Testing harus di device fisik**, bukan emulator + - Emulator kadang tidak akurat untuk notifikasi + +4. **Versi Android yang didukung:** + - Android 5.0+ (API 21+) untuk notifikasi dasar + - Android 8.0+ (API 26+) untuk notification channels + - Android 13+ (API 33+) perlu permission POST_NOTIFICATIONS + +## Kontak +Jika masih ada masalah, check: +1. Logcat untuk error messages +2. Notification settings di Android +3. Battery optimization settings diff --git a/PERBAIKAN_APK_FINAL.md b/PERBAIKAN_APK_FINAL.md deleted file mode 100644 index 3dbe280..0000000 --- a/PERBAIKAN_APK_FINAL.md +++ /dev/null @@ -1,296 +0,0 @@ -# ๐Ÿ”ง Perbaikan APK - Pengering Ikan (FINAL) - -## Tanggal: 1 Juni 2026 - ---- - -## โœ… MASALAH TERPECAHKAN - -### 1. **Loading Stuck saat Refresh** ๐Ÿ”„ -- โœ… Tambah timeout 10 detik -- โœ… Error handling yang proper -- โœ… Loading pasti berhenti - -### 2. **Timer Durasi Stuck** โฑ๏ธ -- โœ… Timer jalan smooth setiap detik -- โœ… Inisialisasi waktu mulai otomatis -- โœ… Tidak tergantung status sistem - -### 3. **Data Tidak Update** ๐Ÿ“Š โญ BARU! -- โœ… Ganti ke dashboard dengan API -- โœ… Data langsung dari Railway backend -- โœ… Real-time update via MQTT -- โœ… Fallback ke multiple MQTT brokers - ---- - -## ๐Ÿ”„ Perubahan Utama - -### Dashboard Baru: API + MQTT Hybrid - -**Sebelumnya:** -- Hanya MQTT (tidak reliable) -- Data sering tidak update -- Tergantung koneksi MQTT saja - -**Sekarang:** -- **API Railway** untuk data awal -- **MQTT** untuk real-time updates -- **Hybrid approach** = lebih reliable! - -### File yang Diubah: - -1. **lib/screens/splash_screen.dart** - ```dart - // Ganti dari dashboard_screen.dart - import 'dashboard_screen_with_api.dart'; - - // Navigasi ke DashboardScreenWithApi - const DashboardScreenWithApi() - ``` - -2. **pubspec.yaml** - ```yaml - # Tambah HTTP package - http: ^1.1.0 - ``` - -3. **lib/services/api_service.dart** - - URL: `https://web-production-47eb.up.railway.app` - - Endpoint: `/api/data/latest` - - Timeout: 10 detik - -4. **lib/services/mqtt_service.dart** - - Multiple brokers fallback - - Auto reconnect - - Better error handling - ---- - -## ๐Ÿ“ก Cara Kerja Baru - -### Saat Aplikasi Dibuka: -1. **Test API Connection** โ†’ Cek Railway backend -2. **Load Latest Data** โ†’ Ambil data terakhir dari database -3. **Connect MQTT** โ†’ Subscribe untuk real-time updates -4. **Listen Streams** โ†’ Update UI otomatis - -### Saat Pull-to-Refresh: -1. **Fetch dari API** โ†’ Data terbaru dari database -2. **Update UI** โ†’ Tampilkan data baru -3. **Max 10 detik** โ†’ Timeout jika lambat - -### Real-time Updates: -- ESP32 publish โ†’ MQTT broker -- Backend subscribe โ†’ Save ke database -- App subscribe โ†’ Update UI langsung - ---- - -## ๐Ÿงช Data Test dari Backend - -**Backend Status:** โœ… AKTIF -```json -{ - "status": "OK", - "message": "Pengering Ikan Backend Server - MQTT Client Enabled", - "version": "1.0.1", - "uptime": 248399 seconds -} -``` - -**Latest Data:** โœ… ADA DATA -```json -{ - "suhu": 25.9, - "berat": 2183, - "target": 628, - "relay1": false, - "relay2": false, - "relay3": false, - "relay4": false, - "status": "SCANNING", - "timestamp": "2026-06-01T02:47:58.176Z" -} -``` - ---- - -## ๐Ÿ“ฑ APK Info - -- **Nama:** PengeringIkan.apk -- **Lokasi:** Desktop -- **Ukuran:** 20.69 MB -- **Build:** Release -- **Version:** 1.0.0 - -### Fitur: -- โœ… Data dari Railway API -- โœ… Real-time MQTT updates -- โœ… Auto refresh -- โœ… Timer durasi jalan -- โœ… Loading tidak stuck -- โœ… Error handling -- โœ… Connection indicators (WiFi + Cloud icons) - ---- - -## ๐Ÿš€ Cara Install & Test - -### 1. Uninstall APK Lama -``` -Settings โ†’ Apps โ†’ Pengering Ikan โ†’ Uninstall -``` - -### 2. Install APK Baru -``` -File Manager โ†’ Desktop โ†’ PengeringIkan.apk โ†’ Install -``` - -### 3. Test Koneksi -- Buka aplikasi -- Lihat icon di kanan atas: - - **WiFi icon hijau** = MQTT connected - - **Cloud icon hijau** = API connected - - **Icon merah** = Disconnected - -### 4. Test Data Update -- Pull-to-refresh (swipe down) -- Data harus update dalam 10 detik -- Cek suhu, berat, status - -### 5. Test Timer -- Timer durasi harus jalan setiap detik -- Format: HH:MM:SS (00:00:01, 00:00:02, dst) - ---- - -## ๐Ÿ” Troubleshooting - -### Data Tidak Update? - -**Cek 1: Connection Icons** -- WiFi icon merah? โ†’ MQTT gagal connect -- Cloud icon merah? โ†’ API gagal connect -- Kedua merah? โ†’ Cek internet HP - -**Cek 2: Pull-to-Refresh** -- Swipe down untuk refresh manual -- Loading harus muncul dan berhenti -- Data harus berubah - -**Cek 3: Backend Status** -- Buka browser di HP -- Akses: `https://web-production-47eb.up.railway.app/` -- Harus return JSON dengan status OK - -**Cek 4: ESP32 Status** -- ESP32 harus nyala dan connect WiFi -- ESP32 harus publish data ke MQTT -- Cek serial monitor ESP32 - -### Loading Stuck? -- Max 10 detik, pasti berhenti -- Jika lebih dari 10 detik, restart app -- Cek koneksi internet - -### Timer Tidak Jalan? -- Restart aplikasi -- Timer harus jalan sejak app dibuka -- Tidak tergantung status pengeringan - ---- - -## ๐Ÿ“Š Monitoring - -### Indikator Koneksi (Kanan Atas): -- ๐ŸŸข **WiFi hijau** = MQTT OK -- ๐Ÿ”ด **WiFi merah** = MQTT gagal -- ๐ŸŸข **Cloud hijau** = API OK -- ๐Ÿ”ด **Cloud merah** = API gagal - -### Status Sistem: -- **CONNECTING** = Sedang connect -- **READY** = Siap, tunggu ikan -- **SCANNING** = Deteksi berat ikan -- **BERJALAN** = Proses pengeringan -- **SELESAI** = Pengeringan selesai -- **DISCONNECTED** = Tidak terkoneksi - ---- - -## ๐ŸŽฏ Checklist Testing - -- [ ] Install APK baru -- [ ] Buka aplikasi -- [ ] Cek connection icons (hijau semua) -- [ ] Cek timer jalan (detik bergerak) -- [ ] Cek data (suhu, berat, status) -- [ ] Pull-to-refresh (loading berhenti) -- [ ] Refresh berkali-kali (tidak stuck) -- [ ] Tunggu 1 menit (data auto update via MQTT) -- [ ] Test dengan ESP32 aktif - ---- - -## ๐Ÿ“ Technical Details - -### API Endpoints Used: -- `GET /` - Health check -- `GET /api/data/latest` - Latest sensor data -- `GET /api/data/history` - Historical data -- `GET /api/status/history` - Status history -- `GET /api/stats` - Statistics -- `POST /api/control` - Send commands - -### MQTT Topics: -- `novil/pengering/data` - Sensor data -- `novil/pengering/status` - Status messages -- `novil/pengering/control` - Control commands - -### MQTT Brokers (Fallback): -1. broker.hivemq.com:1883 -2. broker.emqx.io:1883 -3. test.mosquitto.org:1883 - ---- - -## ๐Ÿ”„ Update History - -### Version 1.0.0 (1 Juni 2026) -- โœ… Fix loading stuck -- โœ… Fix timer stuck -- โœ… **Fix data tidak update (API integration)** -- โœ… Add connection indicators -- โœ… Add error handling -- โœ… Add timeout protection -- โœ… Hybrid API + MQTT approach - ---- - -## ๐Ÿ’ก Tips - -1. **Koneksi Internet Stabil** - - Pastikan HP connect WiFi/data - - Backend Railway butuh internet - -2. **ESP32 Harus Aktif** - - ESP32 publish data ke MQTT - - Backend subscribe dan save ke database - - App ambil dari database + MQTT - -3. **Refresh Manual** - - Jika data lama, pull-to-refresh - - Data langsung dari database - -4. **Monitor Logs** - - Jika ada masalah, cek logs di console - - Error akan terprint dengan emoji โŒ - ---- - -**Build Date:** 1 Juni 2026 -**Version:** 1.0.0 (API Integrated) -**Status:** โœ… PRODUCTION READY - -๐ŸŽ‰ **Semua masalah sudah diperbaiki!** diff --git a/PERBAIKAN_STATUS_MQTT.md b/PERBAIKAN_STATUS_MQTT.md deleted file mode 100644 index 2e89fad..0000000 --- a/PERBAIKAN_STATUS_MQTT.md +++ /dev/null @@ -1,235 +0,0 @@ -# ๐Ÿ”ง Perbaikan Status MQTT - API Only - -## Masalah yang Diperbaiki - -### โŒ Masalah Sebelumnya: -- Status stuck di "Menghubungkan..." -- Icon WiFi merah terus (MQTT gagal connect) -- Membingungkan user padahal data sudah update -- MQTT tidak diperlukan untuk monitoring - -### โœ… Solusi: -- **MQTT dihapus** - tidak digunakan lagi -- **Icon WiFi dihapus** - tidak membingungkan -- **Hanya API** - polling setiap 5 detik -- **Status langsung READY** - tidak stuck - ---- - -## ๐Ÿ“ฑ Tampilan Baru - -### AppBar (Kanan Atas): -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Dashboard Pengering Ikan โ˜๏ธ๐ŸŸข โ”‚ โ† Hanya 1 icon -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**Sebelumnya:** ๐Ÿ“ถ๐Ÿ”ด โ˜๏ธ๐ŸŸข (2 icon, WiFi merah membingungkan) -**Sekarang:** โ˜๏ธ๐ŸŸข (1 icon, jelas API connected) - ---- - -## โ˜๏ธ Status Icon - -### Icon Cloud (API Status): -- ๐ŸŸข **Hijau** = Terhubung ke Railway API -- ๐Ÿ”ด **Merah** = Tidak terhubung ke API - -**Artinya:** -- Hijau = Data update setiap 5 detik dari database -- Merah = Tidak bisa ambil data, cek internet - ---- - -## ๐Ÿ”„ Cara Kerja Baru - -### Sistem Sederhana: -``` -ESP32 (Hardware) - โ†“ - โ†“ publish ke MQTT - โ†“ -Backend Railway - โ†“ - โ†“ save to database - โ†“ -Database MySQL - โ†‘ - โ†‘ fetch via API (polling 5 detik) - โ†‘ -Aplikasi Flutter -``` - -### Update Data: -1. **Setiap 5 detik** โ†’ Fetch data dari API -2. **Update UI** โ†’ Tampilkan data terbaru -3. **Cek status** โ†’ Trigger notifikasi jika berubah - -**Tidak ada MQTT di aplikasi!** - ---- - -## ๐Ÿ“Š Status Sistem - -### Saat Buka Aplikasi: -``` -DISCONNECTED (loading...) - โ†“ -Test API connection - โ†“ -โœ… API Connected - โ†“ -READY (Terhubung ke server) - โ†“ -Load data pertama kali - โ†“ -Mulai polling setiap 5 detik -``` - -### Status yang Muncul: -- **READY** = Siap, menunggu data -- **SCANNING** = Mendeteksi ikan -- **BERJALAN** = Proses pengeringan -- **SELESAI** = Pengeringan selesai -- **DISCONNECTED** = API gagal (cek internet) - -**Tidak ada "CONNECTING" atau "Menghubungkan..." lagi!** - ---- - -## โœ… Keuntungan API Only - -### 1. **Lebih Sederhana** -- Tidak perlu MQTT broker -- Tidak perlu handle MQTT connection -- Tidak ada icon WiFi yang membingungkan - -### 2. **Lebih Reliable** -- API Railway stabil -- Database sebagai single source of truth -- Tidak tergantung MQTT broker publik - -### 3. **Lebih Jelas** -- 1 icon = 1 status -- Cloud hijau = semua OK -- Cloud merah = cek internet - -### 4. **Tetap Real-time** -- Update setiap 5 detik -- Cukup cepat untuk monitoring -- Notifikasi tetap instant - ---- - -## ๐Ÿงช Testing - -### Cek Status Icon: -1. Buka aplikasi -2. Lihat icon kanan atas -3. **Harus cloud hijau** โ˜๏ธ๐ŸŸข -4. Jika merah, cek internet HP - -### Cek Data Update: -1. Lihat timestamp data -2. Tunggu 5 detik -3. Data harus berubah -4. Status harus update - -### Cek Notifikasi: -1. Ubah status di ESP32 -2. Tunggu max 5 detik -3. Notifikasi harus muncul -4. Status di app harus berubah - ---- - -## ๐Ÿ” Troubleshooting - -### Icon Cloud Merah? -**Penyebab:** -- HP tidak connect internet -- Railway backend down -- Firewall block HTTPS - -**Solusi:** -- Cek WiFi/data HP -- Test di browser: https://web-production-47eb.up.railway.app/ -- Restart aplikasi - -### Data Tidak Update? -**Penyebab:** -- Icon cloud merah -- ESP32 tidak kirim data -- Backend tidak save data - -**Solusi:** -- Pastikan icon cloud hijau -- Cek ESP32 status -- Cek backend logs - -### Status Stuck? -**Tidak akan terjadi lagi!** -- Status langsung READY saat API connected -- Tidak ada proses "Menghubungkan..." MQTT -- Jika stuck, restart aplikasi - ---- - -## ๐Ÿ“ Technical Details - -### Removed: -- โŒ MQTT client connection -- โŒ MQTT service instance -- โŒ MQTT stream listeners -- โŒ WiFi icon indicator -- โŒ isConnected variable -- โŒ _connectMQTT() function - -### Kept: -- โœ… API service -- โœ… Polling timer (5 seconds) -- โœ… Notification manager -- โœ… Cloud icon indicator -- โœ… isApiConnected variable -- โœ… _loadLatestData() function - -### Added: -- โœ… Direct status set to READY -- โœ… Cleaner initialization -- โœ… Simpler connection flow - ---- - -## ๐Ÿ“ฆ APK Info - -- **Nama:** PengeringIkan.apk -- **Lokasi:** Desktop -- **Ukuran:** 20.44 MB (lebih kecil!) -- **Mode:** API Only (No MQTT) - ---- - -## ๐ŸŽฏ Kesimpulan - -### Sebelumnya: -- 2 icon (WiFi + Cloud) -- MQTT gagal connect -- Status stuck "Menghubungkan..." -- Membingungkan user - -### Sekarang: -- 1 icon (Cloud saja) -- API polling 5 detik -- Status langsung READY -- Jelas dan sederhana - -**Data tetap update, notifikasi tetap jalan, monitoring tetap berjalan!** โœ… - ---- - -**Build Date:** 1 Juni 2026 -**Version:** 1.0.0 (API Only) -**Status:** โœ… PRODUCTION READY - -๐ŸŽ‰ **Tidak ada lagi status "Menghubungkan..." yang stuck!** diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 24f5c95..0912867 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -18,6 +18,9 @@ android { kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() + + // Suppress obsolete Java version warnings + freeCompilerArgs += listOf("-Xlint:-options") } defaultConfig { @@ -36,6 +39,17 @@ android { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig = signingConfigs.getByName("debug") + + // Disable minify and shrink to prevent notification issues + // Set to true if you want smaller APK size, but make sure ProGuard rules are correct + isMinifyEnabled = false + isShrinkResources = false + + // ProGuard rules (uncomment if you enable minify) + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) } } } diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..d8ac655 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,69 @@ +# Flutter Wrapper +-keep class io.flutter.app.** { *; } +-keep class io.flutter.plugin.** { *; } +-keep class io.flutter.util.** { *; } +-keep class io.flutter.view.** { *; } +-keep class io.flutter.** { *; } +-keep class io.flutter.plugins.** { *; } + +# Flutter Local Notifications +-keep class com.dexterous.** { *; } +-keep class com.dexterous.flutterlocalnotifications.** { *; } +-dontwarn com.dexterous.flutterlocalnotifications.** + +# AndroidX +-keep class androidx.core.app.NotificationCompat { *; } +-keep class androidx.core.app.NotificationCompat$** { *; } +-keep class androidx.core.app.NotificationManagerCompat { *; } + +# Keep notification channels (Android 8.0+) +-keep class android.app.NotificationChannel { *; } +-keep class android.app.NotificationManager { *; } + +# MQTT Client +-keep class org.eclipse.paho.** { *; } +-keep class org.eclipse.paho.client.mqttv3.** { *; } +-dontwarn org.eclipse.paho.** + +# Kotlin +-keep class kotlin.** { *; } +-keep class kotlin.Metadata { *; } +-dontwarn kotlin.** +-keepclassmembers class **$WhenMappings { + ; +} +-keepclassmembers class kotlin.Metadata { + public ; +} + +# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items). +-keep,allowobfuscation,allowshrinking interface retrofit2.Call +-keep,allowobfuscation,allowshrinking class retrofit2.Response + +# With R8 full mode generic signatures are stripped for classes that are not +# kept. Suspend functions are wrapped in continuations where the type argument +# is used. +-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation + +# Keep native methods +-keepclasseswithmembernames class * { + native ; +} + +# Keep setters in Views so that animations can still work. +-keepclassmembers public class * extends android.view.View { + void set*(***); + *** get*(); +} + +# Keep classes with @Keep annotation +-keep @androidx.annotation.Keep class * {*;} +-keepclasseswithmembers class * { + @androidx.annotation.Keep ; +} +-keepclasseswithmembers class * { + @androidx.annotation.Keep ; +} +-keepclasseswithmembers class * { + @androidx.annotation.Keep (...); +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b06d57b..dc34d55 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,10 +3,22 @@ + + + + + + + + + + + + + android:icon="@mipmap/launcher_icon"> + android:windowSoftInputMode="adjustResize" + android:showWhenLocked="true" + android:turnScreenOn="true"> + + + + + + + + + + = Build.VERSION_CODES.O) { + val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager + + // Channel untuk notifikasi pengeringan selesai + val channel = NotificationChannel( + "pengering_ikan_channel", + "Notifikasi Pengering Ikan", + NotificationManager.IMPORTANCE_HIGH + ).apply { + description = "Notifikasi untuk status pengeringan ikan" + enableVibration(true) + enableLights(true) + } + + notificationManager.createNotificationChannel(channel) + } + } +} + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml b/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml new file mode 100644 index 0000000..5f349f7 --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/android/app/src/main/res/mipmap-hdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..116f99e905e5eb357136af0e69fe1971f7eb1ba2 GIT binary patch literal 1628 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 R|5Nbkv~TM{Ufr?_`4@@3-{$}T literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d738d10015b6913e286c0824c66b3d3b215cc435 GIT binary patch literal 3652 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 V9%=}3jh(DR0K&)uUSsI-@V_W>=2idz literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 2ff3651..0d40556 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -1,4 +1,4 @@ - #1976D2 + #2196F3 \ No newline at end of file diff --git a/android/build.gradle.kts b/android/build.gradle.kts index dbee657..0fc51b8 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -14,6 +14,18 @@ rootProject.layout.buildDirectory.value(newBuildDir) subprojects { val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) project.layout.buildDirectory.value(newSubprojectBuildDir) + + // Force all subprojects to use Java 17 + afterEvaluate { + if (project.hasProperty("android")) { + project.extensions.configure("android") { + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + } + } + } } subprojects { project.evaluationDependsOn(":app") diff --git a/android/gradle.properties b/android/gradle.properties index fbee1d8..7942ce8 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,2 +1,16 @@ org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true + +# Suppress obsolete Java warnings +android.suppressUnsupportedCompileSdk=34 +org.gradle.warning.mode=none + +# Force Java 17 for all modules +# org.gradle.java.home= +kotlin.jvm.target.validation.mode=warning + +# Enable R8 full mode (optional, for better optimization) +android.enableR8.fullMode=false + +# Suppress Kotlin compiler warnings +kotlin.compiler.suppressWarnings=false diff --git a/backend/CARA_PUSH.md b/backend/CARA_PUSH.md new file mode 100644 index 0000000..80bed03 --- /dev/null +++ b/backend/CARA_PUSH.md @@ -0,0 +1,112 @@ +# Cara Push Backend ke GitHub + +## ๐Ÿ“Œ **Cara 1: Menggunakan Script PowerShell (RECOMMENDED)** + +### Push dengan pesan custom: +```powershell +cd backend +.\push.ps1 "Fix bug di notifikasi" +``` + +### Push dengan pesan default: +```powershell +cd backend +.\push.ps1 +``` +(Akan menggunakan pesan default: "Update backend") + +--- + +## ๐Ÿ“Œ **Cara 2: Menggunakan Git Alias** + +### Setup (Hanya sekali): +```powershell +cd backend +git config --local alias.quick '!f() { git add -A && git commit -m "$1" && git push origin main; }; f' +``` + +### Penggunaan: +```powershell +cd backend +git quick "Fix bug di notifikasi" +``` + +--- + +## ๐Ÿ“Œ **Cara 3: Manual (Standard Git)** + +```powershell +cd backend +git add -A +git commit -m "Pesan commit Anda" +git push origin main +``` + +--- + +## ๐Ÿ”— **Repository Backend** +- **GitHub**: https://github.com/novil04/backend_aplikasi_novil +- **Railway**: Auto-deploy setelah push (2-5 menit) + +--- + +## โš ๏ธ **Catatan Penting** + +1. **Selalu bekerja di folder backend** + ```powershell + cd c:\Users\abilh\aplikasi_novil\backend + ``` + +2. **Cek status sebelum push** + ```powershell + git status + ``` + +3. **Lihat history commit** + ```powershell + git log --oneline -5 + ``` + +4. **Jangan lupa file .env** + - File `.env` TIDAK akan ter-push (sudah di .gitignore) + - Atur variabel environment langsung di Railway dashboard + +5. **Cek Railway setelah push** + - Buka Railway dashboard + - Tunggu build & deploy selesai + - Cek logs jika ada error + +--- + +## ๐Ÿš€ **Quick Reference** + +| Tujuan | Command | +|--------|---------| +| Push cepat | `.\push.ps1 "pesan"` | +| Cek perubahan | `git status` | +| Lihat log | `git log --oneline -5` | +| Batal commit terakhir | `git reset --soft HEAD~1` | +| Pull update terbaru | `git pull origin main` | + +--- + +## ๐Ÿ†˜ **Troubleshooting** + +### Error: "Push rejected" +```powershell +# Pull dulu, lalu push lagi +git pull origin main +git push origin main +``` + +### Error: "Permission denied" +```powershell +# Pastikan sudah login GitHub +# Atau gunakan Personal Access Token +``` + +### Script tidak jalan +```powershell +# Enable execution policy (run as Administrator) +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` diff --git a/backend/push.ps1 b/backend/push.ps1 new file mode 100644 index 0000000..70a5adb --- /dev/null +++ b/backend/push.ps1 @@ -0,0 +1,42 @@ +# Script untuk push perubahan backend ke GitHub +# Usage: .\push.ps1 "commit message" + +param( + [Parameter(Mandatory=$false)] + [string]$Message = "Update backend" +) + +Write-Host "๐Ÿ”„ Pushing backend to GitHub..." -ForegroundColor Cyan +Write-Host "" + +# Check if there are changes +$status = git status --porcelain +if ([string]::IsNullOrEmpty($status)) { + Write-Host "โœ… No changes to commit" -ForegroundColor Green + exit 0 +} + +# Show changes +Write-Host "๐Ÿ“ Changes detected:" -ForegroundColor Yellow +git status --short + +Write-Host "" +Write-Host "๐Ÿ“ฆ Adding all changes..." -ForegroundColor Cyan +git add -A + +Write-Host "๐Ÿ’พ Committing with message: '$Message'" -ForegroundColor Cyan +git commit -m $Message + +Write-Host "๐Ÿš€ Pushing to GitHub (backend_aplikasi_novil)..." -ForegroundColor Cyan +git push origin main + +if ($LASTEXITCODE -eq 0) { + Write-Host "" + Write-Host "โœ… Successfully pushed to GitHub!" -ForegroundColor Green + Write-Host "๐Ÿ”— Repository: https://github.com/novil04/backend_aplikasi_novil" -ForegroundColor Cyan + Write-Host "โณ Railway will auto-deploy in 2-5 minutes" -ForegroundColor Yellow +} else { + Write-Host "" + Write-Host "โŒ Failed to push to GitHub" -ForegroundColor Red + exit 1 +} diff --git a/backend/server.js b/backend/server.js index 895f2e5..00b2101 100644 --- a/backend/server.js +++ b/backend/server.js @@ -55,8 +55,8 @@ let latestData = { // Last data received timestamp let lastDataTimestamp = Date.now(); -// Connection timeout (30 seconds) -const CONNECTION_TIMEOUT = 30000; +// Connection timeout (5 minutes = 300 seconds) +const CONNECTION_TIMEOUT = 300000; // Check connection status periodically setInterval(() => { diff --git a/create_adaptive_icon.html b/create_adaptive_icon.html new file mode 100644 index 0000000..cbfcb32 --- /dev/null +++ b/create_adaptive_icon.html @@ -0,0 +1,185 @@ + + + + + + Android Adaptive Icon Generator + + + +
+

๐ŸŽจ Android Adaptive Icon Generator

+ +
+ ๐Ÿ“ฑ Android Adaptive Icon Safe Zone:
+ Your icon will be displayed in different shapes (circle, square, squircle).
+ Important content must stay within the red circle (66% area). +
+ + + +
+
+

Original

+ +
+
+

Adaptive Icon (66% size)

+
+ +
+
+
+
+ + + +
+ Next steps after download:
+ 1. Replace assets/images/app_icon.png with downloaded file
+ 2. Run: flutter pub run flutter_launcher_icons
+ 3. Build: flutter build apk --release +
+
+ + + + diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/esp32_pengering_ikan_railway.ino b/esp32_pengering_ikan_railway.ino index 16204b1..03e036a 100644 --- a/esp32_pengering_ikan_railway.ino +++ b/esp32_pengering_ikan_railway.ino @@ -761,15 +761,36 @@ void loop() { // ===================================================== float berat1 = 0; float berat2 = 0; + bool scale1Valid = false; + bool scale2Valid = false; // Cek dan baca scale 1 (style kode test) if (scale1.is_ready()) { berat1 = scale1.get_units(5); + scale1Valid = true; + } else { + Serial.println("WARNING: Scale 1 not ready!"); } // Cek dan baca scale 2 (style kode test) if (scale2.is_ready()) { berat2 = scale2.get_units(5); + scale2Valid = true; + } else { + Serial.println("WARNING: Scale 2 not ready!"); + } + + // Jika kedua scale tidak ready, skip loop ini + if (!scale1Valid && !scale2Valid) { + Serial.println("ERROR: Kedua load cell tidak ready!"); + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print("Scale Error!"); + lcd.setCursor(0, 1); + lcd.print("Check wiring"); + client.publish(topicStatus, "LOAD CELL ERROR"); + delay(2000); + return; } // ===================================================== @@ -806,11 +827,24 @@ void loop() { berat_trigger = berat_daging + sisa_air; beratTersimpan = true; + Serial.println("=================================="); Serial.println("BERAT AWAL TERSIMPAN"); - Serial.print("Berat Awal : "); - Serial.println(berat_awal); - Serial.print("Target : "); - Serial.println(berat_trigger); + Serial.print("Berat Awal : "); + Serial.print(berat_awal, 2); + Serial.println(" g"); + Serial.print("Berat Daging : "); + Serial.print(berat_daging, 2); + Serial.println(" g (30%)"); + Serial.print("Berat Air Awal : "); + Serial.print(berat_air_awal, 2); + Serial.println(" g (70%)"); + Serial.print("Sisa Air Target : "); + Serial.print(sisa_air, 2); + Serial.println(" g (40% dari air)"); + Serial.print("Target Akhir : "); + Serial.print(berat_trigger, 2); + Serial.println(" g"); + Serial.println("=================================="); String msg = "PENGERINGAN BERJALAN - Awal:"; msg += String(berat_awal, 0); @@ -824,70 +858,117 @@ void loop() { // TARGET TERCAPAI โ†’ SELESAI // ===================================================== if (beratTersimpan && berat <= berat_trigger) { - modePengeringan = false; - pengeringanSelesai = true; - // Matikan heater 1, 2 dan fan, nyalakan exhaust untuk cooling - digitalWrite(RELAY_HEATER1, HIGH); - digitalWrite(RELAY_HEATER2, HIGH); - digitalWrite(RELAY_FAN, HIGH); - digitalWrite(RELAY_EXHAUST, LOW); // EXHAUST ON + static unsigned long targetReachedTime = 0; + static bool targetReachedFlag = false; - Serial.println("PENGERINGAN SELESAI"); - client.publish(topicStatus, "PENGERINGAN SELESAI"); - - // Kirim data dengan status COMPLETED untuk trigger notifikasi - kirimStatusRelay(suhu, berat); - - // Tampilkan selesai selama 5 detik - lcd.clear(); - lcd.setCursor(0, 0); - lcd.print("PENGERINGAN"); - lcd.setCursor(0, 1); - lcd.print("SELESAI"); - delay(5000); - - // Exhaust jalan 20 detik untuk buang panas - lcd.clear(); - lcd.setCursor(0, 0); - lcd.print("Cooling down..."); - lcd.setCursor(0, 1); - lcd.print("20 seconds"); - - // Countdown 20 detik - for (int i = 20; i > 0; i--) { - lcd.setCursor(0, 1); - lcd.print(" "); - lcd.setCursor(0, 1); - lcd.print(String(i) + " seconds left"); - delay(1000); - - // Tetap kirim MQTT status relay saat cooling - kirimStatusRelay(suhu, berat); - client.loop(); + // Pertama kali target tercapai + if (!targetReachedFlag) { + targetReachedTime = millis(); + targetReachedFlag = true; + Serial.println("=================================="); + Serial.println("TARGET TERCAPAI - Verifikasi 10 detik..."); + Serial.print("Berat Sekarang: "); + Serial.print(berat, 2); + Serial.println(" g"); + Serial.print("Target: "); + Serial.print(berat_trigger, 2); + Serial.println(" g"); + Serial.println("=================================="); + client.publish(topicStatus, "Verifikasi target..."); } - // Matikan exhaust dan kembali ke ready - digitalWrite(RELAY_EXHAUST, HIGH); // EXHAUST OFF - - // Reset variabel - pengeringanSelesai = false; - beratTersimpan = false; - berat_awal = 0; - berat_trigger = 0; - waktuMulaiBerat = 0; - - lcd.clear(); - tampilReady(); - client.publish(topicStatus, "PENGERINGAN SIAP"); - - // Kirim status relay setelah reset - kirimStatusRelay(suhu, berat); - - Serial.println("Sistem kembali ke READY"); - - delay(500); - return; + // Tunggu 10 detik untuk memastikan berat stabil di bawah target + if (millis() - targetReachedTime >= 10000) { + // Setelah 10 detik masih di bawah target, baru selesai + modePengeringan = false; + pengeringanSelesai = true; + targetReachedFlag = false; // Reset flag + + // Matikan heater 1, 2 dan fan, nyalakan exhaust untuk cooling + digitalWrite(RELAY_HEATER1, HIGH); + digitalWrite(RELAY_HEATER2, HIGH); + digitalWrite(RELAY_FAN, HIGH); + digitalWrite(RELAY_EXHAUST, LOW); // EXHAUST ON + + Serial.println("=================================="); + Serial.println("PENGERINGAN SELESAI"); + Serial.print("Berat Awal : "); + Serial.print(berat_awal, 2); + Serial.println(" g"); + Serial.print("Berat Akhir : "); + Serial.print(berat, 2); + Serial.println(" g"); + Serial.print("Target : "); + Serial.print(berat_trigger, 2); + Serial.println(" g"); + Serial.print("Susut : "); + Serial.print(berat_awal - berat, 2); + Serial.println(" g"); + Serial.print("Persen Susut: "); + Serial.print(((berat_awal - berat) / berat_awal) * 100, 1); + Serial.println(" %"); + Serial.println("=================================="); + + client.publish(topicStatus, "PENGERINGAN SELESAI"); + + // Kirim data dengan status COMPLETED untuk trigger notifikasi + kirimStatusRelay(suhu, berat); + + // Tampilkan selesai selama 5 detik + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print("PENGERINGAN"); + lcd.setCursor(0, 1); + lcd.print("SELESAI"); + delay(5000); + + // Exhaust jalan 20 detik untuk buang panas + lcd.clear(); + lcd.setCursor(0, 0); + lcd.print("Cooling down..."); + lcd.setCursor(0, 1); + lcd.print("20 seconds"); + + // Countdown 20 detik + for (int i = 20; i > 0; i--) { + lcd.setCursor(0, 1); + lcd.print(" "); + lcd.setCursor(0, 1); + lcd.print(String(i) + " seconds left"); + delay(1000); + + // Tetap kirim MQTT status relay saat cooling + kirimStatusRelay(suhu, berat); + client.loop(); + } + + // Matikan exhaust dan kembali ke ready + digitalWrite(RELAY_EXHAUST, HIGH); // EXHAUST OFF + + // Reset variabel + pengeringanSelesai = false; + beratTersimpan = false; + berat_awal = 0; + berat_trigger = 0; + waktuMulaiBerat = 0; + + lcd.clear(); + tampilReady(); + client.publish(topicStatus, "PENGERINGAN SIAP"); + + // Kirim status relay setelah reset + kirimStatusRelay(suhu, berat); + + Serial.println("Sistem kembali ke READY"); + + delay(500); + return; + } + } else { + // Berat naik lagi atau di atas target, reset flag verifikasi + static bool targetReachedFlag = false; + targetReachedFlag = false; } // ===================================================== @@ -895,24 +976,47 @@ void loop() { // Relay HANYA ON setelah berat_awal & target tersimpan // ===================================================== if (beratTersimpan) { + // Debug: Print status berat setiap 5 detik + static unsigned long lastDebug = 0; + if (millis() - lastDebug > 5000) { + Serial.println("--- STATUS PENGERINGAN ---"); + Serial.print("Berat Sekarang : "); + Serial.print(berat, 2); + Serial.println(" g"); + Serial.print("Target : "); + Serial.print(berat_trigger, 2); + Serial.println(" g"); + Serial.print("Selisih : "); + Serial.print(berat - berat_trigger, 2); + Serial.println(" g"); + Serial.print("Kondisi : "); + if (berat <= berat_trigger) { + Serial.println("TARGET TERCAPAI โœ“"); + } else { + Serial.println("MASIH MENGERINGKAN..."); + } + Serial.println("-------------------------"); + lastDebug = millis(); + } + // Berat sudah tersimpan, mulai pengeringan // FAN selalu ON saat pengeringan digitalWrite(RELAY_FAN, LOW); // FAN ON // Kontrol HEATER dan EXHAUST berdasarkan suhu - if (suhu >= 60.0) { - // Suhu sudah tinggi (โ‰ฅ60ยฐC) + if (suhu >= 55.0) { + // Suhu sudah tinggi (โ‰ฅ55ยฐC) digitalWrite(RELAY_HEATER1, HIGH); // HEATER 1 OFF digitalWrite(RELAY_HEATER2, HIGH); // HEATER 2 OFF digitalWrite(RELAY_EXHAUST, LOW); // EXHAUST ON untuk buang panas - Serial.println("Suhu >= 60C: HEATER OFF, EXHAUST ON"); + // Serial.println("Suhu >= 55C: HEATER OFF, EXHAUST ON"); } else { - // Suhu masih rendah (<60ยฐC) + // Suhu masih rendah (<55ยฐC) digitalWrite(RELAY_HEATER1, LOW); // HEATER 1 ON digitalWrite(RELAY_HEATER2, LOW); // HEATER 2 ON digitalWrite(RELAY_EXHAUST, HIGH); // EXHAUST OFF - Serial.println("Suhu < 60C: HEATER ON, EXHAUST OFF"); + // Serial.println("Suhu < 55C: HEATER ON, EXHAUST OFF"); } } else { // Masih scanning berat, relay tetap OFF diff --git a/fix_icon.py b/fix_icon.py new file mode 100644 index 0000000..67d412e --- /dev/null +++ b/fix_icon.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +""" +Fix App Icon - Add padding to prevent cropping on Android +""" + +from PIL import Image, ImageDraw +import os + +def create_icon_with_padding(input_path, output_path, padding_percent=15): + """ + Create icon with padding to fit Android adaptive icon safe zone + + Android adaptive icons use a circular mask, so we need to ensure + the important content fits within a circle of ~80% of the icon size + """ + print(f"๐Ÿ“ท Loading icon: {input_path}") + + # Open original image + img = Image.open(input_path).convert('RGBA') + original_size = img.size[0] + + print(f" Original size: {original_size}x{original_size}") + + # Calculate new size with padding + padding = int(original_size * (padding_percent / 100)) + new_size = original_size + (padding * 2) + + print(f" Adding padding: {padding}px on each side") + print(f" New size: {new_size}x{new_size}") + + # Create new image with transparent background + new_img = Image.new('RGBA', (new_size, new_size), (0, 0, 0, 0)) + + # Paste original image in center + new_img.paste(img, (padding, padding), img) + + # Save result + new_img.save(output_path, 'PNG') + print(f"โœ… Icon saved: {output_path}") + + return output_path + +def create_icon_with_background(input_path, output_path, bg_color='#1976D2', padding_percent=15): + """ + Create icon with colored background (better for adaptive icons) + """ + print(f"๐Ÿ“ท Loading icon: {input_path}") + + # Open original image + img = Image.open(input_path).convert('RGBA') + original_size = img.size[0] + + print(f" Original size: {original_size}x{original_size}") + + # Calculate new size with padding + padding = int(original_size * (padding_percent / 100)) + new_size = original_size + (padding * 2) + + print(f" Adding padding: {padding}px on each side") + print(f" New size: {new_size}x{new_size}") + print(f" Background color: {bg_color}") + + # Create new image with colored background + new_img = Image.new('RGBA', (new_size, new_size), bg_color) + + # Paste original image in center + new_img.paste(img, (padding, padding), img) + + # Save result + new_img.save(output_path, 'PNG') + print(f"โœ… Icon saved: {output_path}") + + return output_path + +if __name__ == '__main__': + import sys + + print("๐Ÿ”ง Android Adaptive Icon Fixer") + print("=" * 50) + print() + + # Paths + input_icon = 'assets/images/app_icon.png' + + if not os.path.exists(input_icon): + print(f"โŒ Error: Icon not found at {input_icon}") + sys.exit(1) + + # Backup original + backup_path = 'assets/images/app_icon_backup.png' + if not os.path.exists(backup_path): + Image.open(input_icon).save(backup_path) + print(f"๐Ÿ’พ Backup saved: {backup_path}") + print() + + print("Choose icon style:") + print("1. Transparent background with padding (default)") + print("2. Blue background with padding (recommended for Android)") + print() + + choice = input("Enter choice (1 or 2, default=2): ").strip() or "2" + + print() + + if choice == "1": + # Transparent with padding + create_icon_with_padding( + input_icon, + input_icon, + padding_percent=20 + ) + else: + # Blue background with padding + create_icon_with_background( + input_icon, + input_icon, + bg_color='#1976D2', # Material Blue + padding_percent=15 + ) + + print() + print("=" * 50) + print("โœ… Icon fixed successfully!") + print() + print("Next steps:") + print("1. Run: flutter pub run flutter_launcher_icons") + print("2. Build APK: flutter build apk --release") + print("3. Check the new icon on your device") + print() + print("If icon still looks wrong, try running this script again") + print("and choose the other option.") diff --git a/flutter_02.png b/flutter_02.png new file mode 100644 index 0000000000000000000000000000000000000000..3eea1ade28e7da82b9f0a4295f1655f1a2a0c5ed GIT binary patch literal 92132 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 Xar2EC6W^YL^F=7jtIHM0ngsqYp=IK5 literal 0 HcmV?d00001 diff --git a/flutter_command.txt b/flutter_command.txt new file mode 100644 index 0000000..4286f42 --- /dev/null +++ b/flutter_command.txt @@ -0,0 +1 @@ +r diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 6ea8b8550c6b4d3022213627c7a9f72eacf9aa28..02275486df291cb0753a8dc1837d72ae42072605 100644 GIT binary patch literal 16071 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 literal 16657 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%0A1TwCTUP16bFuvsJApP{Baoyz4tR-ztr zf20tQ-NVH6j%L<3y`WkEdP9Gqp3w(i;}aP7fZ2sB_0mIaP=@dukIi6EnHLJiHvx)D zlt8dyxm8cu*>se1N%?dkK&Bgd*Q(EL`I=$SU$)dD1CK*2D$H6aOf(Dm=g0D4m~Qd- zQ`?xB+d8d&=hiKQ`5~FP(UIf6MXaRNbq!}8=LKzLZ0~raapF-8&geVs=`YZHO^h>Y z*=;JDGtZL(v!wOPMn$ged0}LJEV!jU=Fc#*P0ddcN6brjX^zi>gba&3YD@=eTcmr6 zU5V@ER|9$ooZh~gh1`vj+lD90=#@?$8S1T!l@F=VoBsM_^h7fE;6Kiq7N3LLq<$O$ zj3ejd#AN-LfWCTyR^FM#(>GX7OvHm-Ybs)#?i|3YkaY!t?4#{W4D%OlCAfea*fO`o z)35vPsxIoE-g{RSxQStaQo>n1N*6}}$r}z_pjjNng~z*xfb&Mu0L6#9W7GbOJ)Bq* z0Wf^0L)H~?LPf7p9Ibbgwg%1WSa7kWjfI%=FvIt@Lm}RZ3WD>7u26cE&9IXq?9^KHw^I`5Uo7GtT0?w>MMFo++`K3OpG7gU%(&^bjXi^| zcgz0w;jQ}rKB%37uK4Dt`$FJq#ww6x0mw>)0eXFl){!Y(T(hxMWK(vKkW#`p&#yUs zRUaCHqM;syggty15Z&^Pg|z3(^@z+3qX=7AZHYM==DJi zvHl_g>YjL zevv1cbUb=1r!GgLEoAh<+qG{~a6*Rn4^73XT?D=2iOqmv`6+XcA!uH$`|z#Yx9G$M zF}>V%L``@J8D0G7&v0ALS|9*>zk$t>_3=w(j*SW;5$mG-V@uE#uy@Eo02(#|`tGEiiW+sij17|DoP_Ok+37}w z$+7kq_NUo9buM%ta`pW)!~eqskS}Sv=+Sq==_1&DXS9#rhfe2op{WUxWcOJS}HonmUzq?&U^-G9Z?arXDx9WW0*bAA&rpp0kDAD0d8oHko z&r4QU%}p$=vrS|N#9TLmy4(6uUQAXAV36WFs^yb3s&(6%ml67y{zlpf)jbROw(?)A z(%!#R-5tsm1j8w3qXeR~)iSPwzF+E`?9Cnl+V<4TMe0c#M2|<|!UBcB z@YDUV=vfD?kt4v{W#UbcU;7UkVvZ!LX(P_=&jN6Px^RKJYgq{GC9Hq|oRgIotrkYo z98X*KqbLr;@8MeNxXO;ofsaEr`@`<>1{WA6A%K1v3v@2UF)_RIFvz0_v-#+;>Re*q zC9!c5jK%iNI!;2$rESEZ;c2?$=+L%Jsan}Aq>LZO`21<>G7OhVE941}NqRuwU3rbd zZzyNkySuNfm$0q9f_9;8B<*};(`|n(ITvfeDH}MMXC4IFXcHV6Jgo31MN2LEe;OoB zg!1MS1#)9|y$}wMFdzg>_CtSNGK-_OW`8sQqs-hbTR*$#^^FQaQ`1m+bs=^zxPm{# z>Iplt!gF?PVWE{&sU|+({K?gpz~pq?s^r*2HIZWVXH2^5k-}i<&C%au1ndH0?i~QK z4^&piR)-Fp#O*Kh7?@w@OTrwJ0&#Wj(Db~rP)b$Lc0WA5Pj|JlkX+?*D-006z6xX~ z0>tOMj-ur;5uhQPXLA5;q8ZOZs#n1Vw}w>F28E03#5$5~S3}0=(B|`cXV&ov&b=(y zP&87FQ{Q{Fr?wq}j>~Fu+Tm_fq!9Mk?qneVe74iX{>~DU((uQEhfWYu4+!lBpjiY9 z_6(k5V(cL_$vz)`h5u>@K+R_#Jt6?RiXoU3NdShoxxARl6Ikd{b^rk7Jybn<*m^*D z30DtsJMIAh=IPpsQ?%cK|3EB;PC4WZ%{Nrz9zTIUz%X=VQ$9F1l^>(N8*7_B%*&fd zfUiHN!bTf2!9g(uQX6eBI{=VSaY!JgFY&S}IkcSE+m1mMFn9w#CVI}8Irzyt2Hw40 zd5z6XHLK;Kym_<9J+y)Z+YuN+tj7KPFAqyS?Fikk~PO`_!kOX(Zo z%l85QN`q{;-zF=p(Lz0eGw9J<3CRn!F$@@1)d%!_Snws%n}#~85OJYmR59S$Ts0>( z=PR$ar8hd*3;_E-_qHIGau{$y!C+$YME@%C@;tnzl+rqQYQ%|BAY13EG3(i z8^4f1mwZb8`7j2|Nr1R7N2@uXc8Bh+Z<1e3f_iNnG588EI~`>?wsrzF2M@r*p7&l_LelMTbB z_wQGsBM!~&n($N>H4*$1g#nONEH@1I})?nY+{`wVMBpIzZg}OWhN<0n2UhaBIbT z(~d_Jo`yX%g3NhOsOZVfWO6g7<5NkA@WSGszKQ|oW@{S_p;3R40?oz+>!5TBK@JKX zO?)7(g~V3OgJuEU_{K-$C6yyoa{$gyzRJ?K*xwB&ud_oZr@m7N5=}(cKisq$#O*)o zyz`XMs4}4M8q}Ha)Uo0tLIED`ZM)wr3W0_vgMQ~oMov0xf%P#t^1`Q z#D(pTUP`VbX=3r9#D zcKLswuchh(rgCn=R{7!R+l1gof}MnhT9<-{CB{JFSG&Pikp(#=uP94EM<|<>L&AkG%>)IMB?F*b!A(ZgMqR$N-P}+UA%h4cx6$LWdqYnIl?7ZBU=IHLur+f}> zsCUxNiF^W^`x}9S_U&Qoe~TH~Q+ot@cA6egoOI)}c6=o;5s3zg^lRQB1tQidaS3pt zZv7>d)UKtDNJs@C&TV(eSA~FESZ2)9Sj`&+QbI_)%td}d@N{jHH(Ql=+o-_#$u%q*BlWhcTvLAzxeV)OFjUee$^GnBsE;V zdAi62%5nlK^3h(-ZPf8i){0&lyR8bV-ITI;&5GAlzovV@zE5BQU~TP)0qRfRy)*_T zJopk-^nJdpG?bYGMhAvyD{g$-AXH}ftnRU?|B=-|xciNQKz)q57e2;w4;U<8u6N!eGUHulK9O&0bP!j+$)`)5QxZG2bq<5=04Z+q@as#Phhv_ zAF0jQAM*+UWSN6Eoy(DHl0d`jR5fRA+xlO2DAo6-!%-X1EBYzQV{PWct8^v#Ra>FN zhi;?@Vxsu);K%j}PEhrYYj8lz?`Xd~OtAa-z5kzpWkh|NGlxot(8IrlSyK6^WW1+) zAe2;*wS5n#5K>h@Ly09HUr{XO6AI<7)q{(%+6g0I{Q)#@j5cU4-t)`qr%*S{VKhc9 zjrBe_?5IM3QU7ztqHI(fN4JS;XcUulrJXL#XD$lLo|Ljg#n>|k>FoqK<^O#icVEpf7Iw()P9hricI z12S6>Wm>rWmtetx%gl`>H07~!R!8H8lEn;c**lR95};X|$-SjC(H`Q~wlM>PK~5j? z%qkfa@_{bGJGdS@9_ww>{TIa!cCf4L%^_Hjd9JH>xJo*5INxiiNah=~65`#G8A3<|G6A=4Gm~qy ztp_piYWeV&YvTh)h>vG9{cq{vLCQUE+FZC%WUg}K_Dc`aPN6)oXr!V=%kA0PSUvDNwCP&&}uiQbK0lvwjlboQAzxhEcflxe@ukErJ3BMZHqge%Nt~%nPe1#++KCed#>=^n6D6|K9y9SkBktO zinJgV+j;-xGgVBu%mA@kGk|#0)V$##Yde9!ixJHoG;eINRY~HGu<(N5-mZ!K6@ICfji9!nr1H6e=ey^ z*@!P0f7dsp%W@ zl0eD)`}yn)uIa%FJAH+7XXQHs;+OU0Bq-PbhKWq>9*1D{^4ZbK2l=$obhK(`6s}yu z)SFQ-&>{nGp=Kv2@Q#Vt`0sC+l!e)1n!TBz?Ema&m$pv z5W9wW@c0vltBe)LKpq^~nzPrFd&WAI;RVO3D>2~TYA%n)$^(BcsjIB;p;rJgA-i@7 zg3#Pm>@Aq_IZ(~nv0Xxyb8e`9V2oCH3<@OsdU#;XWW~f1E@LBbTgT+ezA5pW=ecvO z7yg({ZXZ+E$%QA8tbFJh&x1XYCwdJS9YQl-oE@q=Jm-JBeSBpzKsWmecZk-(d?YHn z7pAym*@qSEgrMskF;of(hOIE?{RE=0F%U2^tQl^T^PdJI#PE2GJJ|dNrTU z#wePTq0=bh+C?yvOp7$=@qQWnf0C3q5WN<&2oscfIU^v644Ns`oR)*EfPgt$o?q$A zkAVTmOg@9V)O@59b*{=hi;ZaH3_-9SswZjtK2pYgR!ybe$#>AiUc0{{Z3dPxW*%is z0=w*;kmkLtKm0oWXkHE1kbNwBo?(tMO0&W{IM5NG@&9AD(d9~1-iqGKUNz1&by)-$SPdcx-kZWY$Ijc zX0`}3yb41$i&|1DDA5go$ zYiYJBT!Zp{B&)4r6Oinj9$r0=PYAXtHE2GCMI?4ji1nzNpYETmLW>x>0>*EmF7y~& z=f4LZ(e5w3O%n1+1(Yy2-toxR2c%#>Tk8#1VX87(1Wy@udfuyWhF$VN=;;O^3vW)k z0ZU`8Gfq2i-8M7@d`kZnePT1Fd544!ukB55$vvT`kbQ$tM;UxGzUO3x<|;g_j6Su;;YMDMu1cqq<{F<^Z5heaSzA$tS$u}+OM5Vc_) z#(Y+b|k?u*hCG%#Uq##-1=mvykgixmEkQ97^=aL(k*l8urp; z$Zv8fOb7VP3dl-g7~9ve1-iw>QoN&;9c&%SN`faK#YDhBC0Q=;sEgOf?gJ$vc*qAA zn~dH`73q>hRc!iBhU|XieI)LF$5ai6Ds>+i)x?hrqhrZP?`!E)G7_RLb4=?Av?=*k z9N;HDPIGGI-KcTA5{I?KVHjx3fLZhGerYqDb0DLpU45(bYM9s1L~J55sb~d}wF@6P z1oHfpF2XPAEE@^X^UBx4T6*eLuVcC`ydbXq-fK6(9hos-!mK}t)=hDdD+jE}v#Uxj zk4s9y-nQsZ?o82OadaHJom&eNbu zG+2U%t|;n`Wx@sD@}`ju(zTD2!wR%8Ai;l71vFcmEMuS}uK|^TJknv^VTizeZ^x%c z5Xqf~PSH|kdwESHRhEF;1`J~>-15)XC&WLgy!Sr@yt+BC4997!+;)xfN;D9aNC$nMg71OXsML@=E695C2k{}+GT@QwRD@}B*TrUoz#Hl zOac+6=)K{pM%Re-R=m0n^_Q@XbjogD;YBOx6&2*(x(==uLH-4;#R`~hCqZ_t6a3l$ z_EeV`(2boIYvryMIX-`<(1tWqj4xP(wR9|sxr;!ce zW?Z?f<@mPDUTGt+cvlBNyB<#O5U$z=t85o5=R`}+dAHe#yQOvXReIMEXq95t?+M`Z zi5~!Jj(B5uN@2e%>30NAtL%~s z{gIm~f@zlh+nk^ID*pa90%6s2hc|T8sNc-JSNYU?Q<<&s>bn44`WPNnuyFgo2SNq$ zJ;lLi114{}K0xcMH-v7vQFzLIqPOFB<>Gl`15ju@ghM45Z5dPcDj(kZGSF#%n+4_5 z{Bq7|vIe=e%81s!x>D_}WAKaRU(XC(+C$sB;1p7@*cYm?c{?nYbx?j_yqZ7zM)W@L z-A4=EExLZ@YJQu?yLizQiWBwG$zY>^UMK(r{EKHu zn+A6+L_M}&}ZqIX~xMZQB=T%rEEoPNNTaLv5&X^r) z*kIt~?vs-oN)G#`gY^E%r2YB#=rz7K-rq4ubo|XJ9*nDqUvdbbJ{p8%+QSNSK2)C? z$30`Nj5)WK!t~&&y;P&QCMzqiV9S?-VRYj4Fy9)Ee`3|-_Xz!KRB9Jnq4JT~tDk;q zVqmoW5ukL&B=%@sUInW!*4HTX>RpDOXTf3sEa>t1GC65)oo$$3$n{Sez>tf5wxEFp zUCrFOKW`Q)IkORR1}GmTg#P)z;lB#GS5`<9}3>I6WPYoja zq>#}k13{n2-~R-O!=gX_>+qkJP&@d~H2yme|DgtY68?kA|A+1ej9ZqUQONI;XV9HB NK4g9{?*Qfc{|9Yp)ye<> diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 18397103f408299e604aeaf5a13caee1de3de58f..3e870cb5150260419e74dd12fdb606b2356e0b02 100644 GIT binary patch delta 376 zcmV-;0f+v#1Be5V8Gix*0008(idp~w0Z&OpK~#90W05Gm$jCq@U;*i5BvU&JxwZ~z z&5VqUj12$(qf79zGYN6C{Qmp@<>%jj|KZTg#K=(XDbc*`1%xlo$C7R<;;GFiC&b1G zj)k9p{+)UFZTac<>o0sjvjgPp3_FqgZ+eh4pKZ)kPO%oAcJK{~W)Q{9m}n&= z$8S_bP&EJl&k(=h;j@pw85kIT{P|zL{@KKR zuidnGb)+~UQT6!!uaoz`9=P@8_h0n*WcmO9|D~tjQ7rlRd%^K{xEzng&*XuTs2Ko9 W#e#|UBQ=%)0000!So%iBSY`H^+ zZmSP(c<=G}{C?l}9_?6rs8WCq8bQl4iqdN))JpWHg%l+X5Pzb6z--WgG11^_1j#&) zn%F|9qky2>fz7k`pUo}{jpZ3T+`|_1GVP$GiU2Bth+DRZx0Fp{7D8{hsi+`T%88Yk6GbAU{Mu_QYdw><#B?`@!0k{Rb>sH-pU9@ zI79+2EKS?6cYm2u3gwBOf`5uZcSl>(I~s8sWtl|3&7dB+Ub%$8Q5-wo9(YErO{Efo zNN{?WQ5PmZc`WX;C{#qOp1dMou3=%EslS*CT$aa1IDzO(Q7M#O$`@kg`;#aO1nXfA zyXPO68#2og8{a0Lsp2~Jg^TBcyjFuIgy1nzQg)H!5h1!tw~VeDSxf$fzvmmd1cQpM SO2be9 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 742e296ecdfccb2387147bdd09f588dca9463ce8..798f1718ed61100c097706fd30c36549b5c56170 100644 GIT binary patch delta 750 zcmVVi|K~#90W05Gm$jC4NfCXeL;{aIALVqhq zEgrRa)Z$T#hqA?t3=C{6j6eVUCq@G|8?~mXAl4CJ5 z6T{>P+4QB4{^PKkg^9sQo!4EP&r*q7UWknmwNm;2|No0me}7Kg`?}}GryW;6eg93A z2cSXiqsw3EAT43t;#L#qSev2bGv)4+55Eap%m7Ma zOmpMp{HEQz^ZI9{hXhHsGB7YqJ@C4F_bWF|-USH?V!W(9AuFPDd$IM(CkXr1 z=Rd(S?|;pUlh=^oAl24qAAct=dU)dnPVb^vy!6z&nTOvX%RPAe%VYALYt~mr|CXJ8zwGpTMg|5sAvSqoHeqgN zHWsGefB(P#_UG~Y-}m4A{QHj>>rgHJ&%p5cCsF$TGcY`S_v_(1e1$Lx77qv*wRqIx gQHw_{9(oo70L5ns+_!jHc>n+a07*qoM6N<$g4suS_W%F@ delta 810 zcmV+_1J(Td1-1r|8Gi-<004~sxNQIc0{uxuK~#90?UrjuQ(+j#|C`4-ww5++Eoxqd zWDtDw4dlW;W)U zySaIG&UWIwgrZZo<6CHOe(-)chvz)}&Uv2myf0%s^uezH5`QsEVThbUp_*xt$zWz$ zAQrM9EwUgjvLG$8AT6>WEksZOK*cD)@`B_9T9pzeeIoQ4hUDPF0^ETx`rI5m!SLS^ zOi%)K6(;Oy_zoe4BclX1GEV8DSt8IBkOi%GKfej!YPIi8!-$F)FU~N*BbRa@1F6VvD5*+g8q_k`u`@ zX-JzCm`7V=ZMGJfsVaQ?HFc>cor4*vDqP%df<~pJ7F?gBL;JDZG55rF%AgT+_&6MF z`hk{x*?&l4=p`rO=X82dWSEPf`i2#q4i@)6daxy*zRcrkw*~Ok!AchlM+EG>ZNqz8 zkX#>cwxiHIC$8BSFTUY04m9?~6&odZ;(;B_pS&a24j)f`mTOfRSZY*L%k_IWG6y^C zvAbg){G#@qV(x6i(NPiY%`3M?bxyZPYvPX{yws!h)^j`m@ zfgxVN*{2R%e(prsie$1E7i4LtlnsU=cxw%iz4&gs2TngP<()D1M6%? o79*rZ7NkWMq(v5_W!4M+20O+I;Kvn~LjV8(07*qoM6N<$f>l<1ZvX%Q diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 4973cb025bcf8d295199a283d40afb9e2f17ec74..77b4a37fa1b6a85f882388817a7e31a8a9f6db1d 100644 GIT binary patch delta 1089 zcmV-H1it&D3CjqO8Gix*006a~P9*>U1QJO^K~#90?U-v!Q&$|o&wcexv6PBsm4_%M zVloqCW=xbB;&4HuabO@CmF9k(AxaB{C@mDCv{1ydG>QPV46~1VBR)t*An96Z@+=}ji76#0 ziXe=aqsF{uiZ$7oS5}XY<69cXkkeaIO3xS>Zy0CC$v~N;d^vKj+kf?_f5hwKLmB{(o1r?g@uNK#O)%R&NmG>PYQM-*OK}uty^KJ!Qxw@L zipqT5TZ+BZZadp#G1-}Dr2zmX*&5^9u@iUAAvP@1%MKOkm(N#4_IUt+L>0bch3@Om z5`S*79=OCtz!yx2h6nbrZfpYiv_hD}Q~(d$lSb{z3jdD`9vQ$xEQepi5$CneCcTT|E8 z_8Bdn$%h9EllU@r(cW{-7L^p+yet975DZ0*ePwvb(ho8;ORuv>oTS zV7k%aI8>yMmt#D+1*L-=gPfGo2r9K*SFbf-q-V* zJHzWgj!#hjhka~gx9jd-F381!J(~KcZ+{~v-+1qFtD?4K}Q!M-ze`yQFZu!yZ2-JD~(d8^U z)%U|DLsqgpYSnQ#j=S>2QGJzk(&6>j)1+y}x4mv)YN7Ju#T&ZtWN%qc!p_fi28|>N zVbIb)^4;yB=9jSj##BB?Pb9du>CN3tynjA${*m=Ut9A7vb#b<4V5?1Zqh{K~#90?U-w96jc<*|1PEun!3=m&y9ty&ZM$*+nJs`eXTEGm|YfH5j0Vo;g_ZFmUP z*azLx-C$exz1>dt(KRzj?Y1-R&fQ0o+4&`#G`pF5&+m5bJ%8t1<&uW0ZVJ#KLr`+E z6s2A0K+=VBp#w-avar;Vg{6)xEOlgIsUr(Z9a&iF$ih-b7M41hD@;>>QLBJ^Jd)H2 zv?>bA^E4=0q(ZJ<0lk_AO;HF%qVP{dFzOG(<_h7aGl+K2bKQkKF0BZ`hcz07F9$9HfT2L(c27e`XJ!;0w59i~ni%z_MYB(v| zjrY)M*jNCoBZw1MJ%SIiFX^5q%y_G+04l}&uPiC-*xMhVlNZ`j`m5=y1TGQDE!(T<%a+&=fv=(Slpv5YF|D5sf=%9?sJ%(Xyii z&o=i@?Vs{u9d=im$t*rkX)#koRwv!Wvz5@PKfS0fH4;sf zY?v@v85WoDWHgHUHXDAp=HZHroGb;=WXa^3eVpjIxP&T4ijZTM-CPN|(6n}u z<$vONg8FdQLlT0$ zl{kFCi6fm(Y+PeRO_>Rgtuk;8Jt?EUFn;Rx;G0etuGq-Zj}S4!)6wjME zqg7H^zg&;<5*jq0=RA;N2;#3ri6px zXiWM^1STBi8ugjYI=c`$_7ac&{}L}1_f~U})?~^q1QSl~t*BTs_g*?vkcFj=EG%_o sVW}evOC4EQ>d3-UM;4Yk87KTNx)b|?If$(f1poj507*qoM6N<$g4$N1e64j8Gix*007zX@K^u<0ozGLK~#90W05Gm$jCr_zydOqk@_aHP~F5) zb4Sf3-&`&>CT2#4Z@>Qi{ZF*HaTY?GFMWLf?Jt^~sXUjj9>2LFw~8n`CmRz31H<2c z|DS#Qec|!x%BbdZ=9BZ%vBO$pBpD1G5h{6m}4{~IDa|haflOLp#jx#Z+KoaQnxFjy#Z_k>92t$f13z+j`oy)a$@;y_&B=bwM?zy0kB zlGc~uEM5B)r@0Ia3?W8>58nQodf@ex2wD7wGB7YqJMg-9&#Ozdn$c!L2XB4ea`_WZ za~T*Iie1Dc`F~i&cvKh?N>g9nFwe}auQ=YMAynQ@9~-2zT@Rquy;}yKU$fpC?m*5 z(9pmC{?}}LcIM$XoaU}L^KR0vGaWe>2EsKSUu00000NkvXX Hu0mjflLQKM delta 568 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 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 0747683e8483c5d9807c083c46ee056563c9d099..dacbc0750fc58cf0e15a47f754a557a89494b2cc 100644 GIT binary patch literal 1664 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 delta 810 zcmV+_1J(Td1-1r|8Gi-<004~sxNQIc0{uxuK~#90?UrjuQ(+j#|C`4-ww5++Eoxqd zWDtDw4dlW;W)U zySaIG&UWIwgrZZo<6CHOe(-)chvz)}&Uv2myf0%s^uezH5`QsEVThbUp_*xt$zWz$ zAQrM9EwUgjvLG$8AT6>WEksZOK*cD)@`B_9T9pzeeIoQ4hUDPF0^ETx`rI5m!SLS^ zOi%)K6(;Oy_zoe4BclX1GEV8DSt8IBkOi%GKfej!YPIi8!-$F)FU~N*BbRa@1F6VvD5*+g8q_k`u`@ zX-JzCm`7V=ZMGJfsVaQ?HFc>cor4*vDqP%df<~pJ7F?gBL;JDZG55rF%AgT+_&6MF z`hk{x*?&l4=p`rO=X82dWSEPf`i2#q4i@)6daxy*zRcrkw*~Ok!AchlM+EG>ZNqz8 zkX#>cwxiHIC$8BSFTUY04m9?~6&odZ;(;B_pS&a24j)f`mTOfRSZY*L%k_IWG6y^C zvAbg){G#@qV(x6i(NPiY%`3M?bxyZPYvPX{yws!h)^j`m@ zfgxVN*{2R%e(prsie$1E7i4LtlnsU=cxw%iz4&gs2TngP<()D1M6%? o79*rZ7NkWMq(v5_W!4M+20O+I;Kvn~LjV8(07*qoM6N<$f>l<1ZvX%Q diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 098599ea5e2abc480cc901b6ebd37f01e5b687b8..aaa01a80df4fdd3940103d18d04a0502cf5b3efb 100644 GIT binary patch delta 1454 zcmV;f1yTCT47Cf88Gix*0004VW%>XB1$;?FK~#90?VEc{Q&$+k?`bc+(1)~z7PLhk zBGWw-Hw>_iNt{{SQwL;dbb>S6+`}2Om>H7AEZa2Buo$B(TVO$>QI|Q{7(`r%7}=r@ zCW=NNg|-y>5NNsfLAkf>-2f3p`a1W*lFR-5*T?z3`%CZn?tgdANzwS-Hvlq+Ku2uB z98yOA?+ci1IAONK39}tenC)=FY=;wOJDf1v;e^=^C(L#@VYb5wvmH*D?Qp_uXF?d| zA#w8}dIw2n6A1VqIaaVxE{K~KAs6z+7*Bxm5Crk~C>P-yba^dyvd2bh`-ktjeaxnw zmPx{VaC~dh`hV{_?X(jsCFrU&adxsOJwceNz)*hhqa2E&Ot`0|$8|;PY-o3A`i5y< zgFha+rI*G>9xqPXP_7;JP!O?XLVn%~X z81b&_hJPV{BR8&;?SE4x2%#V`f*9kM7O2zJQ6*JIFHD8GkKgBTlz>-Rl=wzjm)=S| z`e{*?eBb&7;Z`R4yqy&z!uUJR7${ihK#hk&5^-`{(yK>0@geWz_p?((U#**QTQg4X zJ8!;g_Y`NxtbJnMgZJ}RNDVgPK%E6HY>-YXC4UzCVe`UG6j zWD1LZhiA+dK@cB>)VJ9u3-frOWNm!j_gxT?L+YhBu9R#0i6aet2!d?RltZKm7r6*k zNq;B$+~sQFlrLl^3l}M{&hQ(uP!|t-*2d|rM3dgNSUIx~p)vS8@Z*-mz17A(yJu6T zeDWL~0|1bdDw;uI9sq~lQkVV#St=lSN)`3X_C*1{kRXSdSz#;>F%&FTVh}lI2o$Pt zh9Ue-ou&FFUYIc(b2t=?P@QI|!l#?8hkq}Q6fF}@&X10O$iz6;bWVs-Aqh8X9G{)N z|5*B|*|u}F5+W0`bXeR0+TH*J7uH*e_fB+^r(^4J{SXPiKJ+WR|Gee* zR=*nn6oTxnHnkXB`_`+Xqh@11QxsM4=fIb>W|Gco*q`v(>$an#opWH$wcuBEuF`zf zsx%3miwH`$mDqExx1qy9Kh!kB|9^D5c8{-(f?^ybH&*CgS}NJKM)_!qPaoqTS$3)K z#8oRn?}vTBRAJq~aPcv{JIrm`3wP`n?%1DMDBO@Cdu>@X{hQq5r5ZJk(@nP8TeyeO z{rQK4@7l=?<=TFS2QF4~x2w6^Rr0GbD_NMECdp0_Jw88D5c0iP+u?-S4kyfZIAON)FVmm;*i!IpIsgCw07*qo IM6N<$g3(LMKmY&$ literal 1611 zcmb_d`8yK~0A55nwrL}b3TZh;IX+1qY_XY}hUMJosmT%MK6;F-STt9OB=?c~YDj5h z$uTKHZPn(E8WCB2KHvY)^Ssv&@6Yd>=H!3@O2Q-o000ncZRxV-PXF_u_?~7STCWEH z#35KqBrfK`YGHiP1y^O(^TayNOkO=_2H2%1?w<_KH)$zRlY*r3O4(>m#rh&3JuW$&c@+wq@Nc8N!((SIv>1tT@AnTrf-!t>=L3ieC zLbi~#mp+*-{VDn(*Y%eLO8IypkIaS6aX`>P%bCAU+18V*eE7ZLN$%DaT$> zYk4c;c&hRBTmHMq1G)u8cJsi;xrKR$Xc@oI2^;4V*?~jhk=C|OrzNP=yHVHfm?pEq zNSrUlrDp$2a!%H+y zyXKd>mu4e3_3JJ(XF9RWWK>AXAoW#<`!}S^tLUNerf($Cf+IeN!8e;jlr5FcI5(h!eZir%cCC#zeqm`kx|m|hm2V_&EcS}&8WlHHjGjT zG-OkA&f{Fy+EOz*$b-&AXy}~{8_7KGzA`)@g(Xz6ZZh{e(&I~<=oj#amQOMISr-X@ zp@S>^@xs9SnToha?C-4Qly(LI))n=8b^%@Dx-oyD>;kViZx#QHb=v{_;kug>Macob zW*aM@YDv%xb%(AN;mtqYV&uQcf?{)s?~m1QuQ0@pPLA{vM8Mu1L?Nf(3=VW}I*dEp zKQD$J9(^}9fU&YKR0;&YFKa9HZaXV3w;i1U4UCKQW>SJ&8yc$DH$hN_2B8h zq8|hdaDN1c(ggKiN0LPB>%BE*f~mCi=K025lN(>x*0d0>CwUbCVWpfIZpiZ{%!xby z-0^Kcuz`44Ufc`6iHU*R?F7~_aD3SYUNMYb*nsk>L@)Z88yj^k>_;)^AKf_11D&;? zTsLEZa3phI_naS8Wo$t--DbEt0J@;3g5}MEPspp$^6H0hM}Z*M?n+q61E+fod* zRT5g0Ku;X${ym8cB#djIycR-t3iDzu6F^b-&7El03`0>4_Dn898iaD!O=y2-H+KOW XPlaD9YYM{lP6B|na$1cAKS<=xbv#@qRqs7<*fbQzvCl^6>DSvb+v+ILgTX zkKpm6-5~HPk%vc+VhQ`xDU!HKjJ9_gm3g<#i6$h*{J7QdIqo<{_v+4@`PXF3Q~G(? zV5BS`aF`4QIYKNMW4Sh<%rP|v*oTs>DW)lm`AaUdI8)hB_bfE!WV+!osi@1B%37*c z;-`FpsBNY#8F6amy-DwWgk|5FUDLx}6nZk6gZlTO$Yff~sGxMxgQjU0P*ouP8{P29 z;h8G`v;Q&0snXnW_1aEGqr4V2xl_p{klz%NTbaYvA-947H$mUe0}PZ+lxAuu73+?5 zsbr$!C-w#o@|X^ouF4*AZEf>Wq$FdV=WBI5!1>l3WAp(PJ$oVTgVz5OA^s4^k{+Bmehm|aeV65j@K_% zM~mNFu*&hpQ04h3C_vm7wH;Z0^x(d>bSiSf{3>$7{gFIkDk8e9aPyr{ojJ{vs!C}p zon~*GThlg0R3WLqbC0_grzN+px7@IbQc;&WLJ>GTxvC%2IWVUCEj8oEOJGN|c0#ra zd%*27{M>Sxrp51pxh32@NVJO5$A4?9xzlo4S~Y#V8|7}ZhBn}U`lp=zdvcIQd4(=F z%D!%|U*DXP{GNiYy}JJ`5xrO7F2^~N^w zCO+qm=;X?+3Fkd9!zS#p1z1-UZKGJ59~Y(~p4%?;7HwDdeR&!!XQ-;|-_~?(_dDGN zZf4}Soa^AZufeeJa`ie~Y48=_r})c0RG+EAoa-yqyZO_rpK2CH=u%>S&7zjui+M!$ zn2CPKVQpj^03c5T!qT<#g_1VQ08J}wZAK=Vqid6p^}~{HC2uv)nX09j0fiOKptok< zZ_G7w^FNh-{x~Das19K?P1oi7DbSDA@-1dmVNHO{% z6>NRWm2|*Lh91n6s1X>Y8Vs!6XGz3zIT`jY2g5t5i-XEv9t@7EY(HxlkggCLGI-v%SCHLP?vknAfhMx17{_d3woWk zz3ceNt5DWl5jsW!#OXVg$yVbBFFxi=MZR(Agx;vR)_a1$aLj3c{kp3^WOFiKi5Rt> z<6GksGB=vkalP712`^0GPA63ERcwsq!7Lm&y?3Z{)OPyYhY?(o+cb+U~x<}@HUT8u9Gi-@?u zIy#jG#Z^=$7zdSPb@>!)byg(f?IK+0@`uqC)~$js**dfT~0y5wUG;y^vQ^@g-&M`leIjS~?|L zAwDx@_BM9&Jd<|Ed2#eDDp)ZTD}BukZ)qU!lP zN5tH&j)at6d$CJv1s@|t&L&P=6%UEOfoY)rRvG4{0}AuWYI%NW+=1~1awwli`3z!r zX=4r=16os<8$lAgr}B#;8~n1|gTr)qz+d(_8w$!|AiQ5q(y-KpD)^(;63b;}le_u$k> zHp2&bh9Ru0SSn%)Clr23tO21rW;9A7ghwnaa5KJ2e4C4myspvBzv*t*BxlP@E&?fR+puN$^@M6SHNyA3=(TX?)RF^nM+tK7;-$O$ zDqJU=rP&tFi3GR09a?%8;`$QL&9XESGhGbOH-+cRo3y3NYe(-PnE3)I0&_Ubj0)tH z8iYgIB#R`-lU>(4}qgnyk9#A$1N4)3>-tRQ_r s420sxC2%}F?E`&A{HX%R{}X1XlTQn$M>^uujy@M2OSmnp!puAFf6zcwGynhq literal 2368 zcmchZi9ZvJ1BchKkz3eX?zE6Ak)!mw@0e?gobx)~T#LlUm^atvN{-0Qlp}I8Lb*1_ zrm}>VBlMze&SZ|MU%!9i{d}J9^Zgf|6nm%zKaV&M007{(vNU!4{pkOX8}!@PeYI%- zz!@$pQzIC8``p(u})nf{XiHkH-|@8LO+HJDyX^RsKBh9HKI${ ziC#!ql z>%^oV*)3@tz4{%zTLACWMShu7I^@zc$tubEXI*AeX-eqA-ZbT}t-f6$(%Z#=s$|9% ztukz%lwCXHHj_Bp`dWehO+8?4z%rM(>+|lLf2Ou9?Mvo#8Gd-|x(NFr(cyw?Pp6!K z0Y;YRiexRRn&dH$q^Y{6CZJR@&CDo(N1lw`kx@^F|v@I&VCa- zOp#eIbjy6j1YI(FV-9mA7o_u~8Xmml!$`>^t47oGY7nTNpI{+ZoK zV1rh)&d~iXCf+O=pCxk~eukmvAy{U7^7wJ<)$?gI`M2&&0lAY0`vu=dZrEptIQQ!K zc57*;HeLAJq=l~x1S;`@oxZCz^0_$usA?il&(yNq;J%K$j|cSn+uTUZbAF^9Hav$< zBj)e`13&jb4lMd<;Fy^+GSW^Dt_jki2#?+kjX6(x(00#6V~X>fFP|~_j^|NOMC;N< zD91la#Z%AKU%k%H}F?!RzXpl{{ous&Aoy=!OH>(j(p2{n7${7bqcF(44oKO}( z+J-$X7XCn|WRe!0aY3+<%}!w2m0ff5lYyVdBT;2T(_)QGQL0ScX!ok?z0qE&tZFsn z+Hgs;k$^$Bf2r`jvXFj~IQycI^R^{`5$CoKyBWOd_pRQZ3Vn}Rx~6Xwi(4M4=!iwY z`U#pWGz4U~0#lrPm4#Lr6!Lz*)Hem=tl3>!pvRt-0OdXvmcrXLq;uk~>SxHK@FY!R zUvBuP8|5~qaElY7*-!sAP49{HDxi+0dPj}gh}W%L=$JJC;@%P~Mt9dsd$8V+v*{;% zu%5e1xDCE1Chg+rn-*xh79%pYXufucwJ&>~*U^}R&on4X_D~QHd)>lSVf52QOTnEd z5*(HxUyq1sJ=z^x4%<0t50SUQm7G6=jMKkr+aVQY|MXKZ=TQ{306D z1@ozVaE7P|7~`Gis3_+GQ$SK0bCsd~5CZv@>P@xIN|aE+5ykIvVA&_7@D8lLOZ2jB zLyb!5H`hh9On&uWJcb$hifHGf)kxt4I7P7@nC z{F)>fXweI`R#)pM5WL;6VuxfrbExjkO_-`v3?UD#u|qRpkl{6JYDiurvqM(1I>wYX zC(Q2w<~)c43jFy*iHTPb!9cYxY%aX?yiRm;cAbqch-jk!n{6LfP1twftbYq=N%zxu z%#Pyq)YFc%%5m(xv`-vn_yH62@B^9-=bG-BoUJmO7=LgF6f&YeFNZ+;!Asirz3h=G^7g_GPg#HsN{qi2h3h*h8?S_3$kD4h57v?0Qd0YJNb;PmaV9$2L9{elJ<#~=xLg`) zr|`@t;E#PeZ~ME-#au+Q9%Mrh{Jg>%!5}V;$7#v2Tfx4nDFZ7a;f%t4P);EqOE9gLhFnTv^`N^}8UKIoQ3lwh! z!O){Vk)*DYjA-T6p&#%F0MqOhfAMbmdPzFW`R9d*2RgevmU*3DK3v}^`^=W#bS&2p zV!9^uM^%D8f@Ptcl4#pH8vjb5b(X9gB;EJ%P`~T4{Zu`4u7x%urUG`Fr@q!rJ2yrO zDcD1f^U(s3`rNjwIK%Z{n6Ah4@8b#YhoI9#%$x>q=O((Iw6!eH<`H#1_I3Uh^{$qm iIYM9?$^V4nUng?*_=_f>=ApmK2w-IfHEl3P#QzW1Wo4=W diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png index 1a8f8e783a0661f25d5c20d2bc1ffaac1aeb35ca..41ad28d49916394727af2cc99a78a6544af1304a 100644 GIT binary patch delta 918 zcmV;H18My92cZX$8Gix*005C)ALal6187M^K~#90?bcgJQ*j)?@!#2wb93{k^Rh*5 z+C>vF3$cq7R1^u)1u9VSg7p#-L1CAtqTYgfkf=a0&4(1bJ#?X>>_T2bM6omyr_MH= zmmR0uj@!;W`$rU7bI$YMgnT&Pr}5{%Kc9_r{{KIzKHo*aihqmzWg=D_jQrmK-w7<7 z1Qt#L3nzhvlfc4BVBsXNaQegAldNd&XIKvL{F<5~x37_>t(L8ck*KMVaH*IS!7PWY zPLFBYJ!p1)9d>>mWG3vhL9sHlVY!)6rS%5n7p9`fyv(RW=@BVvDY4Y$(SuO;u=DAc zsfKn7&2WLR0Ds`nrbs=_UVA;}a~>rXUD&TG+@_R}BA!c&2qHaRmL4xF+o!5-q01Y` z8P-`6T7k58^GzNc@}F-uPNet#25F;P*dJ$z?CH{k++q`1&K z`I>-DZVnx<)pr>kH;%<;r&7`oQDuR4Z@JDi?Z#sPKvWoc|3qTWEgj8p7qe9|3Ezil zVSVGycfEE1sBfFhPNe`KGK{>iUv>7O5t{`78l|MVIH9C=XkSWrz$O4lj+5r3QC&ty zaaQ!);D3RQ)VasN`F;8q-A=b8$}0-A{NFp@tBZ-Mq6Du&d8jy6L5^DeZE1_L>@M#1 zhl!o*6~VB4h7hv5me?tLvp5ebtuuV=v-gfUH>jk`%jz&V0%0M9ELM%VYA+RvtE5}`g`Qe z10&0!{4L5LK?tF$Ci=Be1mTg>%s7XG)Ud6v!TDXP(r z`G4I@9Bx$c$z1u#yjdgLogBV3F)VeJG)6&s-Ea^>W476z5oep8dEaa69&z&5c(ZC+ zX*a)Lg8(`Wj=76~Tq06YBqbG#MZ~NJSsmP{)$MR&{4xGmJWUH@SkHi&4HB`im3^0A sX$dTx1Qt#L3nzhvlfc4BVBz!=#*Q&AkpzjwRN>E<2Sww2cB2r^iWJQh^&)eMa2UtNB85kP*Q#6=0DG5`? zt6*RfkfK(CRsbUb77lL^Jzs~>Ve{c#mlrQP+!%0;h%YkFl#IgpX(+4d1k2M!&(PR1 zKMm{iQZdt@27g6Syc-U(ESkH#xb=1jS6&Um?hXhN_`#T=wSLYxSnPfrf6y1P5TjP$ z;A%Ze7U`hkx?BJXibCGxB=Xz0N{@3d>?p78hua?%RA0y(*}WnI9dpYf*C6LTFQBHZvev43$Ax(5BIZ*z}%wkDTBRf!1+ zO8!GA7EC%7ZtR?j(hC;Uo83H0{3PtEMJp*(mYA@iq8;V|AGw!3ArTctMo}e(11z>* zw4llA!O6{&Nk^#@6sRgTVP(0QEDmvraHJ*ExU?e+1*gqqA|6_!CqqC?KK6U@xYb4e z*ED`1B7Yp?l4&vr_g=H|t}Eg`V$`W{t~3+dE`GwQnJMwc9YW3oH40`i#v8fEL)3B^U~srk_X%_U)$=8sZ3ar zhwmLYcBd!g{+_E=l2jHg&_ON{qj?;=_9 z;eP^ysA-CI(2paxdvL$SA*lQq5pE~t_Ka^)61SUghH$U>D@yazv2C#qnd!p6N|^h7 zIQznen#MuFM+p2X;`9cw^}GdrJlAc{FpJ7M8>;GTm}^YJ@|+Yb%t}V?L^Z~1>98q1 zH+1@(BWUUNqS4~UlU661yS;*g4=ZxM!D3EwpIG5aaM|jw8LLL6AZHaut%OoRVI;`H z;R&GEF-$t(PcSMXMB;aFXNq>4U$px8@c*6J|8-;$sUeF<4Ov8L$Rbih7LgjVh}8HF Xd45l}nI}0|00000NkvXXu0mjfX&T?7 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png index 4838a3edc62601a3f03cf83f3f95a40b34f25560..4df6ddb7f2bf59b98463f199245a7c6902d95ab9 100644 GIT binary patch literal 1910 zcmb`I`8OMg7RN)|X)U2jMHzI^P)n3j6wUBZgdo-gGnOPoLqw~wR4ARqPN^lrvyB?H zleTu%#FFXMF0r@TVk(MKTLt62f8o7zzW4h*=l*uj`JVepa)jRo3d;%u005wkwFTlN z(f^rqf+v0)CwK|~IBRNSVdfH1_@yA)&e>h&Rdn=B7!3AM>4|Ph2kRkZFi|-x2?mx4 zho_|wz{zy5Y%~53_V`~~&e!won{^Z~`Nn^^HsyhqeQyIs;$} z#-Rm%ZUS6J0x~rL*3y9geLck9^!IhPU@^rywPmroAHe&)@XZuaELCLETmf=96hddC z#SS?^%Hd#opO3Q`>gEKNHab3fCrjP_#^~A7H2JLo#Za(Yz83NcSH+#)IpT31R7@Wz zmhY!aEJ7_m_513W#fDs)QFybxThTV7)dnqKpvvFMnceI;(w7pNw3JhUW$2H|*rLxz zPoo_c8pnQL>UEEXt*pJ<~L{c&c>6j z@PtwJZng>kX1x#t1?NgQOYW3q{nZJ;2ZR{$Th&`L@3-^x0DBA_sn>wXb45ww0u?ykPqR>6i% zW$AmYBBPICIRCIhB1tJe41WzwmFVYK5Nx=MN8?UoxD6}GK?KzSl!CDxo0t|b;3-W` zcD-aFtIYBIwPc5kZ`}=AN5hM2{uetL;yM9Mq2T)ug1Etohvv>_0ZvquqGi8Zb8S<6Jz@(S-pYPBLrC`#M0q;_P3-%>a1~IzFuQ_{* zss%Hyj0&^5cF?DxKZlZk;d^J#uSR(FQ$lTT*lRj7Erg6J4f-^1P3!rTr~<>PscgTs zk%gBX)`7h27yR&S?f9ufpV(%o}c&n>Qput3%QPL(9vQgU}E&* zBp&C;mJNG!wLOd2K|jm)$hlx=dM=}tZ!u<|6tm7nYEJ}mo(g|S{!*Z=9#ew&Tm}B# zz+GlW`aYkdh-Bz7p>@Uby6MQ*WA(%X4)Jy|UEp*V#5JE(P7S(SGAcunC#yC&jks`w zSu&~nYyl~HrL`3zE|Us9xFNdJe?9w*7z{#F`1=l0^oIn8_q}rdc?yJ+>X*(R7ZBl@ z(CT}4XG`DAJ@=+uRr0(2)Y`DGtWX6k*xFJsP5+y%E-k^jl`VPjHvBg^Oeef7abPH` zMYE69kbfQ?Zd_4&Q#_CV$gz8(d3AY2NGuOHb4xCb+B6776t`9v%7b8yk-wJd?A_P@ zamj%eaxnG6=U6DO$+*Ywif2@sl!gGQrIp)qgs$$fyZ`L1y=mW`BQ#lJkJ4V7;2%@h zDsIP#>uD=>{H`g^U(|LDp(Og9?ppY(lf04xKqG5DE%_l6J{h5KmFamAXyuT}8>{Qe_dvm?jYIzh)=M~q(Mg4*zT3LYX8@R%2D4awlGG`w&}%u)T;)l(v%`CWo${Png>I*m=|;mkif03i$QDVeU5b zZP$nPCy4o|-@YFDAsLz3IqF_I*A4Yvd1_;$yFcSykE@Jrd*eX*z>g*3-M3uUwVI*% z8=|yPMt27<1?G+W$~U;Mf8sd5c$>%?WDPo;+SOzhy-2VRr_TqFUaVr~f;b@>xN?$2 zBJT&)X{(MjUK>z+W_uUYcJd^4^O{#N zG|d-jAHK08BNzS0n%+JIggZr;HT%C}<-crt1+_Lwa*6S9UpnC_ Nz{V18QD^QG{|_n{f&c&j literal 2008 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 b$`Ahmck5^Q)#W=&00000NkvXXu0mjfh;0X0 delta 1133 zcmV-z1d{u!2=@q(8Gi-<004{_gM|P91U^YbK~#90?U{RQQ&k+tzqh@;>$L&h+xoa`uQ`Wo?9r|F-aeKb?f-cDlx+E*;lB_TxnVV%mG$tb` z$telU0Vr~quxz0Kr_BVL*@!Hj1Bo7zVi+8a;@^OT^S%gt!(%DA=WI+!F3jbzv(kxY z_w+#4=vkaEV1NB}Hk6g*qSR%9E!(K5UjJnYr@KaRtX;$pEn>>0r*zkf)pop9>BQTI z`xFN-aKL>x*zwG|e3ZJf!Ev1GMikmi*s#V<%UD##Pi-RVPxx^1!iefv@t83tx89MD z3%&@x{KcQJ^19_YcxTfRtSC&~IT&x`Xm23pc)N(2Lw~)rOD7d``Um7qkcN8(BRJMR z9JdmAdv8`av8^m01`TgcDR-{0qH#wFUO(W)*S`;GjuUrwB~P>d2{$%<+=c1TWrQkXFGn zo>9E?jenQU_8&d&LeT<&_O6{(PIL@}>3CJkoOyDoLqNk**WjW0^E9=%9VWa`UXYAK zLUtV;KuceUUN`)F2{o0A=`|S^@7})zw|~%vv6#A;2^Rx5x-6)zcG1-G*e!Vw`1FlI z0-3}}8X=TSo6y{O0?Jof(b%HyZ7N#vkksDa?sl@D53;>m-zn z$uhR@^WYCp5REODv2l%EYx|N$mSx%owIq~$??@jG|8*tsq2>T~+*N?(i{|I=%Tl28JyZ}zSNpmCOake*z z_rLGQkFDyLanqS1Nm2#rik?Ab({wZjG@SHf@2NplU2ns~r4G9N5_oOnRSZRGqTlzc zA59&p*-K)wq&z8AsD(^O2hRj($q@{=tvClaxh=SMv3cV7gREf2w`F8wJQN&1{$>c{ zY;OpsyGHO^BksS+k6d;AhD$ubg&;b^N!%Vdb2$#~_Ahdh)_lCje|q7})o+&KTQ zBT1IgC0Rk2WCdN46?92f&?Q+xmt+N9k`?{~L(*vf<@wQ^00000NkvXXu0mjfwpcgj diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png index c12d6ccd99a04134c53d5ba5f3f346b90bc09fb0..853ac2e5a80159386d059f0112e5c2efb67a1227 100644 GIT binary patch literal 1996 zcmcJQ`#TegAIGOCbd=-NX-#gq6lrdaz0JnvsV&-4D_{kms|Fqe`9Ndf=>DN73& zYG42Px#9=*FQ7w40{}QovV=h$!YIPr2p{|Fr(Z?*e{fd7S|5>(=pt%fe#H`TeZ)Y< zT}$^caRD6{!vprT)l@(dR1*u%)pTh^>6fmeb4KDm1?R*ybxrh#b%5@^3_pQe)c9nj z_EodR_hVmo==kXTJ^BP?UEsmkjO=GF5L;*(#iHV_Fk1@hT$XlGA9!j{!dpzFDLxhh zHK8dMpr!53i&IaC@+A-auWeE9UK`S-%RY&tyupxxc=>QIt-BZP&o4ezSuFisAd>)M z+9?&f<6AaB#yw42j>*Zs!P~tA4u7uF>oZ7T6e_rU>k_Y1ELqL25(6IBy%dFab#LPx zn_ytf7B*2wd#r$z>Tb&{OL#EfCR^BEpM2p)h4abta_&tRPMbFlbW5yBcMn(*dV47J zw%4p!2XbNNwWXR<sBSfc zSR~#=8hnZilt-h)n zS5o}tE&AWMN;(yLawtA@Jvje?UNDk?nJYM_VT{err)kmxA_L#>bwd59w$?~~i~5AZ zyXWK;_%GXX?_wMf-8)E^MB; zkIOLmyz2Sm^{O*F!Au23^z;sFkH-0L%tS#LivDAtKiH(=nmwRud)=xh?GyEQlb?HsRTe|o8hqL#B`sW+ zv)qD#%Zcw>74>%W=%ze=PW*93n$ebr@+wCeSimx?_Aev!_gE=907Y;v^v};r8oM4T zmUtYs)BrvK28h@oKU}kcVA^i1w00BZoK_@$W&Vs?E(2Cw>$uu5zMRJ~uGm|@j+zeR z&tcbcu1x9HrE5?!^$Bi?ESAi&kuFUL2bW~H{6On=*5eo6_#K6|NLJ{@llMK}sT>2Q&>CrDKVdQH!6k38twVB^ZX-dUK+5hc@ktmP1&`~na z%T?>xEtntm&w8lettzZGYM(Tp*v5>RRd+6~KB2-)mtI6T<@RM;Yn-+!58$LfTfBRa zWP&|Z`Z_V=g{>z-mUl34E%qf=jbR2>u|MMFpgXp zO`xvAng)w>39-PUjRo0MDe&IFn?G{IV5uYKnk}4X8})b90Ia^2L@R$~N{% zBihL*Rww*%+C;|0Rtdd!h-QOUiDoeE%{0F}CJybJfw(-$wTC1$@)z!n-_i zVL-z4PYk8NsxoJrGvQ4gviY(<7-Yb;Xej@P%pi!7q- z$X;m2c45|C*-ztt^Z!|m33Q+Xz!{smd#+Gqpqjx-u9&Qwfi7$ZFeTKLze@b69z#S5sB>H zfb;jg`zPvZ)GY=>m)k3ADa?tTMcby3xsT;giDHN+Wrkaq+gp*>T=kqV#9!i=U zdu!KN2??g$CxUYQ;Yi^~+0w^3FAu4=r7DC0x48Lc>&(6hvcNXefP{rl!vkoMGY-%A zquCqmkEtHL$_<&M@3%Q$(IMCGZf!K+Mp;?22DuM2Adm7Mubm~LcLOyf9ap#r;$z8I z9eS%*xb?^yYLdI^FIMEU>Ua#m&2Z%@n-@-)GEF>44cirs25Pb3$II~hT?aN`8W>BZ zEn3b@a_e0Vjq*s~nXf5k2Tn!TkpKVy literal 2089 zcmchZ3qKPI1BWRcl9hWIp{1BhNF2;1wGk7xSlS}i(%fP!HqtmHGgIc4&~m>Vn@gG~ z>TEWb6FW?bQjg^MJHhV1mW*u&l5FeV6KcPF~XY;+K=U&mfZ` zJ8Mo`P#B?XleXJBGtSp~Y$g8jd5LNMs_{ud33sV&yP)OwT>$opkKw}{rv&TcN?yDI z9+ckWeVWjsyNc%MQ5K!6H03?apUqV-+2}MhmVLuQ9CR;bD40yyu%%YSE!=I{zgcy6 z`)mKzN?)sfpG^Y`dX0MF$*6M0aPp;ILIK+JzWU;)dnm*E-y;Rg_YE^e&X@s}lyGGQ zr=1>#RIPE1d)Z-UoNPn@+Orjh(ked;4r;Y_=*t@Wkv?wo3Tgzb*-+8ocM9#p4DuI*8#Zd5Lljz3GZUhg_oslB{4j$%1P z@Z$5>jQSD4mImjQg{9hX2&AR>X%D*972seqL48<-o2{x?I8XxHXBq61P1G@Y{vmUU zgIK>Sxw*mL+iq2-C&Vn<5>QMC!Qa_2-#IJeh}|!jUU`Q?S2+I&T*jh#^!Uxu(oU6e zr?=v6eXnv}_qyb2*HZJ5%421tiMNr*1DO+U5~06q9V&SCuBRv1gUj;{UvZOrB21pJ zVw6XseUyo4EcHV0a3{I-x)J+1ZGRuVyB@Z?iS(I#ONxnwE-6QMt4E--)f>95duw_y zEMQ9Z)+56H?r_Kn_d&KuP9s>ZFrOdpH`ZQ~%;I~!4b#Z)?{i7(%w#+N3P#5blw$Qm zdv}_5f{DS{Mi{Kv=B0Ms{kL#N7`7dF;DKj%-@EqLb^dJRY{z(8&B=GGLv1#Kr9;R* zu{Ro|x-@=lu~c0R!MFV~3Gmx$k`(4fyUbhxD7(}yVgA~}yhK7JTqMv=Iq$sL-qd!Y1S9iv zW4`gH&LXjJgBKOYu+v$)u#$*37gGJVGu;(qvOidp@Hl&2o!R%4u~xHD8O!94it7r@ z<--N%;C!Dv{EgZy*67Se;+=gHe2-KX&Md5`^X0B1ri>nw_zmtN@MBC3-Vo6si$%;| zd~H+O)Srsj(*#k_H;i-Jigh1{F;mS6%3jJ^vIF)mb84yTi!_;!jmqjbTNbIrqAYF0mH=TAR6IfQM{XGo6e_J^!=ap$c+Fb8(flveuHZRl4*8vGn33$S{#VB^=JD1{$zFK1VDk^Mh5tlxZ~%EMFAI9ZIg%@%=IKPa zE+trfHTB3xF*h{rF<`<`6W9Ob{?U6D~}zPw#4O99E#}7ot5q z8LPFUzCY|mr#a($g%)>Ua<)qpz!{64qziJIEfPsU;>oC>yrNh@+$~atF2>86SKGJC z?)3tq;&rI2(OZ8Vq?Y(=LrzP5`gbcsvxLISDm}$DctKYVV2n~_x7;k{&RoEigu2$e z<=9plQUt+jT?agPbPUq6%}6T=nfuUqq?yKEz;>?*MJ6-=XAk^2=+1eo`O``u>W1Y9 zWu@Mcs~`G%MG!fUHNZLVEQ%}4;pX+vkv}@l&fwduL7#L@0~@GQi+jQSS=vpHe0B5! z`v=#C=6rJ5-7}`r;pYkbfTX%K&Dy=dh4zS1wKB8-<2VseBfeFaCOGkU?#j)>Ms;6& zBK%0UE5>U(M(Ml$* d<$uhq-dyb3scqW)@XrL0aff+9>(2!J_8udl3 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index edc350621ac8e59fee869971390d159fa1778362..afac9e16c818408180b65aeab34e01ba974d793d 100644 GIT binary patch literal 2272 zcmchZ`9BkmAIFs=i{$R(T$E8tj>$1cAKS<=xbv#@qRqs7<*fbQzvCl^6>DSvb+v+ILgTX zkKpm6-5~HPk%vc+VhQ`xDU!HKjJ9_gm3g<#i6$h*{J7QdIqo<{_v+4@`PXF3Q~G(? zV5BS`aF`4QIYKNMW4Sh<%rP|v*oTs>DW)lm`AaUdI8)hB_bfE!WV+!osi@1B%37*c z;-`FpsBNY#8F6amy-DwWgk|5FUDLx}6nZk6gZlTO$Yff~sGxMxgQjU0P*ouP8{P29 z;h8G`v;Q&0snXnW_1aEGqr4V2xl_p{klz%NTbaYvA-947H$mUe0}PZ+lxAuu73+?5 zsbr$!C-w#o@|X^ouF4*AZEf>Wq$FdV=WBI5!1>l3WAp(PJ$oVTgVz5OA^s4^k{+Bmehm|aeV65j@K_% zM~mNFu*&hpQ04h3C_vm7wH;Z0^x(d>bSiSf{3>$7{gFIkDk8e9aPyr{ojJ{vs!C}p zon~*GThlg0R3WLqbC0_grzN+px7@IbQc;&WLJ>GTxvC%2IWVUCEj8oEOJGN|c0#ra zd%*27{M>Sxrp51pxh32@NVJO5$A4?9xzlo4S~Y#V8|7}ZhBn}U`lp=zdvcIQd4(=F z%D!%|U*DXP{GNiYy}JJ`5xrO7F2^~N^w zCO+qm=;X?+3Fkd9!zS#p1z1-UZKGJ59~Y(~p4%?;7HwDdeR&!!XQ-;|-_~?(_dDGN zZf4}Soa^AZufeeJa`ie~Y48=_r})c0RG+EAoa-yqyZO_rpK2CH=u%>S&7zjui+M!$ zn2CPKVQpj^03c5T!qT<#g_1VQ08J}wZAK=Vqid6p^}~{HC2uv)nX09j0fiOKptok< zZ_G7w^FNh-{x~Das19K?P1oi7DbSDA@-1dmVNHO{% z6>NRWm2|*Lh91n6s1X>Y8Vs!6XGz3zIT`jY2g5t5i-XEv9t@7EY(HxlkggCLGI-v%SCHLP?vknAfhMx17{_d3woWk zz3ceNt5DWl5jsW!#OXVg$yVbBFFxi=MZR(Agx;vR)_a1$aLj3c{kp3^WOFiKi5Rt> z<6GksGB=vkalP712`^0GPA63ERcwsq!7Lm&y?3Z{)OPyYhY?(o+cb+U~x<}@HUT8u9Gi-@?u zIy#jG#Z^=$7zdSPb@>!)byg(f?IK+0@`uqC)~$js**dfT~0y5wUG;y^vQ^@g-&M`leIjS~?|L zAwDx@_BM9&Jd<|Ed2#eDDp)ZTD}BukZ)qU!lP zN5tH&j)at6d$CJv1s@|t&L&P=6%UEOfoY)rRvG4{0}AuWYI%NW+=1~1awwli`3z!r zX=4r=16os<8$lAgr}B#;8~n1|gTr)qz+d(_8w$!|AiQ5q(y-KpD)^(;63b;}le_u$k> zHp2&bh9Ru0SSn%)Clr23tO21rW;9A7ghwnaa5KJ2e4C4myspvBzv*t*BxlP@E&?fR+puN$^@M6SHNyA3=(TX?)RF^nM+tK7;-$O$ zDqJU=rP&tFi3GR09a?%8;`$QL&9XESGhGbOH-+cRo3y3NYe(-PnE3)I0&_Ubj0)tH z8iYgIB#R`-lU>(4}qgnyk9#A$1N4)3>-tRQ_r s420sxC2%}F?E`&A{HX%R{}X1XlTQn$M>^uujy@M2OSmnp!puAFf6zcwGynhq literal 2368 zcmchZi9ZvJ1BchKkz3eX?zE6Ak)!mw@0e?gobx)~T#LlUm^atvN{-0Qlp}I8Lb*1_ zrm}>VBlMze&SZ|MU%!9i{d}J9^Zgf|6nm%zKaV&M007{(vNU!4{pkOX8}!@PeYI%- zz!@$pQzIC8``p(u})nf{XiHkH-|@8LO+HJDyX^RsKBh9HKI${ ziC#!ql z>%^oV*)3@tz4{%zTLACWMShu7I^@zc$tubEXI*AeX-eqA-ZbT}t-f6$(%Z#=s$|9% ztukz%lwCXHHj_Bp`dWehO+8?4z%rM(>+|lLf2Ou9?Mvo#8Gd-|x(NFr(cyw?Pp6!K z0Y;YRiexRRn&dH$q^Y{6CZJR@&CDo(N1lw`kx@^F|v@I&VCa- zOp#eIbjy6j1YI(FV-9mA7o_u~8Xmml!$`>^t47oGY7nTNpI{+ZoK zV1rh)&d~iXCf+O=pCxk~eukmvAy{U7^7wJ<)$?gI`M2&&0lAY0`vu=dZrEptIQQ!K zc57*;HeLAJq=l~x1S;`@oxZCz^0_$usA?il&(yNq;J%K$j|cSn+uTUZbAF^9Hav$< zBj)e`13&jb4lMd<;Fy^+GSW^Dt_jki2#?+kjX6(x(00#6V~X>fFP|~_j^|NOMC;N< zD91la#Z%AKU%k%H}F?!RzXpl{{ous&Aoy=!OH>(j(p2{n7${7bqcF(44oKO}( z+J-$X7XCn|WRe!0aY3+<%}!w2m0ff5lYyVdBT;2T(_)QGQL0ScX!ok?z0qE&tZFsn z+Hgs;k$^$Bf2r`jvXFj~IQycI^R^{`5$CoKyBWOd_pRQZ3Vn}Rx~6Xwi(4M4=!iwY z`U#pWGz4U~0#lrPm4#Lr6!Lz*)Hem=tl3>!pvRt-0OdXvmcrXLq;uk~>SxHK@FY!R zUvBuP8|5~qaElY7*-!sAP49{HDxi+0dPj}gh}W%L=$JJC;@%P~Mt9dsd$8V+v*{;% zu%5e1xDCE1Chg+rn-*xh79%pYXufucwJ&>~*U^}R&on4X_D~QHd)>lSVf52QOTnEd z5*(HxUyq1sJ=z^x4%<0t50SUQm7G6=jMKkr+aVQY|MXKZ=TQ{306D z1@ozVaE7P|7~`Gis3_+GQ$SK0bCsd~5CZv@>P@xIN|aE+5ykIvVA&_7@D8lLOZ2jB zLyb!5H`hh9On&uWJcb$hifHGf)kxt4I7P7@nC z{F)>fXweI`R#)pM5WL;6VuxfrbExjkO_-`v3?UD#u|qRpkl{6JYDiurvqM(1I>wYX zC(Q2w<~)c43jFy*iHTPb!9cYxY%aX?yiRm;cAbqch-jk!n{6LfP1twftbYq=N%zxu z%#Pyq)YFc%%5m(xv`-vn_yH62@B^9-=bG-BoUJmO7=LgF6f&YeFNZ+;!Asirz3h=G^7g_GPg#HsN{qi2h3h*h8?S_3$kD4h57v?0Qd0YJNb;PmaV9$2L9{elJ<#~=xLg`) zr|`@t;E#PeZ~ME-#au+Q9%Mrh{Jg>%!5}V;$7#v2Tfx4nDFZ7a;f%t4P);EqOE9gLhFnTv^`N^}8UKIoQ3lwh! z!O){Vk)*DYjA-T6p&#%F0MqOhfAMbmdPzFW`R9d*2RgevmU*3DK3v}^`^=W#bS&2p zV!9^uM^%D8f@Ptcl4#pH8vjb5b(X9gB;EJ%P`~T4{Zu`4u7x%urUG`Fr@q!rJ2yrO zDcD1f^U(s3`rNjwIK%Z{n6Ah4@8b#YhoI9#%$x>q=O((Iw6!eH<`H#1_I3Uh^{$qm iIYM9?$^V4nUng?*_=_f>=ApmK2w-IfHEl3P#QzW1Wo4=W diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index 595597ff62785a5da7669d9f99d07120c6f7ea15..84c10c34c18e770706c87691d584f9f728fe7e67 100644 GIT binary patch literal 3414 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 T>ca1Ti2-bCZPIG==d=F;s~2vn literal 3538 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 GUjG*o0Lw7| diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png index 116f99e905e5eb357136af0e69fe1971f7eb1ba2..07c8fb9d530943d556795edb3b172d01e9db8bf0 100644 GIT binary patch delta 1478 zcmV;%1v&cM49yFW8Gix*007#LBoF`q1(ZodK~#90?OAI~TV)vj>oe(gH24wA{+1 zamc2yuq|;iH87dW%z&{;hRh)vbb2Cvxf(dhR81ROm zq1#|MUkN-4db-p)A+l>()uw^N#)^!fGmd%o{I?{oUT?|*kWi11`DSOf$B;i?^u z6atn5f&f9dXon%SQW%9mU15;@Ph14#rX6+^G9Q(Tp$S0{;i?^u6m>%})D8p-ksJ#* zin^f~Y6pS^>I#b#k1(UC8;YTJAXtdxSh!Kt4aHD95G+tvSfqG_8MPFOYzB;4mtWKc z3K|RvW9S>WNPi5A#-h)^OB+rY%=oMkQ)sMuGfv{OR%1z6QFfeCZ@byA_1hK%L0KZ! zE(s?mH7ZNQ67!i1#O(`K8L)c_)9rDzOJ?jG((6rFU_zX+lw&XiY7eI#s2lE^G&x*x z^4oPAfF~rJqU^ZcnOtEkeYIMb?F0^zC*kBtILDp>7JnQ5hs^lHZB2_*qc&pX=U5Dj z)>M_guV!ds&f-mQ(_pA5JGMGsAQ3PfBmGDT1_I}0MCWBhzyF-*o4@AH|1>RETRhJ4 zZGleYB9|-0#dX65PdSh)W`9tUyjjF@dO#pxAkYgtc%@t8KD{+v)2_5){+o5Nj%A5h zb>(SC8-Hc)%t8=&`2;av&$pzvp?6ElI4_H!}`jdLWgfeoS|}K)?QE!h;!0Q`f8$_2I#k(ycrv zE2J#77*6zz8aYfjOB7|N_|Zsn)yAR=L&Gx`Uw;j*C?3mGAA+E>B}odc^~Z$;z@A7i z?TmjuH_pEbu{MPhL`8$F{U03w;GM$6>H>kCisv8~kBIktGH9{kr0wou2wW&nEv_5B zGd>>-u~D>BdxUF@4+M87Omoa(OW<*e|y|gU6=P;Q45@N1zk?^3M2+ohqMdujfs$D3*SuYGX;=P(dcw!IHjb zP2n-ll_uA=E2EjTGy6qOv&8XC$p+@2(o9Z{Iog>*=4a(;4<#_|Jwe*emTlveZR5F{ zl^)NyKW!ra1dUp7oTOEFec+X_eub&}x_|uhiZn5w5qy_iL#Fa=rS>drT4#IlixGnb zzcrwH`e9B0zWI+awbhbm7;@gNA|)VJdugZky2*Sgi)#eeMb z9ej7QNXM+f_S%(EoA29vx5DZ>RagIjmV|4;rNLyt9&VuahB zE)JlNZ%(z{)t)U$+Ae03o?|*Qet+)9RKu?-3@1oCLCe+F$}dMy3-0tXR-g8mDAlmx0H~MwCY3xI} zanEj!5pbO^bG(5SBYqz?eBV3Q(yP&%F^})m8!QRK32^+{*nq<1hn4OTLx1;(;oa{h z9!-wgC5g^SWoL<4X?zCFy@zu~+r26C!u6Y> zKglC75XR6GI0%>l@ z>I#c|MhY{Ex}g|q2Z9Ca3O|b!k1(UC8;YTJAXtdxSh!Kt4aHD95G+tvSfqG_8AaVt g47CHn0%ddTUs}S%tiTp{rvLx|07*qoM6N<$f`1jo$N&HU delta 1622 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 R|5Nbkv~TM{Ufr?_`4@@3-{$}T diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index e5d0101b2eef2bf23262b73797e4cae43666a024..e552fb24901eaaa8ba29fa8120858c9ecf020bd4 100644 GIT binary patch delta 1452 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 SUBj=9fZP!%=a)_wDE|O&5gOzG diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index bdfce70bdb0db93d07579224189132855e08d493..cd56d53d9bedc1505946cf845dcc0a2622e56019 100644 GIT binary patch literal 3101 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 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index d99c1a0c21561b5b23dfe1d7532869b7ec0d7288..5fa214fe363e2234321a59c653dd5a5e578f4771 100644 GIT binary patch literal 3336 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%0A{#ED&w zJu!FT-<~i`IKJ3^YqLY=i#; Dr7$*f diff --git a/lib/main.dart b/lib/main.dart index dd74e3d..154640c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,15 @@ import 'package:flutter/material.dart'; import 'screens/splash_screen.dart'; +import 'services/notification_service.dart'; -void main() { +void main() async { + // Pastikan Flutter binding sudah diinisialisasi + WidgetsFlutterBinding.ensureInitialized(); + + // Inisialisasi notification service + final notificationService = NotificationService(); + await notificationService.initialize(); + runApp(const MyApp()); } diff --git a/lib/screens/chart_screen.dart b/lib/screens/chart_screen.dart new file mode 100644 index 0000000..3a137b3 --- /dev/null +++ b/lib/screens/chart_screen.dart @@ -0,0 +1,740 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:intl/intl.dart'; +import '../services/api_service.dart'; +import '../models/sensor_data.dart'; + +class ChartScreen extends StatefulWidget { + const ChartScreen({super.key}); + + @override + State createState() => _ChartScreenState(); +} + +class _ChartScreenState extends State { + final ApiService _apiService = ApiService(); + List historyData = []; + bool isLoading = true; + String selectedPeriod = '50'; // 50, 100, 200 + + @override + void initState() { + super.initState(); + _loadHistoryData(); + } + + Future _loadHistoryData() async { + setState(() { + isLoading = true; + }); + + try { + final data = await _apiService.getDataHistory(limit: int.parse(selectedPeriod)); + if (mounted) { + setState(() { + historyData = data; + isLoading = false; + }); + } + } catch (e) { + print('Error loading history: $e'); + if (mounted) { + setState(() { + isLoading = false; + }); + } + } + } + + List _getSuhuSpots() { + return historyData.asMap().entries.map((entry) { + return FlSpot(entry.key.toDouble(), entry.value.suhu); + }).toList(); + } + + List _getBeratSpots() { + return historyData.asMap().entries.map((entry) { + return FlSpot(entry.key.toDouble(), entry.value.berat); + }).toList(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Grafik History'), + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + actions: [ + PopupMenuButton( + icon: const Icon(Icons.filter_list), + onSelected: (value) { + setState(() { + selectedPeriod = value; + _loadHistoryData(); + }); + }, + itemBuilder: (context) => [ + const PopupMenuItem(value: '50', child: Text('50 Data Terakhir')), + const PopupMenuItem(value: '100', child: Text('100 Data Terakhir')), + const PopupMenuItem(value: '200', child: Text('200 Data Terakhir')), + ], + ), + ], + ), + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : historyData.isEmpty + ? Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.inbox, size: 64, color: Colors.grey.shade400), + const SizedBox(height: 16), + Text( + 'Belum ada data history', + style: TextStyle( + fontSize: 16, + color: Colors.grey.shade600, + ), + ), + ], + ), + ) + : RefreshIndicator( + onRefresh: _loadHistoryData, + child: ListView( + padding: const EdgeInsets.all(16), + children: [ + // Info card + _buildInfoCard(), + const SizedBox(height: 16), + // Temperature chart + _buildTemperatureChart(), + const SizedBox(height: 16), + // Weight chart + _buildWeightChart(), + ], + ), + ), + ); + } + + Widget _buildInfoCard() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Colors.blue.shade400, Colors.blue.shade600], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + const Icon(Icons.info_outline, color: Colors.white, size: 32), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'History Data', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ), + const SizedBox(height: 4), + Text( + '$selectedPeriod data terakhir dari database', + style: const TextStyle( + fontSize: 12, + color: Colors.white70, + ), + ), + ], + ), + ), + IconButton( + onPressed: _loadHistoryData, + icon: const Icon(Icons.refresh, color: Colors.white), + ), + ], + ), + ); + } + + Widget _buildTemperatureChart() { + final suhuSpots = _getSuhuSpots(); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.orange.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon(Icons.thermostat, color: Colors.orange, size: 24), + ), + const SizedBox(width: 12), + const Expanded( + child: Text( + 'Grafik Suhu', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + const SizedBox(height: 16), + SizedBox( + height: 200, + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + drawVerticalLine: false, + horizontalInterval: 10, + getDrawingHorizontalLine: (value) => FlLine( + color: Colors.grey.shade200, + strokeWidth: 1, + ), + ), + titlesData: FlTitlesData( + show: true, + rightTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + topTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 30, + interval: suhuSpots.length > 20 ? 20 : 10, + getTitlesWidget: (value, meta) { + if (value.toInt() >= 0 && value.toInt() < historyData.length) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + '#${value.toInt()}', + style: const TextStyle(fontSize: 10, color: Colors.grey), + ), + ); + } + return const Text(''); + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 10, + reservedSize: 40, + getTitlesWidget: (value, meta) => Text( + '${value.toInt()}ยฐC', + style: const TextStyle(fontSize: 10, color: Colors.grey), + ), + ), + ), + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide(color: Colors.grey.shade300), + left: BorderSide(color: Colors.grey.shade300), + ), + ), + minX: 0, + maxX: suhuSpots.isNotEmpty ? suhuSpots.length.toDouble() - 1 : 10, + minY: 20, + maxY: 60, + lineBarsData: [ + LineChartBarData( + spots: suhuSpots, + isCurved: true, + color: Colors.orange, + barWidth: 3, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + Colors.orange.withOpacity(0.3), + Colors.orange.withOpacity(0.1), + Colors.transparent, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ], + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + getTooltipColor: (touchedSpot) => Colors.black87, + tooltipRoundedRadius: 8, + getTooltipItems: (spots) { + return spots.map((spot) { + final index = spot.x.toInt(); + if (index >= 0 && index < historyData.length) { + final data = historyData[index]; + return LineTooltipItem( + '${spot.y.toStringAsFixed(1)}ยฐC\n', + const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + children: [ + TextSpan( + text: data.timestamp, + style: const TextStyle( + fontSize: 10, + color: Colors.white70, + ), + ), + ], + ); + } + return null; + }).toList(); + }, + ), + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildWeightChart() { + final beratSpots = _getBeratSpots(); + final avgTarget = historyData.isNotEmpty + ? historyData.map((e) => e.target).reduce((a, b) => a + b) / historyData.length + : 0.0; + + // Filter data yang valid untuk ditampilkan (ganti nilai 0 dengan nilai sebelumnya atau null) + List displaySpots = []; + double? lastValidValue; + + for (var spot in beratSpots) { + if (spot.y > 10) { + // Data valid, gunakan nilai asli + displaySpots.add(spot); + lastValidValue = spot.y; + } else if (lastValidValue != null) { + // Data invalid (0), gunakan nilai terakhir yang valid + displaySpots.add(FlSpot(spot.x, lastValidValue)); + } + // Jika lastValidValue null dan spot.y invalid, skip point ini + } + + // Jika masih kosong setelah filter, gunakan data asli + if (displaySpots.isEmpty) { + displaySpots = beratSpots; + } + + // Hitung minY dan maxY yang aman untuk menghindari overflow + double minY = 0; + double maxY = 200; + + if (displaySpots.isNotEmpty) { + double dataMin = displaySpots.map((e) => e.y).reduce((a, b) => a < b ? a : b); + double dataMax = displaySpots.map((e) => e.y).reduce((a, b) => a > b ? a : b); + + // Sertakan target dalam perhitungan + if (avgTarget > 0) { + dataMin = dataMin < avgTarget ? dataMin : avgTarget; + dataMax = dataMax > avgTarget ? dataMax : avgTarget; + } + + // Tambah padding 25% + double range = dataMax - dataMin; + double padding = range > 0 ? range * 0.25 : 50; + + minY = (dataMin - padding).clamp(0, double.infinity); + maxY = dataMax + padding; + + // Pastikan range minimal 100 + if (maxY - minY < 100) { + double center = (maxY + minY) / 2; + minY = (center - 50).clamp(0, double.infinity); + maxY = center + 50; + } + } else if (avgTarget > 0) { + minY = 0; + maxY = (avgTarget * 1.5).clamp(100, double.infinity); + } + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: const Icon(Icons.scale, color: Colors.blue, size: 24), + ), + const SizedBox(width: 12), + const Expanded( + child: Text( + 'Grafik Berat', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + const SizedBox(height: 16), + SizedBox( + height: 200, + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + drawVerticalLine: false, + horizontalInterval: 50, + getDrawingHorizontalLine: (value) { + if ((value - avgTarget).abs() < 10) { + return FlLine( + color: Colors.green.shade300, + strokeWidth: 2, + dashArray: [5, 5], + ); + } + return FlLine( + color: Colors.grey.shade200, + strokeWidth: 1, + ); + }, + ), + titlesData: FlTitlesData( + show: true, + rightTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + topTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 30, + interval: beratSpots.length > 20 ? 20 : 10, + getTitlesWidget: (value, meta) { + if (value.toInt() >= 0 && value.toInt() < historyData.length) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + '#${value.toInt()}', + style: const TextStyle(fontSize: 10, color: Colors.grey), + ), + ); + } + return const Text(''); + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 50, + reservedSize: 45, + getTitlesWidget: (value, meta) { + final isTarget = (value - avgTarget).abs() < 10; + return Text( + '${value.toInt()}g', + style: TextStyle( + fontSize: 10, + color: isTarget ? Colors.green : Colors.grey, + fontWeight: isTarget ? FontWeight.bold : FontWeight.normal, + ), + ); + }, + ), + ), + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide(color: Colors.grey.shade300), + left: BorderSide(color: Colors.grey.shade300), + ), + ), + minX: 0, + maxX: beratSpots.isNotEmpty ? beratSpots.length.toDouble() - 1 : 10, + minY: minY, + maxY: maxY, + extraLinesData: ExtraLinesData( + horizontalLines: [ + if (avgTarget > 0) + HorizontalLine( + y: avgTarget, + color: Colors.green.shade400, + strokeWidth: 2, + dashArray: [8, 4], + label: HorizontalLineLabel( + show: true, + alignment: Alignment.topRight, + style: TextStyle( + color: Colors.green.shade700, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + labelResolver: (line) => 'Target', + ), + ), + ], + ), + lineBarsData: [ + LineChartBarData( + spots: displaySpots, + isCurved: true, + color: Colors.blue, + barWidth: 3, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + Colors.blue.withOpacity(0.3), + Colors.blue.withOpacity(0.1), + Colors.transparent, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ], + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + getTooltipColor: (touchedSpot) => Colors.black87, + tooltipRoundedRadius: 8, + getTooltipItems: (spots) { + return spots.map((spot) { + final index = spot.x.toInt(); + if (index >= 0 && index < historyData.length) { + final data = historyData[index]; + return LineTooltipItem( + '${spot.y.toStringAsFixed(0)}g\n', + const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + children: [ + TextSpan( + text: 'Target: ${data.target.toStringAsFixed(0)}g\n', + style: const TextStyle( + fontSize: 10, + color: Colors.green, + ), + ), + TextSpan( + text: data.timestamp, + style: const TextStyle( + fontSize: 10, + color: Colors.white70, + ), + ), + ], + ); + } + return null; + }).toList(); + }, + ), + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildStatisticsCard() { + if (historyData.isEmpty) return const SizedBox.shrink(); + + final avgSuhu = historyData.map((e) => e.suhu).reduce((a, b) => a + b) / historyData.length; + final maxSuhu = historyData.map((e) => e.suhu).reduce((a, b) => a > b ? a : b); + final minSuhu = historyData.map((e) => e.suhu).reduce((a, b) => a < b ? a : b); + + final avgBerat = historyData.map((e) => e.berat).reduce((a, b) => a + b) / historyData.length; + final maxBerat = historyData.map((e) => e.berat).reduce((a, b) => a > b ? a : b); + final minBerat = historyData.map((e) => e.berat).reduce((a, b) => a < b ? a : b); + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Statistik', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: _buildStatItem( + icon: Icons.thermostat, + iconColor: Colors.orange, + label: 'Rata-rata Suhu', + value: '${avgSuhu.toStringAsFixed(1)}ยฐC', + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildStatItem( + icon: Icons.scale, + iconColor: Colors.blue, + label: 'Rata-rata Berat', + value: '${avgBerat.toStringAsFixed(0)}g', + ), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: _buildStatItem( + icon: Icons.arrow_upward, + iconColor: Colors.red, + label: 'Max Suhu', + value: '${maxSuhu.toStringAsFixed(1)}ยฐC', + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildStatItem( + icon: Icons.arrow_downward, + iconColor: Colors.green, + label: 'Min Suhu', + value: '${minSuhu.toStringAsFixed(1)}ยฐC', + ), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + Expanded( + child: _buildStatItem( + icon: Icons.arrow_upward, + iconColor: Colors.red, + label: 'Max Berat', + value: '${maxBerat.toStringAsFixed(0)}g', + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildStatItem( + icon: Icons.arrow_downward, + iconColor: Colors.green, + label: 'Min Berat', + value: '${minBerat.toStringAsFixed(0)}g', + ), + ), + ], + ), + ], + ), + ); + } + + Widget _buildStatItem({ + required IconData icon, + required Color iconColor, + required String label, + required String value, + }) { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.grey.shade50, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + Icon(icon, color: iconColor, size: 24), + const SizedBox(height: 8), + Text( + value, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 4), + Text( + label, + style: const TextStyle( + fontSize: 11, + color: Colors.grey, + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/dashboard_screen.dart b/lib/screens/dashboard_screen.dart deleted file mode 100644 index b57ae93..0000000 --- a/lib/screens/dashboard_screen.dart +++ /dev/null @@ -1,1636 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mqtt_client/mqtt_client.dart'; -import 'package:fl_chart/fl_chart.dart'; -import 'package:intl/intl.dart'; -import '../services/mqtt_service.dart'; -import '../models/sensor_data.dart'; -import '../services/notification_manager.dart'; -import 'notification_screen.dart'; - -class DashboardScreen extends StatefulWidget { - const DashboardScreen({super.key}); - - @override - DashboardScreenState createState() => DashboardScreenState(); -} - -class DashboardScreenState extends State { - final MqttService _mqttService = MqttService(); - final NotificationManager _notificationManager = NotificationManager(); - - // Data dari sensor - double suhu = 35.2; - double berat = 420.0; - double target = 280.0; - - // Data historis untuk grafik - List suhuHistory = []; - List beratHistory = []; - double chartTimeCounter = 0; - - // Status relay - bool heater = false; - bool fanIn = false; - bool lamp = false; - bool exhaust = false; - - // Status sistem - String statusSistem = 'DISCONNECTED'; - bool isConnected = false; - String lastStatusMessage = 'Tekan tombol untuk memulai pengeringan'; - - // Waktu mulai dan durasi - DateTime? waktuMulai; - Duration durasi = Duration.zero; - - // Current navigation index - int _currentIndex = 0; - - // Status pengeringan - bool modePengeringan = false; - bool pengeringanSelesai = false; - bool beratTersimpan = false; - - // Track previous status untuk notifikasi - String _previousStatus = ''; - bool _previousBeratTersimpan = false; - - @override - void initState() { - super.initState(); - waktuMulai = DateTime.now(); // Inisialisasi waktu mulai - _initializeChartData(); - _initializeNotifications(); - _connectMQTT(); - _startDurationTimer(); - } - - void _initializeNotifications() async { - await _notificationManager.initialize(); - } - - void _initializeChartData() { - // Initialize dengan data dummy untuk demo - for (int i = 0; i < 10; i++) { - suhuHistory.add(FlSpot(i.toDouble(), 35.0 + (i % 3) * 0.5)); - beratHistory.add(FlSpot(i.toDouble(), 450.0 - i * 3.0)); - } - chartTimeCounter = 10; - } - - void _startDurationTimer() { - if (!mounted) return; - - Future.delayed(const Duration(seconds: 1), () { - if (!mounted) return; - - // Update durasi jika ada waktu mulai - if (waktuMulai != null) { - if (mounted) { - setState(() { - durasi = DateTime.now().difference(waktuMulai!); - }); - } - } - - // Lanjutkan timer - _startDurationTimer(); - }); - } - - // Connect ke MQTT broker - Future _connectMQTT() async { - if (!mounted) return; - - setState(() { - statusSistem = 'CONNECTING'; - }); - - try { - // Tambahkan timeout untuk mencegah stuck - final connected = await _mqttService.connectWithRetry() - .timeout( - const Duration(seconds: 10), - onTimeout: () { - print('โฑ๏ธ MQTT connection timeout'); - return false; - }, - ); - - if (!mounted) return; - - if (connected) { - setState(() { - isConnected = true; - // Jangan set status BERJALAN otomatis, tunggu data dari ESP32 - if (statusSistem == 'DISCONNECTED' || statusSistem == 'CONNECTING') { - statusSistem = 'READY'; - lastStatusMessage = 'Tekan tombol untuk memulai pengeringan'; - } - }); - - // Listen untuk data dari ESP32 - _mqttService.dataStream.listen((data) { - if (mounted) { - setState(() { - try { - final sensorData = SensorData.fromJson(data); - suhu = sensorData.suhu; - berat = sensorData.berat; - target = sensorData.target; - heater = sensorData.relay1; - fanIn = sensorData.relay2; - lamp = sensorData.relay3; - exhaust = sensorData.relay4; - - // Update status sistem berdasarkan data dari ESP32 - String newStatus = sensorData.status; - - if (newStatus == 'START') { - statusSistem = 'READY'; - modePengeringan = true; - pengeringanSelesai = false; - beratTersimpan = false; - lastStatusMessage = 'Menunggu deteksi ikan...'; - if (waktuMulai == null) { - waktuMulai = DateTime.now(); - } - - // Notifikasi pengeringan dimulai - if (_previousStatus != 'START') { - _notificationManager.notifyPengeringanDimulai(); - } - } else if (newStatus == 'BERJALAN') { - statusSistem = 'BERJALAN'; - modePengeringan = true; - pengeringanSelesai = false; - if (!beratTersimpan && target > 0) { - beratTersimpan = true; - lastStatusMessage = 'Proses pengeringan sedang berlangsung'; - - // Notifikasi ikan terdeteksi (hanya sekali) - if (!_previousBeratTersimpan) { - _notificationManager.notifyIkanTerdeteksi(berat); - } - } else if (!beratTersimpan) { - lastStatusMessage = 'Menunggu deteksi ikan...'; - } else { - lastStatusMessage = 'Proses pengeringan sedang berlangsung'; - } - } else if (newStatus == 'SELESAI') { - statusSistem = 'SELESAI'; - pengeringanSelesai = true; - lastStatusMessage = 'Pengeringan selesai, tekan tombol untuk reset'; - - // Notifikasi pengeringan selesai - if (_previousStatus != 'SELESAI') { - _notificationManager.notifyPengeringanSelesai(berat); - } - } else { - statusSistem = newStatus; - } - - // Update previous status - _previousStatus = newStatus; - _previousBeratTersimpan = beratTersimpan; - - // Update chart data - if (suhuHistory.length > 20) { - suhuHistory.removeAt(0); - beratHistory.removeAt(0); - // Reindex - for (int i = 0; i < suhuHistory.length; i++) { - suhuHistory[i] = FlSpot(i.toDouble(), suhuHistory[i].y); - beratHistory[i] = FlSpot(i.toDouble(), beratHistory[i].y); - } - chartTimeCounter = suhuHistory.length.toDouble(); - } - suhuHistory.add(FlSpot(chartTimeCounter, suhu)); - beratHistory.add(FlSpot(chartTimeCounter, berat)); - chartTimeCounter++; - - print('๐Ÿ“Š Data updated: $sensorData'); - } catch (e) { - print('โŒ Error parsing sensor data: $e'); - } - }); - } - }); - - // Listen untuk status messages - _mqttService.statusStream.listen((status) { - if (mounted) { - setState(() { - lastStatusMessage = status; - }); - } - }); - - // Listen untuk connection status - _mqttService.connectionStream.listen((state) { - if (mounted) { - setState(() { - isConnected = state == MqttConnectionState.connected; - }); - } - }); - } else { - if (mounted) { - setState(() { - isConnected = false; - statusSistem = 'DISCONNECTED'; - lastStatusMessage = 'Gagal terhubung ke server'; - }); - } - } - } catch (e) { - print('โŒ Error in _connectMQTT: $e'); - if (mounted) { - setState(() { - isConnected = false; - statusSistem = 'DISCONNECTED'; - lastStatusMessage = 'Error: ${e.toString()}'; - }); - } - } - } - - @override - void dispose() { - super.dispose(); - } - - String _formatDuration(Duration duration) { - String twoDigits(int n) => n.toString().padLeft(2, '0'); - String hours = twoDigits(duration.inHours); - String minutes = twoDigits(duration.inMinutes.remainder(60)); - String seconds = twoDigits(duration.inSeconds.remainder(60)); - return "$hours:$minutes:$seconds"; - } - - Widget _buildStatusCard() { - // Tentukan warna dan icon berdasarkan status - Color statusColor; - IconData statusIcon; - - if (statusSistem == 'SELESAI') { - statusColor = Colors.green; - statusIcon = Icons.check_circle; - } else if (statusSistem == 'BERJALAN') { - statusColor = Colors.blue; - statusIcon = Icons.play_circle; - } else if (statusSistem == 'READY') { - statusColor = Colors.orange; - statusIcon = Icons.hourglass_empty; - } else { - statusColor = Colors.grey; - statusIcon = Icons.info; - } - - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - children: [ - Row( - children: [ - // Icon status - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: statusColor.withOpacity(0.1), - shape: BoxShape.circle, - ), - child: Icon( - statusIcon, - color: statusColor, - size: 24, - ), - ), - const SizedBox(width: 10), - // Status text - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Status Pengeringan', - style: TextStyle( - fontSize: 11, - color: Colors.grey, - ), - ), - const SizedBox(height: 2), - Text( - statusSistem, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: statusColor, - ), - ), - const SizedBox(height: 2), - Row( - children: [ - Container( - width: 5, - height: 5, - decoration: BoxDecoration( - color: statusColor, - shape: BoxShape.circle, - ), - ), - const SizedBox(width: 5), - Expanded( - child: Text( - lastStatusMessage, - style: const TextStyle( - fontSize: 9, - color: Colors.grey, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], - ), - ), - ], - ), - const SizedBox(height: 10), - // Waktu dan durasi dalam row terpisah - Row( - children: [ - Expanded( - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Colors.grey.shade50, - borderRadius: BorderRadius.circular(8), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Waktu Mulai', - style: TextStyle( - fontSize: 9, - color: Colors.grey, - ), - ), - const SizedBox(height: 3), - Text( - waktuMulai != null - ? DateFormat('HH:mm:ss').format(waktuMulai!) - : '--:--:--', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ), - const SizedBox(width: 8), - Expanded( - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: statusColor.withOpacity(0.1), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Durasi', - style: TextStyle( - fontSize: 9, - color: Colors.grey, - ), - ), - const SizedBox(height: 3), - Row( - children: [ - Icon(Icons.timer, size: 13, color: statusColor), - const SizedBox(width: 3), - Expanded( - child: Text( - _formatDuration(durasi), - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.bold, - color: statusColor, - ), - maxLines: 1, - ), - ), - ], - ), - ], - ), - ), - ), - ], - ), - ], - ), - ); - } - - Widget _buildMetricCard({ - required IconData icon, - required Color iconColor, - required String label, - required String value, - required String unit, - required String status, - required List chartData, - required Color chartColor, - }) { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - color: iconColor.withOpacity(0.1), - borderRadius: BorderRadius.circular(6), - ), - child: Icon(icon, color: iconColor, size: 16), - ), - const SizedBox(width: 6), - Expanded( - child: Text( - label, - style: const TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 8), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Flexible( - child: Text( - value, - style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - const SizedBox(width: 3), - Padding( - padding: const EdgeInsets.only(bottom: 3), - child: Text( - unit, - style: const TextStyle( - fontSize: 11, - color: Colors.grey, - ), - ), - ), - ], - ), - const SizedBox(height: 2), - Text( - status, - style: const TextStyle( - fontSize: 9, - color: Colors.grey, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ); - } - - Widget _buildProgressCard() { - // Hitung progress: dari berat awal ke target - // Progress = (berat awal - berat sekarang) / (berat awal - target) * 100 - // Asumsi: berat awal = berat sekarang + (berat sekarang - target) jika berat > target - double progress = 0; - if (target > 0 && berat > target) { - // Berat masih di atas target, hitung progress - double beratAwal = berat + (berat - target); // estimasi berat awal - progress = ((beratAwal - berat) / (beratAwal - target) * 100).clamp(0, 100); - } else if (target > 0 && berat <= target) { - // Sudah mencapai atau melewati target - progress = 100; - } - - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'Progres', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - ), - const SizedBox(height: 10), - Center( - child: SizedBox( - width: 75, - height: 75, - child: Stack( - alignment: Alignment.center, - children: [ - SizedBox( - width: 75, - height: 75, - child: CircularProgressIndicator( - value: progress / 100, - strokeWidth: 6, - backgroundColor: Colors.grey.shade200, - valueColor: const AlwaysStoppedAnimation(Colors.green), - ), - ), - Text( - '${progress.toInt()}%', - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.green, - ), - ), - ], - ), - ), - ), - const SizedBox(height: 8), - const Center( - child: Text( - 'Menuju Target', - style: TextStyle( - fontSize: 9, - color: Colors.grey, - ), - ), - ), - ], - ), - ); - } - - Widget _buildProgressBar() { - double sisaBerat = berat - target; - if (sisaBerat < 0) sisaBerat = 0; - - // Hitung progress: semakin mendekati target, semakin tinggi progress - double progressValue = 0; - if (target > 0 && berat > target) { - // Asumsi berat awal adalah 2x target atau berat saat ini + sisa - double beratAwal = berat + sisaBerat; - progressValue = ((beratAwal - berat) / (beratAwal - target)).clamp(0.0, 1.0); - } else if (target > 0 && berat <= target) { - progressValue = 1.0; // Sudah mencapai target - } - - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.1), - borderRadius: BorderRadius.circular(6), - ), - child: const Icon(Icons.water_drop, color: Colors.blue, size: 16), - ), - const SizedBox(width: 6), - const Text( - 'Progress Pengeringan', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), - const SizedBox(height: 6), - Text( - 'Sisa ${sisaBerat.toInt()} gram menuju target', - style: const TextStyle( - fontSize: 10, - color: Colors.grey, - ), - ), - const SizedBox(height: 10), - ClipRRect( - borderRadius: BorderRadius.circular(6), - child: LinearProgressIndicator( - value: progressValue, - minHeight: 10, - backgroundColor: Colors.grey.shade200, - valueColor: const AlwaysStoppedAnimation(Colors.green), - ), - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - '${berat.toInt()} g', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - const SizedBox(width: 3), - Container( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), - decoration: BoxDecoration( - color: Colors.grey.shade200, - borderRadius: BorderRadius.circular(3), - ), - child: const Icon(Icons.flag, size: 8, color: Colors.grey), - ), - ], - ), - const Text( - 'Berat Saat Ini', - style: TextStyle( - fontSize: 8, - color: Colors.grey, - ), - ), - ], - ), - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '${target.toInt()} g', - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - const Text( - 'Target Berat', - style: TextStyle( - fontSize: 8, - color: Colors.grey, - ), - ), - ], - ), - ), - ], - ), - ], - ), - ); - } - - Widget _buildDeviceCard({ - required IconData icon, - required Color iconColor, - required String label, - required String description, - required bool status, - }) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: iconColor.withOpacity(0.1), - borderRadius: BorderRadius.circular(10), - ), - child: Icon(icon, color: iconColor, size: 24), - ), - PopupMenuButton( - icon: const Icon(Icons.more_vert, size: 18), - itemBuilder: (context) => [ - const PopupMenuItem( - value: 'info', - child: Text('Info'), - ), - ], - ), - ], - ), - const SizedBox(height: 10), - Text( - label, - style: const TextStyle( - fontSize: 13, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 2), - Text( - description, - style: const TextStyle( - fontSize: 10, - color: Colors.grey, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 10), - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - decoration: BoxDecoration( - color: status ? iconColor.withOpacity(0.1) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - status ? 'ON' : 'OFF', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: status ? iconColor : Colors.grey, - ), - ), - ), - const SizedBox(height: 8), - // Toggle switch - Transform.scale( - scale: 0.75, - child: Switch( - value: status, - onChanged: null, // Disabled karena mode monitoring - activeColor: iconColor, - ), - ), - ], - ), - ); - } - - Widget _buildCombinedHeaterFanCard() { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Flexible( - child: Text( - 'PTC Heater & Fan In', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - PopupMenuButton( - icon: const Icon(Icons.more_vert, size: 16), - itemBuilder: (context) => [ - const PopupMenuItem( - value: 'info', - child: Text('Info'), - ), - ], - ), - ], - ), - const SizedBox(height: 10), - - // PTC Heater - Row( - children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: Colors.deepOrange.withOpacity(0.1), - borderRadius: BorderRadius.circular(6), - ), - child: const Icon(Icons.whatshot, color: Colors.deepOrange, size: 18), - ), - const SizedBox(width: 8), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'PTC Heater', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - Text( - 'Pemanas', - style: TextStyle( - fontSize: 8, - color: Colors.grey, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), - decoration: BoxDecoration( - color: heater ? Colors.deepOrange.withOpacity(0.1) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(5), - ), - child: Text( - heater ? 'ON' : 'OFF', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: heater ? Colors.deepOrange : Colors.grey, - ), - ), - ), - ], - ), - const SizedBox(height: 10), - - // Divider - Divider(color: Colors.grey.shade200, height: 1), - const SizedBox(height: 10), - - // Fan In - Row( - children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: Colors.blue.withOpacity(0.1), - borderRadius: BorderRadius.circular(6), - ), - child: const Icon(Icons.air, color: Colors.blue, size: 18), - ), - const SizedBox(width: 8), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Fan In', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - Text( - 'Kipas Masuk', - style: TextStyle( - fontSize: 8, - color: Colors.grey, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), - decoration: BoxDecoration( - color: fanIn ? Colors.blue.withOpacity(0.1) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(5), - ), - child: Text( - fanIn ? 'ON' : 'OFF', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: fanIn ? Colors.blue : Colors.grey, - ), - ), - ), - ], - ), - ], - ), - ); - } - - Widget _buildExhaustCard() { - return Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Flexible( - child: Text( - 'Exhaust Fan', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - PopupMenuButton( - icon: const Icon(Icons.more_vert, size: 16), - itemBuilder: (context) => [ - const PopupMenuItem( - value: 'info', - child: Text('Info'), - ), - ], - ), - ], - ), - const SizedBox(height: 10), - - // Exhaust - Row( - children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: Colors.teal.withOpacity(0.1), - borderRadius: BorderRadius.circular(6), - ), - child: const Icon(Icons.wind_power, color: Colors.teal, size: 18), - ), - const SizedBox(width: 8), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Exhaust', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - Text( - 'Kipas Keluar', - style: TextStyle( - fontSize: 8, - color: Colors.grey, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), - decoration: BoxDecoration( - color: exhaust ? Colors.teal.withOpacity(0.1) : Colors.grey.shade100, - borderRadius: BorderRadius.circular(5), - ), - child: Text( - exhaust ? 'ON' : 'OFF', - style: TextStyle( - fontSize: 10, - fontWeight: FontWeight.bold, - color: exhaust ? Colors.teal : Colors.grey, - ), - ), - ), - ], - ), - ], - ), - ); - } - - - Widget _buildRealtimeChart() { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Colors.purple.withOpacity(0.1), - borderRadius: BorderRadius.circular(8), - ), - child: const Icon(Icons.show_chart, color: Colors.purple, size: 20), - ), - const SizedBox(width: 8), - const Text( - 'Grafik Real-time', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - ], - ), - const SizedBox(height: 20), - // Suhu Chart - Row( - children: [ - Container( - width: 8, - height: 8, - decoration: const BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - ), - const SizedBox(width: 8), - const Text( - 'Grafik Suhu (ยฐC)', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - ), - ], - ), - const SizedBox(height: 12), - SizedBox( - height: 120, - child: LineChart( - LineChartData( - gridData: FlGridData( - show: true, - drawVerticalLine: false, - horizontalInterval: 5, - getDrawingHorizontalLine: (value) { - return FlLine( - color: Colors.grey.shade200, - strokeWidth: 1, - ); - }, - ), - titlesData: FlTitlesData( - leftTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: 35, - interval: 5, - getTitlesWidget: (value, meta) { - return Text( - value.toInt().toString(), - style: const TextStyle(fontSize: 10, color: Colors.grey), - ); - }, - ), - ), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: 25, - interval: 2, - getTitlesWidget: (value, meta) { - int minutes = (value.toInt() * 6) % 60; - int hours = 10 + ((value.toInt() * 6) ~/ 60); - return Padding( - padding: const EdgeInsets.only(top: 8), - child: Text( - '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}', - style: const TextStyle(fontSize: 9, color: Colors.grey), - ), - ); - }, - ), - ), - rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), - topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), - ), - borderData: FlBorderData(show: false), - minY: 20, - maxY: 45, - lineBarsData: [ - LineChartBarData( - spots: suhuHistory, - isCurved: true, - color: Colors.red, - barWidth: 2.5, - dotData: FlDotData( - show: true, - getDotPainter: (spot, percent, barData, index) { - if (index == suhuHistory.length - 1) { - return FlDotCirclePainter( - radius: 5, - color: Colors.white, - strokeWidth: 2.5, - strokeColor: Colors.red, - ); - } - return FlDotCirclePainter( - radius: 0, - color: Colors.transparent, - ); - }, - ), - belowBarData: BarAreaData( - show: true, - color: Colors.red.withOpacity(0.1), - ), - ), - ], - lineTouchData: LineTouchData( - touchTooltipData: LineTouchTooltipData( - getTooltipItems: (touchedSpots) { - return touchedSpots.map((spot) { - return LineTooltipItem( - '${spot.y.toStringAsFixed(1)}ยฐC', - const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ); - }).toList(); - }, - ), - ), - ), - ), - ), - const SizedBox(height: 24), - // Berat Chart - Row( - children: [ - Container( - width: 8, - height: 8, - decoration: const BoxDecoration( - color: Colors.blue, - shape: BoxShape.circle, - ), - ), - const SizedBox(width: 8), - const Text( - 'Grafik Berat (gram)', - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - ), - ], - ), - const SizedBox(height: 12), - SizedBox( - height: 120, - child: LineChart( - LineChartData( - gridData: FlGridData( - show: true, - drawVerticalLine: false, - horizontalInterval: 50, - getDrawingHorizontalLine: (value) { - return FlLine( - color: Colors.grey.shade200, - strokeWidth: 1, - ); - }, - ), - titlesData: FlTitlesData( - leftTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: 35, - interval: 100, - getTitlesWidget: (value, meta) { - return Text( - value.toInt().toString(), - style: const TextStyle(fontSize: 10, color: Colors.grey), - ); - }, - ), - ), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: 25, - interval: 2, - getTitlesWidget: (value, meta) { - int minutes = (value.toInt() * 6) % 60; - int hours = 10 + ((value.toInt() * 6) ~/ 60); - return Padding( - padding: const EdgeInsets.only(top: 8), - child: Text( - '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}', - style: const TextStyle(fontSize: 9, color: Colors.grey), - ), - ); - }, - ), - ), - rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), - topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), - ), - borderData: FlBorderData(show: false), - minY: 0, - maxY: 600, - lineBarsData: [ - LineChartBarData( - spots: beratHistory, - isCurved: true, - color: Colors.blue, - barWidth: 2.5, - dotData: FlDotData( - show: true, - getDotPainter: (spot, percent, barData, index) { - if (index == beratHistory.length - 1) { - return FlDotCirclePainter( - radius: 5, - color: Colors.white, - strokeWidth: 2.5, - strokeColor: Colors.blue, - ); - } - return FlDotCirclePainter( - radius: 0, - color: Colors.transparent, - ); - }, - ), - belowBarData: BarAreaData( - show: true, - color: Colors.blue.withOpacity(0.1), - ), - ), - ], - lineTouchData: LineTouchData( - touchTooltipData: LineTouchTooltipData( - getTooltipItems: (touchedSpots) { - return touchedSpots.map((spot) { - return LineTooltipItem( - '${spot.y.toStringAsFixed(0)}g', - const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ); - }).toList(); - }, - ), - ), - ), - ), - ), - ], - ), - ); - } - - @override - Widget build(BuildContext context) { - // List of screens - final List screens = [ - _buildDashboardContent(), - const NotificationScreen(), - ]; - - return Scaffold( - backgroundColor: Colors.grey.shade50, - appBar: _currentIndex == 0 ? AppBar( - backgroundColor: Colors.white, - elevation: 0, - title: const Text( - 'Dashboard', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.black, - ), - ), - actions: [ - Padding( - padding: const EdgeInsets.only(right: 16), - child: Row( - children: [ - Icon( - Icons.wifi, - color: isConnected ? Colors.green : Colors.grey, - size: 18, - ), - const SizedBox(width: 6), - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: isConnected ? Colors.green : Colors.grey, - borderRadius: BorderRadius.circular(12), - ), - child: Row( - children: [ - Container( - width: 6, - height: 6, - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - ), - const SizedBox(width: 6), - Text( - isConnected ? 'ONLINE' : 'OFFLINE', - style: const TextStyle( - color: Colors.white, - fontSize: 11, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(right: 12), - child: Center( - child: Text( - DateFormat('HH:mm:ss').format(DateTime.now()), - style: const TextStyle( - fontSize: 12, - color: Colors.grey, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ], - ) : null, - body: screens[_currentIndex], - bottomNavigationBar: BottomNavigationBar( - type: BottomNavigationBarType.fixed, - backgroundColor: Colors.white, - selectedItemColor: Colors.blue, - unselectedItemColor: Colors.grey, - currentIndex: _currentIndex, - onTap: (index) { - setState(() { - _currentIndex = index; - }); - }, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'Dashboard', - ), - BottomNavigationBarItem( - icon: Icon(Icons.notifications), - label: 'Notifikasi', - ), - ], - ), - ); - } - - Widget _buildDashboardContent() { - return RefreshIndicator( - onRefresh: () async { - try { - await _connectMQTT(); - } catch (e) { - print('โŒ Error during refresh: $e'); - // Tetap selesaikan refresh meskipun error - } - }, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Status Card - _buildStatusCard(), - const SizedBox(height: 12), - - // Metrics Row - Row( - children: [ - Expanded( - child: _buildMetricCard( - icon: Icons.thermostat, - iconColor: Colors.red, - label: 'Suhu', - value: suhu.toStringAsFixed(1), - unit: 'ยฐC', - status: 'Normal', - chartData: suhuHistory, - chartColor: Colors.red, - ), - ), - const SizedBox(width: 10), - Expanded( - child: _buildMetricCard( - icon: Icons.scale, - iconColor: Colors.blue, - label: 'Berat Ikan', - value: berat.toStringAsFixed(0), - unit: 'gram', - status: 'Berat Saat Ini', - chartData: beratHistory, - chartColor: Colors.blue, - ), - ), - ], - ), - const SizedBox(height: 10), - - // Target and Progress Row - Row( - children: [ - Expanded( - child: _buildMetricCard( - icon: Icons.flag, - iconColor: Colors.purple, - label: 'Target Berat', - value: target.toStringAsFixed(0), - unit: 'gram', - status: 'Berat Target', - chartData: const [], - chartColor: Colors.purple, - ), - ), - const SizedBox(width: 10), - Expanded( - child: _buildProgressCard(), - ), - ], - ), - const SizedBox(height: 12), - - // Progress Bar - _buildProgressBar(), - const SizedBox(height: 12), - - // Device Cards Grid - Row( - children: [ - Expanded( - child: _buildCombinedHeaterFanCard(), - ), - const SizedBox(width: 10), - Expanded( - child: _buildExhaustCard(), - ), - ], - ), - const SizedBox(height: 12), - ], - ), - ), - ); - } -} diff --git a/lib/screens/dashboard_screen_with_api.dart b/lib/screens/dashboard_screen_with_api.dart index eb2a23f..2ed38fb 100644 --- a/lib/screens/dashboard_screen_with_api.dart +++ b/lib/screens/dashboard_screen_with_api.dart @@ -3,9 +3,14 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; import '../services/api_service.dart'; import '../services/notification_manager.dart'; import '../models/sensor_data.dart'; +import '../widgets/temperature_chart.dart'; +import '../widgets/weight_chart.dart'; +import 'chart_screen.dart'; +import 'notification_screen.dart'; class DashboardScreenWithApi extends StatefulWidget { const DashboardScreenWithApi({super.key}); @@ -23,6 +28,11 @@ class _DashboardScreenWithApiState extends State { double berat = 0.0; double target = 0.0; + // Data historis untuk grafik + List suhuHistory = []; + List beratHistory = []; + double chartTimeCounter = 0; + // Status relay bool heater = false; bool fanIn = false; @@ -38,10 +48,14 @@ class _DashboardScreenWithApiState extends State { // Timer untuk auto-update Timer? _pollingTimer; + + // Current navigation index + int _currentIndex = 0; @override void initState() { super.initState(); + _initializeChartData(); // Load initial chart data _initializeNotifications(); _initializeConnections(); _startPolling(); @@ -52,6 +66,53 @@ class _DashboardScreenWithApiState extends State { await _notificationManager.initialize(); } + // Initialize chart data + Future _initializeChartData() async { + // Init dengan dummy data dulu + _initializeDummyChartData(); + + try { + // Load 20 data terakhir dari API untuk grafik + final historyData = await _apiService.getDataHistory(limit: 20); + + if (historyData.isNotEmpty && mounted) { + setState(() { + suhuHistory.clear(); + beratHistory.clear(); + + for (int i = 0; i < historyData.length; i++) { + final data = historyData[i]; + suhuHistory.add(FlSpot(i.toDouble(), data.suhu)); + beratHistory.add(FlSpot(i.toDouble(), data.berat)); + } + + chartTimeCounter = historyData.length.toDouble(); + }); + } + } catch (e) { + print('โŒ Error loading chart data: $e'); + // Tetap pakai dummy data jika error + } + } + + void _initializeDummyChartData() { + suhuHistory.clear(); + beratHistory.clear(); + + double beratAwal = 450.0; + double suhuBase = 28.0; + + for (int i = 0; i < 15; i++) { + double suhuVal = suhuBase + (i * 0.3) + (i % 2 == 0 ? 0.2 : -0.1); + suhuHistory.add(FlSpot(i.toDouble(), suhuVal)); + + double beratVal = beratAwal - (i * 2.5); + beratHistory.add(FlSpot(i.toDouble(), beratVal)); + } + + chartTimeCounter = 15; + } + // Auto-update data setiap 5 detik void _startPolling() { _pollingTimer = Timer.periodic(const Duration(seconds: 5), (timer) { @@ -98,18 +159,26 @@ class _DashboardScreenWithApiState extends State { fanIn = data.relay2; exhaust = data.relay4; + // Update chart data + if (suhuHistory.length > 20) { + suhuHistory.removeAt(0); + beratHistory.removeAt(0); + // Reindex + for (int i = 0; i < suhuHistory.length; i++) { + suhuHistory[i] = FlSpot(i.toDouble(), suhuHistory[i].y); + beratHistory[i] = FlSpot(i.toDouble(), beratHistory[i].y); + } + chartTimeCounter = suhuHistory.length.toDouble(); + } + suhuHistory.add(FlSpot(chartTimeCounter, suhu)); + beratHistory.add(FlSpot(chartTimeCounter, berat)); + chartTimeCounter++; + // Update status dan trigger notifikasi String newStatus = data.status; // Notifikasi hanya untuk pengeringan selesai if (newStatus != _previousStatus) { - // HAPUS notifikasi untuk status lain - // if (newStatus == 'READY' && _previousStatus != 'READY') { - // _notificationManager.notifyPengeringanDimulai(); - // } else if (newStatus == 'BERJALAN' && _previousStatus != 'BERJALAN') { - // _notificationManager.notifyIkanTerdeteksi(berat); - // } - // HANYA notifikasi pengeringan selesai if (newStatus == 'SELESAI' && _previousStatus != 'SELESAI') { print('๐Ÿ”” Dashboard: Status changed to SELESAI'); @@ -169,8 +238,14 @@ class _DashboardScreenWithApiState extends State { @override Widget build(BuildContext context) { + // List of screens + final List screens = [ + _buildDashboardContent(), + const NotificationScreen(), + ]; + return Scaffold( - appBar: AppBar( + appBar: _currentIndex == 0 ? AppBar( title: const Text('Dashboard Pengering Ikan'), actions: [ // API status only @@ -183,95 +258,155 @@ class _DashboardScreenWithApiState extends State { ), ), ], - ), - body: RefreshIndicator( - onRefresh: _loadLatestData, - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Status Card - _buildStatusCard(), - const SizedBox(height: 16), - - // Metrics - Row( - children: [ - Expanded( - child: _buildMetricCard( - icon: Icons.thermostat, - iconColor: Colors.orange, - label: 'Suhu', - value: suhu.toStringAsFixed(1), - unit: 'ยฐC', - ), - ), - const SizedBox(width: 12), - Expanded( - child: _buildMetricCard( - icon: Icons.scale, - iconColor: Colors.blue, - label: 'Berat', - value: berat.toStringAsFixed(0), - unit: 'g', - ), - ), - ], - ), - const SizedBox(height: 12), - - // Target - _buildMetricCard( - icon: Icons.flag, - iconColor: Colors.green, - label: 'Target Berat', - value: target.toStringAsFixed(0), - unit: 'g', - ), - const SizedBox(height: 16), - - // Devices - const Text( - 'Perangkat', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 12), - - GridView.count( - crossAxisCount: 3, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - mainAxisSpacing: 12, - crossAxisSpacing: 12, - childAspectRatio: 0.9, - children: [ - _buildDeviceCard( - icon: Icons.whatshot, - iconColor: Colors.deepOrange, - label: 'Heater', - status: heater, - ), - _buildDeviceCard( - icon: Icons.air, - iconColor: Colors.blue, - label: 'Fan', - status: fanIn, - ), - _buildDeviceCard( - icon: Icons.wind_power, - iconColor: Colors.teal, - label: 'Exhaust', - status: exhaust, - ), - ], - ), - ], + ) : null, + body: screens[_currentIndex], + bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + backgroundColor: Colors.white, + selectedItemColor: Colors.blue, + unselectedItemColor: Colors.grey, + currentIndex: _currentIndex, + onTap: (index) { + if (index == 2) { + // Navigate to chart screen + Navigator.push( + context, + MaterialPageRoute(builder: (context) => const ChartScreen()), + ); + } else { + setState(() { + _currentIndex = index; + }); + } + }, + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Dashboard', ), + BottomNavigationBarItem( + icon: Icon(Icons.notifications), + label: 'Notifikasi', + ), + BottomNavigationBarItem( + icon: Icon(Icons.bar_chart), + label: 'Grafik', + ), + ], + ), + ); + } + + Widget _buildDashboardContent() { + return RefreshIndicator( + onRefresh: _loadLatestData, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Status Card + _buildStatusCard(), + const SizedBox(height: 16), + + // Metrics + Row( + children: [ + Expanded( + child: _buildMetricCard( + icon: Icons.thermostat, + iconColor: Colors.orange, + label: 'Suhu', + value: suhu.toStringAsFixed(1), + unit: 'ยฐC', + ), + ), + const SizedBox(width: 12), + Expanded( + child: _buildMetricCard( + icon: Icons.scale, + iconColor: Colors.blue, + label: 'Berat', + value: berat.toStringAsFixed(0), + unit: 'g', + ), + ), + ], + ), + const SizedBox(height: 12), + + // Target + _buildMetricCard( + icon: Icons.flag, + iconColor: Colors.green, + label: 'Target Berat', + value: target.toStringAsFixed(0), + unit: 'g', + ), + const SizedBox(height: 16), + + // ======= GRAFIK CHARTS ======= + // Temperature Chart Widget + TemperatureChart( + dataPoints: suhuHistory, + title: 'Suhu Real-time', + lineColor: Colors.orange, + gradientStartColor: Colors.orange, + gradientEndColor: Colors.red, + ), + const SizedBox(height: 12), + + // Weight Chart Widget + WeightChart( + dataPoints: beratHistory, + targetWeight: target, + title: 'Berat Real-time', + lineColor: Colors.blue, + gradientStartColor: Colors.blue, + gradientEndColor: Colors.cyan, + ), + const SizedBox(height: 16), + + // Devices + const Text( + 'Perangkat', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 12), + + GridView.count( + crossAxisCount: 3, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + mainAxisSpacing: 12, + crossAxisSpacing: 12, + childAspectRatio: 0.9, + children: [ + _buildDeviceCard( + icon: Icons.whatshot, + iconColor: Colors.deepOrange, + label: 'Heater', + status: heater, + ), + _buildDeviceCard( + icon: Icons.air, + iconColor: Colors.blue, + label: 'Fan', + status: fanIn, + ), + _buildDeviceCard( + icon: Icons.wind_power, + iconColor: Colors.teal, + label: 'Exhaust', + status: exhaust, + ), + ], + ), + ], ), ), ); diff --git a/lib/services/api_service.dart b/lib/services/api_service.dart index 9ef5895..af5dd95 100644 --- a/lib/services/api_service.dart +++ b/lib/services/api_service.dart @@ -8,8 +8,8 @@ class ApiService { factory ApiService() => _instance; ApiService._internal(); - // Base URL Railway - static const String baseUrl = 'https://web-production-47eb.up.railway.app'; + // Base URL Railway (Updated account) + static const String baseUrl = 'https://backendaplikasinovil-production.up.railway.app'; // Timeout static const Duration timeout = Duration(seconds: 10); diff --git a/lib/services/notification_manager.dart b/lib/services/notification_manager.dart index fd64f6b..e649290 100644 --- a/lib/services/notification_manager.dart +++ b/lib/services/notification_manager.dart @@ -115,38 +115,23 @@ class NotificationManager extends ChangeNotifier { } } - // Notification helpers for specific events - Future notifyPengeringanDimulai() async { - await addNotification( - title: 'Pengeringan Dimulai', - message: 'Proses pengeringan ikan telah dimulai', - type: NotificationType.info, - ); - } - - Future notifyIkanTerdeteksi(double berat) async { - await addNotification( - title: 'Ikan Terdeteksi', - message: 'Berat ikan: ${berat.toStringAsFixed(0)} gram', - type: NotificationType.info, - ); - } - - Future notifyPengeringanBerjalan(double berat, double target) async { - await addNotification( - title: 'Pengeringan Berjalan', - message: 'Berat: ${berat.toStringAsFixed(0)}g | Target: ${target.toStringAsFixed(0)}g', - type: NotificationType.info, - showPush: false, // Don't show push for regular updates - ); - } - + // Notification helper untuk pengeringan selesai Future notifyPengeringanSelesai(double beratAkhir) async { + print('๐Ÿ”” Calling notifyPengeringanSelesai with berat: $beratAkhir'); + + // Panggil notification service langsung + final notificationService = NotificationService(); + await notificationService.notifyPengeringanSelesai(beratAkhir); + + // Simpan juga ke notification manager await addNotification( title: 'Pengeringan Selesai', message: 'Target tercapai! Berat akhir: ${beratAkhir.toStringAsFixed(0)} gram', type: NotificationType.success, + showPush: true, ); + + print('โœ… notifyPengeringanSelesai completed'); } Future notifySuhuTinggi(double suhu) async { diff --git a/lib/widgets/temperature_chart.dart b/lib/widgets/temperature_chart.dart new file mode 100644 index 0000000..8a4d525 --- /dev/null +++ b/lib/widgets/temperature_chart.dart @@ -0,0 +1,264 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; + +class TemperatureChart extends StatelessWidget { + final List dataPoints; + final String title; + final Color lineColor; + final Color gradientStartColor; + final Color gradientEndColor; + final double minY; + final double maxY; + final String unit; + + const TemperatureChart({ + super.key, + required this.dataPoints, + this.title = 'Suhu Real-time', + this.lineColor = Colors.orange, + this.gradientStartColor = Colors.orange, + this.gradientEndColor = Colors.red, + this.minY = 20, + this.maxY = 60, + this.unit = 'ยฐC', + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: lineColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon(Icons.thermostat, color: lineColor, size: 20), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + Text( + dataPoints.isNotEmpty + ? '${dataPoints.last.y.toStringAsFixed(1)} $unit' + : '-- $unit', + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + ), + ), + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: lineColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon( + Icons.fiber_manual_record, + color: lineColor, + size: 8, + ), + const SizedBox(width: 4), + const Text( + 'LIVE', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.black54, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + SizedBox( + height: 200, // Naik dari 180 jadi 200 + child: dataPoints.isEmpty + ? Center( + child: Text( + 'Menunggu data...', + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade400, + ), + ), + ) + : Padding( + padding: const EdgeInsets.only(left: 8, right: 8), + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + drawVerticalLine: false, + horizontalInterval: 10, + getDrawingHorizontalLine: (value) { + return FlLine( + color: Colors.grey.shade200, + strokeWidth: 1, + ); + }, + ), + titlesData: FlTitlesData( + show: true, + rightTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + topTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 30, + interval: 5, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + '${value.toInt()}s', + style: const TextStyle( + fontSize: 10, + color: Colors.grey, + ), + ), + ); + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 10, + reservedSize: 45, + getTitlesWidget: (value, meta) { + return Text( + '${value.toInt()}$unit', + style: const TextStyle( + fontSize: 10, + color: Colors.grey, + ), + ); + }, + ), + ), + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide(color: Colors.grey.shade300), + left: BorderSide(color: Colors.grey.shade300), + ), + ), + minX: dataPoints.isNotEmpty + ? dataPoints.first.x + : 0, + maxX: dataPoints.isNotEmpty + ? dataPoints.last.x + : 10, + minY: minY, + maxY: maxY, + lineBarsData: [ + LineChartBarData( + spots: dataPoints, + isCurved: true, + curveSmoothness: 0.3, + color: lineColor, + barWidth: 3, + isStrokeCapRound: true, + dotData: FlDotData( + show: true, + getDotPainter: (spot, percent, barData, index) { + // Hanya tampilkan dot di titik terakhir + if (index == dataPoints.length - 1) { + return FlDotCirclePainter( + radius: 5, + color: Colors.white, + strokeWidth: 3, + strokeColor: lineColor, + ); + } + return FlDotCirclePainter( + radius: 0, + color: Colors.transparent, + ); + }, + ), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + gradientStartColor.withOpacity(0.3), + gradientEndColor.withOpacity(0.1), + Colors.transparent, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ], + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + getTooltipColor: (touchedSpot) => Colors.black87, + tooltipRoundedRadius: 8, + fitInsideHorizontally: true, + fitInsideVertically: true, + tooltipPadding: const EdgeInsets.all(8), + tooltipMargin: 8, + getTooltipItems: (List touchedSpots) { + return touchedSpots.map((LineBarSpot touchedSpot) { + return LineTooltipItem( + '${touchedSpot.y.toStringAsFixed(1)} $unit', + const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ); + }).toList(); + }, + ), + handleBuiltInTouches: true, + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/weight_chart.dart b/lib/widgets/weight_chart.dart new file mode 100644 index 0000000..f96dcf6 --- /dev/null +++ b/lib/widgets/weight_chart.dart @@ -0,0 +1,385 @@ +import 'package:flutter/material.dart'; +import 'package:fl_chart/fl_chart.dart'; + +class WeightChart extends StatelessWidget { + final List dataPoints; + final double targetWeight; + final String title; + final Color lineColor; + final Color gradientStartColor; + final Color gradientEndColor; + final String unit; + + const WeightChart({ + super.key, + required this.dataPoints, + required this.targetWeight, + this.title = 'Berat Real-time', + this.lineColor = Colors.blue, + this.gradientStartColor = Colors.blue, + this.gradientEndColor = Colors.cyan, + this.unit = 'g', + }); + + @override + Widget build(BuildContext context) { + // Filter data yang valid untuk ditampilkan (ganti nilai 0 dengan nilai sebelumnya) + List displayPoints = []; + double? lastValidValue; + + for (var spot in dataPoints) { + if (spot.y > 10) { + // Data valid, gunakan nilai asli + displayPoints.add(spot); + lastValidValue = spot.y; + } else if (lastValidValue != null) { + // Data invalid (0), gunakan nilai terakhir yang valid + displayPoints.add(FlSpot(spot.x, lastValidValue)); + } + } + + // Jika masih kosong setelah filter, gunakan data asli + if (displayPoints.isEmpty) { + displayPoints = dataPoints; + } + + // Hitung min dan max Y berdasarkan data yang akan ditampilkan + double minY = 0; + double maxY = 200; + + if (displayPoints.isNotEmpty) { + // Cari nilai min dan max dari data yang akan ditampilkan + double dataMin = displayPoints.map((e) => e.y).reduce((a, b) => a < b ? a : b); + double dataMax = displayPoints.map((e) => e.y).reduce((a, b) => a > b ? a : b); + + // Jika ada target, sertakan dalam perhitungan + if (targetWeight > 0) { + dataMin = dataMin < targetWeight ? dataMin : targetWeight; + dataMax = dataMax > targetWeight ? dataMax : targetWeight; + } + + // Pastikan minY tidak negatif dan ada padding yang cukup + double range = dataMax - dataMin; + double padding = range > 0 ? range * 0.25 : 50; // 25% padding atau minimal 50 + + minY = (dataMin - padding).clamp(0, double.infinity); + maxY = dataMax + padding; + + // Pastikan ada range minimal 100 + if (maxY - minY < 100) { + double center = (maxY + minY) / 2; + minY = (center - 50).clamp(0, double.infinity); + maxY = center + 50; + } + + // Debug log + print('๐Ÿ“Š Weight Chart: min=$minY, max=$maxY, dataMin=$dataMin, dataMax=$dataMax, displayPoints=${displayPoints.length}/${dataPoints.length}'); + + } else if (targetWeight > 0) { + // Kalau belum ada data, gunakan target + minY = 0; + maxY = (targetWeight * 1.5).clamp(100, double.infinity); + } else { + // Fallback default + minY = 0; + maxY = 200; + } + + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: lineColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + child: Icon(Icons.scale, color: lineColor, size: 20), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + Row( + children: [ + Text( + dataPoints.isNotEmpty + ? '${dataPoints.last.y.toStringAsFixed(0)} $unit' + : '-- $unit', + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade600, + ), + ), + const SizedBox(width: 8), + Icon(Icons.arrow_forward, size: 12, color: Colors.grey.shade400), + const SizedBox(width: 4), + Text( + '${targetWeight.toStringAsFixed(0)} $unit', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.green.shade600, + ), + ), + ], + ), + ], + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: lineColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + ), + child: Row( + children: [ + Icon( + Icons.fiber_manual_record, + color: lineColor, + size: 8, + ), + const SizedBox(width: 4), + const Text( + 'LIVE', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: Colors.black54, + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 16), + SizedBox( + height: 200, // Naik dari 180 jadi 200 biar lebih lapang + child: dataPoints.isEmpty + ? Center( + child: Text( + 'Menunggu data...', + style: TextStyle( + fontSize: 12, + color: Colors.grey.shade400, + ), + ), + ) + : Padding( + padding: const EdgeInsets.only(left: 8, right: 8), // Tambah padding kiri kanan + child: LineChart( + LineChartData( + gridData: FlGridData( + show: true, + drawVerticalLine: false, + horizontalInterval: 50, + getDrawingHorizontalLine: (value) { + // Garis target lebih tebal + if ((value - targetWeight).abs() < 5) { + return FlLine( + color: Colors.green.shade300, + strokeWidth: 2, + dashArray: [5, 5], + ); + } + return FlLine( + color: Colors.grey.shade200, + strokeWidth: 1, + ); + }, + ), + titlesData: FlTitlesData( + show: true, + rightTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + topTitles: const AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 30, + interval: 5, + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + '${value.toInt()}s', + style: const TextStyle( + fontSize: 10, + color: Colors.grey, + ), + ), + ); + }, + ), + ), + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 50, + reservedSize: 50, // Turun ke 50, karena masalahnya bukan disini + getTitlesWidget: (value, meta) { + // Tandai target dengan warna hijau + final isTarget = (value - targetWeight).abs() < 5; + return Text( + '${value.toInt()}$unit', + style: TextStyle( + fontSize: 10, + color: isTarget ? Colors.green : Colors.grey, + fontWeight: isTarget ? FontWeight.bold : FontWeight.normal, + ), + ); + }, + ), + ), + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide(color: Colors.grey.shade300), + left: BorderSide(color: Colors.grey.shade300), + ), + ), + minX: displayPoints.isNotEmpty + ? displayPoints.first.x + : 0, + maxX: displayPoints.isNotEmpty + ? displayPoints.last.x + : 10, + minY: minY, + maxY: maxY, + extraLinesData: ExtraLinesData( + horizontalLines: [ + HorizontalLine( + y: targetWeight, + color: Colors.green.shade400, + strokeWidth: 2, + dashArray: [8, 4], + label: HorizontalLineLabel( + show: true, + alignment: Alignment.topRight, + padding: const EdgeInsets.only(right: 5, bottom: 5), + style: TextStyle( + color: Colors.green.shade700, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + labelResolver: (line) => 'Target', + ), + ), + ], + ), + lineBarsData: [ + LineChartBarData( + spots: displayPoints, + isCurved: true, + curveSmoothness: 0.3, + color: lineColor, + barWidth: 3, + isStrokeCapRound: true, + dotData: FlDotData( + show: true, + getDotPainter: (spot, percent, barData, index) { + // Hanya tampilkan dot di titik terakhir + if (index == displayPoints.length - 1) { + return FlDotCirclePainter( + radius: 5, + color: Colors.white, + strokeWidth: 3, + strokeColor: lineColor, + ); + } + return FlDotCirclePainter( + radius: 0, + color: Colors.transparent, + ); + }, + ), + belowBarData: BarAreaData( + show: true, + gradient: LinearGradient( + colors: [ + gradientStartColor.withOpacity(0.3), + gradientEndColor.withOpacity(0.1), + Colors.transparent, + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + ), + ], + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + getTooltipColor: (touchedSpot) => Colors.black87, + tooltipRoundedRadius: 8, + fitInsideHorizontally: true, + fitInsideVertically: true, + tooltipPadding: const EdgeInsets.all(8), + tooltipMargin: 8, + getTooltipItems: (List touchedSpots) { + return touchedSpots.map((LineBarSpot touchedSpot) { + final remaining = touchedSpot.y - targetWeight; + return LineTooltipItem( + '${touchedSpot.y.toStringAsFixed(0)} $unit\n', + const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + children: [ + TextSpan( + text: remaining > 0 + ? 'Sisa ${remaining.toStringAsFixed(0)} $unit' + : 'Target tercapai!', + style: TextStyle( + color: remaining > 0 + ? Colors.orange.shade200 + : Colors.green.shade200, + fontSize: 10, + ), + ), + ], + ); + }).toList(); + }, + ), + handleBuiltInTouches: true, + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/nixpacks.toml b/nixpacks.toml new file mode 100644 index 0000000..9e834cf --- /dev/null +++ b/nixpacks.toml @@ -0,0 +1,8 @@ +[phases.setup] +nixPkgs = ['nodejs-18_x'] + +[phases.build] +cmds = ['cd backend && npm install'] + +[start] +cmd = 'cd backend && node server.js' diff --git a/pubspec.yaml b/pubspec.yaml index 5eacffe..2a77e1a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.1.0+2 +version: 1.2.0+3 environment: sdk: '>=3.5.1 <4.0.0' @@ -117,13 +117,25 @@ flutter: # Flutter Launcher Icons # Generates app icons for Android and iOS flutter_launcher_icons: - android: true + android: "launcher_icon" ios: true image_path: "assets/images/app_icon.png" - # Adaptive icon for Android 8.0+ - adaptive_icon_background: "#1976D2" # Blue color matching splash screen + min_sdk_android: 21 + + # Adaptive icon for Android 8.0+ (responsive di semua device) + adaptive_icon_background: "#2196F3" # Material Blue 500 adaptive_icon_foreground: "assets/images/app_icon.png" + # iOS specific + remove_alpha_ios: true + + # Web (jika diperlukan) + web: + generate: true + image_path: "assets/images/app_icon.png" + background_color: "#2196F3" + theme_color: "#2196F3" + # Flutter Native Splash # DISABLED - Menggunakan custom splash screen saja # flutter_native_splash: diff --git a/web/favicon.png b/web/favicon.png index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..1ad5b66cddbcb3b33f621ca2343cf373934fe6aa 100644 GIT binary patch delta 315 zcmV-B0mS~52h{?QBYy!LNkl!uP?yx@PF+u24zup23IXUtWGUm z`-Gu8Scc*L+g}Vju71J{12-)`1~f}iz|=@NFs&fWhUtP=pMNvN&40k4DapYwD@Kli zodtWcxc%w}!^$)7Q4RR<`yWHXf`<%GKKuq_mkGBSlB`4+tdzJJB=}hwe*OK=aQD?u zh8@>FF)TjumNen}@BgR<+<5VW0om<$U;kpL-uMi+U(moHNJs;2001JPTv3P#Bh&x@ N002ovPDHLkV1ly6lw|+_ literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png index b749bfef07473333cf1dd31e9eed89862a5d52aa..d738d10015b6913e286c0824c66b3d3b215cc435 100644 GIT binary patch literal 3652 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 V9%=}3jh(DR0K&)uUSsI-@V_W>=2idz literal 5292 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 diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png index 88cfd48dff1169879ba46840804b412fe02fefd6..ff8b1334574b81325a9e0f37955deebba87703de 100644 GIT binary patch literal 9374 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 diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png index eb9b4d76e525556d5d89141648c724331630325d..d738d10015b6913e286c0824c66b3d3b215cc435 100644 GIT binary patch literal 3652 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 V9%=}3jh(DR0K&)uUSsI-@V_W>=2idz literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png index d69c56691fbdb0b7efa65097c7cc1edac12a6d3e..ff8b1334574b81325a9e0f37955deebba87703de 100644 GIT binary patch literal 9374 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 MPgg&ebxsLQ025IeI{*Lx diff --git a/web/manifest.json b/web/manifest.json index a77dcd8..986f4e6 100644 --- a/web/manifest.json +++ b/web/manifest.json @@ -3,8 +3,8 @@ "short_name": "aplikasi_novil", "start_url": ".", "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", + "background_color": "#2196F3", + "theme_color": "#2196F3", "description": "A new Flutter project.", "orientation": "portrait-primary", "prefer_related_applications": false, @@ -32,4 +32,4 @@ "purpose": "maskable" } ] -} +} \ No newline at end of file