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 '../ibu/dashboard_ibu.dart'; class ProfileIbuPage extends StatefulWidget { const ProfileIbuPage({super.key}); @override State createState() => _ProfileIbuPageState(); } class _ProfileIbuPageState extends State { // --- CONTROLLER TABEL USERS --- final TextEditingController namaC = TextEditingController(); final TextEditingController emailC = TextEditingController(); final TextEditingController passwordC = TextEditingController(); final TextEditingController noHpC = TextEditingController(); final TextEditingController roleC = TextEditingController(); final TextEditingController statusC = TextEditingController(); // --- CONTROLLER TABEL IBU --- final TextEditingController nikC = TextEditingController(); final TextEditingController noKkC = TextEditingController(); final TextEditingController namaSuamiC = TextEditingController(); final TextEditingController tempatLahirC = TextEditingController(); final TextEditingController tanggalLahirC = TextEditingController(); final TextEditingController pendidikanC = TextEditingController(); final TextEditingController pekerjaanC = TextEditingController(); final TextEditingController alamatDetailC = TextEditingController(); final TextEditingController desaC = TextEditingController(); final TextEditingController dusunC = TextEditingController(); // Variabel State String? idUser; String? fotoUser; String? namaKader; XFile? _pickedFile; bool _obscurePassword = true; bool isEditMode = false; // Variabel Dropdown String? selectedAgama; String? selectedGoldar; final List listAgama = [ 'Islam', 'Kristen', 'Katolik', 'Hindu', 'Budha', 'Khonghucu' ]; final List listGoldar = ['A', 'B', 'AB', 'O', '-']; @override void initState() { super.initState(); _loadUserData(); } @override void dispose() { namaC.dispose(); emailC.dispose(); passwordC.dispose(); noHpC.dispose(); roleC.dispose(); statusC.dispose(); nikC.dispose(); noKkC.dispose(); namaSuamiC.dispose(); tempatLahirC.dispose(); tanggalLahirC.dispose(); pendidikanC.dispose(); pekerjaanC.dispose(); alamatDetailC.dispose(); desaC.dispose(); dusunC.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') ?? ""; fotoUser = prefs.getString('foto'); namaKader = prefs.getString('nama_kader') ?? "Kader"; namaC.text = prefs.getString('nama') ?? ""; emailC.text = prefs.getString('email') ?? ""; passwordC.text = prefs.getString('password') ?? ""; noHpC.text = prefs.getString('no_hp') ?? ""; roleC.text = "Ibu"; statusC.text = prefs.getString('status_aktif') ?? "Aktif"; nikC.text = prefs.getString('nik') ?? "-"; noKkC.text = prefs.getString('no_kk') ?? "-"; namaSuamiC.text = prefs.getString('nama_suami') ?? ""; tempatLahirC.text = prefs.getString('tempat_lahir') ?? "-"; tanggalLahirC.text = prefs.getString('tanggal_lahir') ?? "-"; pendidikanC.text = prefs.getString('pendidikan') ?? ""; pekerjaanC.text = prefs.getString('pekerjaan') ?? ""; alamatDetailC.text = prefs.getString('alamat_detail') ?? ""; desaC.text = prefs.getString('nama_desa') ?? "-"; dusunC.text = prefs.getString('nama_dusun') ?? "-"; String? dbAgama = prefs.getString('agama'); if (listAgama.contains(dbAgama)) selectedAgama = dbAgama; String? dbGoldar = prefs.getString('golongan_darah'); if (listGoldar.contains(dbGoldar)) selectedGoldar = dbGoldar; }); } void _showError(String msg) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(msg), backgroundColor: Colors.red), ); } // Fungsi untuk menangani klik foto/hapus saat belum edit void _warnEditFirst() { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( "Klik tombol edit di bawah untuk mengubah atau menghapus foto"), backgroundColor: Colors.red, ), ); } Future _pickImage() async { if (!isEditMode) { _warnEditFirst(); return; } final ImagePicker picker = ImagePicker(); final XFile? image = await picker.pickImage(source: ImageSource.gallery); if (image != null) setState(() => _pickedFile = image); } void _removeImage() { if (!isEditMode) { _warnEditFirst(); return; } setState(() { _pickedFile = null; fotoUser = ""; }); } void _saveProfile() async { if (!isEditMode) return; if (namaC.text.isEmpty || emailC.text.isEmpty) { _showError("Nama dan Email tidak boleh kosong"); return; } try { var request = http.MultipartRequest( 'POST', Uri.parse( "http://ta.myhost.id/E31230549/mposyandu_api/users/update_profile_ibu.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 ?? ""; request.fields['nama_suami'] = namaSuamiC.text; request.fields['golongan_darah'] = selectedGoldar ?? ""; request.fields['pendidikan'] = pendidikanC.text; request.fields['pekerjaan'] = pekerjaanC.text; request.fields['agama'] = selectedAgama ?? ""; request.fields['alamat_detail'] = alamatDetailC.text; 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('no_hp', noHpC.text); await prefs.setString('nama_suami', namaSuamiC.text); await prefs.setString('golongan_darah', selectedGoldar ?? ""); await prefs.setString('agama', selectedAgama ?? ""); if (result['foto'] != null) await prefs.setString('foto', result['foto']); setState(() { isEditMode = false; _pickedFile = null; if (result['foto'] != null) fotoUser = result['foto']; }); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Profil berhasil diperbarui")), ); } } catch (e) { _showError("Gagal menyimpan: $e"); } } @override Widget build(BuildContext context) { bool hasPhoto = _pickedFile != null || (fotoUser != null && fotoUser!.isNotEmpty); return PopScope( canPop: false, onPopInvokedWithResult: (didPop, result) async { if (didPop) return; Navigator.pushAndRemoveUntil( context, MaterialPageRoute( builder: (_) => const DashboardIbuPage(), ), (route) => false, ); }, child: 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 DashboardIbuPage()), (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.vertical(bottom: 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, ), ), // Tombol Kamera Positioned( bottom: 0, right: hasPhoto ? 40 : 0, child: GestureDetector( onTap: _pickImage, child: CircleAvatar( radius: 18, backgroundColor: isEditMode ? Colors.black : Colors.grey, child: const Icon(Icons.camera_alt, color: Colors.white, size: 18), ), ), ), // Tombol Hapus if (hasPhoto) Positioned( bottom: 0, right: 0, child: GestureDetector( onTap: _removeImage, child: CircleAvatar( radius: 18, backgroundColor: isEditMode ? Colors.red : Colors.grey, child: const 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, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15)), child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSectionTitle("Informasi Akun & Kontak"), _buildField("Nama Lengkap", namaC), _buildField("Email", emailC), _buildPasswordField("Password", passwordC), _buildField("No. HP", noHpC, isNumber: true), const Divider(height: 30), _buildSectionTitle("Informasi Resmi"), Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(bottom: 15), decoration: BoxDecoration( color: Colors.amber.shade50, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.amber.shade200), ), child: Row( children: [ const Icon(Icons.info_outline, color: Colors.amber, size: 20), const SizedBox(width: 10), Expanded( child: Text( "Jika ada kesalahan data pada kolom di bawah ini, silahkan hubungi kader: $namaKader", style: GoogleFonts.poppins( fontSize: 11, color: Colors.brown.shade700, fontStyle: FontStyle.italic), ), ), ], ), ), _buildField("NIK", nikC, isReadOnly: true), _buildField("No. KK", noKkC, isReadOnly: true), _buildField("Tempat Lahir", tempatLahirC, isReadOnly: true), _buildField("Tanggal Lahir", tanggalLahirC, isReadOnly: true), _buildField("Desa", desaC, isReadOnly: true), _buildField("Dusun", dusunC, isReadOnly: true), const Divider(height: 30), _buildSectionTitle("Data Tambahan"), _buildField("Nama Suami", namaSuamiC), _buildDropdown( "Golongan Darah", selectedGoldar, listGoldar, (val) => setState(() => selectedGoldar = val)), _buildField("Pendidikan", pendidikanC), _buildField("Pekerjaan", pekerjaanC), _buildDropdown("Agama", selectedAgama, listAgama, (val) => setState(() => selectedAgama = val)), _buildField("Alamat Detail", alamatDetailC), ], ), ), ), const SizedBox(height: 25), Row( children: [ Expanded( child: _buildActionButton( label: "Edit Profil", color: Colors.orange, onTap: () => setState(() => isEditMode = true))), const SizedBox(width: 15), Expanded( child: _buildActionButton( label: "Simpan", color: Colors.blue, onTap: _saveProfile)), ], ), const SizedBox(height: 40), ], ), ), ], ), ), ), ); } Widget _buildSectionTitle(String title) { return Padding( padding: const EdgeInsets.only(bottom: 10), child: Text(title, style: GoogleFonts.poppins( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.blueGrey)), ); } Widget _buildDropdown(String label, String? value, List items, Function(String?) onChanged) { 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), Container( padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: !isEditMode ? Colors.grey[100] : Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade300), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: value, isExpanded: true, items: items .map((s) => DropdownMenuItem( value: s, child: Text(s, style: GoogleFonts.poppins(fontSize: 14)))) .toList(), onChanged: isEditMode ? onChanged : null, ), ), ), ], ), ); } 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[100] : Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey.shade300)), contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), ), ), ], ), ); } 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, style: GoogleFonts.poppins(fontSize: 14), decoration: InputDecoration( filled: true, fillColor: !isEditMode ? Colors.grey[100] : Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: Colors.grey.shade300)), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off : Icons.visibility), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), ), ), ), ], ), ); } Widget _buildActionButton( {required String label, required Color color, required VoidCallback onTap}) { return ElevatedButton( onPressed: onTap, style: ElevatedButton.styleFrom( backgroundColor: color, padding: const EdgeInsets.symmetric(vertical: 15), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: Text(label, style: GoogleFonts.poppins( color: Colors.white, fontWeight: FontWeight.bold)), ); } }