2026-01-13 11:36:24 +05:30

456 lines
16 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:onufitness/constants/color_constant.dart';
import 'package:onufitness/constants/constant.dart';
import 'package:onufitness/constants/string_constant.dart';
import 'package:onufitness/constants/text_constant.dart';
import 'package:onufitness/screens/accounts/Controllers/edit_account_controller.dart';
import 'package:onufitness/utils/custom_sneakbar.dart';
import 'package:onufitness/utils/helper_function.dart';
import 'package:onufitness/widgets/Dropdowns/custom_lebel_dropdown.dart';
import 'package:onufitness/widgets/bottomsheet/common_upload_option_bottomsheet.dart'
show CommonUploadBottomSheet;
import 'package:onufitness/widgets/pickers/custom_date_picker.dart';
import 'package:onufitness/widgets/TextFields/custom_textfield_with_inside_container_label_text.dart';
class TraineeCoachEditProfileScreen extends StatefulWidget {
const TraineeCoachEditProfileScreen({super.key});
@override
State<TraineeCoachEditProfileScreen> createState() =>
_TraineeCoachEditProfileScreenState();
}
class _TraineeCoachEditProfileScreenState
extends State<TraineeCoachEditProfileScreen> {
final controller = Get.find<EditAccountController>();
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
apicall();
});
super.initState();
}
apicall() async {
await controller.fetchUserDetails();
}
// Simple age validation
bool _isOver18(DateTime birthDate) {
final now = DateTime.now();
final age = now.year - birthDate.year;
if (now.month < birthDate.month ||
(now.month == birthDate.month && now.day < birthDate.day)) {
return (age - 1) >= 18;
}
return age >= 18;
}
void pickBirthDate() async {
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now().subtract(Duration(days: 18 * 365)),
firstDate: DateTime(1900),
lastDate: DateTime.now(),
);
if (pickedDate != null) {
if (_isOver18(pickedDate)) {
controller.selectedDate.value =
"${pickedDate.day}/${pickedDate.month}/${pickedDate.year}";
} else {
customSnackbar(
title: "Age Restriction",
message: "You must be at least 18 years old to proceed.",
duration: 3,
);
}
}
}
void handleDoneButtonPress() async {
if (controller.validateForm()) {
try {
await controller.updateUserProfile();
customSnackbar(
title: "Success",
message: "Profile updated successfully!",
duration: 2,
);
} catch (e) {
customSnackbar(
title: "Failed",
message: "Failed to update profile. Please try again.",
duration: 2,
);
}
}
}
Widget buildProfileImage(bool isTablet) {
return Obx(() {
if (controller.isLoading.value) {
return Container(
width: isTablet ? 150.r : 100.r,
height: isTablet ? 150.r : 100.r,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[300],
),
child: Center(
child: CircularProgressIndicator(
color: Colors.black,
strokeWidth: 2,
),
),
);
}
if (controller.profileImage.value != null) {
return CircleAvatar(
backgroundImage: FileImage(File(controller.profileImage.value!.path)),
radius: isTablet ? 60.r : 50.r,
);
}
if (controller.hasNetworkImage) {
return CircleAvatar(
radius: isTablet ? 60.r : 50.r,
backgroundColor: lightGreyColor.withValues(alpha: 0.5),
child: ClipOval(
child: Image.network(
controller.userProfileImageUrl!,
width: isTablet ? 120.r : 100.r,
height: isTablet ? 120.r : 100.r,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
width: isTablet ? 120.r : 100.r,
height: isTablet ? 120.r : 100.r,
decoration: BoxDecoration(
color: lightGreyColor.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: Center(
child: CircularProgressIndicator(
color: Colors.black,
strokeWidth: 2,
),
),
);
},
errorBuilder: (context, error, stackTrace) {
return buildDefaultAvatar(isTablet);
},
),
),
);
}
return buildDefaultAvatar(isTablet);
});
}
Widget buildDefaultAvatar(bool isTablet) {
return Container(
width: isTablet ? 150.r : 100.r,
height: isTablet ? 150.r : 100.r,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
becomeACoachGradient2,
becomeACoachGradient1.withValues(alpha: 0.3),
],
begin: Alignment.topCenter,
end: Alignment.bottomRight,
),
),
child: Icon(
Icons.person,
size: isTablet ? 90.sp : 50.sp,
color: Colors.white,
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.white,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () => Get.back(),
),
title: Text(
editProfile,
style: customTextStyle.copyWith(
color: appbarTextColor,
fontWeight: FontWeight.w600,
fontSize: isTablet ? 24.sp : 20.sp,
),
),
actions: [
Obx(
() => TextButton(
onPressed:
controller.isUpdating.value ? null : handleDoneButtonPress,
child:
controller.isUpdating.value
? SizedBox(
height: 20.h,
width: 20.w,
child: CircularProgressIndicator(
color: Colors.black,
strokeWidth: 2,
),
)
: Text(
doneText,
style: TextStyle(
fontSize: isTablet ? 25.sp : 20.sp,
fontWeight: FontWeight.w600,
color: appbarTextColor,
),
),
),
),
],
),
body: LayoutBuilder(
builder: (context, constraints) {
bool isTablet = constraints.maxWidth >= 600;
return Obx(() {
if (controller.fetchUserDetailsLoading.value) {
return Center(
child: CircularProgressIndicator(color: Colors.black),
);
}
return SingleChildScrollView(
padding: EdgeInsets.symmetric(
horizontal: isTablet ? 20.w : 15.w,
vertical: 16.h,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
alignment: Alignment.bottomRight,
children: [
CircleAvatar(
radius: isTablet ? 60.r : 50.r,
backgroundColor: Colors.grey[300],
child: buildProfileImage(isTablet),
),
Positioned(
bottom: 5,
right: 5,
child: CircleAvatar(
radius: 15.r,
backgroundColor: Colors.white,
child:
controller.isUploadingImage.value
? SizedBox(
width: 15.r,
height: 15.r,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.black,
),
)
: IconButton(
icon: Icon(
Icons.edit,
size: isTablet ? 20.sp : 15.sp,
color: Colors.black,
),
onPressed: () {
CommonUploadBottomSheet.show(
context: context,
title: "Upload Photo From",
allowedFileTypes: [
'jpg',
'jpeg',
'png',
],
maxFileSizeMB: 5.0,
showCamera: true,
showGallery: true,
showDocument: true,
onFileSelected: (
String filePath,
String fileName,
String fileExtension,
) async {
controller.profileImage.value = XFile(
filePath,
);
await controller
.uploadProfilePictureAPIcall();
},
onError: (String errorMessage) {
// Show error message
customSnackbar(
title: "Error",
message: errorMessage,
);
},
);
},
),
),
),
],
),
SizedBox(height: 20.h),
// Form Fields with validation indicators
CustomTextFieldContainerInsideLabel(
label: "First Name*",
controller: controller.firstNameController,
borderColor: lightGreyColor,
),
SizedBox(height: 20.h),
CustomTextFieldContainerInsideLabel(
label: "Last name*",
controller: controller.lastNameController,
borderColor: lightGreyColor,
),
SizedBox(height: 20.h),
CustomTextFieldContainerInsideLabel(
label: "About me",
controller: controller.aboutMeController,
maxLine: 4,
borderColor: lightGreyColor,
topContentPadding: 25.h,
),
SizedBox(height: 20.h),
Obx(
() => IntlPhoneField(
controller: controller.phoneNumberController,
initialCountryCode: controller.selectedCountryCode.value,
style: TextStyle(fontSize: regularSizeText),
decoration: InputDecoration(
fillColor: textFieldFillColor,
filled: true,
labelText: "Phone Number*",
labelStyle: TextStyle(
color: greyTextColor1,
fontSize: regularSizeText,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: lightGreyColor,
width: 1,
),
borderRadius: BorderRadius.circular(10.r),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: lightGreyColor,
width: 1,
),
borderRadius: BorderRadius.circular(10.r),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 1),
borderRadius: BorderRadius.circular(10.r),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.red, width: 1),
borderRadius: BorderRadius.circular(10.r),
),
),
dropdownTextStyle: TextStyle(fontSize: regularSizeText),
onChanged: (value) {
controller.selectedCountryCode.value =
value.countryISOCode;
},
),
),
SizedBox(height: 20.h),
// Date Picker
Obx(
() => CustomDatePicker(
label: "Date of birth",
selectedDate: controller.selectedDate.value,
onDatePicked: controller.pickDate,
borderColor: lightGreyColor,
),
),
SizedBox(height: 20.h),
// Gender Dropdown
Obx(
() => EnhancedLebelTextDropdown(
title: 'Gender',
items: const ["Male", "Female", "Other"],
selectedItem: controller.selectedGender.value,
onChanged: (value) {
if (value != null) {
controller.selectedGender.value = value;
}
},
borderColor: lightGreyColor,
),
),
SizedBox(height: 20.h),
// Social Media Links
CustomTextFieldContainerInsideLabel(
label: "Facebook Profile Link",
controller: controller.facebookController,
borderColor: lightGreyColor,
),
SizedBox(height: 20.h),
CustomTextFieldContainerInsideLabel(
label: "Instagram Profile Link",
controller: controller.instagramController,
borderColor: lightGreyColor,
),
SizedBox(height: 20.h),
CustomTextFieldContainerInsideLabel(
label: "LinkedIn Profile Link",
controller: controller.linkedInController,
borderColor: lightGreyColor,
),
SizedBox(height: 20.h),
CustomTextFieldContainerInsideLabel(
label: "Twitter Profile Link",
controller: controller.twitterController,
borderColor: lightGreyColor,
),
SizedBox(height: 20.h),
],
),
);
});
},
),
);
}
}