456 lines
16 KiB
Dart
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),
|
|
],
|
|
),
|
|
);
|
|
});
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|