970 lines
28 KiB
Dart
970 lines
28 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:percent_indicator/circular_percent_indicator.dart';
|
|
import '../services/session_manager.dart';
|
|
|
|
class ProfileScreen extends StatefulWidget {
|
|
const ProfileScreen({super.key});
|
|
|
|
@override
|
|
_ProfileScreenState createState() => _ProfileScreenState();
|
|
}
|
|
|
|
class _ProfileScreenState extends State<ProfileScreen> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
final _usernameController = TextEditingController();
|
|
final _emailController = TextEditingController();
|
|
final _phoneController = TextEditingController();
|
|
final _addressController = TextEditingController();
|
|
final _farmNameController = TextEditingController();
|
|
|
|
final SupabaseClient _supabase = Supabase.instance.client;
|
|
final ImagePicker _picker = ImagePicker();
|
|
String? _avatarUrl;
|
|
bool _isLoading = false;
|
|
User? _user;
|
|
|
|
// Statistics data
|
|
int _totalFields = 0;
|
|
int _activeSchedules = 0;
|
|
int _completedHarvests = 0;
|
|
double _averageYield = 0;
|
|
String _mostPlantedCrop = '-';
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_initializeProfile();
|
|
}
|
|
|
|
Future<void> _initializeProfile() async {
|
|
// Check session validity first
|
|
final isAuthenticated = SessionManager.isAuthenticated;
|
|
if (!isAuthenticated) {
|
|
debugPrint('Profile: User not authenticated or session expired');
|
|
setState(() {
|
|
_user = null;
|
|
_isLoading = false;
|
|
});
|
|
return;
|
|
}
|
|
|
|
_user = _supabase.auth.currentUser;
|
|
if (_user != null) {
|
|
await _loadProfile();
|
|
await _loadStatistics();
|
|
} else {
|
|
debugPrint('Profile: No current user found');
|
|
setState(() {
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> _loadProfile() async {
|
|
if (_user == null) return;
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
final response =
|
|
await _supabase
|
|
.from('profiles')
|
|
.select(
|
|
'user_id, username, email, phone, address, avatar_url, farm_name',
|
|
)
|
|
.eq('user_id', _user!.id)
|
|
.maybeSingle();
|
|
|
|
if (response == null) {
|
|
await _createProfile(); // Pastikan ini membuat semua field yang dibutuhkan
|
|
|
|
final newProfile =
|
|
await _supabase
|
|
.from('profiles')
|
|
.select(
|
|
'user_id, username, email, phone, address, avatar_url, farm_name',
|
|
)
|
|
.eq('user_id', _user!.id)
|
|
.single();
|
|
|
|
_updateControllers(newProfile);
|
|
} else {
|
|
_updateControllers(response);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Error loading profile: $e');
|
|
_showErrorSnackbar('Gagal memuat profil: ${e.toString()}');
|
|
} finally {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _loadStatistics() async {
|
|
if (_user == null) return;
|
|
|
|
try {
|
|
// Reset values first
|
|
setState(() {
|
|
_totalFields = 0;
|
|
_activeSchedules = 0;
|
|
_completedHarvests = 0;
|
|
_averageYield = 0.0;
|
|
}); // Fetch fields count safely
|
|
final fieldsResponse = await _supabase
|
|
.from('fields')
|
|
.select('id')
|
|
.eq('user_id', _user!.id);
|
|
|
|
_totalFields = fieldsResponse.length;
|
|
|
|
// Fetch active schedules safely
|
|
final now = DateTime.now().toIso8601String();
|
|
final schedulesResponse = await _supabase
|
|
.from('crop_schedules')
|
|
.select()
|
|
.eq('user_id', _user!.id)
|
|
.gt('end_date', now);
|
|
|
|
_activeSchedules = schedulesResponse.length;
|
|
|
|
// Fetch harvest results safely
|
|
final harvestResponse = await _supabase
|
|
.from('harvest_results')
|
|
.select('productivity')
|
|
.eq('user_id', _user!.id);
|
|
|
|
if (harvestResponse.isNotEmpty) {
|
|
_completedHarvests = harvestResponse.length;
|
|
|
|
// Calculate average yield safely
|
|
double totalYield = 0;
|
|
int validRecords = 0;
|
|
|
|
for (final harvest in harvestResponse) {
|
|
final productivity = harvest['productivity'] as num?;
|
|
if (productivity != null) {
|
|
totalYield += productivity.toDouble();
|
|
validRecords++;
|
|
}
|
|
}
|
|
|
|
_averageYield = validRecords > 0 ? totalYield / validRecords : 0.0;
|
|
}
|
|
|
|
if (mounted) setState(() {});
|
|
} catch (e) {
|
|
debugPrint('Error loading statistics: $e');
|
|
if (mounted) {
|
|
setState(() {
|
|
_totalFields = 0;
|
|
_activeSchedules = 0;
|
|
_completedHarvests = 0;
|
|
_averageYield = 0.0;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _createProfile() async {
|
|
if (_user == null) return;
|
|
|
|
try {
|
|
final username =
|
|
_user!.email?.split('@').first ??
|
|
'user_${DateTime.now().millisecondsSinceEpoch}';
|
|
|
|
await _supabase.from('profiles').insert({
|
|
'user_id': _user!.id,
|
|
'username': username,
|
|
'email': _user!.email ?? '',
|
|
'created_at': DateTime.now().toUtc().toIso8601String(),
|
|
'updated_at': DateTime.now().toUtc().toIso8601String(),
|
|
});
|
|
} catch (e) {
|
|
debugPrint('Error creating profile: $e');
|
|
_showErrorSnackbar('Error membuat profil: ${e.toString()}');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
void _updateControllers(Map<String, dynamic> data) {
|
|
_usernameController.text = data['username'] ?? '';
|
|
_emailController.text = data['email'] ?? '';
|
|
_phoneController.text = data['phone'] ?? '';
|
|
_addressController.text = data['address'] ?? '';
|
|
_farmNameController.text = data['farm_name'] ?? '';
|
|
|
|
setState(() {
|
|
_avatarUrl = data['avatar_url'] ?? '';
|
|
});
|
|
}
|
|
|
|
Future<void> _updateProfile() async {
|
|
if (!_formKey.currentState!.validate() || _user == null) return;
|
|
setState(() => _isLoading = true);
|
|
|
|
try {
|
|
final updates = {
|
|
'username': _usernameController.text.trim(),
|
|
'phone': _phoneController.text.trim(),
|
|
'address': _addressController.text.trim(),
|
|
'farm_name': _farmNameController.text.trim(),
|
|
'updated_at': DateTime.now().toUtc().toIso8601String(),
|
|
};
|
|
|
|
await _supabase
|
|
.from('profiles')
|
|
.update(updates)
|
|
.eq('user_id', _user!.id)
|
|
.select();
|
|
|
|
_showSuccessSnackbar('Profil berhasil diperbarui');
|
|
} catch (e) {
|
|
debugPrint('Error updating profile: $e');
|
|
_showErrorSnackbar('Error memperbarui profil: ${e.toString()}');
|
|
} finally {
|
|
if (mounted) setState(() => _isLoading = false);
|
|
}
|
|
}
|
|
|
|
Future<void> _uploadAvatar() async {
|
|
final picked = await _picker.pickImage(source: ImageSource.gallery);
|
|
if (picked == null) return;
|
|
|
|
try {
|
|
final file = File(picked.path);
|
|
final fileExt = picked.path.split('.').last;
|
|
final filePath = 'avatars/${_user!.id}/avatar.$fileExt';
|
|
|
|
await _supabase.storage.from('avatars').upload(
|
|
filePath,
|
|
file,
|
|
fileOptions: FileOptions(
|
|
upsert: true,
|
|
contentType: 'image/$fileExt',
|
|
),
|
|
);
|
|
|
|
// Get the public URL instead of a signed URL
|
|
final avatarUrl = _supabase.storage.from('avatars').getPublicUrl(filePath);
|
|
|
|
await _supabase.from('profiles').update({
|
|
'avatar_url': avatarUrl,
|
|
}).eq('user_id', _user!.id);
|
|
|
|
setState(() {
|
|
_avatarUrl = avatarUrl;
|
|
});
|
|
|
|
_showSuccessSnackbar('Avatar berhasil diunggah');
|
|
} catch (e) {
|
|
debugPrint('Upload error: $e');
|
|
_showErrorSnackbar('Gagal mengunggah avatar');
|
|
}
|
|
}
|
|
|
|
void _showErrorSnackbar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(message), backgroundColor: Colors.red),
|
|
);
|
|
}
|
|
|
|
void _showSuccessSnackbar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(message), backgroundColor: Colors.green),
|
|
);
|
|
}
|
|
Future<void> _signOut() async {
|
|
await _supabase.auth.signOut();
|
|
if (mounted) {
|
|
Navigator.of(context).pushReplacementNamed('/login');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (_user == null) {
|
|
return _buildNoUserScreen();
|
|
}
|
|
|
|
if (_isLoading) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8F9FA),
|
|
body: Center(
|
|
child: CircularProgressIndicator(
|
|
valueColor: AlwaysStoppedAnimation<Color>(const Color(0xFF056839)),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8F9FA),
|
|
appBar: _buildAppBar(),
|
|
body: SingleChildScrollView(
|
|
child: Column(
|
|
children: [
|
|
_buildProfileHeader(),
|
|
const SizedBox(height: 20),
|
|
_buildFarmStatsSummary(),
|
|
const SizedBox(height: 20),
|
|
_buildProfileForm(),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
PreferredSizeWidget _buildAppBar() {
|
|
return AppBar(
|
|
elevation: 0,
|
|
backgroundColor: Colors.white,
|
|
foregroundColor: Colors.black87,
|
|
centerTitle: false,
|
|
title: Text(
|
|
'Profil Saya',
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey[100],
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(Icons.logout, size: 18, color: Colors.red[700]),
|
|
),
|
|
onPressed: _signOut,
|
|
),
|
|
const SizedBox(width: 8),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildProfileHeader() {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
offset: const Offset(0, 2),
|
|
blurRadius: 8,
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: [
|
|
_buildAvatarWithEditButton(),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
_usernameController.text,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
Text(
|
|
_emailController.text,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Colors.grey[600],
|
|
),
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildActionButtons(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAvatarWithEditButton() {
|
|
return Stack(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(4),
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(color: const Color(0xFF056839), width: 2),
|
|
),
|
|
child: CircleAvatar(
|
|
radius: 60,
|
|
backgroundColor: Colors.grey[200],
|
|
backgroundImage:
|
|
_avatarUrl != null && _avatarUrl!.isNotEmpty
|
|
? NetworkImage(_avatarUrl!)
|
|
: null,
|
|
child: _avatarUrl == null || _avatarUrl!.isEmpty
|
|
? const Icon(Icons.person, size: 60, color: Colors.grey)
|
|
: null,
|
|
),
|
|
),
|
|
Positioned(
|
|
bottom: 0,
|
|
right: 0,
|
|
child: GestureDetector(
|
|
onTap: _uploadAvatar,
|
|
child: Container(
|
|
height: 40,
|
|
width: 40,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF056839),
|
|
shape: BoxShape.circle,
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.1),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: const Icon(Icons.camera_alt, color: Colors.white, size: 20),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildActionButtons() {
|
|
return SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
// No buttons needed as per user request
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildFarmStatsSummary() {
|
|
final currency = NumberFormat.currency(locale: 'id_ID', symbol: 'Rp ');
|
|
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 20),
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
spreadRadius: 1,
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'Statistik Pertanian',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF056839).withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
'Aktif',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: const Color(0xFF056839),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 20),
|
|
|
|
// Performance indicators
|
|
_buildPerformanceIndicators(),
|
|
|
|
const Divider(height: 32),
|
|
|
|
// Financial summary
|
|
_buildFinancialSummary(currency),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildPerformanceIndicators() {
|
|
// Calculate percentage for circular indicator based on average yield
|
|
double yieldPercentage = 0.0;
|
|
if (_averageYield > 0) {
|
|
// Assuming optimal yield is 8 ton/ha, calculate percentage
|
|
yieldPercentage = (_averageYield / 8.0).clamp(0.0, 1.0);
|
|
}
|
|
|
|
return Row(
|
|
children: [
|
|
Expanded(
|
|
child: CircularPercentIndicator(
|
|
radius: 60.0,
|
|
lineWidth: 10.0,
|
|
percent: yieldPercentage,
|
|
center: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
'${_averageYield.toStringAsFixed(1)}',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
Text(
|
|
'ton/ha',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
progressColor: const Color(0xFF056839),
|
|
backgroundColor: const Color(0xFF056839).withOpacity(0.2),
|
|
animation: true,
|
|
animationDuration: 1200,
|
|
),
|
|
),
|
|
const SizedBox(width: 16),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildStatRow('Total Lahan', '$_totalFields Lahan'),
|
|
const SizedBox(height: 8),
|
|
_buildStatRow('Tanaman Aktif', '$_activeSchedules Jenis'),
|
|
const SizedBox(height: 8),
|
|
_buildStatRow('Total Panen', '$_completedHarvests Kali'),
|
|
const SizedBox(height: 8),
|
|
_buildStatRow('Tanaman Terbanyak', _mostPlantedCrop),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildFinancialSummary(NumberFormat currency) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 4),
|
|
child: Column(
|
|
children: [
|
|
IntrinsicHeight(
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: _buildMetricCard(
|
|
'Rata-rata Panen',
|
|
'${(_averageYield * 10).toStringAsFixed(1)} kilogram/ha',
|
|
Icons.trending_up,
|
|
const Color(0xFF056839),
|
|
'Rata-rata hasil panen per hektar',
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: _buildMetricCard(
|
|
'Total Panen',
|
|
'$_completedHarvests Kali',
|
|
Icons.check_circle_outline,
|
|
Colors.blue.shade700,
|
|
'Jumlah panen yang telah dilakukan',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
IntrinsicHeight(
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: _buildMetricCard(
|
|
'Musim Tanam',
|
|
'$_activeSchedules Aktif',
|
|
Icons.calendar_today,
|
|
Colors.orange.shade700,
|
|
'Jumlah tanaman yang sedang ditanam',
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: _buildMetricCard(
|
|
'Total Lahan',
|
|
'$_totalFields Lahan',
|
|
Icons.eco,
|
|
Colors.green.shade700,
|
|
'Jumlah lahan yang dimiliki',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildMetricCard(String title, String value, IconData icon, Color color, String tooltip) {
|
|
return Container(
|
|
constraints: const BoxConstraints(minHeight: 100),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: color.withOpacity(0.2)),
|
|
),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(icon, size: 18, color: color),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 13,
|
|
color: color,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const Spacer(),
|
|
Text(
|
|
value,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatRow(String label, String value) {
|
|
return Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
flex: 3,
|
|
child: Text(
|
|
label,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
value,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
textAlign: TextAlign.end,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildProfileForm() {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 20),
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
spreadRadius: 1,
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Informasi Pengguna',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
_buildInputField(
|
|
controller: _usernameController,
|
|
label: 'Nama Pengguna',
|
|
icon: Icons.person_outlined,
|
|
validator: (value) =>
|
|
value == null || value.isEmpty
|
|
? 'Nama pengguna wajib diisi'
|
|
: null,
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildInputField(
|
|
controller: _emailController,
|
|
label: 'Email',
|
|
icon: Icons.email_outlined,
|
|
readOnly: true,
|
|
),
|
|
// const SizedBox(height: 16),
|
|
// _buildInputField(
|
|
// controller: _farmNameController,
|
|
// label: 'Nama Lahan',
|
|
// icon: Icons.agriculture_outlined,
|
|
// validator: (value) =>
|
|
// value == null || value.isEmpty
|
|
// ? 'Nama lahan wajib diisi'
|
|
// : null,
|
|
// ),
|
|
const SizedBox(height: 16),
|
|
_buildInputField(
|
|
controller: _phoneController,
|
|
label: 'No. Telepon',
|
|
icon: Icons.phone_outlined,
|
|
keyboardType: TextInputType.phone,
|
|
),
|
|
const SizedBox(height: 16),
|
|
_buildInputField(
|
|
controller: _addressController,
|
|
label: 'Alamat',
|
|
icon: Icons.location_on_outlined,
|
|
maxLines: 3,
|
|
),
|
|
const SizedBox(height: 30),
|
|
_buildSaveButton(),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSaveButton() {
|
|
return SizedBox(
|
|
width: double.infinity,
|
|
height: 55,
|
|
child: ElevatedButton(
|
|
onPressed: _updateProfile,
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF056839),
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 2,
|
|
),
|
|
child: _isLoading
|
|
? const SizedBox(
|
|
height: 20,
|
|
width: 20,
|
|
child: CircularProgressIndicator(
|
|
strokeWidth: 3,
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
|
),
|
|
)
|
|
: Text(
|
|
'Simpan Perubahan',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildInputField({
|
|
required TextEditingController controller,
|
|
required String label,
|
|
required IconData icon,
|
|
String? Function(String?)? validator,
|
|
bool readOnly = false,
|
|
TextInputType? keyboardType,
|
|
int? maxLines,
|
|
}) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 4, bottom: 8),
|
|
child: Text(
|
|
label,
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
color: Colors.grey[700],
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: Colors.grey[300]!),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.03),
|
|
blurRadius: 4,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
),
|
|
child: TextFormField(
|
|
controller: controller,
|
|
decoration: InputDecoration(
|
|
prefixIcon: Icon(icon, color: const Color(0xFF056839), size: 22),
|
|
border: InputBorder.none,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8),
|
|
fillColor: readOnly ? Colors.grey[50] : Colors.white,
|
|
filled: true,
|
|
),
|
|
style: GoogleFonts.poppins(fontSize: 15),
|
|
readOnly: readOnly,
|
|
keyboardType: keyboardType,
|
|
maxLines: maxLines ?? 1,
|
|
validator: validator,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildNoUserScreen() {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFFF8F9FA),
|
|
body: SafeArea(
|
|
child: Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(30),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey[200],
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.person_off_outlined,
|
|
size: 70,
|
|
color: Colors.grey[400],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'Silakan masuk untuk melihat profil',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.grey[700],
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
'Anda perlu masuk ke akun untuk mengakses fitur ini',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Colors.grey[600],
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
const SizedBox(height: 40),
|
|
Container(
|
|
width: 200,
|
|
height: 55,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(0xFF056839).withOpacity(0.3),
|
|
blurRadius: 12,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: ElevatedButton(
|
|
onPressed: () => Navigator.of(context).pushReplacementNamed('/login'),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFF056839),
|
|
foregroundColor: Colors.white,
|
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 15),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
child: Text(
|
|
'Masuk',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_usernameController.dispose();
|
|
_emailController.dispose();
|
|
_phoneController.dispose();
|
|
_addressController.dispose();
|
|
_farmNameController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|