import 'dart:io'; import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:image_picker/image_picker.dart'; import 'package:http/http.dart' as http; import '../pages/login_page.dart'; import '../pages/dashboard_admin.dart'; class ProfileAdminPage extends StatefulWidget { const ProfileAdminPage({super.key}); @override State createState() => _ProfileKaderPageState(); } class _ProfileKaderPageState extends State { final TextEditingController namaC = TextEditingController(); final TextEditingController emailC = TextEditingController(); final TextEditingController passwordC = TextEditingController(); final TextEditingController roleC = TextEditingController(); final TextEditingController desaIdC = TextEditingController(); final TextEditingController dusuIdC = TextEditingController(); final TextEditingController noHpC = TextEditingController(); final TextEditingController statusC = TextEditingController(); String? idUser; String? fotoUser; XFile? _pickedFile; bool _obscurePassword = true; bool isEditMode = false; @override void initState() { super.initState(); _loadUserData(); } @override void dispose() { namaC.dispose(); emailC.dispose(); passwordC.dispose(); roleC.dispose(); desaIdC.dispose(); dusuIdC.dispose(); noHpC.dispose(); statusC.dispose(); super.dispose(); } Future _loadUserData() async { final prefs = await SharedPreferences.getInstance(); final isLogin = prefs.getBool('isLogin') ?? false; if (!isLogin) { if (!mounted) return; Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (_) => const LoginPage(fromGuard: true)), (route) => false, ); return; } setState(() { idUser = prefs.getString('id_user') ?? ""; namaC.text = prefs.getString('nama') ?? ""; emailC.text = prefs.getString('email') ?? ""; passwordC.text = prefs.getString('password') ?? ""; roleC.text = (prefs.getString('role') ?? "Kader").toLowerCase(); desaIdC.text = prefs.getString('nama_desa') ?? "-"; dusuIdC.text = prefs.getString('nama_dusun') ?? "-"; String savedNoHp = prefs.getString('no_hp') ?? ""; noHpC.text = (savedNoHp.isEmpty || savedNoHp == "-") ? "-" : savedNoHp; statusC.text = prefs.getString('status_aktif') ?? "Aktif"; fotoUser = prefs.getString('foto'); }); } // Fungsi untuk menampilkan SnackBar Peringatan void _showEditWarning() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Klik tombol edit di bawah untuk mengubah foto."), backgroundColor: Colors.red, duration: Duration(seconds: 2), ), ); } Future _pickImage() async { // Jika belum mode edit, tampilkan snackbar dan hentikan fungsi if (!isEditMode) { _showEditWarning(); return; } final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image != null) { setState(() { _pickedFile = image; }); } } void _removeImage() { // Jika belum mode edit, tampilkan snackbar dan hentikan fungsi if (!isEditMode) { _showEditWarning(); return; } setState(() { _pickedFile = null; fotoUser = ""; }); } void _enableEdit() { setState(() { isEditMode = true; }); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Mode edit diaktifkan"), backgroundColor: Colors.orange, ), ); } bool _validatePassword(String value) { String pattern = r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6}$'; RegExp regExp = RegExp(pattern); return regExp.hasMatch(value); } bool _validateNoHp(String value) { String pattern = r'^[0-9]{10,13}$'; RegExp regExp = RegExp(pattern); return regExp.hasMatch(value); } void _saveProfile() async { if (!isEditMode) return; if (!_validateNoHp(noHpC.text)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("No. HP harus berupa angka dan berjumlah 10-13 digit!"), backgroundColor: Colors.red, ), ); return; } if (!_validatePassword(passwordC.text)) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Password harus 6 digit (kombinasi huruf & angka)!"), backgroundColor: Colors.red, ), ); return; } try { var request = http.MultipartRequest( 'POST', Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/users/update_profile_kader.php"), ); request.fields['id_user'] = idUser ?? ""; request.fields['nama'] = namaC.text; request.fields['email'] = emailC.text; request.fields['password'] = passwordC.text; request.fields['no_hp'] = noHpC.text; request.fields['foto_lama'] = fotoUser ?? ""; if (_pickedFile != null) { Uint8List data = await _pickedFile!.readAsBytes(); request.files.add(http.MultipartFile.fromBytes( 'foto', data, filename: _pickedFile!.name, )); } var response = await request.send(); var responseData = await response.stream.bytesToString(); var result = json.decode(responseData); if (result['status'] == 'success') { final prefs = await SharedPreferences.getInstance(); await prefs.setString('nama', namaC.text); await prefs.setString('email', emailC.text); await prefs.setString('password', passwordC.text); await prefs.setString('no_hp', noHpC.text); if (result['foto'] != null) { await prefs.setString('foto', result['foto']); setState(() { fotoUser = result['foto']; }); } if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Profil berhasil diperbarui"), backgroundColor: Colors.green), ); setState(() { isEditMode = false; _pickedFile = null; }); } else { throw result['message']; } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text("Gagal menyimpan: $e"), backgroundColor: Colors.red), ); } } @override Widget build(BuildContext context) { bool hasPhoto = _pickedFile != null || (fotoUser != null && fotoUser!.isNotEmpty); return Scaffold( backgroundColor: const Color(0xfff4f6fb), appBar: AppBar( backgroundColor: Colors.blue, elevation: 0, leading: IconButton( icon: const Icon(Icons.arrow_back, color: Colors.white), onPressed: () { Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (context) => const DashboardAdminPage()), (route) => false, ); }, ), title: Text("", style: GoogleFonts.poppins(color: Colors.white, fontSize: 18)), ), body: SingleChildScrollView( child: Column( children: [ Container( width: double.infinity, decoration: const BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.only( bottomLeft: Radius.circular(30), bottomRight: Radius.circular(30), ), ), padding: const EdgeInsets.only(bottom: 30), child: Column( children: [ Stack( children: [ CircleAvatar( radius: 65, backgroundColor: Colors.white, child: CircleAvatar( radius: 60, backgroundColor: Colors.blue.shade100, backgroundImage: _pickedFile != null ? (kIsWeb ? NetworkImage(_pickedFile!.path) : FileImage(File(_pickedFile!.path)) as ImageProvider) : (fotoUser != null && fotoUser!.isNotEmpty) ? NetworkImage( "http://ta.myhost.id/E31230549/mposyandu_api/uploads/$fotoUser") : null, child: !hasPhoto ? const Icon(Icons.person, size: 60, color: Colors.blue) : null, ), ), Positioned( bottom: 0, right: hasPhoto ? 40 : 0, child: GestureDetector( onTap: _pickImage, // Sekarang diproteksi di dalam fungsi child: CircleAvatar( radius: 18, backgroundColor: isEditMode ? Colors.black : Colors.grey.shade700, child: const Icon(Icons.camera_alt, color: Colors.white, size: 18), ), ), ), if (hasPhoto) Positioned( bottom: 0, right: 0, child: GestureDetector( onTap: _removeImage, // Sekarang diproteksi di dalam fungsi child: const CircleAvatar( radius: 18, backgroundColor: Colors.red, child: Icon(Icons.delete, color: Colors.white, size: 18), ), ), ), ], ), const SizedBox(height: 12), Text( namaC.text, style: GoogleFonts.poppins( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white), ), ], ), ), Padding( padding: const EdgeInsets.all(20), child: Column( children: [ Card( color: Colors.white, elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15)), child: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ _buildField("Nama Lengkap", namaC), _buildField("Email", emailC), _buildPasswordField( "Password (6 Digit Huruf & Angka)", passwordC), _buildField("Role User", roleC, isReadOnly: true), _buildField("No.HP", noHpC, isNumber: true), _buildField("Status", statusC, isReadOnly: true), ], ), ), ), const SizedBox(height: 30), Row( children: [ Expanded( child: _buildActionButton( label: "Edit Profil", color: isEditMode ? Colors.grey : Colors.orange, onTap: isEditMode ? () {} : _enableEdit, ), ), const SizedBox(width: 15), Expanded( child: _buildActionButton( label: "Simpan", color: Colors.blue, onTap: _saveProfile, ), ), ], ), ], ), ), ], ), ), ); } Widget _buildField(String label, TextEditingController controller, {bool isReadOnly = false, bool isNumber = false}) { return Padding( padding: const EdgeInsets.only(bottom: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.grey[600])), const SizedBox(height: 5), TextField( controller: controller, readOnly: isReadOnly || !isEditMode, keyboardType: isNumber ? TextInputType.phone : TextInputType.text, style: GoogleFonts.poppins(fontSize: 14), decoration: InputDecoration( filled: true, fillColor: (isReadOnly || !isEditMode) ? Colors.grey[50] : Colors.white, contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), ), ), ], ), ); } Widget _buildPasswordField(String label, TextEditingController controller) { return Padding( padding: const EdgeInsets.only(bottom: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(label, style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.grey[600])), const SizedBox(height: 5), TextField( controller: controller, obscureText: _obscurePassword, readOnly: !isEditMode, maxLength: 6, style: GoogleFonts.poppins(fontSize: 14), decoration: InputDecoration( counterText: "", filled: true, fillColor: !isEditMode ? Colors.grey[50] : Colors.white, suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off : Icons.visibility), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)), ), ), ], ), ); } Widget _buildActionButton( {required String label, required Color color, required VoidCallback onTap}) { return OutlinedButton( onPressed: onTap, style: OutlinedButton.styleFrom( foregroundColor: color, side: BorderSide(color: color, width: 1.5), padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), backgroundColor: Colors.white, ), child: Text(label, style: GoogleFonts.poppins(fontWeight: FontWeight.bold, fontSize: 14)), ); } }