TKK_E32220565/newata2/lib/screens/control_screen.dart

723 lines
32 KiB
Dart

// screens/control_screen.dart (Sudah Lengkap)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/schedule_provider.dart'; // Pastikan path benar
class ControlScreen extends StatefulWidget {
const ControlScreen({super.key});
@override
_ControlScreenState createState() => _ControlScreenState();
}
class _ControlScreenState extends State<ControlScreen> {
// Hapus TextEditingController dan FormKey
// final _startTimeController = TextEditingController();
// final _endTimeController = TextEditingController();
// final _formKey = GlobalKey<FormState>();
// Gunakan state lokal untuk menyimpan waktu yang DIPILIH user (sebelum disimpan)
TimeOfDay? _selectedStartTime;
TimeOfDay? _selectedEndTime;
// State lokal untuk menyimpan waktu dari provider (untuk tampilan jadwal saat ini)
TimeOfDay? _fetchedStartTime;
TimeOfDay? _fetchedEndTime;
@override
void initState() {
super.initState();
print("ControlScreen: initState called.");
WidgetsBinding.instance.addPostFrameCallback((_) {
print("ControlScreen: Requesting fetchSchedule.");
final provider = context.read<ScheduleProvider>();
provider.fetchSchedule().then((_) {
if (mounted) {
print(
"ControlScreen: fetchSchedule completed. Updating local state.");
// Set state lokal dari provider saat pertama kali load
setState(() {
_fetchedStartTime = provider.startTime;
_fetchedEndTime = provider.endTime;
// Juga set waktu terpilih awal agar tombol menampilkan nilai yang ada
_selectedStartTime = provider.startTime;
_selectedEndTime = provider.endTime;
});
}
});
});
}
// Hapus dispose untuk controller
// @override
// void dispose() {
// _startTimeController.dispose();
// _endTimeController.dispose();
// super.dispose();
// }
// Fungsi untuk menampilkan Time Picker
Future<void> _selectTime(BuildContext context, bool isStartTime) async {
print(
"ControlScreen: Opening time picker for ${isStartTime ? 'start' : 'end'} time.");
// Tentukan waktu awal untuk picker
final TimeOfDay initialTime = isStartTime
? (_selectedStartTime ?? _fetchedStartTime ?? TimeOfDay.now())
: (_selectedEndTime ??
_fetchedEndTime ??
TimeOfDay(
hour: (TimeOfDay.now().hour + 1) % 24,
minute: TimeOfDay.now().minute)); // Default end time +1 jam
final TimeOfDay? picked = await showTimePicker(
context: context,
initialTime: initialTime,
helpText: isStartTime ? 'PILIH WAKTU MULAI' : 'PILIH WAKTU SELESAI',
// Gunakan tema dari MaterialApp
// builder: (context, child) {
// return Theme(
// data: Theme.of(context), // Menggunakan tema utama
// child: child!,
// );
// },
);
print("ControlScreen: Time picker closed. Picked: $picked");
if (picked != null) {
setState(() {
if (isStartTime) {
_selectedStartTime = picked;
} else {
_selectedEndTime = picked;
}
});
}
}
// Format TimeOfDay ke string HH:mm (24 jam)
String _formatTimeOfDay(TimeOfDay? time) {
if (time == null) return 'Belum diatur';
final hour = time.hour.toString().padLeft(2, '0');
final minute = time.minute.toString().padLeft(2, '0');
return '$hour:$minute';
}
// Fungsi untuk menyimpan jadwal
Future<void> _saveNewSchedule() async {
print("ControlScreen: Save button pressed.");
// Validasi sederhana: pastikan kedua waktu sudah dipilih
if (_selectedStartTime == null || _selectedEndTime == null) {
print("ControlScreen: Save aborted, start or end time not selected.");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Harap pilih waktu mulai dan selesai.'),
backgroundColor: Colors.orangeAccent),
);
return;
}
// Validasi tambahan (opsional): cek apakah waktu mulai sebelum waktu selesai
final startTimeMinutes =
_selectedStartTime!.hour * 60 + _selectedStartTime!.minute;
final endTimeMinutes =
_selectedEndTime!.hour * 60 + _selectedEndTime!.minute;
if (startTimeMinutes >= endTimeMinutes) {
print("ControlScreen: Save aborted, start time is not before end time.");
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Waktu mulai harus sebelum waktu selesai.'),
backgroundColor: Colors.orangeAccent),
);
return;
}
final provider = context.read<ScheduleProvider>();
final success =
await provider.saveSchedule(_selectedStartTime!, _selectedEndTime!);
print("ControlScreen: saveSchedule result: $success");
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(success
? 'Jadwal berhasil disimpan!'
: provider.errorMessage ?? 'Gagal menyimpan jadwal.'),
backgroundColor: success ? Colors.green[600] : Colors.redAccent,
behavior: SnackBarBehavior.floating,
),
);
if (success) {
// Update state fetched time setelah berhasil simpan
setState(() {
_fetchedStartTime = _selectedStartTime;
_fetchedEndTime = _selectedEndTime;
});
}
}
}
@override
Widget build(BuildContext context) {
print("ControlScreen: Building UI.");
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Consumer<ScheduleProvider>(
builder: (context, provider, child) {
// Sinkronisasi state lokal jika provider berubah (misal setelah fetch awal)
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted &&
(_fetchedStartTime != provider.startTime ||
_fetchedEndTime != provider.endTime)) {
print(
"ControlScreen Consumer: Provider state changed (fetch), updating local state.");
setState(() {
_fetchedStartTime = provider.startTime;
_fetchedEndTime = provider.endTime;
// Set juga selected time jika belum pernah dipilih user
_selectedStartTime ??= provider.startTime;
_selectedEndTime ??= provider.endTime;
});
}
});
print(
"ControlScreen Consumer: Building content. isLoading: ${provider.isLoading}, error: ${provider.errorMessage}");
// Logika tampilan jadwal saat ini (sama seperti sebelumnya)
String currentScheduleDisplay;
bool isScheduleSet =
_fetchedStartTime != null && _fetchedEndTime != null;
bool hasSchedulePassedToday = false;
if (isScheduleSet) {
final now = TimeOfDay.now();
final nowInMinutes = now.hour * 60 + now.minute;
final endTimeInMinutes =
_fetchedEndTime!.hour * 60 + _fetchedEndTime!.minute;
if (nowInMinutes > endTimeInMinutes) {
hasSchedulePassedToday = true;
}
}
if (!isScheduleSet || (isScheduleSet && hasSchedulePassedToday)) {
currentScheduleDisplay =
"Belum diatur atau jadwal hari ini telah lewat.";
} else {
currentScheduleDisplay =
'${_formatTimeOfDay(_fetchedStartTime)} - ${_formatTimeOfDay(_fetchedEndTime)}';
}
return RefreshIndicator(
onRefresh: () async {
print("ControlScreen: Refresh requested.");
await provider.fetchSchedule();
// Update state lokal setelah refresh
setState(() {
_fetchedStartTime = provider.startTime;
_fetchedEndTime = provider.endTime;
_selectedStartTime =
provider.startTime; // Reset pilihan user ke data terbaru
_selectedEndTime = provider.endTime;
});
},
color: Colors.teal[600]!,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
// Hapus Form widget
// child: Form(
// key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Jadwal Nonaktif CCTV',
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.teal[800]),
),
const SizedBox(height: 8),
Text(
'Atur rentang waktu kapan perangkat CCTV tidak akan merekam atau mengirim data.',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: Colors.grey[700]),
),
const SizedBox(height: 25),
// Tampilkan jadwal saat ini
Card(
/* ... (UI Card sama, menampilkan currentScheduleDisplay) ... */
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(/* ... (Header Card sama) ... */),
const SizedBox(height: 10),
if (provider.isLoading && !isScheduleSet)
const Padding(
padding: EdgeInsets.symmetric(vertical: 10.0),
child: Center(
child: CircularProgressIndicator(
strokeWidth: 3, color: Colors.teal)),
)
else if (provider.errorMessage != null &&
!isScheduleSet)
Text(provider.errorMessage!,
style:
const TextStyle(color: Colors.redAccent))
else
Text(
currentScheduleDisplay,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: (isScheduleSet &&
!hasSchedulePassedToday)
? Colors.black87
: Colors.grey[600]),
),
],
),
),
),
const SizedBox(height: 25),
// Tombol untuk memilih waktu (kembali ke OutlinedButton)
Text(
'Setel Jadwal Baru:',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600, color: Colors.teal[700]),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: OutlinedButton.icon(
icon: const Icon(Icons.timer_outlined, size: 18),
label: Text(_selectedStartTime != null
? _formatTimeOfDay(_selectedStartTime)
: 'Pilih Mulai'),
onPressed: () => _selectTime(
context, true), // Panggil _selectTime
style: OutlinedButton.styleFrom(
foregroundColor: Colors.teal,
side: BorderSide(color: Colors.teal[200]!),
padding:
const EdgeInsets.symmetric(vertical: 12)),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text("-",
style: TextStyle(
fontSize: 24, color: Colors.grey[600])),
),
Expanded(
child: OutlinedButton.icon(
icon:
const Icon(Icons.timer_off_outlined, size: 18),
label: Text(_selectedEndTime != null
? _formatTimeOfDay(_selectedEndTime)
: 'Pilih Selesai'),
onPressed: () => _selectTime(
context, false), // Panggil _selectTime
style: OutlinedButton.styleFrom(
foregroundColor: Colors.teal,
side: BorderSide(color: Colors.teal[200]!),
padding:
const EdgeInsets.symmetric(vertical: 12)),
),
),
],
),
const SizedBox(height: 30),
// Tombol Simpan
Center(
child: provider.isLoading
? CircularProgressIndicator(color: Colors.teal[600])
: ElevatedButton.icon(
icon:
const Icon(Icons.save_alt_outlined, size: 20),
label: const Text('Simpan Jadwal'),
onPressed: (_selectedStartTime == null ||
_selectedEndTime == null)
? null // Disable jika waktu belum dipilih
: _saveNewSchedule, // Panggil fungsi simpan
),
),
// Tampilkan error saving jika ada
if (provider.errorMessage != null &&
!provider.isLoading &&
(_selectedStartTime != null ||
_selectedEndTime !=
null)) // Tampilkan jika ada error DAN user sudah mencoba memilih
Padding(
padding: const EdgeInsets.only(top: 15.0),
child: Center(
child: Text(provider.errorMessage!,
style: const TextStyle(color: Colors.redAccent),
textAlign: TextAlign.center),
),
),
const SizedBox(height: 20),
],
),
// ), // Tutup Form (dihapus)
),
);
},
),
),
);
}
}
// class ControlScreen extends StatefulWidget {
// const ControlScreen({super.key});
// @override
// _ControlScreenState createState() => _ControlScreenState();
// }
// class _ControlScreenState extends State<ControlScreen> {
// TimeOfDay? _selectedStartTime;
// TimeOfDay? _selectedEndTime;
// @override
// void initState() {
// super.initState();
// print("ControlScreen: initState called.");
// WidgetsBinding.instance.addPostFrameCallback((_) {
// print("ControlScreen: Requesting fetchSchedule.");
// final provider = context.read<ScheduleProvider>();
// provider.fetchSchedule().then((_) {
// if (mounted) {
// print(
// "ControlScreen: fetchSchedule completed. Updating local state.");
// setState(() {
// _selectedStartTime = provider.startTime;
// _selectedEndTime = provider.endTime;
// });
// }
// });
// });
// }
// Future<TimeOfDay?> _selectTime(
// BuildContext context, TimeOfDay? initialTime) async {
// print("ControlScreen: Opening time picker.");
// final TimeOfDay? picked = await showTimePicker(
// context: context,
// initialTime: initialTime ?? TimeOfDay.now(),
// helpText: 'PILIH WAKTU', // Ubah teks helper
// builder: (context, child) {
// // Gunakan tema dari MaterialApp
// return child!;
// // Atau override tema spesifik di sini jika perlu
// // return Theme(
// // data: Theme.of(context).copyWith(
// // timePickerTheme: TimePickerThemeData(...) // Override spesifik
// // ),
// // child: child!,
// // );
// },
// );
// print("ControlScreen: Time picker closed. Picked: $picked");
// return picked;
// }
// String _formatTimeOfDay(TimeOfDay? time) {
// if (time == null) return 'Belum diatur';
// final now = DateTime.now();
// final dt = DateTime(now.year, now.month, now.day, time.hour, time.minute);
// return DateFormat('HH:mm', 'id_ID')
// .format(dt); // Gunakan format 24 jam agar lebih jelas
// }
// @override
// Widget build(BuildContext context) {
// print("ControlScreen: Building UI.");
// return Scaffold(
// // AppBar tidak perlu karena sudah ada di Dashboard
// body: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Consumer<ScheduleProvider>(
// builder: (context, provider, child) {
// // Sinkronisasi state lokal
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (mounted &&
// (_selectedStartTime != provider.startTime ||
// _selectedEndTime != provider.endTime)) {
// print(
// "ControlScreen Consumer: Provider state changed, updating local state.");
// setState(() {
// _selectedStartTime = provider.startTime;
// _selectedEndTime = provider.endTime;
// });
// }
// });
// print(
// "ControlScreen Consumer: Building content. isLoading: ${provider.isLoading}, error: ${provider.errorMessage}");
// return RefreshIndicator(
// // Tambahkan RefreshIndicator
// onRefresh: () async {
// print("ControlScreen: Refresh requested.");
// await provider.fetchSchedule();
// },
// color: Colors.teal,
// child: SingleChildScrollView(
// physics:
// const AlwaysScrollableScrollPhysics(), // Agar bisa refresh walau konten pendek
// child: Column(
// crossAxisAlignment:
// CrossAxisAlignment.stretch, // Buat elemen memenuhi lebar
// children: [
// Text(
// 'Jadwal Nonaktif CCTV',
// style: Theme.of(context)
// .textTheme
// .headlineSmall
// ?.copyWith(
// fontWeight: FontWeight.bold,
// color: Colors.teal[800]),
// ),
// const SizedBox(height: 8),
// Text(
// 'Atur rentang waktu kapan perangkat CCTV tidak akan merekam atau mengirim data.',
// style: Theme.of(context)
// .textTheme
// .bodyMedium
// ?.copyWith(color: Colors.grey[700]),
// ),
// const SizedBox(height: 25),
// // Tampilkan jadwal saat ini
// Card(
// child: Padding(
// padding: const EdgeInsets.all(16.0),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Row(
// // Tambahkan ikon dan tombol refresh
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// 'Jadwal Saat Ini:',
// style: Theme.of(context)
// .textTheme
// .titleMedium
// ?.copyWith(
// fontWeight: FontWeight.w600,
// color: Colors.teal[700]),
// ),
// // Tombol refresh kecil
// IconButton(
// icon: Icon(Icons.refresh,
// color: Colors.grey[500]),
// tooltip: 'Muat Ulang Jadwal',
// iconSize: 20,
// onPressed: provider.isLoading
// ? null
// : () async {
// print(
// "ControlScreen: Inline refresh pressed.");
// await provider.fetchSchedule();
// },
// )
// ],
// ),
// const SizedBox(height: 10),
// if (provider.isLoading &&
// _selectedStartTime == null)
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 10.0),
// child: Center(
// child: CircularProgressIndicator(
// strokeWidth: 3, color: Colors.teal)),
// )
// else if (provider.errorMessage != null &&
// _selectedStartTime == null)
// Text(provider.errorMessage!,
// style:
// const TextStyle(color: Colors.redAccent))
// else
// Row(
// mainAxisAlignment:
// MainAxisAlignment.spaceAround, // Beri jarak
// children: [
// _buildTimeDisplay(
// 'Mulai Nonaktif', _selectedStartTime),
// Icon(Icons.arrow_forward,
// color: Colors
// .grey[400]), // Panah penanda rentang
// _buildTimeDisplay(
// 'Selesai Nonaktif', _selectedEndTime),
// ],
// ),
// ],
// ),
// ),
// ),
// const SizedBox(height: 25),
// // Tombol untuk memilih waktu
// Text(
// 'Setel Jadwal Baru:',
// style: Theme.of(context).textTheme.titleMedium?.copyWith(
// fontWeight: FontWeight.w600, color: Colors.teal[700]),
// ),
// const SizedBox(height: 10),
// Row(
// children: [
// Expanded(
// child: OutlinedButton.icon(
// icon: const Icon(Icons.timer_outlined,
// size: 18), // Kecilkan ikon
// label: Text(_selectedStartTime != null
// ? _formatTimeOfDay(_selectedStartTime)
// : 'Pilih Mulai'),
// onPressed: () async {
// final time = await _selectTime(
// context, _selectedStartTime);
// if (time != null) {
// setState(() {
// _selectedStartTime = time;
// });
// }
// },
// style: OutlinedButton.styleFrom(
// foregroundColor: Colors.teal,
// side: BorderSide(color: Colors.teal[200]!),
// padding: const EdgeInsets.symmetric(
// vertical: 12) // Sesuaikan padding
// ),
// ),
// ),
// const SizedBox(width: 10),
// Expanded(
// child: OutlinedButton.icon(
// icon: const Icon(Icons.timer_off_outlined,
// size: 18), // Kecilkan ikon
// label: Text(_selectedEndTime != null
// ? _formatTimeOfDay(_selectedEndTime)
// : 'Pilih Selesai'),
// onPressed: () async {
// final time =
// await _selectTime(context, _selectedEndTime);
// if (time != null) {
// setState(() {
// _selectedEndTime = time;
// });
// }
// },
// style: OutlinedButton.styleFrom(
// foregroundColor: Colors.teal,
// side: BorderSide(color: Colors.teal[200]!),
// padding: const EdgeInsets.symmetric(
// vertical: 12) // Sesuaikan padding
// ),
// ),
// ),
// ],
// ),
// const SizedBox(
// height: 30), // Beri jarak lebih sebelum tombol simpan
// // Tombol Simpan
// Center(
// child: provider.isLoading
// ? const CircularProgressIndicator(color: Colors.teal)
// : ElevatedButton.icon(
// icon:
// const Icon(Icons.save_alt_outlined, size: 20),
// label: const Text('Simpan Jadwal',
// style: TextStyle(fontSize: 16)),
// style: ElevatedButton.styleFrom(
// padding: const EdgeInsets.symmetric(
// vertical: 14,
// horizontal:
// 30)), // Buat tombol lebih besar
// onPressed: (_selectedStartTime == null ||
// _selectedEndTime == null)
// ? null // Disable jika waktu belum dipilih
// : () async {
// print(
// "ControlScreen: Save button pressed.");
// final success = await context
// .read<ScheduleProvider>()
// .saveSchedule(_selectedStartTime!,
// _selectedEndTime!);
// print(
// "ControlScreen: saveSchedule result: $success");
// if (mounted) {
// ScaffoldMessenger.of(context)
// .showSnackBar(
// SnackBar(
// content: Text(success
// ? 'Jadwal berhasil disimpan!'
// : context
// .read<
// ScheduleProvider>()
// .errorMessage ??
// 'Gagal menyimpan jadwal.'),
// backgroundColor: success
// ? Colors.green[600]
// : Colors.redAccent,
// behavior: SnackBarBehavior
// .floating, // SnackBar mengambang
// ),
// );
// }
// },
// ),
// ),
// // Tampilkan error saving jika ada
// if (provider.errorMessage != null &&
// !provider.isLoading &&
// _selectedStartTime !=
// null) // Tampilkan hanya jika user mencoba menyimpan
// Padding(
// padding: const EdgeInsets.only(top: 15.0),
// child: Center(
// child: Text(provider.errorMessage!,
// style: const TextStyle(color: Colors.redAccent),
// textAlign: TextAlign.center),
// ),
// ),
// const SizedBox(height: 20), // Padding bawah
// ],
// ),
// ),
// );
// },
// ),
// ),
// );
// }
// // Helper widget untuk menampilkan waktu
// Widget _buildTimeDisplay(String label, TimeOfDay? time) {
// return Column(
// crossAxisAlignment: CrossAxisAlignment.center,
// children: [
// Text(label, style: TextStyle(color: Colors.grey[600], fontSize: 12)),
// const SizedBox(height: 2),
// Text(
// _formatTimeOfDay(time),
// style: const TextStyle(
// fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87),
// ),
// ],
// );
// }
// }