TKK_E32231651/lib/profile.dart

660 lines
25 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
class ProfileScreen extends StatefulWidget {
const ProfileScreen({super.key});
@override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
String? imageName;
bool _isLoading = false;
int _refreshKey = 0;
final String baseUrl = "http://192.168.100.9/kopikaocare_api/uploads/";
final String apiUpload =
"http://192.168.100.9/kopikaocare_api/upload_image.php";
final String apiUpdate =
"http://192.168.100.9/kopikaocare_api/update_profile.php";
@override
void initState() {
super.initState();
loadProfile();
}
Future<void> loadProfile() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
nameController.text = prefs.getString('name') ?? '';
emailController.text = prefs.getString('email') ?? '';
imageName = prefs.getString('image');
_refreshKey++;
});
}
String getImageUrl() {
if (imageName == null || imageName!.isEmpty) return '';
return "$baseUrl$imageName?t=${DateTime.now().millisecondsSinceEpoch}";
}
Future<void> pickImage() async {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
setState(() => _isLoading = true);
try {
final bytes = await pickedFile.readAsBytes();
final base64Image = base64Encode(bytes);
final prefs = await SharedPreferences.getInstance();
final email = prefs.getString('email') ?? '';
final response = await http.post(
Uri.parse(apiUpload),
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: {
'email': email,
'image': base64Image,
},
);
final data = jsonDecode(response.body);
if (data['status'] == 'success') {
await prefs.setString('image', data['image']);
setState(() {
imageName = data['image'];
_isLoading = false;
_refreshKey++;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Foto berhasil diupload'),
backgroundColor: Colors.green,
),
);
}
} else {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Gagal: ${data['message']}'),
backgroundColor: Colors.red,
),
);
}
} catch (e) {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e'), backgroundColor: Colors.red),
);
}
}
}
Future<void> removeImage() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('image');
setState(() {
imageName = null;
_refreshKey++;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Foto dihapus'),
backgroundColor: Colors.orange,
),
);
}
Future<void> saveChanges(String newName, String newEmail) async {
final prefs = await SharedPreferences.getInstance();
final currentName = prefs.getString('name') ?? '';
final currentEmail = prefs.getString('email') ?? '';
bool nameChanged = currentName != newName;
bool emailChanged = currentEmail != newEmail;
if (!nameChanged && !emailChanged) {
return;
}
setState(() => _isLoading = true);
try {
final response = await http.post(
Uri.parse(apiUpdate),
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: {
'old_email': currentEmail,
'name': newName,
'email': newEmail,
},
);
final data = jsonDecode(response.body);
if (data['status'] == 'success') {
if (nameChanged) await prefs.setString('name', newName);
if (emailChanged) await prefs.setString('email', newEmail);
setState(() {
_isLoading = false;
});
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Data berhasil diupdate'),
backgroundColor: Colors.green,
),
);
}
} else {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Gagal: ${data['message']}'),
backgroundColor: Colors.red,
),
);
}
} catch (e) {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e'), backgroundColor: Colors.red),
);
}
}
void showEditDialog() {
final TextEditingController tempNameCtrl =
TextEditingController(text: nameController.text);
final TextEditingController tempEmailCtrl =
TextEditingController(text: emailController.text);
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
child: Container(
padding: const EdgeInsets.all(25),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.white,
Colors.red.shade50,
],
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
shape: BoxShape.circle,
),
child:
const Icon(Icons.edit_note, color: Colors.red, size: 32),
),
const SizedBox(height: 20),
const Text(
'Edit Profile',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Color(0xff2d2d2d),
),
),
const SizedBox(height: 10),
const Text(
'Ubah informasi profil Anda',
style: TextStyle(
fontSize: 14,
color: Color(0xff666666),
),
),
const SizedBox(height: 25),
TextField(
controller: tempNameCtrl,
decoration: InputDecoration(
labelText: 'Nama Lengkap',
prefixIcon:
const Icon(Icons.person_outline, color: Colors.red),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Colors.grey.shade300),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(color: Colors.red, width: 2),
),
),
),
const SizedBox(height: 15),
TextField(
controller: tempEmailCtrl,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon:
const Icon(Icons.email_outlined, color: Colors.red),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Colors.grey.shade300),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Colors.grey.shade300),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(color: Colors.red, width: 2),
),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 25),
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
style: OutlinedButton.styleFrom(
foregroundColor: Colors.grey,
side: BorderSide(color: Colors.grey.shade300),
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Batal'),
),
),
const SizedBox(width: 15),
Expanded(
child: ElevatedButton(
onPressed: () async {
Navigator.pop(context);
await saveChanges(tempNameCtrl.text.trim(),
tempEmailCtrl.text.trim());
await loadProfile();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text('Simpan'),
),
),
],
),
],
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
final imageUrl = getImageUrl();
return Scaffold(
backgroundColor: const Color(0xfff8f9fa),
appBar: AppBar(
title: const Text(
'Profile',
style: TextStyle(
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
),
),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
elevation: 0,
actions: [
Container(
margin: const EdgeInsets.only(right: 10),
child: IconButton(
icon: const Icon(Icons.edit_note),
onPressed: showEditDialog,
tooltip: 'Edit Nama & Email',
style: IconButton.styleFrom(
backgroundColor: Colors.white.withOpacity(0.2),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
body: SingleChildScrollView(
child: Column(
children: [
// Header Gradient
Container(
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.red.shade700,
Colors.red.shade500,
Colors.red.shade300,
],
),
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
),
child: Column(
children: [
const SizedBox(height: 30),
// Foto Profil dengan efek glow
GestureDetector(
onTap: pickImage,
child: Stack(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
blurRadius: 30,
color: Colors.white.withOpacity(0.5),
spreadRadius: 5,
),
],
),
child: CircleAvatar(
key: ValueKey(_refreshKey),
radius: 70,
backgroundColor: Colors.white,
backgroundImage: imageUrl.isNotEmpty
? NetworkImage(imageUrl)
: null,
child: imageUrl.isEmpty
? const Icon(Icons.person,
size: 70, color: Colors.red)
: null,
),
),
if (_isLoading)
const Positioned.fill(
child: CircleAvatar(
radius: 70,
backgroundColor: Colors.black54,
child: CircularProgressIndicator(
color: Colors.white),
),
),
Positioned(
bottom: 5,
right: 5,
child: Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
blurRadius: 10,
color: Colors.black.withOpacity(0.2),
),
],
),
child: const Icon(Icons.camera_alt,
size: 22, color: Colors.red),
),
),
],
),
),
const SizedBox(height: 20),
// Nama
Text(
nameController.text.isEmpty
? 'Belum diisi'
: nameController.text,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.white,
letterSpacing: 1,
),
),
const SizedBox(height: 8),
// Email
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 6),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: Text(
emailController.text.isEmpty
? 'Email belum diisi'
: emailController.text,
style: const TextStyle(
fontSize: 14,
color: Colors.white,
),
),
),
const SizedBox(height: 30),
// Tombol Hapus Foto
if (imageName != null)
TextButton.icon(
onPressed: removeImage,
icon: const Icon(Icons.delete_outline, size: 18),
label: const Text('Hapus Foto'),
style: TextButton.styleFrom(
foregroundColor: Colors.white,
),
),
const SizedBox(height: 10),
],
),
),
const SizedBox(height: 30),
// Info Card
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Informasi Akun',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xff2d2d2d),
),
),
const SizedBox(height: 15),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
blurRadius: 15,
color: Colors.black.withOpacity(0.05),
offset: const Offset(0, 5),
),
],
),
child: Column(
children: [
// Nama Item
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
border: Border(
bottom: BorderSide(
color: Colors.grey.shade200,
width: 1,
),
),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
),
child: const Icon(Icons.person,
color: Colors.red, size: 24),
),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Nama Lengkap',
style: TextStyle(
fontSize: 12,
color: Color(0xff888888),
),
),
const SizedBox(height: 4),
Text(
nameController.text.isEmpty
? 'Belum diisi'
: nameController.text,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xff2d2d2d),
),
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.verified,
color: Colors.red, size: 16),
),
],
),
),
// Email Item
Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20),
),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.red.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
),
child: const Icon(Icons.email,
color: Colors.red, size: 24),
),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Alamat Email',
style: TextStyle(
fontSize: 12,
color: Color(0xff888888),
),
),
const SizedBox(height: 4),
Text(
emailController.text.isEmpty
? 'Belum diisi'
: emailController.text,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xff2d2d2d),
),
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.check_circle,
color: Colors.green, size: 16),
),
],
),
),
],
),
),
],
),
),
const SizedBox(height: 30),
],
),
),
);
}
}