E32221349_Medibox/lib/home_screen.dart

711 lines
25 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'jadwal_kotak_obat1_screen.dart';
import 'jadwal_kotak_obat2_screen.dart';
import 'login_screen.dart';
//import 'firebase_messaging_service.dart';
import 'notifikasi_service.dart';
import 'toast.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({
super.key,
required String username,
required void Function() onSignOut,
});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Timer? _syncTimer;
final bool _hasSentKotak1 = false;
final bool _hasSentKotak2 = false;
DateTime? _lastSentKotak1;
DateTime? _lastSentKotak2;
static const Duration minInterval = Duration(seconds: 10);
String username = '';
bool isLoading = true;
// Tambahan state untuk tombol dan status
bool button1 = false;
bool button2 = false;
bool buzzer = false;
bool lampu = false;
bool _canAccessKotak1 = false;
bool _canAccessKotak2 = false;
String infoText = "Tidak Ada Informasi";
@override
void initState() {
super.initState();
//NotifikasiService.listenToAllNotifikasi(); // Realtime listener
_initializeAsyncTasks(); // panggil fungsi async tanpa await
_checkUserAccessToKotak();
_syncTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
_updateStatus(); // update tombol dan info tiap 2 detik
});
}
@override
void dispose() {
_syncTimer?.cancel();
super.dispose();
}
Future<void> _initializeAsyncTasks() async {
await fetchUsername();
}
Future<void> fetchUsername() async {
try {
final user = FirebaseAuth.instance.currentUser;
if (user != null) {
final uid = user.uid;
final doc =
await FirebaseFirestore.instance.collection('users').doc(uid).get();
if (doc.exists && doc.data() != null) {
if (!mounted) return;
setState(() {
username = doc['username'] ?? 'User';
isLoading = false;
});
}
}
} catch (e) {
print('Error fetching username: $e');
if (!mounted) return;
setState(() {
username = 'User';
isLoading = false;
});
}
}
Future<void> _showLogoutConfirmationDialog() async {
final shouldLogout = await showDialog<bool>(
context: context,
builder:
(context) => AlertDialog(
title: const Text('Konfirmasi Logout'),
content: const Text(
'Apakah Anda yakin ingin logout dari akun ini?',
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text('Tidak'),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Ya'),
),
],
),
);
if (shouldLogout == true) {
_logout();
}
}
// Fungsi baru: Update status tombol dan info
Future<void> _updateStatus() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
final uid = user.uid;
try {
final button1Ref = FirebaseDatabase.instance.ref('status/button1');
final button2Ref = FirebaseDatabase.instance.ref('status/button2');
final buzzerRef = FirebaseDatabase.instance.ref('status/buzzer');
final lampuRef = FirebaseDatabase.instance.ref('status/led');
final kotak1UserIdRef = FirebaseDatabase.instance.ref(
'jadwal/kotak1/userId',
);
final kotak2UserIdRef = FirebaseDatabase.instance.ref(
'jadwal/kotak2/userId',
);
final button1Snap = await button1Ref.get();
final button2Snap = await button2Ref.get();
final buzzerSnap = await buzzerRef.get();
final lampuSnap = await lampuRef.get();
final kotak1UserIdSnap = await kotak1UserIdRef.get();
final kotak2UserIdSnap = await kotak2UserIdRef.get();
// Cek userId sesuai
final button1UserMatch =
kotak1UserIdSnap.exists && kotak1UserIdSnap.value == uid;
final button2UserMatch =
kotak2UserIdSnap.exists && kotak2UserIdSnap.value == uid;
if (!mounted) return;
setState(() {
button1 = button1Snap.exists && button1Snap.value == true;
button2 = button2Snap.exists && button2Snap.value == true;
buzzer = buzzerSnap.exists && buzzerSnap.value == true;
lampu = lampuSnap.exists && lampuSnap.value == true;
// Jika userId tidak cocok maka tombol tetap tampil tapi tidak bisa ditekan, tapi kita simpan status tombol sesuai DB
if (!button1UserMatch) button1 = button1; // tetap tampil status
if (!button2UserMatch) button2 = button2; // tetap tampil status
});
// Update infoText sesuai kondisi:
String info = "Tidak Ada Informasi";
// Cek alarm1 dan alarm2
final alarm1Snap =
await FirebaseDatabase.instance.ref('status/alarm1').get();
final alarm2Snap =
await FirebaseDatabase.instance.ref('status/alarm2').get();
// Cek jumlahObat kotak1 dan kotak2
final jumlahObat1Snap =
await FirebaseDatabase.instance.ref('jadwal/kotak1/jumlahObat').get();
final jumlahObat2Snap =
await FirebaseDatabase.instance.ref('jadwal/kotak2/jumlahObat').get();
final kotak1UserIdCheck =
kotak1UserIdSnap.exists && kotak1UserIdSnap.value == uid;
final kotak2UserIdCheck =
kotak2UserIdSnap.exists && kotak2UserIdSnap.value == uid;
if (kotak1UserIdCheck) {
if (alarm1Snap.exists && alarm1Snap.value == true) {
info = "Alarm Kotak 1 Aktif";
} else if (jumlahObat1Snap.exists) {
final val = jumlahObat1Snap.value;
int? jumlah = (val is int) ? val : int.tryParse(val.toString());
if (jumlah != null) {
if (jumlah == 0) {
info = "Obat Kotak 1 Habis, Segera isi ulang";
} else if (jumlah <= 3 && jumlah > 0) {
info = "Obat Kotak 1 Sisa $jumlah, Waktunya isi ulang";
}
}
}
}
if (kotak2UserIdCheck) {
if (alarm2Snap.exists && alarm2Snap.value == true) {
info = "Alarm Kotak 2 Aktif";
} else if (jumlahObat2Snap.exists) {
final val = jumlahObat2Snap.value;
int? jumlah = (val is int) ? val : int.tryParse(val.toString());
if (jumlah != null) {
if (jumlah == 0) {
info = "Obat Kotak 2 Habis, Segera isi ulang";
} else if (jumlah <= 3 && jumlah > 0) {
info = "Obat Kotak 2 Sisa $jumlah, Waktunya isi ulang";
}
}
}
}
if (!mounted) return;
setState(() {
infoText = info;
});
} catch (e) {
print('Gagal update status tombol dan info: $e');
}
}
Future<void> _logout() async {
await FirebaseAuth.instance.signOut();
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.remove('userId');
FlutterBackgroundService().invoke("stopService");
print("✅ Logout berhasil dan background service dihentikan");
print('❌ userId dihapus dari SharedPreferences');
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const LoginScreen()),
);
showToast(message: "Successfully Logout");
}
// Fungsi untuk toggle button kotak 1
void _toggleButton1() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
final userId = user.uid;
final kotak1UserIdSnap =
await FirebaseDatabase.instance.ref('jadwal/kotak1/userId').get();
if (!kotak1UserIdSnap.exists || kotak1UserIdSnap.value != userId) return;
await FirebaseDatabase.instance.ref('status/button1').set(!button1);
}
// Fungsi untuk toggle button kotak 2
void _toggleButton2() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
final userId = user.uid;
final kotak2UserIdSnap =
await FirebaseDatabase.instance.ref('jadwal/kotak2/userId').get();
if (!kotak2UserIdSnap.exists || kotak2UserIdSnap.value != userId) return;
await FirebaseDatabase.instance.ref('status/button2').set(!button2);
}
Future<void> _checkUserAccessToKotak() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) return;
final uid = user.uid;
try {
final kotak1UserIdSnap =
await FirebaseDatabase.instance.ref('jadwal/kotak1/userId').get();
final kotak2UserIdSnap =
await FirebaseDatabase.instance.ref('jadwal/kotak2/userId').get();
bool canAccessKotak1 = false;
bool canAccessKotak2 = false;
// Jika userId kosong atau tidak ada, maka semua user boleh akses
if (!kotak1UserIdSnap.exists ||
kotak1UserIdSnap.value == null ||
kotak1UserIdSnap.value.toString().trim().isEmpty) {
canAccessKotak1 = true;
} else {
canAccessKotak1 = kotak1UserIdSnap.value == uid;
}
if (!kotak2UserIdSnap.exists ||
kotak2UserIdSnap.value == null ||
kotak2UserIdSnap.value.toString().trim().isEmpty) {
canAccessKotak2 = true;
} else {
canAccessKotak2 = kotak2UserIdSnap.value == uid;
}
if (!mounted) return;
setState(() {
_canAccessKotak1 = canAccessKotak1;
_canAccessKotak2 = canAccessKotak2;
});
} catch (e) {
print('Error checking access to kotak: $e');
}
}
void _showAccessDeniedDialog(String kotakName) {
showDialog(
context: context,
builder:
(_) => AlertDialog(
title: const Text('Akses Ditolak'),
content: Text('$kotakName Sudah diisi user lain'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
}
Future<void> _refresh() async {
setState(() {
isLoading = true;
});
await Future.wait([
_initializeAsyncTasks(),
_checkUserAccessToKotak(),
_updateStatus(),
]);
if (mounted) {
setState(() {
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
if (isLoading) {
return const Center(child: CircularProgressIndicator());
}
return RefreshIndicator(
onRefresh: _refresh,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 35, horizontal: 24),
decoration: const BoxDecoration(
color: Color(0xFF3FA535),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.center, // Menyusun elemen-elemen ke tengah horizontal
crossAxisAlignment:
CrossAxisAlignment
.center, // Menyusun elemen-elemen ke tengah vertikal
children: [
const Icon(
Icons.account_circle,
color: Colors.white,
size: 35,
),
const SizedBox(width: 10),
Text(
'Hello, $username',
style: const TextStyle(
fontSize: 20,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
IconButton(
onPressed: _showLogoutConfirmationDialog,
icon: const Icon(
Icons.logout,
color: Colors.white,
size: 30,
),
),
],
),
),
Container(
margin: const EdgeInsets.all(24),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.grey.shade300,
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/logowarna.png',
width: 60,
height: 60,
),
SizedBox(width: 10), // jarak antar gambar
Image.asset(
'assets/images/logopolije.png',
width: 60,
height: 60,
),
],
),
const SizedBox(height: 10),
const Text(
'SmartMediBox',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10),
const Text(
'Pilih kotak obat untuk atur jadwal obat',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.black54),
),
const SizedBox(height: 30),
// Tombol Jadwal Kotak Obat 1 dengan pengecekan akses
ElevatedButton(
onPressed:
_canAccessKotak1
? () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) =>
const JadwalKotakObat1Screen(),
),
);
}
: () => _showAccessDeniedDialog('Kotak Obat 1'),
style: ElevatedButton.styleFrom(
backgroundColor:
_canAccessKotak1
? const Color(0xFF3FA535)
: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 16),
minimumSize: const Size.fromHeight(50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
'Jadwal Kotak Obat 1',
style: TextStyle(
fontSize: 16,
color: _canAccessKotak1 ? Colors.white : Colors.white70,
),
),
),
const SizedBox(height: 20),
// Tombol Jadwal Kotak Obat 2 dengan pengecekan akses
ElevatedButton(
onPressed:
_canAccessKotak2
? () {
Navigator.push(
context,
MaterialPageRoute(
builder:
(context) =>
const JadwalKotakObat2Screen(),
),
);
}
: () => _showAccessDeniedDialog('Kotak Obat 2'),
style: ElevatedButton.styleFrom(
backgroundColor:
_canAccessKotak2
? const Color(0xFF3FA535)
: Colors.grey,
padding: const EdgeInsets.symmetric(vertical: 16),
minimumSize: const Size.fromHeight(50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
'Jadwal Kotak Obat 2',
style: TextStyle(
fontSize: 16,
color: _canAccessKotak2 ? Colors.white : Colors.white70,
),
),
),
],
),
),
// ======== TAMBAHAN CONTAINER SESUAI DESAIN =========
Container(
margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 0),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: Colors.grey.shade300,
blurRadius: 10,
offset: const Offset(0, 5),
),
],
),
child: Column(
children: [
// Row Tombol Kotak 1 & Kotak 2
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Tombol Kotak 1
Expanded(
child: Column(
children: [
Text(
"Tombol Kotak 1",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 6),
ElevatedButton(
onPressed: _toggleButton1,
style: ElevatedButton.styleFrom(
backgroundColor:
button1 ? Colors.green : Colors.red,
minimumSize: const Size(80, 36),
padding: const EdgeInsets.symmetric(
horizontal: 8,
),
),
child: Text(
button1 ? 'ON' : 'OFF',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
const SizedBox(width: 16),
// Tombol Kotak 2
Expanded(
child: Column(
children: [
Text(
"Tombol Kotak 2",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 6),
ElevatedButton(
onPressed: _toggleButton2,
style: ElevatedButton.styleFrom(
backgroundColor:
button2 ? Colors.green : Colors.red,
minimumSize: const Size(80, 36),
padding: const EdgeInsets.symmetric(
horizontal: 8,
),
),
child: Text(
button2 ? 'ON' : 'OFF',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
),
const SizedBox(height: 16),
// Row Buzzer & Lampu (status read-only)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Buzzer
Expanded(
child: Column(
children: [
Text(
"Buzzer",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 6),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: buzzer ? Colors.green : Colors.red,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
buzzer ? 'ON' : 'OFF',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
const SizedBox(width: 16),
// Lampu
Expanded(
child: Column(
children: [
Text(
"Lampu",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
const SizedBox(height: 6),
Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: lampu ? Colors.green : Colors.red,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
lampu ? 'ON' : 'OFF',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
),
const SizedBox(height: 20),
// Info Text
Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 20,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Text(
"Info :\n$infoText",
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
height: 1.4,
),
),
),
],
),
),
],
),
),
);
}
}