Merge branch 'master' into haroon_dev

pull/110/head
haroon amjad 1 month ago
commit ca47315b2c

@ -330,7 +330,7 @@ class Utils {
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.h, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr())
.toText16(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
.toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true)
.paddingSymmetrical(64.w, 0.h),
SizedBox(height: 16.h),
callToActionButton

@ -263,7 +263,7 @@ extension EmailValidator on String {
style: TextStyle(fontSize: 19.f, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4),
);
Widget toText20({Color? color, FontWeight? weight, bool isBold = false}) => Text(
Widget toText20({Color? color, FontWeight? weight, bool isBold = false, }) => Text(
this,
style: TextStyle(
fontSize: 20.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4),

@ -101,10 +101,7 @@ class AuthenticationRepoImp implements AuthenticationRepo {
}
@override
Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({
required dynamic checkPatientAuthenticationReq,
String? languageID,
}) async {
Future<Either<Failure, GenericApiModel<dynamic>>> checkPatientAuthentication({required dynamic checkPatientAuthenticationReq, String? languageID}) async {
int isOutKsa = (checkPatientAuthenticationReq.zipCode == '966' || checkPatientAuthenticationReq.zipCode == '+966') ? 0 : 1;
checkPatientAuthenticationReq.patientOutSA = isOutKsa;
try {
@ -149,7 +146,6 @@ class AuthenticationRepoImp implements AuthenticationRepo {
sendActivationCodeReq.isDentalAllowedBackend = false;
final payload = sendActivationCodeReq.toJson();
if (isFormFamilyFile) {
payload.remove("MobileNo");
payload.remove("NationalID");
payload.remove("SMSSignature");

@ -852,16 +852,13 @@ class AuthenticationViewModel extends ChangeNotifier {
request['isRegister'] = true;
_appState.setAppAuthToken = response['LogInTokenID'];
if (isPatientOutsideSA(request: response)) {
print("=======OUT SA=======");
sendActivationCode(
otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]),
nationalIdOrFileNumber: request["PatientIdentificationID"].toString(),
phoneNumber: request["PatientMobileNumber"].toString(),
payload: request,
isForRegister: true,
);
isForRegister: true);
} else {
print("=======IN SA=======");
chekUserNHICData(request: request);
}
}

@ -95,7 +95,6 @@ class MedicalFileViewModel extends ChangeNotifier {
void onFamilyFileTabChange(int index) {
setSelectedFamilyFileTabIndex = index;
notifyListeners();
}
setIsPatientVaccineListLoading(bool isLoading) {
@ -474,9 +473,6 @@ class MedicalFileViewModel extends ChangeNotifier {
);
}
Future<void> addFamilyFile({required OTPTypeEnum otpTypeEnum}) async {
LoaderBottomSheet.showLoader();
AuthenticationViewModel authVM = getIt.get<AuthenticationViewModel>();
@ -521,6 +517,13 @@ class MedicalFileViewModel extends ChangeNotifier {
});
}
Future<void> clearAuthValues() async {
authVM.nationalIdController.clear();
authVM.phoneNumberController.clear();
authVM.emailController.clear();
authVM.dobController.clear();
}
Future<void> handleFamilyFileRequestOTPVerification() async {
LoaderBottomSheet.showLoader();
if (!_appState.getIsChildLoggedIn) {
@ -632,7 +635,7 @@ class MedicalFileViewModel extends ChangeNotifier {
getFamilyFiles(status: 0);
getAllPendingRecordsByResponseId();
LoaderBottomSheet.hideLoader();
onFamilyFileTabChange(0);
// onFamilyFileTabChange(0);
}
},
);

@ -88,10 +88,10 @@ class LoginScreenState extends State<LoginScreen> {
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 10.h),
leadingIcon: AppAssets.student_card,
errorMessage: "Please enter a valid national ID or file number",
errorMessage: "Please enter a valid national ID or file number".needTranslation,
hasError: false,
),
SizedBox(height: 16.h), // Adjusted to sizer unit (approx 16px)
SizedBox(height: 16.h),
CustomButton(
height: 50.h,
text: LocaleKeys.login.tr(),
@ -110,17 +110,12 @@ class LoginScreenState extends State<LoginScreen> {
}
},
),
SizedBox(height: 10.h), // Adjusted to sizer unit (approx 14px)
SizedBox(height: 10.h),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
style: context.dynamicTextStyle(
color: Colors.black,
fontSize: 14.f, // Adjusted to sizer unit
height: 26 / 16, // This height is a ratio, may need re-evaluation
fontWeight: FontWeight.w500,
),
style: context.dynamicTextStyle(color: Colors.black, fontSize: 14.f, height: 26 / 16, fontWeight: FontWeight.w500),
children: <TextSpan>[
TextSpan(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()),
TextSpan(text: " "),
@ -140,9 +135,9 @@ class LoginScreenState extends State<LoginScreen> {
),
],
),
).withVerticalPadding(2), // Adjusted to sizer unit
).withVerticalPadding(2.h),
),
SizedBox(height: 20.h), // Adjusted to sizer unit (approx 14px)
SizedBox(height: 20.h),
],
),
),

