MIF_E31222656/lib/screens/calendar/add_field_bottom_sheet.dart

258 lines
7.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:uuid/uuid.dart';
import 'dart:async'; // Tambahkan import untuk TimeoutException
class AddFieldBottomSheet extends StatefulWidget {
final Function? onFieldAdded;
const AddFieldBottomSheet({super.key, this.onFieldAdded});
@override
State<AddFieldBottomSheet> createState() => _AddFieldBottomSheetState();
}
class _AddFieldBottomSheetState extends State<AddFieldBottomSheet> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _locationController = TextEditingController();
final _sizeController = TextEditingController();
final _notesController = TextEditingController();
bool _isLoading = false;
final _fieldNameFocus = FocusNode();
final _locationFocus = FocusNode();
final _areaFocus = FocusNode();
@override
void initState() {
super.initState();
// Pastikan keyboard tidak terbuka saat dialog muncul
WidgetsBinding.instance.addPostFrameCallback((_) {
FocusScope.of(context).unfocus();
});
}
@override
void dispose() {
_nameController.dispose();
_locationController.dispose();
_sizeController.dispose();
_notesController.dispose();
_fieldNameFocus.dispose();
_locationFocus.dispose();
_areaFocus.dispose();
super.dispose();
}
Future<void> _submit() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isLoading = true);
try {
final id = const Uuid().v4();
final userId = Supabase.instance.client.auth.currentUser?.id;
if (userId == null) {
_showError('User ID is null');
return;
}
// PENTING: Hanya kirim data yang ada di database
// Berdasarkan struktur database yang terlihat, hanya kolom ini yang ada
final data = {
'id': id,
'user_id': userId,
'name': _nameController.text,
'plot_count': 1,
'created_at': DateTime.now().toIso8601String(),
};
// Insert to database
await Supabase.instance.client.from('fields').insert(data);
if (mounted) {
// Call the callback if provided
if (widget.onFieldAdded != null) {
widget.onFieldAdded!();
}
Navigator.of(context).pop(true); // Return true to indicate success
}
} catch (e) {
debugPrint('Error adding field: $e');
if (mounted) {
setState(() => _isLoading = false);
_showError(
'Gagal menambahkan lahan: ${e.toString().contains("timeout") ? "Koneksi timeout" : "Terjadi kesalahan"}',
);
}
}
}
void _showError(String message) {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message), backgroundColor: Colors.red),
);
}
Widget _buildTextField({
required TextEditingController controller,
required String label,
required IconData icon,
String? Function(String?)? validator,
TextInputType keyboardType = TextInputType.text,
FocusNode? focusNode,
}) {
return TextFormField(
controller: controller,
focusNode: focusNode,
autofocus: false,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(icon),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
),
keyboardType: keyboardType,
validator: validator,
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) => FocusScope.of(context).nextFocus(),
);
}
@override
Widget build(BuildContext context) {
// Using simpler widgets to avoid crashes
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 16,
right: 16,
top: 16,
),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Title
const Center(
child: Text(
'Tambah Lahan',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 16),
// Field Name
_buildTextField(
controller: _nameController,
label: 'Nama Lahan',
icon: Icons.landscape,
validator:
(value) =>
value == null || value.isEmpty
? 'Nama lahan harus diisi'
: null,
focusNode: _fieldNameFocus,
),
const SizedBox(height: 16),
// Location
_buildTextField(
controller: _locationController,
label: 'Lokasi (Opsional)',
icon: Icons.location_on,
focusNode: _locationFocus,
),
const SizedBox(height: 16),
// Size
_buildTextField(
controller: _sizeController,
label: 'Luas Lahan (m²)',
icon: Icons.straighten,
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Luas lahan harus diisi';
}
if (int.tryParse(value) == null) {
return 'Luas lahan harus berupa angka';
}
return null;
},
focusNode: _areaFocus,
),
const SizedBox(height: 16),
// Notes
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Catatan (opsional)',
border: UnderlineInputBorder(),
),
maxLines: 2,
),
const SizedBox(height: 24),
// Buttons
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: _isLoading ? null : () => Navigator.pop(context),
child: const Text('Batal'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: _isLoading ? null : _submit,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
child:
_isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
)
: const Text('Simpan'),
),
],
),
const SizedBox(height: 8),
],
),
),
);
}
}
// 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),
);
}