1140 lines
38 KiB
Dart
1140 lines
38 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
import 'package:tugas_akhir_supabase/core/theme/app_colors.dart';
|
|
import 'dart:async'; // Tambahkan import untuk TimeoutException
|
|
import 'dart:convert';
|
|
import 'package:tugas_akhir_supabase/domain/entities/field.dart';
|
|
import 'package:tugas_akhir_supabase/screens/calendar/location_picker_dialog.dart';
|
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
|
|
|
class AddFieldBottomSheet extends StatefulWidget {
|
|
final Function? onFieldAdded;
|
|
final Field? fieldToEdit;
|
|
|
|
const AddFieldBottomSheet({super.key, this.onFieldAdded, this.fieldToEdit});
|
|
|
|
@override
|
|
State<AddFieldBottomSheet> createState() => _AddFieldBottomSheetState();
|
|
}
|
|
|
|
class _AddFieldBottomSheetState extends State<AddFieldBottomSheet> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
final _nameController = TextEditingController();
|
|
final _locationController = TextEditingController();
|
|
final _areaSizeController = TextEditingController();
|
|
final _plotCountController = TextEditingController(text: '1');
|
|
final _ownerNameController = TextEditingController();
|
|
final _customRegionController = TextEditingController();
|
|
final _customSystemTypeController = TextEditingController();
|
|
final _customLandFeatureController = TextEditingController();
|
|
final _previousCropController = TextEditingController();
|
|
final _riverDistanceController = TextEditingController();
|
|
|
|
bool _isLoading = false;
|
|
bool _isEditMode = false;
|
|
String? _fieldId;
|
|
|
|
// Koordinat lokasi
|
|
double? _latitude;
|
|
double? _longitude;
|
|
|
|
// Focus nodes untuk setiap field
|
|
final _fieldNameFocus = FocusNode();
|
|
final _locationFocus = FocusNode();
|
|
final _areaSizeFocus = FocusNode();
|
|
final _plotCountFocus = FocusNode();
|
|
final _ownerNameFocus = FocusNode();
|
|
final _customRegionFocus = FocusNode();
|
|
final _customSystemTypeFocus = FocusNode();
|
|
final _customLandFeatureFocus = FocusNode();
|
|
final _previousCropFocus = FocusNode();
|
|
final _riverDistanceFocus = FocusNode();
|
|
|
|
// Region selection
|
|
String _selectedRegion = 'Jawa';
|
|
final List<String> _regions = [
|
|
'Jawa',
|
|
'Sumatera',
|
|
'Kalimantan',
|
|
'Sulawesi',
|
|
'Bali & Nusa Tenggara',
|
|
'Maluku & Papua',
|
|
'Lainnya',
|
|
];
|
|
|
|
// Ownership type selection
|
|
String _selectedOwnershipType = 'Milik Sendiri';
|
|
final List<String> _ownershipTypes = [
|
|
'Milik Sendiri',
|
|
'Sewa',
|
|
'Bagi Hasil',
|
|
'Lainnya',
|
|
];
|
|
|
|
// Topografi selection
|
|
String _selectedTopography = 'Dataran rendah (0-100m)';
|
|
final List<String> _topographies = [
|
|
'Dataran rendah (0-100m)',
|
|
'Dataran sedang (100-500m)',
|
|
'Dataran tinggi (>500m)',
|
|
];
|
|
|
|
// Kemiringan lahan selection
|
|
String _selectedSlope = 'Datar (0-8%)';
|
|
final List<String> _slopes = [
|
|
'Datar (0-8%)',
|
|
'Landai (8-15%)',
|
|
'Miring (15-25%)',
|
|
'Curam (>25%)',
|
|
];
|
|
|
|
// Jenis tanah selection
|
|
String _selectedSoilType = 'Lempung';
|
|
final List<String> _soilTypes = [
|
|
'Lempung',
|
|
'Aluvial',
|
|
'Andosol',
|
|
'Latosol',
|
|
'Regosol',
|
|
'Podsolik',
|
|
'Grumusol',
|
|
'Litosol',
|
|
'Organosol',
|
|
'Gambut',
|
|
];
|
|
|
|
// Sumber air selection
|
|
String _selectedWaterSource = 'Irigasi';
|
|
final List<String> _waterSources = [
|
|
'Irigasi',
|
|
'Tadah Hujan',
|
|
'Sumur',
|
|
'Sungai',
|
|
'Mata Air',
|
|
];
|
|
|
|
// Jenis irigasi selection
|
|
String _selectedIrrigationType = 'Irigasi Teknis';
|
|
final List<String> _irrigationTypes = [
|
|
'Irigasi Teknis',
|
|
'Semi Teknis',
|
|
'Sederhana',
|
|
'Irigasi permukaan',
|
|
'Irigasi tetes',
|
|
'Irigasi sprinkler',
|
|
];
|
|
|
|
// Region-specific form fields
|
|
Map<String, dynamic> _regionSpecificData = {};
|
|
|
|
// Jawa specific fields
|
|
String _selectedPetakSystem = 'Petak Sawah';
|
|
final List<String> _petakSystems = [
|
|
'Petak Sawah',
|
|
'Tegal',
|
|
'Kebun',
|
|
'Campuran',
|
|
];
|
|
|
|
// Sumatera specific fields
|
|
String _selectedBlokSystem = 'Ladang';
|
|
final List<String> _blokSystems = ['Ladang', 'Kebun', 'Plasma', 'Campuran'];
|
|
|
|
// Kalimantan specific fields
|
|
String _selectedLadangSystem = 'Tetap';
|
|
final List<String> _ladangSystems = ['Tetap', 'Berpindah', 'Semi Permanen'];
|
|
|
|
// Sulawesi specific fields
|
|
String _selectedKebunSystem = 'Permanen';
|
|
final List<String> _kebunSystems = ['Permanen', 'Rotasi', 'Campuran'];
|
|
|
|
String _selectedTerrainType = 'Datar';
|
|
final List<String> _terrainTypes = [
|
|
'Datar',
|
|
'Berbukit',
|
|
'Terasering',
|
|
'Lereng',
|
|
];
|
|
|
|
// Bali & Nusa Tenggara specific fields
|
|
String _selectedSubakSystem = 'Subak Tradisional';
|
|
final List<String> _subakSystems = [
|
|
'Subak Tradisional',
|
|
'Subak Modern',
|
|
'Non-Subak',
|
|
'Campuran',
|
|
];
|
|
|
|
// Maluku & Papua specific fields
|
|
String _selectedGardenSystem = 'Kebun Sagu';
|
|
final List<String> _gardenSystems = [
|
|
'Kebun Sagu',
|
|
'Perkebunan Kelapa',
|
|
'Agroforestri',
|
|
'Ladang',
|
|
];
|
|
|
|
String _selectedForestType = 'Hutan Primer';
|
|
final List<String> _forestTypes = [
|
|
'Hutan Primer',
|
|
'Hutan Sekunder',
|
|
'Bekas Tebangan',
|
|
'Lahan Konversi',
|
|
];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// Pastikan keyboard tidak terbuka saat dialog muncul
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
FocusScope.of(context).unfocus();
|
|
});
|
|
|
|
// Inisialisasi data jika dalam mode edit
|
|
if (widget.fieldToEdit != null) {
|
|
_isEditMode = true;
|
|
_fieldId = widget.fieldToEdit!.id;
|
|
_nameController.text = widget.fieldToEdit!.name;
|
|
_locationController.text = widget.fieldToEdit!.location ?? '';
|
|
_latitude = widget.fieldToEdit!.latitude;
|
|
_longitude = widget.fieldToEdit!.longitude;
|
|
|
|
if (widget.fieldToEdit!.areaSize != null) {
|
|
_areaSizeController.text = widget.fieldToEdit!.areaSize.toString();
|
|
}
|
|
_plotCountController.text = widget.fieldToEdit!.plotCount.toString();
|
|
_selectedRegion = widget.fieldToEdit!.region ?? 'Jawa';
|
|
_selectedOwnershipType =
|
|
widget.fieldToEdit!.ownershipType ?? 'Milik Sendiri';
|
|
_ownerNameController.text = widget.fieldToEdit!.ownerName ?? '';
|
|
|
|
// Inisialisasi data spesifik region jika ada
|
|
if (widget.fieldToEdit!.regionSpecificData != null) {
|
|
final data = widget.fieldToEdit!.regionSpecificData!;
|
|
|
|
// Data umum
|
|
_selectedTopography = data['topografi'] ?? 'Dataran rendah (0-100m)';
|
|
_selectedSlope = data['kemiringan_lahan'] ?? 'Datar (0-8%)';
|
|
_selectedSoilType = data['jenis_tanah'] ?? 'Lempung';
|
|
_selectedWaterSource = data['sumber_air'] ?? 'Irigasi';
|
|
_selectedIrrigationType = data['jenis_irigasi'] ?? 'Irigasi Teknis';
|
|
_previousCropController.text = data['tanaman_sebelumnya'] ?? '';
|
|
|
|
// Data spesifik region
|
|
switch (_selectedRegion) {
|
|
case 'Jawa':
|
|
_selectedPetakSystem = data['sistem_petak'] ?? 'Petak Sawah';
|
|
break;
|
|
case 'Sumatera':
|
|
_selectedBlokSystem = data['sistem_blok'] ?? 'Ladang';
|
|
break;
|
|
case 'Kalimantan':
|
|
_selectedLadangSystem = data['sistem_ladang'] ?? 'Tetap';
|
|
_riverDistanceController.text = data['jarak_sungai'] ?? '';
|
|
break;
|
|
case 'Sulawesi':
|
|
_selectedKebunSystem = data['sistem_kebun'] ?? 'Permanen';
|
|
_selectedTerrainType = data['kontur_lahan'] ?? 'Datar';
|
|
break;
|
|
case 'Bali & Nusa Tenggara':
|
|
_selectedSubakSystem = data['sistem_subak'] ?? 'Subak Tradisional';
|
|
break;
|
|
case 'Maluku & Papua':
|
|
_selectedGardenSystem = data['sistem_kebun'] ?? 'Kebun Sagu';
|
|
_selectedForestType = data['tipe_hutan'] ?? 'Hutan Primer';
|
|
break;
|
|
case 'Lainnya':
|
|
_customRegionController.text = data['nama_wilayah'] ?? '';
|
|
_customSystemTypeController.text = data['sistem_lahan'] ?? '';
|
|
_customLandFeatureController.text = data['fitur_lahan'] ?? '';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_nameController.dispose();
|
|
_locationController.dispose();
|
|
_areaSizeController.dispose();
|
|
_plotCountController.dispose();
|
|
_ownerNameController.dispose();
|
|
_customRegionController.dispose();
|
|
_customSystemTypeController.dispose();
|
|
_customLandFeatureController.dispose();
|
|
_previousCropController.dispose();
|
|
_riverDistanceController.dispose();
|
|
_fieldNameFocus.dispose();
|
|
_locationFocus.dispose();
|
|
_areaSizeFocus.dispose();
|
|
_plotCountFocus.dispose();
|
|
_ownerNameFocus.dispose();
|
|
_customRegionFocus.dispose();
|
|
_customSystemTypeFocus.dispose();
|
|
_customLandFeatureFocus.dispose();
|
|
_previousCropFocus.dispose();
|
|
_riverDistanceFocus.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
// Update region-specific data based on selected region
|
|
void _updateRegionSpecificData() {
|
|
// Tambahkan data umum untuk semua wilayah
|
|
_regionSpecificData = {
|
|
'topografi': _selectedTopography,
|
|
'kemiringan_lahan': _selectedSlope,
|
|
'jenis_tanah': _selectedSoilType,
|
|
'sumber_air': _selectedWaterSource,
|
|
'jenis_irigasi': _selectedIrrigationType,
|
|
'tanaman_sebelumnya': _previousCropController.text,
|
|
};
|
|
|
|
// Tambahkan data spesifik wilayah
|
|
switch (_selectedRegion) {
|
|
case 'Jawa':
|
|
_regionSpecificData['sistem_petak'] = _selectedPetakSystem;
|
|
break;
|
|
case 'Sumatera':
|
|
_regionSpecificData['sistem_blok'] = _selectedBlokSystem;
|
|
break;
|
|
case 'Kalimantan':
|
|
_regionSpecificData['sistem_ladang'] = _selectedLadangSystem;
|
|
_regionSpecificData['jarak_sungai'] = _riverDistanceController.text;
|
|
break;
|
|
case 'Sulawesi':
|
|
_regionSpecificData['sistem_kebun'] = _selectedKebunSystem;
|
|
_regionSpecificData['kontur_lahan'] = _selectedTerrainType;
|
|
break;
|
|
case 'Bali & Nusa Tenggara':
|
|
_regionSpecificData['sistem_subak'] = _selectedSubakSystem;
|
|
break;
|
|
case 'Maluku & Papua':
|
|
_regionSpecificData['sistem_kebun'] = _selectedGardenSystem;
|
|
_regionSpecificData['tipe_hutan'] = _selectedForestType;
|
|
break;
|
|
case 'Lainnya':
|
|
_regionSpecificData['nama_wilayah'] = _customRegionController.text;
|
|
_regionSpecificData['sistem_lahan'] = _customSystemTypeController.text;
|
|
_regionSpecificData['fitur_lahan'] = _customLandFeatureController.text;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Tambahkan metode untuk memilih lokasi
|
|
Future<void> _showLocationPicker() async {
|
|
final result = await showDialog<LocationResult>(
|
|
context: context,
|
|
builder:
|
|
(context) => LocationPickerDialog(
|
|
initialAddress: _locationController.text,
|
|
initialLatitude: _latitude,
|
|
initialLongitude: _longitude,
|
|
),
|
|
);
|
|
|
|
if (result != null) {
|
|
setState(() {
|
|
_locationController.text = result.address;
|
|
_latitude = result.latitude;
|
|
_longitude = result.longitude;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _submit() async {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
|
|
// Dismiss keyboard immediately when saving
|
|
FocusScope.of(context).unfocus();
|
|
|
|
final userId = Supabase.instance.client.auth.currentUser?.id;
|
|
if (userId == null) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('User tidak ditemukan, silakan login ulang.'),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
// Update region-specific data
|
|
_updateRegionSpecificData();
|
|
|
|
final id = _fieldId ?? const Uuid().v4();
|
|
final fieldData = {
|
|
'id': id,
|
|
'user_id': userId,
|
|
'name': _nameController.text,
|
|
'plot_count': int.parse(_plotCountController.text),
|
|
'region':
|
|
_selectedRegion == 'Lainnya'
|
|
? _customRegionController.text
|
|
: _selectedRegion,
|
|
'location': _locationController.text,
|
|
'latitude': _latitude,
|
|
'longitude': _longitude,
|
|
'area_size':
|
|
_areaSizeController.text.isNotEmpty
|
|
? double.parse(_areaSizeController.text)
|
|
: null,
|
|
'area_unit': 'm²',
|
|
'ownership_type': _selectedOwnershipType,
|
|
'owner_name':
|
|
_selectedOwnershipType != 'Milik Sendiri'
|
|
? _ownerNameController.text
|
|
: null,
|
|
'region_specific_data': _regionSpecificData,
|
|
};
|
|
|
|
if (_isEditMode) {
|
|
// Update existing field
|
|
final response =
|
|
await Supabase.instance.client
|
|
.from('fields')
|
|
.update(fieldData)
|
|
.eq('id', id)
|
|
.select();
|
|
|
|
print('Update response: $response');
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Lahan berhasil diperbarui'),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
} else {
|
|
// Create new field
|
|
final response =
|
|
await Supabase.instance.client
|
|
.from('fields')
|
|
.insert(fieldData)
|
|
.select();
|
|
|
|
print('Insert response: $response');
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text('Lahan berhasil ditambahkan'),
|
|
backgroundColor: Colors.green,
|
|
),
|
|
);
|
|
}
|
|
|
|
if (widget.onFieldAdded != null) {
|
|
widget.onFieldAdded!();
|
|
}
|
|
|
|
if (mounted) {
|
|
Navigator.pop(context, true);
|
|
}
|
|
} catch (e) {
|
|
print('Error saving field: ${e.toString()}');
|
|
if (mounted) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Gagal menyimpan lahan: ${e.toString()}'),
|
|
backgroundColor: Colors.red,
|
|
),
|
|
);
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// UI Components
|
|
Widget _buildHeader() {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
decoration: BoxDecoration(
|
|
gradient: LinearGradient(
|
|
colors: [const Color.fromARGB(255, 0, 79, 39), Colors.green.shade900],
|
|
begin: Alignment.topCenter,
|
|
end: Alignment.bottomCenter,
|
|
),
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(16),
|
|
topRight: Radius.circular(16),
|
|
),
|
|
),
|
|
child: Center(
|
|
child: Column(
|
|
children: [
|
|
Container(
|
|
width: 50,
|
|
height: 5,
|
|
decoration: BoxDecoration(
|
|
color: Colors.white.withOpacity(0.5),
|
|
borderRadius: BorderRadius.circular(2.5),
|
|
),
|
|
margin: const EdgeInsets.only(bottom: 16),
|
|
),
|
|
Text(
|
|
_isEditMode ? 'Edit Lahan' : 'Tambah Lahan Baru',
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
_isEditMode
|
|
? 'Perbarui informasi lahan Anda'
|
|
: 'Lengkapi informasi lahan pertanian Anda',
|
|
style: TextStyle(
|
|
color: Colors.white.withOpacity(0.8),
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTextField({
|
|
required String label,
|
|
required TextEditingController controller,
|
|
required IconData icon,
|
|
required FocusNode focusNode,
|
|
TextInputType keyboardType = TextInputType.text,
|
|
String? Function(String?)? validator,
|
|
String? hintText,
|
|
bool readOnly = false,
|
|
}) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: TextFormField(
|
|
controller: controller,
|
|
focusNode: focusNode,
|
|
decoration: InputDecoration(
|
|
labelText: label,
|
|
hintText: hintText,
|
|
prefixIcon: Icon(icon),
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 14,
|
|
),
|
|
),
|
|
keyboardType: keyboardType,
|
|
validator: validator,
|
|
readOnly: readOnly,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDropdown({
|
|
required String label,
|
|
required String value,
|
|
required List<String> items,
|
|
required Function(String?) onChanged,
|
|
required IconData icon,
|
|
}) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: DropdownButtonFormField<String>(
|
|
value: value,
|
|
decoration: InputDecoration(
|
|
labelText: label,
|
|
prefixIcon: Icon(icon),
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 14,
|
|
),
|
|
),
|
|
items:
|
|
items.map((String item) {
|
|
return DropdownMenuItem<String>(value: item, child: Text(item));
|
|
}).toList(),
|
|
onChanged: _isLoading ? null : onChanged,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSectionTitle(String title) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 4,
|
|
height: 20,
|
|
decoration: BoxDecoration(
|
|
color: Colors.green,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
title,
|
|
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRegionSpecificFields() {
|
|
switch (_selectedRegion) {
|
|
case 'Jawa':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Pulau Jawa'),
|
|
_buildDropdown(
|
|
label: 'Sistem Petak',
|
|
value: _selectedPetakSystem,
|
|
items: _petakSystems,
|
|
icon: Icons.grid_on,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedPetakSystem = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
],
|
|
);
|
|
case 'Sumatera':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Pulau Sumatera'),
|
|
_buildDropdown(
|
|
label: 'Sistem Blok',
|
|
value: _selectedBlokSystem,
|
|
items: _blokSystems,
|
|
icon: Icons.dashboard,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedBlokSystem = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
],
|
|
);
|
|
case 'Kalimantan':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Pulau Kalimantan'),
|
|
_buildDropdown(
|
|
label: 'Sistem Ladang',
|
|
value: _selectedLadangSystem,
|
|
items: _ladangSystems,
|
|
icon: Icons.agriculture,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedLadangSystem = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildTextField(
|
|
label: 'Jarak dari Sungai (meter)',
|
|
controller: _riverDistanceController,
|
|
icon: Icons.waves,
|
|
focusNode: _riverDistanceFocus,
|
|
keyboardType: TextInputType.number,
|
|
hintText: 'Contoh: 500',
|
|
),
|
|
],
|
|
);
|
|
case 'Sulawesi':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Pulau Sulawesi'),
|
|
_buildDropdown(
|
|
label: 'Sistem Kebun',
|
|
value: _selectedKebunSystem,
|
|
items: _kebunSystems,
|
|
icon: Icons.eco,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedKebunSystem = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildDropdown(
|
|
label: 'Kontur Lahan',
|
|
value: _selectedTerrainType,
|
|
items: _terrainTypes,
|
|
icon: Icons.terrain,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedTerrainType = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
],
|
|
);
|
|
case 'Bali & Nusa Tenggara':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Pulau Bali & Nusa Tenggara'),
|
|
_buildDropdown(
|
|
label: 'Sistem Subak',
|
|
value: _selectedSubakSystem,
|
|
items: _subakSystems,
|
|
icon: Icons.water_damage,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedSubakSystem = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
],
|
|
);
|
|
case 'Maluku & Papua':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Pulau Maluku & Papua'),
|
|
_buildDropdown(
|
|
label: 'Sistem Kebun',
|
|
value: _selectedGardenSystem,
|
|
items: _gardenSystems,
|
|
icon: Icons.forest,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedGardenSystem = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildDropdown(
|
|
label: 'Tipe Hutan',
|
|
value: _selectedForestType,
|
|
items: _forestTypes,
|
|
icon: Icons.park,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedForestType = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
],
|
|
);
|
|
case 'Lainnya':
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('Informasi Wilayah Kustom'),
|
|
_buildTextField(
|
|
label: 'Nama Wilayah',
|
|
controller: _customRegionController,
|
|
icon: Icons.location_city,
|
|
focusNode: _customRegionFocus,
|
|
validator: (value) {
|
|
if (_selectedRegion == 'Lainnya' &&
|
|
(value == null || value.isEmpty)) {
|
|
return 'Nama wilayah harus diisi';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
_buildTextField(
|
|
label: 'Sistem Pengelolaan Lahan',
|
|
controller: _customSystemTypeController,
|
|
icon: Icons.agriculture,
|
|
focusNode: _customSystemTypeFocus,
|
|
validator: (value) {
|
|
if (_selectedRegion == 'Lainnya' &&
|
|
(value == null || value.isEmpty)) {
|
|
return 'Sistem pengelolaan lahan harus diisi';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
_buildTextField(
|
|
label: 'Fitur Khusus Lahan',
|
|
controller: _customLandFeatureController,
|
|
icon: Icons.landscape,
|
|
focusNode: _customLandFeatureFocus,
|
|
),
|
|
],
|
|
);
|
|
default:
|
|
return Container();
|
|
}
|
|
}
|
|
|
|
// Modifikasi metode _buildTextField untuk lokasi agar menggunakan tombol pilih lokasi
|
|
Widget _buildLocationField() {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(bottom: 16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Lokasi',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: _locationController,
|
|
focusNode: _locationFocus,
|
|
readOnly: true,
|
|
decoration: InputDecoration(
|
|
hintText: 'Pilih lokasi di peta',
|
|
prefixIcon: const Icon(Icons.location_on),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 16,
|
|
vertical: 14,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
ElevatedButton(
|
|
onPressed: _isLoading ? null : _showLocationPicker,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: AppColors.primary,
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
padding: const EdgeInsets.all(12),
|
|
),
|
|
child: const Icon(Icons.map),
|
|
),
|
|
],
|
|
),
|
|
if (_latitude != null && _longitude != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 8),
|
|
child: Text(
|
|
'Koordinat: ${_latitude!.toStringAsFixed(6)}, ${_longitude!.toStringAsFixed(6)}',
|
|
style: TextStyle(color: Colors.grey[600], fontSize: 12),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
height: MediaQuery.of(context).size.height * 0.9,
|
|
decoration: const BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.only(
|
|
topLeft: Radius.circular(16),
|
|
topRight: Radius.circular(16),
|
|
),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
_buildHeader(),
|
|
Expanded(
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: SingleChildScrollView(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Informasi Dasar
|
|
_buildSectionTitle('Informasi Dasar'),
|
|
_buildTextField(
|
|
label: 'Nama Lahan',
|
|
controller: _nameController,
|
|
icon: Icons.agriculture,
|
|
focusNode: _fieldNameFocus,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Nama lahan wajib diisi';
|
|
}
|
|
return null;
|
|
},
|
|
hintText: 'Contoh: Lahan Cabai',
|
|
),
|
|
_buildDropdown(
|
|
label: 'Wilayah',
|
|
value: _selectedRegion,
|
|
items: _regions,
|
|
icon: Icons.map,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedRegion = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
|
|
// Ganti TextField lokasi dengan custom widget
|
|
_buildLocationField(),
|
|
|
|
_buildTextField(
|
|
label: 'Jumlah Petak',
|
|
controller: _plotCountController,
|
|
icon: Icons.grid_4x4,
|
|
focusNode: _plotCountFocus,
|
|
keyboardType: TextInputType.number,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) {
|
|
return 'Jumlah petak wajib diisi';
|
|
}
|
|
if (int.tryParse(value) == null ||
|
|
int.parse(value) < 1) {
|
|
return 'Jumlah petak harus berupa angka positif';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
_buildTextField(
|
|
label: 'Luas Lahan (m²)',
|
|
controller: _areaSizeController,
|
|
icon: Icons.straighten,
|
|
focusNode: _areaSizeFocus,
|
|
keyboardType: TextInputType.number,
|
|
validator: (value) {
|
|
if (value != null && value.isNotEmpty) {
|
|
if (double.tryParse(value) == null) {
|
|
return 'Luas lahan harus berupa angka';
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
hintText: 'Contoh: 1000',
|
|
),
|
|
|
|
// Kepemilikan
|
|
_buildSectionTitle('Informasi Kepemilikan'),
|
|
_buildDropdown(
|
|
label: 'Jenis Kepemilikan',
|
|
value: _selectedOwnershipType,
|
|
items: _ownershipTypes,
|
|
icon: Icons.person,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedOwnershipType = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
if (_selectedOwnershipType != 'Milik Sendiri')
|
|
_buildTextField(
|
|
label: 'Nama Pemilik',
|
|
controller: _ownerNameController,
|
|
icon: Icons.person_outline,
|
|
focusNode: _ownerNameFocus,
|
|
validator: (value) {
|
|
if (_selectedOwnershipType != 'Milik Sendiri' &&
|
|
(value == null || value.isEmpty)) {
|
|
return 'Nama pemilik wajib diisi';
|
|
}
|
|
return null;
|
|
},
|
|
),
|
|
|
|
// Karakteristik Lahan
|
|
_buildSectionTitle('Karakteristik Lahan'),
|
|
_buildDropdown(
|
|
label: 'Topografi',
|
|
value: _selectedTopography,
|
|
items: _topographies,
|
|
icon: Icons.terrain,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedTopography = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildDropdown(
|
|
label: 'Kemiringan Lahan',
|
|
value: _selectedSlope,
|
|
items: _slopes,
|
|
icon: Icons.line_axis,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedSlope = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildDropdown(
|
|
label: 'Jenis Tanah',
|
|
value: _selectedSoilType,
|
|
items: _soilTypes,
|
|
icon: Icons.layers,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedSoilType = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildDropdown(
|
|
label: 'Sumber Air',
|
|
value: _selectedWaterSource,
|
|
items: _waterSources,
|
|
icon: Icons.water,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedWaterSource = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildDropdown(
|
|
label: 'Jenis Irigasi',
|
|
value: _selectedIrrigationType,
|
|
items: _irrigationTypes,
|
|
icon: Icons.water_drop,
|
|
onChanged: (value) {
|
|
if (value != null) {
|
|
setState(() {
|
|
_selectedIrrigationType = value;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
_buildTextField(
|
|
label: 'Tanaman Sebelumnya',
|
|
controller: _previousCropController,
|
|
icon: Icons.history,
|
|
focusNode: _previousCropFocus,
|
|
hintText: 'Contoh: Padi, Jagung',
|
|
),
|
|
|
|
// Region-specific fields
|
|
_buildRegionSpecificFields(),
|
|
|
|
// Buttons
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: OutlinedButton(
|
|
onPressed:
|
|
_isLoading
|
|
? null
|
|
: () => Navigator.pop(context),
|
|
style: OutlinedButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 12,
|
|
),
|
|
side: BorderSide(color: Colors.grey.shade400),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child: Text(
|
|
'Batal',
|
|
style: TextStyle(color: Colors.grey.shade700),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
child: ElevatedButton(
|
|
onPressed: _isLoading ? null : _submit,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color.fromARGB(
|
|
255,
|
|
0,
|
|
69,
|
|
1,
|
|
),
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 12,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
),
|
|
child:
|
|
_isLoading
|
|
? const SizedBox(
|
|
width: 20,
|
|
height: 20,
|
|
child: CircularProgressIndicator(
|
|
color: Colors.white,
|
|
strokeWidth: 2,
|
|
),
|
|
)
|
|
: Text(
|
|
_isEditMode
|
|
? 'Simpan Perubahan'
|
|
: 'Tambah Lahan',
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// Helper method to show the bottom sheet
|
|
Future<void> showAddFieldBottomSheet({
|
|
required BuildContext context,
|
|
Function? onFieldAdded,
|
|
}) async {
|
|
await showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
shape: const RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
),
|
|
builder: (context) => AddFieldBottomSheet(onFieldAdded: onFieldAdded),
|
|
);
|
|
}
|