@ -78,8 +78,7 @@ class _RegisterNew extends State<RegisterNew> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Utils.showLottie(
context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.h, height: 200.h, fit: BoxFit.cover, repeat: true),
Utils.showLottie(context: context, assetPath: 'assets/animations/lottie/register.json', width: 200.w, height: 200.h, fit: BoxFit.cover, repeat: true),
SizedBox(height: 16.h),
LocaleKeys.prepareToElevate.tr().toText32(isBold: true),
SizedBox(height: 24.h),
@ -113,7 +112,7 @@ class _RegisterNew extends State<RegisterNew> {
Divider(height: 1),
TextInputWidget(
labelText: LocaleKeys.dob.tr(),
hintText: "11 July, 1994",
hintText: "11 July, 1994".needTranslation,
controller: authVm.dobController,
focusNode: _dobFocusNode,
isEnable: true,
@ -240,7 +239,7 @@ class _RegisterNew extends State<RegisterNew> {
),
),
),
SizedBox(height: 30),
SizedBox(height: 30.h),
],
),
),

@ -69,7 +69,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
height: double.infinity,
child: SingleChildScrollView(
reverse: false,
padding: EdgeInsets.only(left: 24.h, right: 24.h, top: 0.h),
padding: EdgeInsets.only(left: 24.w, right: 24.w, top: 0.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
@ -98,7 +98,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
leadingIcon: AppAssets.user_circle,
labelColor: AppColors.textColor,
).paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor),
Divider(height: 1.h, color: AppColors.greyColor),
TextInputWidget(
labelText: LocaleKeys.nationalIdNumber.tr(),
hintText: authVM!.isUserFromUAE() ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : (appState.getNHICUserData.idNumber ?? ""),
@ -130,7 +130,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
hasSelectionCustomIcon: true,
isAllowRadius: false,
labelColor: AppColors.textColor,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.user_full,
).withVerticalPadding(8);
@ -167,7 +167,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
hasSelectionCustomIcon: true,
isAllowRadius: false,
labelColor: AppColors.textColor,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.smart_phone,
).withVerticalPadding(8);
@ -188,7 +188,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
leadingIcon: AppAssets.smart_phone,
onChange: (value) {})
.paddingSymmetrical(0.h, 16.h),
Divider(height: 1, color: AppColors.greyColor),
Divider(height: 1.h, color: AppColors.greyColor),
authVM!.isUserFromUAE()
? Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
@ -217,10 +217,10 @@ class _RegisterNew extends State<RegisterNewStep2> {
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
isAllowRadius: false,
padding: const EdgeInsets.only(top: 8, bottom: 8, left: 0, right: 0),
padding: EdgeInsets.only(top: 8.h, bottom: 8.h, left: 0, right: 0),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
).withVerticalPadding(8);
).withVerticalPadding(8.h);
},
)
: TextInputWidget(
@ -256,7 +256,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
leadingIcon: AppAssets.call)
.paddingSymmetrical(0.h, 16.h),
Divider(
height: 1,
height: 1.h,
color: AppColors.greyColor,
),
TextInputWidget(
@ -292,9 +292,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
iconColor: AppColors.primaryRedColor,
),
),
SizedBox(
width: 16,
),
SizedBox(width: 16.w),
Expanded(
child: CustomButton(
backgroundColor: AppColors.primaryRedColor,

@ -27,7 +27,6 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/allergies/allergies_list_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
@ -55,6 +54,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/expandable_list_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
@ -86,6 +86,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
appState = getIt.get<AppState>();
scheduleMicrotask(() {
if (appState.isAuthenticated) {
labViewModel.initLabProvider();
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
@ -149,20 +150,6 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Type any record".needTranslation,
controller: TextEditingController(),
keyboardType: TextInputType.number,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.student_card,
).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h),
Container(
width: double.infinity,
@ -226,9 +213,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}".needTranslation,
labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
iconColor: AppColors.primaryRedColor,
labelPadding: EdgeInsetsDirectional.only(end: 4.w, start: 0.w),
labelPadding: EdgeInsetsDirectional.only(end: 8.w),
),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
@ -237,8 +224,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
textColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 12.w,
backgroundColor:
insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
backgroundColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(end: 8.w),
);
}),
@ -249,27 +235,112 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h),
Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
return Column(
TextInputWidget(
labelText: LocaleKeys.search.tr(context: context),
hintText: "Type any record".needTranslation,
controller: TextEditingController(),
keyboardType: TextInputType.number,
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
isAllowLeadingIcon: true,
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
leadingIcon: AppAssets.search_icon,
hintColor: AppColors.textColor,
).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h),
// Using CustomExpandableList
CustomExpandableList(
expansionMode: ExpansionMode.exactlyOne,
dividerColor: Color(0xFF2B353E1A),
itemPadding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 14.h),
items: [
ExpandableListItem(
title: "Medical Services".toText18(weight: FontWeight.w600),
children: [
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
tabs: [
CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
CustomTabBarModel(AppAssets.more, "More".needTranslation),
SizedBox(height: 10.h),
getSelectedTabData(0),
],
onTabChange: (index) {
medicalFileVM.onTabChanged(index);
},
).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 24.h),
getSelectedTabData(medicalFileVM.selectedTabIndex),
),
ExpandableListItem(
title: "Medical Reports".toText18(
weight: FontWeight.w600,
),
expandedBackgroundColor: Colors.transparent,
children: [
SizedBox(height: 10.h),
getSelectedTabData(2),
]),
ExpandableListItem(
title: "Insurance & Payments".toText18(weight: FontWeight.w600),
expandedBackgroundColor: Colors.transparent,
children: [
SizedBox(height: 10.h),
getSelectedTabData(1),
],
);
}),
),
ExpandableListItem(
title: "Tracker & Others".toText18(weight: FontWeight.w600),
expandedBackgroundColor: Colors.transparent,
children: [
Text("Blood Report"),
SizedBox(height: 8),
Text("X-Ray Report"),
],
),
],
theme: ExpandableListTheme.custom(
defaultTrailingIcon: Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 22.h, width: 22.w, iconColor: AppColors.textColor),
),
).paddingSymmetrical(16.w, 0.0),
// ExpansionTileList(
// shrinkWrap: true,
// trailing: Icon(Icons.arrow_drop_down),
// expansionMode: ExpansionMode.exactlyOne,
// children: <ExpansionTile>[
// ExpansionTile(
// title: "Medical Services".toText20(weight: FontWeight.w600, color: AppColors.textColor),
// iconColor: Color(0xFF2E3039),
// trailing: Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 22.h, width: 22.w),
// children: <Widget>[Text('Child 1')],
// ),
// ExpansionTile(
// title: "Medical Reports".toText20(weight: FontWeight.w600, color: AppColors.textColor),
// children: <Widget>[Text('Child 2')],
// ),
// ExpansionTile(
// title: "Insurance & Payments".toText20(weight: FontWeight.w600, color: AppColors.textColor),
// children: <Widget>[Text('Child 3')],
// ),
// ExpansionTile(
// title: "Tracker & Others".toText20(weight: FontWeight.w600, color: AppColors.textColor),
// children: <Widget>[Text('Child 4')],
// ),
// ],
// ),
// Consumer<MedicalFileViewModel>(builder: (context, medicalFileVM, child) {
// return Column(
// children: [
// CustomTabBar(
// activeTextColor: AppColors.primaryRedColor,
// activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
// tabs: [
// CustomTabBarModel(AppAssets.myFilesBottom, LocaleKeys.general.tr(context: context).needTranslation),
// CustomTabBarModel(AppAssets.insurance, LocaleKeys.insurance.tr(context: context)),
// CustomTabBarModel(AppAssets.requests, LocaleKeys.request.tr(context: context).needTranslation),
// CustomTabBarModel(AppAssets.more, "More".needTranslation),
// ],
// onTabChange: (index) {
// medicalFileVM.onTabChanged(index);
// },
// ).paddingSymmetrical(24.w, 0.0),
// SizedBox(height: 24.h),
// getSelectedTabData(medicalFileVM.selectedTabIndex),
// ],
// );
// }),
],
),
);
@ -325,7 +396,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Appointments & visits".needTranslation.toText18(isBold: true),
"Appointments & visits".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
Row(
children: [
LocaleKeys.viewAll.tr().toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
@ -334,7 +405,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
],
),
],
).paddingSymmetrical(24.w, 0.h).onPress(() {
).paddingSymmetrical(0.w, 0.h).onPress(() {
Navigator.of(context).push(
CustomPageRoute(
page: MyAppointmentsPage(),
@ -357,8 +428,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
? Container(
padding: EdgeInsets.all(12.w),
width: MediaQuery.of(context).size.width,
decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: true),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: true),
child: Column(
children: [
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
@ -391,7 +461,6 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
: ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemCount: myAppointmentsVM.patientAppointmentsHistoryList.length,
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
@ -409,45 +478,18 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
onRescheduleTap: () {
openDoctorScheduleCalendar(myAppointmentsVM.patientAppointmentsHistoryList[index]);
},
onAskDoctorTap: () async {
LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation);
await myAppointmentsViewModel.isDoctorAvailable(
projectID: myAppointmentsVM.patientAppointmentsHistoryList[index].projectID,
doctorId: myAppointmentsVM.patientAppointmentsHistoryList[index].doctorID,
clinicId: myAppointmentsVM.patientAppointmentsHistoryList[index].clinicID,
onSuccess: (value) async {
if (value) {
await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
title: LocaleKeys.askDoctor.tr(context: context),
child: AskDoctorRequestTypeSelect(
askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList,
myAppointmentsViewModel: myAppointmentsViewModel,
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else {
print("Doctor is not available");
}
});
},
onAskDoctorTap: () {},
)),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w),
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 12.h),
),
);
).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 24.h),
"Lab & Radiology".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
SizedBox(height: 10.h),
"Lab & Radiology".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
SizedBox(height: 16.h),
Row(
children: [
@ -459,7 +501,6 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
labOrderTests: labViewModel.isLabOrdersLoading ? [] : labViewModel.labOrderTests,
isLoading: labViewModel.isLabOrdersLoading,
).onPress(() {
labViewModel.initLabProvider();
Navigator.of(context).push(
CustomPageRoute(
page: LabOrdersPage(),
@ -467,11 +508,12 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
);
}),
),
SizedBox(width: 16.h),
SizedBox(width: 8.w),
Expanded(
child: LabRadCard(
icon: AppAssets.radiology_icon,
labelText: LocaleKeys.radiology.tr(context: context),
labelText: "${LocaleKeys.radiology.tr(context: context)} Results".needTranslation,
// labOrderTests: ["Complete blood count", "Creatinine", "Blood Sugar",
// labOrderTests: ["Chest X-ray", "Abdominal Ultrasound", "Dental X-ray"],
labOrderTests: [],
isLoading: false,
@ -484,13 +526,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
}),
),
],
).paddingSymmetrical(24.w, 0.h),
).paddingSymmetrical(0.w, 0.h),
SizedBox(height: 24.h),
"Active Medications & Prescriptions".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
"Active Medications & Prescriptions".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
return prescriptionVM.isPrescriptionsOrdersLoading
? const CommonShimmerWidget().paddingSymmetrical(24.w, 0.h)
? const CommonShimmerWidget().paddingSymmetrical(0.w, 0.h)
: prescriptionVM.patientPrescriptionOrders.isNotEmpty
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@ -533,13 +575,11 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
spacing: 3.w,
runSpacing: 4.w,
children: [
AppCustomChipWidget(
labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!),
AppCustomChipWidget(labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!),
AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate(
DateUtil.convertStringToDate(
prescriptionVM.patientPrescriptionOrders[index].appointmentDate),
DateUtil.convertStringToDate(prescriptionVM.patientPrescriptionOrders[index].appointmentDate),
false,
),
),
@ -552,19 +592,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small,
width: 15.w,
height: 15.h,
fit: BoxFit.contain,
iconColor: AppColors.textColor)),
icon: AppAssets.forward_arrow_icon_small, width: 15.w, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)),
],
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
CustomPageRoute(
page: PrescriptionDetailPage(
isFromAppointments: false,
prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
page: PrescriptionDetailPage(isFromAppointments: false, prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
),
);
}),
@ -574,9 +608,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
SizedBox(height: 8.h),
SizedBox(height: 24.h),
const Divider(color: AppColors.dividerColor),
SizedBox(height: 8.h),
SizedBox(height: 24.h),
Row(
children: [
Expanded(
@ -637,14 +671,14 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
width: 62.w,
height: 62.h,
),
).paddingSymmetrical(24.w, 0.h);
).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 24.h),
//My Doctor Section
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.myDoctor.tr(context: context).toText18(isBold: true),
LocaleKeys.myDoctor.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2),
Row(
children: [
LocaleKeys.viewAll.tr().toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
@ -660,7 +694,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
);
}),
],
).paddingSymmetrical(24.w, 0.h),
).paddingSymmetrical(0.w, 0.h),
SizedBox(height: 16.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
return myAppointmentsVM.isPatientMyDoctorsLoading
@ -674,10 +708,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true, radius: 50.r),
SizedBox(height: 8.h),
("Dr. John Smith Smith Smith")
.toString()
.toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
.toShimmer2(isShow: true),
("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true),
],
)
: myAppointmentsVM.patientMyDoctorsList.isEmpty
@ -695,14 +726,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
width: 62.w,
height: 62.h,
),
).paddingSymmetrical(24.w, 0.h)
).paddingSymmetrical(0.w, 0.h)
: SizedBox(
height: 110.h,
height: 100.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: myAppointmentsVM.patientMyDoctorsList.length,
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
@ -722,8 +752,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false, radius: 50.r),
SizedBox(height: 8.h),
SizedBox(
width: 80.w,
Expanded(
child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName)
.toString()
.toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
@ -762,10 +791,10 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h),
),
);
).paddingSymmetrical(0.w, 0);
}),
SizedBox(height: 24.h),
"Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
"Others".needTranslation.toText16(weight: FontWeight.w500, letterSpacing: -0.2),
SizedBox(height: 16.h),
GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
@ -825,7 +854,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
);
}),
],
).paddingSymmetrical(24.w, 0.0),
).paddingSymmetrical(0.w, 0.0),
SizedBox(height: 24.h),
],
);
@ -840,7 +869,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
labOrder: null,
index: index,
isLoading: true,
).paddingSymmetrical(24.w, 0.0)
).paddingSymmetrical(0.w, 0.0)
: insuranceVM.patientInsuranceList.isNotEmpty
? PatientInsuranceCard(
insuranceCardDetailsModel: insuranceVM.patientInsuranceList.first,
@ -867,14 +896,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
onPressed: () {
insuranceViewModel.setIsInsuranceUpdateDetailsLoading(true);
insuranceViewModel.getPatientInsuranceDetailsForUpdate(appState.getAuthenticatedUser()!.patientId.toString(),
appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
showCommonBottomSheetWithoutHeight(context,
child: PatientInsuranceCardUpdateCard(),
callBackFunc: () {},
title: "",
isCloseButtonVisible: false,
isFullScreen: false);
insuranceViewModel.getPatientInsuranceDetailsForUpdate(
appState.getAuthenticatedUser()!.patientId.toString(), appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
showCommonBottomSheetWithoutHeight(context, child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false);
},
backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
borderColor: AppColors.bgGreenColor.withOpacity(0.0),
@ -886,7 +910,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
height: isFoldable ? 50.h : 40.h,
).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h),
),
).paddingSymmetrical(24.w, 0.h);
).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 10.h),
GridView(
@ -941,7 +965,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconSize: 36.w,
),
],
).paddingSymmetrical(24.w, 0.0),
).paddingSymmetrical(0.w, 0.0),
SizedBox(height: 16.h),
],
);
@ -954,12 +978,12 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
? PatientSickLeaveCard(
patientSickLeavesResponseModel: PatientSickLeavesResponseModel(),
isLoading: true,
).paddingSymmetrical(24.w, 0.0)
).paddingSymmetrical(0.w, 0.0)
: medicalFileVM.patientSickLeaveList.isNotEmpty
? PatientSickLeaveCard(
patientSickLeavesResponseModel: medicalFileVM.patientSickLeaveList.first,
isLoading: false,
).paddingSymmetrical(24.w, 0.0)
).paddingSymmetrical(0.w, 0.0)
: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
@ -973,7 +997,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
width: 62.w,
height: 62.h,
),
).paddingSymmetrical(24.w, 0.h);
).paddingSymmetrical(0.w, 0.h);
}),
SizedBox(height: 16.h),
GridView(
@ -1026,7 +1050,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
);
}),
],
).paddingSymmetrical(24.w, 0.0),
).paddingSymmetrical(0.w, 0.0),
SizedBox(height: 24.h),
],
);

