TKK_E32231105/lib/screens/kontrol_page.dart

796 lines
29 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/kandang_model.dart';
import '../models/penjadwalan_model.dart';
import '../providers/kandang_provider.dart';
import '../providers/penjadwalan_provider.dart';
class KontrolPage extends StatefulWidget {
const KontrolPage({super.key});
@override
State<KontrolPage> createState() => _KontrolPageState();
}
class _KontrolPageState extends State<KontrolPage> {
int _selectedTab = 0; // 0: Penjadwalan, 1: Tambah Kandang
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.orange.shade100,
Colors.amber.shade100,
Colors.greenAccent.shade100,
],
),
),
child: SafeArea(
child: Column(
children: [
// Tab Selector
Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Row(
children: [
Expanded(
child: GestureDetector(
onTap: () => setState(() => _selectedTab = 0),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: _selectedTab == 0
? Colors.orange.shade600
: Colors.transparent,
width: 2,
),
),
),
child: Text(
'Penjadwalan',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: _selectedTab == 0
? FontWeight.bold
: FontWeight.w500,
color: _selectedTab == 0
? Colors.orange.shade600
: Colors.grey.shade600,
),
),
),
),
),
Expanded(
child: GestureDetector(
onTap: () => setState(() => _selectedTab = 1),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: _selectedTab == 1
? Colors.orange.shade600
: Colors.transparent,
width: 2,
),
),
),
child: Text(
'Tambah Kandang',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: _selectedTab == 1
? FontWeight.bold
: FontWeight.w500,
color: _selectedTab == 1
? Colors.orange.shade600
: Colors.grey.shade600,
),
),
),
),
),
],
),
),
// Content
Expanded(
child: _selectedTab == 0
? _buildPenjadwalanTab()
: _buildTambahKandangTab(),
),
],
),
),
),
);
}
Widget _buildPenjadwalanTab() {
final penjadwalanProvider = context.watch<PenjadwalanProvider>();
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Atur Penjadwalan Panen',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.orange.shade700,
),
),
const SizedBox(height: 8),
Text(
'Kelola jadwal panen untuk semua kandang',
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
),
const SizedBox(height: 20),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: penjadwalanProvider.penjadwalans.length,
itemBuilder: (context, index) {
final jadwal = penjadwalanProvider.penjadwalans[index];
final isSore = jadwal.tipeJadwal == 'sore';
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
jadwal.jam,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
color: isSore
? Colors.deepOrange.shade300
: Colors.amber.shade300,
borderRadius: BorderRadius.circular(6),
),
child: Text(
isSore ? 'Sore' : 'Pagi',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: isSore
? Colors.white
: Colors.brown.shade700,
),
),
),
],
),
const SizedBox(height: 4),
Text(
jadwal.keterangan,
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
const SizedBox(height: 4),
Text(
'Durasi: ${jadwal.durasi}',
style: TextStyle(
fontSize: 11,
color: Colors.orange.shade600,
fontWeight: FontWeight.w500,
),
),
],
),
Column(
children: [
Switch(
value: jadwal.aktif,
onChanged: (value) {
penjadwalanProvider.togglePenjadwalan(
jadwal.id,
);
},
activeColor: Colors.green.shade600,
),
Text(
jadwal.aktif ? 'Aktif' : 'Nonaktif',
style: TextStyle(
fontSize: 10,
color: jadwal.aktif
? Colors.green.shade600
: Colors.grey.shade600,
),
),
],
),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () =>
_showEditJadwalDialog(context, jadwal),
icon: const Icon(Icons.edit),
label: const Text('Edit'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange.shade600,
foregroundColor: Colors.white,
),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
penjadwalanProvider.deletePenjadwalan(
jadwal.id,
);
},
icon: const Icon(Icons.delete),
label: const Text('Hapus'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red.shade600,
foregroundColor: Colors.white,
),
),
),
],
),
],
),
),
);
},
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: Consumer<PenjadwalanProvider>(
builder: (context, penjadwalanProvider, child) {
final isDisabled = penjadwalanProvider.isMaximalPenjadwalan;
return ElevatedButton.icon(
onPressed: isDisabled
? () => _showPenjadwalanDisabledDialog(context)
: () => _showTambahJadwalBaru(context),
icon: const Icon(Icons.add),
label: const Text('Tambah Jadwal Baru'),
style: ElevatedButton.styleFrom(
backgroundColor: isDisabled
? Colors.grey.shade400
: Colors.green.shade600,
foregroundColor:
isDisabled ? Colors.grey.shade700 : Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
),
);
},
),
),
],
),
);
}
Widget _buildTambahKandangTab() {
final kandangProvider = context.watch<KandangProvider>();
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Kelola Kandang',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Colors.orange.shade700,
),
),
const SizedBox(height: 8),
Text(
'Tambah, edit, atau hapus kandang',
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
),
const SizedBox(height: 20),
// Daftar Kandang
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: kandangProvider.kandangs.length,
itemBuilder: (context, index) {
final kandang = kandangProvider.kandangs[index];
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
kandang.nama,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'${kandang.jumlahAyam} ekor ayam',
style: TextStyle(
fontSize: 12,
color: Colors.grey.shade600,
),
),
],
),
),
Icon(Icons.home, color: Colors.orange.shade600),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () =>
_showEditKandangDialog(context, kandang),
icon: const Icon(Icons.edit),
label: const Text('Edit'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange.shade600,
foregroundColor: Colors.white,
),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
kandangProvider.deleteKandang(kandang.id);
},
icon: const Icon(Icons.delete),
label: const Text('Hapus'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red.shade600,
foregroundColor: Colors.white,
),
),
),
],
),
],
),
),
);
},
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: Consumer<KandangProvider>(
builder: (context, kandangProvider, child) {
final isDisabled = kandangProvider.kandangs.length >= 2;
return ElevatedButton.icon(
onPressed: isDisabled
? () => _showKandangDisabledDialog(context)
: () => _showTambahKandangDialog(context),
icon: const Icon(Icons.add),
label: const Text('Tambah Kandang Baru'),
style: ElevatedButton.styleFrom(
backgroundColor: isDisabled
? Colors.grey.shade400
: Colors.green.shade600,
foregroundColor:
isDisabled ? Colors.grey.shade700 : Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
),
);
},
),
),
],
),
);
}
void _showEditJadwalDialog(BuildContext context, Penjadwalan jadwal) {
final jamController = TextEditingController(text: jadwal.jam);
final durasiController = TextEditingController(text: jadwal.durasi);
final keteranganController = TextEditingController(text: jadwal.keterangan);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Edit Jadwal Panen'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: jamController,
decoration: const InputDecoration(
hintText: 'Jam (HH:MM)',
prefixIcon: Icon(Icons.schedule),
),
),
const SizedBox(height: 12),
TextField(
controller: durasiController,
decoration: const InputDecoration(
hintText: 'Durasi (contoh: 30 menit)',
prefixIcon: Icon(Icons.timer),
),
),
const SizedBox(height: 12),
TextField(
controller: keteranganController,
decoration: const InputDecoration(
hintText: 'Keterangan',
prefixIcon: Icon(Icons.notes),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Batal'),
),
TextButton(
onPressed: () {
if (jamController.text.isNotEmpty &&
durasiController.text.isNotEmpty &&
keteranganController.text.isNotEmpty) {
context.read<PenjadwalanProvider>().updatePenjadwalan(
jadwal.id,
jamController.text,
durasiController.text,
keteranganController.text,
jadwal.aktif,
);
Navigator.pop(context);
}
},
child: const Text('Simpan'),
),
],
),
);
}
void _showTambahJadwalBaru(BuildContext context) {
final penjadwalanProvider = context.read<PenjadwalanProvider>();
final jamController = TextEditingController();
final durasiController = TextEditingController();
final keteranganController = TextEditingController();
// Tentukan tipe jadwal berdasarkan jumlah jadwal yang sudah ada
String tipeJadwal = 'pagi'; // Default pagi
String judulTipe = 'Pagi';
if (penjadwalanProvider.penjadwalans.isNotEmpty) {
tipeJadwal = 'sore';
judulTipe = 'Sore';
}
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Tambah Jadwal Panen Baru'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: tipeJadwal == 'sore'
? Colors.deepOrange.shade50
: Colors.amber.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(
tipeJadwal == 'sore' ? Icons.nights_stay : Icons.wb_sunny,
color: tipeJadwal == 'sore'
? Colors.deepOrange.shade600
: Colors.amber.shade600,
),
const SizedBox(width: 8),
Expanded(
child: Text(
'Jadwal Panen $judulTipe',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: tipeJadwal == 'sore'
? Colors.deepOrange.shade700
: Colors.amber.shade700,
),
),
),
],
),
),
const SizedBox(height: 16),
if (tipeJadwal == 'sore') ...[
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.blue.shade50,
border: Border.all(color: Colors.blue.shade200),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'Info: Hasil panen sore akan otomatis direset setiap hari',
style: TextStyle(
fontSize: 12,
color: Colors.blue.shade700,
),
),
),
const SizedBox(height: 12),
],
TextField(
controller: jamController,
decoration: const InputDecoration(
hintText: 'Jam (HH:MM)',
prefixIcon: Icon(Icons.schedule),
),
),
const SizedBox(height: 12),
TextField(
controller: durasiController,
decoration: const InputDecoration(
hintText: 'Durasi (contoh: 30 menit)',
prefixIcon: Icon(Icons.timer),
),
),
const SizedBox(height: 12),
TextField(
controller: keteranganController,
decoration: const InputDecoration(
hintText: 'Keterangan',
prefixIcon: Icon(Icons.notes),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Batal'),
),
TextButton(
onPressed: () {
if (jamController.text.isNotEmpty &&
durasiController.text.isNotEmpty &&
keteranganController.text.isNotEmpty) {
context.read<PenjadwalanProvider>().addPenjadwalan(
'global',
'Semua Kandang',
jamController.text,
durasiController.text,
keteranganController.text,
tipeJadwal: tipeJadwal,
);
Navigator.pop(context);
}
},
child: const Text('Simpan'),
),
],
),
);
}
void _showPenjadwalanDisabledDialog(BuildContext context) {
final penjadwalanProvider = context.read<PenjadwalanProvider>();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Batas Penjadwalan Tercapai'),
content: Text(
'Anda sudah memiliki ${penjadwalanProvider.penjadwalans.length} jadwal panen. Batas maksimal adalah 2 jadwal (pagi dan sore). Silahkan hapus salah satu jadwal terlebih dahulu sebelum menambahkan jadwal baru.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
void _showKandangDisabledDialog(BuildContext context) {
final kandangProvider = context.read<KandangProvider>();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Batas Kandang Tercapai'),
content: Text(
'Anda sudah memiliki ${kandangProvider.kandangs.length} kandang. Batas maksimal kandang adalah 2. Silahkan hubungi pembuat aplikasi untuk menambahkan kandang lebih dari 2.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
void _showTambahKandangDialog(BuildContext context) {
final namaController = TextEditingController();
final jumlahAyamController = TextEditingController();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Tambah Kandang Baru'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: namaController,
decoration: const InputDecoration(
hintText: 'Nama Kandang',
prefixIcon: Icon(Icons.home),
),
),
const SizedBox(height: 12),
TextField(
controller: jumlahAyamController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: 'Jumlah Ayam',
prefixIcon: Icon(Icons.pets),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Batal'),
),
TextButton(
onPressed: () {
if (namaController.text.isNotEmpty &&
int.tryParse(jumlahAyamController.text) != null) {
context.read<KandangProvider>().addKandang(
namaController.text,
int.parse(jumlahAyamController.text),
);
Navigator.pop(context);
}
},
child: const Text('Simpan'),
),
],
),
);
}
void _showEditKandangDialog(BuildContext context, Kandang kandang) {
final namaController = TextEditingController(text: kandang.nama);
final jumlahAyamController = TextEditingController(
text: kandang.jumlahAyam.toString(),
);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Edit Kandang'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: namaController,
decoration: const InputDecoration(
hintText: 'Nama Kandang',
prefixIcon: Icon(Icons.home),
),
),
const SizedBox(height: 12),
TextField(
controller: jumlahAyamController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: 'Jumlah Ayam',
prefixIcon: Icon(Icons.pets),
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Batal'),
),
TextButton(
onPressed: () {
if (namaController.text.isNotEmpty &&
int.tryParse(jumlahAyamController.text) != null) {
context.read<KandangProvider>().updateKandang(
kandang.id,
namaController.text,
int.parse(jumlahAyamController.text),
);
Navigator.pop(context);
}
},
child: const Text('Simpan'),
),
],
),
);
}
}