diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index f1e1593..852071f 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -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 diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index c2a4a87..2039fb8 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -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), diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index 3194ad7..c9796e8 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -101,10 +101,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { } @override - Future>> checkPatientAuthentication({ - required dynamic checkPatientAuthenticationReq, - String? languageID, - }) async { + Future>> 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"); diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index 3260ea5..fa16423 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -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, - ); + otpTypeEnum: OTPTypeEnumExtension.fromInt(request["OTP_SendType"]), + nationalIdOrFileNumber: request["PatientIdentificationID"].toString(), + phoneNumber: request["PatientMobileNumber"].toString(), + payload: request, + isForRegister: true); } else { - print("=======IN SA======="); chekUserNHICData(request: request); } } diff --git a/lib/features/medical_file/medical_file_view_model.dart b/lib/features/medical_file/medical_file_view_model.dart index 9ccc735..de7f067 100644 --- a/lib/features/medical_file/medical_file_view_model.dart +++ b/lib/features/medical_file/medical_file_view_model.dart @@ -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 addFamilyFile({required OTPTypeEnum otpTypeEnum}) async { LoaderBottomSheet.showLoader(); AuthenticationViewModel authVM = getIt.get(); @@ -521,6 +517,13 @@ class MedicalFileViewModel extends ChangeNotifier { }); } + Future clearAuthValues() async { + authVM.nationalIdController.clear(); + authVM.phoneNumberController.clear(); + authVM.emailController.clear(); + authVM.dobController.clear(); + } + Future 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); } }, ); diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 0fa995d..28430a8 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -88,10 +88,10 @@ class LoginScreenState extends State { 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 { } }, ), - 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(text: LocaleKeys.dontHaveAccount.tr(), style: context.dynamicTextStyle()), TextSpan(text: " "), @@ -140,9 +135,9 @@ class LoginScreenState extends State { ), ], ), - ).withVerticalPadding(2), // Adjusted to sizer unit + ).withVerticalPadding(2.h), ), - SizedBox(height: 20.h), // Adjusted to sizer unit (approx 14px) + SizedBox(height: 20.h), ], ), ), diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 9551ba1..f04f7dd 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -78,8 +78,7 @@ class _RegisterNew extends State { 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 { 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 { ), ), ), - SizedBox(height: 30), + SizedBox(height: 30.h), ], ), ), diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index 3466497..71cef96 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -69,7 +69,7 @@ class _RegisterNew extends State { 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: [ @@ -98,7 +98,7 @@ class _RegisterNew extends State { 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 { 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 { 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 { 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? countriesList, NationalityCountries? selectedCountry, bool isArabic})>( selector: (context, authViewModel) { @@ -217,10 +217,10 @@ class _RegisterNew extends State { 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 { 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 { iconColor: AppColors.primaryRedColor, ), ), - SizedBox( - width: 16, - ), + SizedBox(width: 16.w), Expanded( child: CustomButton( backgroundColor: AppColors.primaryRedColor, diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index a21e848..4dbc659 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -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 { appState = getIt.get(); scheduleMicrotask(() { if (appState.isAuthenticated) { + labViewModel.initLabProvider(); insuranceViewModel.initInsuranceProvider(); medicalFileViewModel.setIsPatientSickLeaveListLoading(true); medicalFileViewModel.getPatientSickLeaveList(); @@ -149,20 +150,6 @@ class _MedicalFilePageState extends State { 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 { ), 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(builder: (context, insuranceVM, child) { return AppCustomChipWidget( @@ -237,8 +224,7 @@ class _MedicalFilePageState extends State { 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 { ), ).paddingSymmetrical(24.w, 0.0), SizedBox(height: 16.h), - Consumer(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), - ], - ); - }), + 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: [ + SizedBox(height: 10.h), + getSelectedTabData(0), + ], + ), + 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( + // 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: [Text('Child 1')], + // ), + // ExpansionTile( + // title: "Medical Reports".toText20(weight: FontWeight.w600, color: AppColors.textColor), + // children: [Text('Child 2')], + // ), + // ExpansionTile( + // title: "Insurance & Payments".toText20(weight: FontWeight.w600, color: AppColors.textColor), + // children: [Text('Child 3')], + // ), + // ExpansionTile( + // title: "Tracker & Others".toText20(weight: FontWeight.w600, color: AppColors.textColor), + // children: [Text('Child 4')], + // ), + // ], + // ), + // Consumer(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 { 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 { ], ), ], - ).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 { ? 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 { : 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 { 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 { 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 { ); }), ), - 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 { }), ), ], - ).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(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 { 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 { 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 { }, 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 { 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 { ); }), ], - ).paddingSymmetrical(24.w, 0.h), + ).paddingSymmetrical(0.w, 0.h), SizedBox(height: 16.h), Consumer(builder: (context, myAppointmentsVM, child) { return myAppointmentsVM.isPatientMyDoctorsLoading @@ -674,10 +708,7 @@ class _MedicalFilePageState extends State { 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 { 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 { 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 { }, 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 { ); }), ], - ).paddingSymmetrical(24.w, 0.0), + ).paddingSymmetrical(0.w, 0.0), SizedBox(height: 24.h), ], ); @@ -840,7 +869,7 @@ class _MedicalFilePageState extends State { 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 { 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 { 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 { 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 { ? 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 { 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 { ); }), ], - ).paddingSymmetrical(24.w, 0.0), + ).paddingSymmetrical(0.w, 0.0), SizedBox(height: 24.h), ], ); diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart index 696b332..eef73eb 100644 --- a/lib/presentation/medical_file/widgets/lab_rad_card.dart +++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart @@ -27,20 +27,27 @@ class LabRadCard extends StatelessWidget { Widget build(BuildContext context) { AppState appState = getIt.get(); 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), ); } } diff --git a/lib/presentation/my_family/my_family.dart b/lib/presentation/my_family/my_family.dart index 5020b82..781624a 100644 --- a/lib/presentation/my_family/my_family.dart +++ b/lib/presentation/my_family/my_family.dart @@ -46,6 +46,9 @@ class _FamilyMedicalScreenState extends State { void initState() { super.initState(); medicalVM = context.read(); + WidgetsBinding.instance.addPostFrameCallback((_) { + medicalVM?.onFamilyFileTabChange(0); + }); } @override @@ -65,6 +68,7 @@ class _FamilyMedicalScreenState extends State { text: "Add a new family member".needTranslation, onPressed: () { DialogService dialogService = getIt.get(); + 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,13 +87,17 @@ class _FamilyMedicalScreenState extends State { children: [ appState.isChildLoggedIn ? SizedBox() - : CustomTabBar( - activeBackgroundColor: AppColors.secondaryLightRedColor, - activeTextColor: AppColors.primaryRedColor, - tabs: [CustomTabBarModel(null, LocaleKeys.family.tr()), CustomTabBarModel(null, LocaleKeys.request.tr())], - onTabChange: (index) { - medicalVM!.onFamilyFileTabChange(index); - }, + : Selector( + 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(selector: (_, model) => model.getSelectedFamilyFileTabIndex, builder: (context, selectedIndex, child) => getFamilyTabs(index: selectedIndex)), @@ -117,7 +125,7 @@ class _FamilyMedicalScreenState extends State { case 1: return FamilyCards( profiles: medicalVM!.pendingFamilyFiles, - isRequestDesign: true, + isRequestDesign: medicalVM!.getSelectedFamilyFileTabIndex == 1, onSelect: (FamilyFileResponseModelLists profile) { medicalVM!.acceptRejectFileFromFamilyMembers(id: profile.id, status: 3); }, diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index b91e093..497a009 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -129,13 +129,21 @@ class DialogServiceImp implements DialogService { if (context == null) return; showCommonBottomSheetWithoutHeight(context, title: label ?? "", - child: FamilyCards( - profiles: profiles, - onSelect: (FamilyFileResponseModelLists profile) { - onSwitchPress(profile); - }, - onRemove: (FamilyFileResponseModelLists profile) {}, - isShowDetails: false, + 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); + }, + onRemove: (FamilyFileResponseModelLists profile) {}, + isShowDetails: false, + ), + ], ), callBackFunc: () {}); } diff --git a/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart b/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart index 73a4a47..b18bd58 100644 --- a/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart +++ b/lib/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart @@ -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(), diff --git a/lib/widgets/expandable_list_widget.dart b/lib/widgets/expandable_list_widget.dart new file mode 100644 index 0000000..b948299 --- /dev/null +++ b/lib/widgets/expandable_list_widget.dart @@ -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 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 { + late List _expandedStates; + + @override + void initState() { + super.initState(); + _expandedStates = List.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 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 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 +} + + + diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index 4b3f091..c1a38ab 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -47,38 +47,40 @@ class TextInputWidget extends StatelessWidget { final bool isMultiline; final int minLines; final int maxLines; + final Color? hintColor; // final List countryList; // final Function(Country)? onCountryChange; - TextInputWidget( - {super.key, - required this.labelText, - required this.hintText, - this.controller, - this.onChange, - this.onCalendarTypeChanged, - this.prefix, - this.isEnable = true, - this.isBorderAllowed = true, - this.isAllowRadius = true, - this.isReadOnly = false, - this.keyboardType = TextInputType.number, - this.focusNode, - this.autoFocus = false, - this.padding, - this.isAllowLeadingIcon = false, - this.leadingIcon, - this.isCountryDropDown = false, - this.hasError = false, - this.errorMessage, - this.onCountryChange, - this.selectionType, - this.fontSize, - this.isWalletAmountInput = false, - this.suffix, - this.labelColor, + TextInputWidget({ + super.key, + required this.labelText, + required this.hintText, + this.controller, + this.onChange, + this.onCalendarTypeChanged, + this.prefix, + this.isEnable = true, + this.isBorderAllowed = true, + this.isAllowRadius = true, + this.isReadOnly = false, + this.keyboardType = TextInputType.number, + this.focusNode, + this.autoFocus = false, + this.padding, + this.isAllowLeadingIcon = false, + this.leadingIcon, + this.isCountryDropDown = false, + this.hasError = false, + this.errorMessage, + this.onCountryChange, + this.selectionType, + this.fontSize, + this.isWalletAmountInput = false, + 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, diff --git a/lib/widgets/radio/custom_radio_button.dart b/lib/widgets/radio/custom_radio_button.dart index 080fcb3..a530102 100644 --- a/lib/widgets/radio/custom_radio_button.dart +++ b/lib/widgets/radio/custom_radio_button.dart @@ -11,14 +11,13 @@ class CustomRadioOption extends StatelessWidget { // final Widget child; // The content of your radio option (e.g., Text, Image) - const CustomRadioOption({ - super.key, - required this.value, - required this.groupValue, - required this.onChanged, - // required this.child, - required this.text, - }); + const CustomRadioOption( + {super.key, + required this.value, + required this.groupValue, + required this.onChanged, + // required this.child, + required this.text}); @override Widget build(BuildContext context) { @@ -31,13 +30,12 @@ class CustomRadioOption 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), - ), + shape: BoxShape.circle, + color: isSelected ? AppColors.primaryRedColor : AppColors.whiteColor, + border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h)), ), SizedBox(width: 8.h), text.toText16(weight: FontWeight.w500), // The provided content