@ -27,20 +27,27 @@ class LabRadCard extends StatelessWidget {
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: false),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 18.r, hasShadow: false),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Utils.buildSvgWithAssets(
icon: icon,
width: 40.w,
height: 40.h,
fit: BoxFit.contain,
fit: BoxFit.cover,
).toShimmer2(isShow: false, radius: 12.r),
SizedBox(width: 8.w),
Flexible(child: labelText.toText14(isBold: true).toShimmer2(isShow: false, radius: 6.r, height: 32.h)),
Flexible(
child: labelText.toText12(isBold: true, maxLine: 2),
),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon_small, width: 10.w, height: 10.h, fit: BoxFit.contain, iconColor: AppColors.textColor),
),
],
),
// SizedBox(height: 16.h),
@ -61,21 +68,21 @@ class LabRadCard extends StatelessWidget {
// )
// : "You don't have any records yet".needTranslation.toText13(
// color: AppColors.greyTextColor, isCenter: true),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"View All".toText12(isBold: true),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon_small, width: 15.w, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
.toShimmer2(isShow: false, radius: 12.r),
),
],
)
// SizedBox(height: 16.h),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// SizedBox.shrink(),
// Transform.flip(
// flipX: appState.isArabic(),
// child: Utils.buildSvgWithAssets(
// icon: AppAssets.forward_arrow_icon_small, width: 15.w, height: 15.h, fit: BoxFit.contain, iconColor: AppColors.textColor)
// .toShimmer2(isShow: false, radius: 12.r),
// ),
// ],
// )
],
).paddingAll(16.w),
).paddingAll(12.w),
);
}
}

@ -46,6 +46,9 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
void initState() {
super.initState();
medicalVM = context.read<MedicalFileViewModel>();
WidgetsBinding.instance.addPostFrameCallback((_) {
medicalVM?.onFamilyFileTabChange(0);
});
}
@override
@ -65,6 +68,7 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
text: "Add a new family member".needTranslation,
onPressed: () {
DialogService dialogService = getIt.get<DialogService>();
medicalVM!.clearAuthValues();
dialogService.showAddFamilyFileSheet(
label: "Add Family Member".needTranslation,
message: "Please fill the below field to add a new family member to your profile".needTranslation,
@ -83,14 +87,18 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
children: [
appState.isChildLoggedIn
? SizedBox()
: CustomTabBar(
: Selector<MedicalFileViewModel, int>(
selector: (_, model) => model.getSelectedFamilyFileTabIndex,
builder: (context, selectedIndex, child) => CustomTabBar(
activeBackgroundColor: AppColors.secondaryLightRedColor,
activeTextColor: AppColors.primaryRedColor,
// selectedIndex: selectedIndex,
tabs: [CustomTabBarModel(null, LocaleKeys.family.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())],
onTabChange: (index) {
medicalVM!.onFamilyFileTabChange(index);
},
),
),
appState.isChildLoggedIn ? SizedBox() : SizedBox(height: 25.h),
Selector<MedicalFileViewModel, int>(selector: (_, model) => model.getSelectedFamilyFileTabIndex, builder: (context, selectedIndex, child) => getFamilyTabs(index: selectedIndex)),
SizedBox(height: 20.h),
@ -117,7 +125,7 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
case 1:
return FamilyCards(
profiles: medicalVM!.pendingFamilyFiles,
isRequestDesign: true,
isRequestDesign: medicalVM!.getSelectedFamilyFileTabIndex == 1,
onSelect: (FamilyFileResponseModelLists profile) {
medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 3);
},

@ -129,7 +129,13 @@ class DialogServiceImp implements DialogService {
if (context == null) return;
showCommonBottomSheetWithoutHeight(context,
title: label ?? "",
child: FamilyCards(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
(message).toText16(isBold: false, color: AppColors.textColor),
SizedBox(height: 24.h),
FamilyCards(
profiles: profiles,
onSelect: (FamilyFileResponseModelLists profile) {
onSwitchPress(profile);
@ -137,6 +143,8 @@ class DialogServiceImp implements DialogService {
onRemove: (FamilyFileResponseModelLists profile) {},
isShowDetails: false,
),
],
),
callBackFunc: () {});
}

@ -10,7 +10,7 @@ class ExpandableBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("the currently selected item is ${bottomSheetType}");
print("the currently selected item is $bottomSheetType");
return AnimatedCrossFade(
duration: const Duration(milliseconds: 600),
firstChild:children[BottomSheetType.FIXED] ?? SizedBox.shrink(),

@ -0,0 +1,301 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
// ==================== MAIN CUSTOM WIDGET ====================
class CustomExpandableList extends StatefulWidget {
final List<ExpandableListItem> items;
final ExpansionMode expansionMode;
final EdgeInsetsGeometry? itemPadding;
final EdgeInsetsGeometry? contentPadding;
final Color? dividerColor;
final double dividerHeight;
final bool showDividers;
final Duration animationDuration;
final Curve animationCurve;
final ExpandableListTheme? theme;
final Function(int index, bool isExpanded)? onItemToggled;
const CustomExpandableList({
Key? key,
required this.items,
this.expansionMode = ExpansionMode.multiple,
this.itemPadding,
this.contentPadding,
this.dividerColor,
this.dividerHeight = 1.0,
this.showDividers = true,
this.animationDuration = const Duration(milliseconds: 300),
this.animationCurve = Curves.easeInOut,
this.theme,
this.onItemToggled,
}) : super(key: key);
@override
_CustomExpandableListState createState() => _CustomExpandableListState();
}
class _CustomExpandableListState extends State<CustomExpandableList> {
late List<bool> _expandedStates;
@override
void initState() {
super.initState();
_expandedStates = List<bool>.generate(
widget.items.length,
(index) => widget.items[index].initiallyExpanded,
);
}
void _toggleItem(int index) {
setState(() {
if (widget.expansionMode == ExpansionMode.exactlyOne) {
// Close all others, open this one
for (int i = 0; i < _expandedStates.length; i++) {
_expandedStates[i] = (i == index) ? !_expandedStates[i] : false;
}
} else if (widget.expansionMode == ExpansionMode.atMostOne) {
// Toggle this one, close if opening another
bool wasExpanded = _expandedStates[index];
for (int i = 0; i < _expandedStates.length; i++) {
if (i == index) {
_expandedStates[i] = !wasExpanded;
} else if (!wasExpanded) {
_expandedStates[i] = false;
}
}
} else {
// Multiple - just toggle the clicked item
_expandedStates[index] = !_expandedStates[index];
}
});
widget.onItemToggled?.call(index, _expandedStates[index]);
}
@override
Widget build(BuildContext context) {
final theme = widget.theme ?? ExpandableListTheme.defaultTheme(context);
return Column(
children: List.generate(widget.items.length, (index) {
final item = widget.items[index];
final isExpanded = _expandedStates[index];
final isLast = index == widget.items.length - 1;
return Column(
children: [
_ExpandableListItemWidget(
title: item.title,
isExpanded: isExpanded,
onTap: () => _toggleItem(index),
padding: widget.itemPadding,
contentPadding: widget.contentPadding,
theme: theme,
leading: item.leading,
trailing: item.trailing,
backgroundColor: item.backgroundColor,
expandedBackgroundColor: item.expandedBackgroundColor,
animationDuration: widget.animationDuration,
animationCurve: widget.animationCurve,
children: item.children,
),
if (widget.showDividers && !isLast)
Divider(
height: widget.dividerHeight,
color: widget.dividerColor ?? theme.dividerColor,
).paddingSymmetrical(12.h,0),
],
);
}),
);
}
}
// ==================== SUPPORTING WIDGETS ====================
class _ExpandableListItemWidget extends StatelessWidget {
final Widget title;
final List<Widget> children;
final bool isExpanded;
final VoidCallback onTap;
final EdgeInsetsGeometry? padding;
final EdgeInsetsGeometry? contentPadding;
final ExpandableListTheme theme;
final Widget? leading;
final Widget? trailing;
final Color? backgroundColor;
final Color? expandedBackgroundColor;
final Duration animationDuration;
final Curve animationCurve;
const _ExpandableListItemWidget({
super.key,
required this.title,
required this.children,
required this.isExpanded,
required this.onTap,
this.padding,
this.contentPadding,
required this.theme,
this.leading,
this.trailing,
this.backgroundColor,
this.expandedBackgroundColor,
required this.animationDuration,
required this.animationCurve,
});
@override
Widget build(BuildContext context) {
return Container(
color: isExpanded
? (expandedBackgroundColor ?? theme.expandedBackgroundColor)
: (backgroundColor ?? theme.backgroundColor),
child: Column(
children: [
// Header
InkWell(
onTap: onTap,
child: Padding(
padding: padding ?? theme.itemPadding,
child: Row(
children: [
if (leading != null) ...[
leading!,
SizedBox(width: theme.leadingSpacing),
],
Expanded(child: title),
if (trailing != null)
trailing!
else
AnimatedRotation(
turns: isExpanded ? 0.5 : 0.0,
duration: animationDuration,
curve: animationCurve,
child: theme.defaultTrailingIcon,
),
],
),
),
),
// Content
AnimatedSize(
duration: animationDuration,
curve: animationCurve,
child: Container(
constraints: BoxConstraints(
minHeight: isExpanded ? 0.0 : 0.0,
maxHeight: isExpanded ? double.infinity : 0.0,
),
child: isExpanded
? Padding(
padding: contentPadding ?? theme.contentPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
)
: SizedBox.shrink(),
),
),
],
),
);
}
}
// ==================== DATA MODELS ====================
class ExpandableListItem {
final Widget title;
final List<Widget> children;
final Widget? leading;
final Widget? trailing;
final bool initiallyExpanded;
final Color? backgroundColor;
final Color? expandedBackgroundColor;
const ExpandableListItem({
required this.title,
required this.children,
this.leading,
this.trailing,
this.initiallyExpanded = false,
this.backgroundColor,
this.expandedBackgroundColor,
});
}
// ==================== THEME ====================
class ExpandableListTheme {
final Color backgroundColor;
final Color expandedBackgroundColor;
final Color dividerColor;
final Widget defaultTrailingIcon;
final EdgeInsetsGeometry itemPadding;
final EdgeInsetsGeometry contentPadding;
final double leadingSpacing;
const ExpandableListTheme({
required this.backgroundColor,
required this.expandedBackgroundColor,
required this.dividerColor,
required this.defaultTrailingIcon,
required this.itemPadding,
required this.contentPadding,
this.leadingSpacing = 12.0,
});
factory ExpandableListTheme.defaultTheme(BuildContext context) {
final textColor = Theme.of(context).textTheme.bodyLarge?.color ?? Colors.black;
return ExpandableListTheme(
backgroundColor: Colors.transparent,
expandedBackgroundColor: Colors.transparent,
dividerColor: Colors.grey.shade300,
defaultTrailingIcon: Icon(
Icons.keyboard_arrow_down,
color: textColor,
size: 24,
),
itemPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
contentPadding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0),
);
}
factory ExpandableListTheme.custom({
Color? backgroundColor,
Color? expandedBackgroundColor,
Color? dividerColor,
Widget? defaultTrailingIcon,
EdgeInsetsGeometry? itemPadding,
EdgeInsetsGeometry? contentPadding,
double? leadingSpacing,
}) {
return ExpandableListTheme(
backgroundColor: backgroundColor ?? Colors.transparent,
expandedBackgroundColor: expandedBackgroundColor ?? Colors.grey.shade50,
dividerColor: dividerColor ?? Colors.grey.shade300,
defaultTrailingIcon: defaultTrailingIcon ??
Icon(Icons.keyboard_arrow_down, color: Colors.black, size: 24),
itemPadding: itemPadding ?? const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
contentPadding: contentPadding ?? const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 16.0),
leadingSpacing: leadingSpacing ?? 12.0,
);
}
}
// ==================== ENUMS ====================
enum ExpansionMode {
multiple, // Multiple items can be expanded
exactlyOne, // Exactly one item expanded at a time
atMostOne, // Zero or one item expanded at a time
}

@ -47,12 +47,13 @@ class TextInputWidget extends StatelessWidget {
final bool isMultiline;
final int minLines;
final int maxLines;
final Color? hintColor;
// final List<Country> countryList;
// final Function(Country)? onCountryChange;
TextInputWidget(
{super.key,
TextInputWidget({
super.key,
required this.labelText,
required this.hintText,
this.controller,
@ -79,6 +80,7 @@ class TextInputWidget extends StatelessWidget {
this.suffix,
this.labelColor,
this.onSubmitted,
this.hintColor,
// multiline defaults
this.isMultiline = false,
this.minLines = 3,
@ -274,7 +276,7 @@ class TextInputWidget extends StatelessWidget {
decoration: InputDecoration(
isDense: true,
hintText: hintText,
hintStyle: TextStyle(fontSize: 14.f, height: 21 / 16, fontWeight: FontWeight.w500, color: Color(0xff898A8D), letterSpacing: -0.75),
hintStyle: TextStyle(fontSize: 14.f, height: 21 / 16, fontWeight: FontWeight.w500, color: hintColor != null ? AppColors.textColor : Color(0xff898A8D), letterSpacing: -0.75),
prefixIconConstraints: BoxConstraints(minWidth: 30.h),
prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, weight: FontWeight.w500),
contentPadding: EdgeInsets.zero,

@ -11,14 +11,13 @@ class CustomRadioOption<T> extends StatelessWidget {
// final Widget child; // The content of your radio option (e.g., Text, Image)
const CustomRadioOption({
super.key,
const CustomRadioOption(
{super.key,
required this.value,
required this.groupValue,
required this.onChanged,
// required this.child,
required this.text,
});
required this.text});
@override
Widget build(BuildContext context) {
@ -31,13 +30,12 @@ class CustomRadioOption<T> extends StatelessWidget {
child: Row(
children: [
Container(
width: 20.h,
height: 20.h,
width: 18.h,
height: 18.h,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor,
border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h),
),
border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h)),
),
SizedBox(width: 8.h),
text.toText16(weight: FontWeight.w500), // The provided content

Loading…
Cancel
Save