diff --git a/README.md b/README.md index 08ae79f..1a459e3 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,165 @@ -# hmg_patient_app_new +# HMG Patient App -New HMG Patient App +A comprehensive Flutter-based mobile application for HMG (Habib Medical Group) patients, +providing seamless healthcare services and patient management features. -## Getting Started +## 📱 Features -This project is a starting point for a Flutter application. +- **Patient Registration & Authentication**: Secure login and registration system +- **Appointment Management**: Book, reschedule, and manage medical appointments +- **Medical Records**: Access to personal health records and medical history +- **Doctor Consultation**: Video consultations and chat with healthcare providers +- **Health Monitoring**: Track vital signs and health metrics +- **Prescription Management**: View and manage prescriptions +- **Payment Integration**: Secure payment processing for medical services +- **Multi-language Support**: Available in English and Arabic +- **Push Notifications**: Real-time updates for appointments and health reminders +- **Calendar Integration**: Sync appointments with device calendar +- **Location Services**: Find nearby hospitals and clinics -A few resources to get you started if this is your first Flutter project: +## 🚀 Getting Started -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +### Prerequisites -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +- Flutter SDK (>=3.0.0) +- Dart SDK (>=3.0.0) +- Android Studio / VS Code +- Android SDK (for Android development) +- Xcode (for iOS development, macOS only) + +### Installation + +1. **Clone the repository:** + ```bash + git clone + cd HMG_Patient_App_New + ``` + +2. **Install dependencies:** + ```bash + flutter pub get + ``` + +3. **Configure Firebase:** + - Add your `google-services.json` file to `android/app/` + - Add your `GoogleService-Info.plist` file to `ios/Runner/` + +4. **Run the application:** + ```bash + flutter run + ``` + +## 🏗️ Project Structure + +``` +lib/ +├── core/ # Core utilities and constants +├── extensions/ # Dart extensions +├── features/ # Feature-based modules +├── generated/ # Generated files (localization, etc.) +├── presentation/ # UI screens and widgets +├── routes/ # App navigation and routing +├── services/ # Business logic and API services +├── theme/ # App theming and styling +├── widgets/ # Reusable UI components +├── main.dart # App entry point +└── splashPage.dart # Splash screen + +assets/ +├── animations/ # Lottie animations +├── fonts/ # Custom fonts (Poppins, Gess Two) +├── images/ # PNG and SVG images +├── json/ # JSON data files +└── langs/ # Localization files +``` + +## 🛠️ Technologies Used + +- **Framework**: Flutter +- **Language**: Dart +- **State Management**: [Your state management solution] +- **Backend Services**: Firebase +- **Authentication**: Firebase Auth +- **Database**: Cloud Firestore +- **Push Notifications**: Firebase Cloud Messaging +- **Maps**: Google Maps +- **Payment**: Amazon PayFort +- **Video Calling**: Flutter Zoom Video SDK +- **Local Storage**: SQLite, Shared Preferences + +## 📱 Supported Platforms + +- ✅ Android (API level 21+) +- ✅ iOS (iOS 12.0+) +- ✅ Foldable devices support +- ✅ Tablet optimization + +## 🌐 Localization + +The app supports multiple languages: + +- English (en-US) +- Arabic (ar-SA) + +## 🔧 Configuration + +### Environment Setup + +1. **API Keys**: Configure your API keys in the appropriate configuration files +2. **Firebase**: Set up Firebase project and add configuration files +3. **Maps**: Add Google Maps API key +4. **Payment**: Configure PayFort credentials + +### Build Variants + +- **Debug**: Development build with debugging enabled +- **Release**: Production-ready optimized build + +## 📝 Development Guidelines + +### Code Style + +- Follow Dart/Flutter best practices +- Use consistent naming conventions +- Implement proper error handling +- Write comprehensive documentation + +### Testing + +```bash +# Run unit tests +flutter test + +# Run integration tests +flutter test integration_test/ +``` + +### Building for Production + +**Android:** + +```bash +flutter build apk --release +# or +flutter build appbundle --release +``` + +**iOS:** + +```bash +flutter build ios --release +``` + +## 🤝 Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/new-feature`) +3. Commit your changes (`git commit -am 'Add new feature'`) +4. Push to the branch (`git push origin feature/new-feature`) +5. Create a Pull Request + +## 📄 License + +This project is proprietary software developed for HMG Healthcare Group. + +**Built with ❤️ for better healthcare accessibility** diff --git a/android/app/keystores/keystore b/android/app/keystores/keystore deleted file mode 100644 index e69de29..0000000 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index be64c53..99f28f2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -637,7 +637,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = 3A359E86ZF; ENABLE_BITCODE = NO; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 6dab797..dc5e49a 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -1,5 +1,5 @@ -import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:amazon_payfort/amazon_payfort.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; var MAX_SMALL_SCREEN = 660; final OPENTOK_API_KEY = '46209962'; @@ -570,7 +570,8 @@ var GET_CUSTOMER_INFO = "VerifyCustomer"; //Pharmacy -var GET_PHARMACY_CATEGORISE = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0'; +var GET_PHARMACY_CATEGORISE = + 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0'; var GET_OFFERS_CATEGORISE = 'discountcategories'; var GET_OFFERS_PRODUCTS = 'offerproducts/'; var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id='; @@ -722,7 +723,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In //family Files -const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus'; +const FAMILY_FILES = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus'; var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp'; @@ -789,7 +790,6 @@ class ApiConsts { RCBaseUrl = 'https://rc.hmg.com/test/'; break; case AppEnvironmentTypeEnum.preProd: - baseUrl = "https://webservices.hmg.com/"; payFortEnvironment = FortEnvironment.production; applePayMerchantId = "merchant.com.hmgwebservices"; @@ -833,7 +833,7 @@ class ApiConsts { static final String getUserAgreementContent = 'Services/Patients.svc/REST/GetUsageAgreementText'; static final String checkPatientForRegistration = 'Services/Authentication.svc/REST/CheckPatientForRegisteration'; - static final String checkUserStatus= 'Services/NHIC.svc/REST/GetPatientInfo'; + static final String checkUserStatus = 'Services/NHIC.svc/REST/GetPatientInfo'; static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI'; static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo'; diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart index dbe6f2f..fdd0d30 100644 --- a/lib/core/utils/size_utils.dart +++ b/lib/core/utils/size_utils.dart @@ -11,22 +11,109 @@ extension ResponsiveExtension on num { double get _screenHeight => SizeUtils.height; - /// Scale horizontally (width-based) - double get w => (this * _screenWidth) / figmaDesignWidth; - - /// Scale vertically (height-based) - double get h => (this * _screenHeight) / figmaDesignHeight; - - //radius - double get r => (this * _screenWidth) / figmaDesignWidth; + /// Check if device is likely a foldable + bool get _isFoldable { + double aspectRatio = _screenWidth / _screenHeight; + // Foldable devices typically have aspect ratios close to 1:1 when unfolded + return (aspectRatio > 0.9 && aspectRatio < 1.1) && (_screenWidth > 700 || _screenHeight > 700); + } - /// Scale text size + /// Scale text size - enhanced for foldable devices double get f { - double scale = _screenWidth / figmaDesignWidth; - if (scale > 1.6) scale = 1.6; // optional clamp for tablets + double aspectRatio = _screenWidth / _screenHeight; + double scale = (_screenWidth < _screenHeight ? _screenWidth : _screenHeight) / figmaDesignWidth; + + // Enhanced clamping for different device types + double clamp; + if (SizeUtils.deviceType == DeviceType.tablet || _isFoldable) { + // More conservative scaling for tablets and foldables + clamp = (aspectRatio > 1.5 || aspectRatio < 0.67) ? 1.4 : 1.1; + } else { + // Original logic for phones + clamp = (aspectRatio > 1.3 || aspectRatio < 0.77) ? 1.6 : 1.2; + } + + if (scale > clamp) scale = clamp; return this * scale; } + /// Scale horizontally (width-based) - enhanced for foldable devices + double get w { + double baseScale = (this * _screenWidth) / figmaDesignWidth; + + if (_isFoldable) { + // For foldables, use more conservative width scaling + double scale = _screenWidth / figmaDesignWidth; + scale = scale.clamp(0.8, 1.4); + return this * scale; + } + + return baseScale; + } + + /// Scale vertically (height-based) - enhanced for foldable devices + double get h { + double baseScale = (this * _screenHeight) / figmaDesignHeight; + + if (_isFoldable) { + // For foldables, use height-based scaling but with constraints + double scale = (_screenHeight / figmaDesignHeight).clamp(0.8, 1.4); + return this * scale; + } + + return baseScale; + } + + /// Radius - enhanced for foldable devices + double get r { + double baseScale = (this * _screenWidth) / figmaDesignWidth; + + if (_isFoldable) { + // Use the same logic as enhanced width for foldables + double scale = _screenWidth / figmaDesignWidth; + scale = scale.clamp(0.8, 1.4); + return this * scale; + } + + return baseScale; + } + + // New enhanced getters (additional options) + + /// Enhanced font scaling with device-specific adjustments + double get fh { + double baseScale = _screenHeight / figmaDesignHeight; + + if (_isFoldable) { + // Special handling for foldable devices - use more conservative scaling + baseScale = baseScale.clamp(0.8, 1.3); + } else if (SizeUtils.deviceType == DeviceType.tablet) { + // Tablet-specific scaling + baseScale = baseScale.clamp(0.9, 1.5); + } else { + // Phone scaling + baseScale = baseScale.clamp(0.8, 1.8); + } + + return this * baseScale; + } + + /// Adaptive scaling - automatically chooses best scaling method + double get adaptive { + if (_isFoldable) { + return fh; + } else if (SizeUtils.deviceType == DeviceType.tablet) { + return f * 0.9; // Slightly smaller for tablets + } + return f; + } + + /// Minimum size constraint (useful for touch targets) + double get minSize { + double scaled = adaptive; + return scaled < 44 ? 44 : scaled; // Minimum 44pt for accessibility + } + /// Optional: direct accessors for full width/height static double get screenWidth => SizeUtils.width; @@ -91,10 +178,7 @@ class SizeUtils { /// Device's Width static double width = 375; - static void setScreenSize( - BoxConstraints constraints, - Orientation currentOrientation, - ) { + static void setScreenSize(BoxConstraints constraints, Orientation currentOrientation) { boxConstraints = constraints; orientation = currentOrientation; @@ -130,7 +214,9 @@ class SizeUtils { } log("longerSide: $longerSide"); + log("shorterSide: $shorterSide"); log("isTablet: $isTablet"); + log("isFoldable: $isFoldable"); } } @@ -140,3 +226,9 @@ bool get isTablet => SizeUtils.deviceType == DeviceType.tablet; bool get isMobile => SizeUtils.deviceType == DeviceType.mobile; bool get isDesktop => SizeUtils.deviceType == DeviceType.desktop; + +bool get isFoldable { + double aspectRatio = SizeUtils.width / SizeUtils.height; + // Foldable devices typically have aspect ratios close to 1:1 when unfolded + return (aspectRatio > 0.9 && aspectRatio < 1.1) && (SizeUtils.width > 700 || SizeUtils.height > 700); +} diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 1fc3806..491aa49 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -633,17 +633,19 @@ class Utils { required String url, required Color iconColor, bool isDisabled = false, - double width = 24, - double height = 24, + double? width, + double? height, }) { + final iconH = height ?? 24.h; + final iconW = width ?? 24.w; return SvgPicture.network( url, colorFilter: ColorFilter.mode( isDisabled ? iconColor.withOpacity(0.5) : iconColor, BlendMode.srcIn, ), - width: width, - height: height, + width: iconW, + height: iconH, ); } @@ -662,7 +664,7 @@ class Utils { return Container( decoration: BoxDecoration( border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, - borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 0) : null, + borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, ), child: Image.asset(icon, width: iconW, height: iconH, fit: fit), ); @@ -670,17 +672,27 @@ class Utils { } /// Widget to build an SVG from network - static Widget buildImgWithNetwork({required String url, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover, ImageErrorWidgetBuilder? errorBuilder}) { + static Widget buildImgWithNetwork({ + required String url, + required Color iconColor, + bool isDisabled = false, + double? width, + double? height, + BoxFit fit = BoxFit.cover, + ImageErrorWidgetBuilder? errorBuilder, + }) { + final iconH = height ?? 24.h; + final iconW = width ?? 24.w; return Image.network( url, - width: width, - height: height, + width: iconW, + height: iconH, fit: fit, - errorBuilder: errorBuilder??(_,__,___){ - //todo change the error builder icon that it is returning - return Utils.buildSvgWithAssets(width: width, - height: height,icon: AppAssets.no_visit_icon); - }, + errorBuilder: errorBuilder ?? + (_, __, ___) { + //todo change the error builder icon that it is returning + return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon); + }, ); } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 5c2997d..041e8b7 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -167,16 +167,17 @@ extension EmailValidator on String { decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText14( - {Color? color, - bool isUnderLine = false, - bool isBold = false, - bool isCenter = false, - FontWeight? weight, - int? maxlines, - double? letterSpacing = 0, - double? height, - TextOverflow? textOverflow}) => + Widget toText14({ + Color? color, + bool isUnderLine = false, + bool isBold = false, + bool isCenter = false, + FontWeight? weight, + int? maxlines, + double? letterSpacing = 0, + double? height, + TextOverflow? textOverflow, + }) => Text( this, textAlign: isCenter ? TextAlign.center : null, diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index f5b5b7e..bac44dd 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -37,9 +37,9 @@ import '../../core/dependencies.dart'; import '../medical_file/widgets/medical_file_card.dart'; class AppointmentDetailsPage extends StatefulWidget { - AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel}); + const AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel}); - PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; + final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; @override State createState() => _AppointmentDetailsPageState(); @@ -115,7 +115,7 @@ class _AppointmentDetailsPageState extends State { Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, - borderRadius: 20.h, + borderRadius: 20.r, hasShadow: false, ), child: Padding( @@ -138,7 +138,7 @@ class _AppointmentDetailsPageState extends State { ? Row( children: [ Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), + SizedBox(width: 12.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -155,7 +155,8 @@ class _AppointmentDetailsPageState extends State { children: [ ClipRRect( clipBehavior: Clip.hardEdge, - borderRadius: BorderRadius.circular(24), + borderRadius: BorderRadius.circular(24.r), + // Todo: what is this???? Api Key??? 😲 child: Image.network( "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", fit: BoxFit.contain, @@ -173,19 +174,19 @@ class _AppointmentDetailsPageState extends State { double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), widget.patientAppointmentHistoryResponseModel.projectName); }, - backgroundColor: AppColors.textColor.withOpacity(0.8), - borderColor: - AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) - .withOpacity(0.01), + backgroundColor: AppColors.textColor.withValues(alpha: 0.8), + borderColor: AppointmentType.getNextActionButtonColor( + widget.patientAppointmentHistoryResponseModel.nextAction) + .withValues(alpha: 0.01), textColor: AppColors.whiteColor, fontSize: 14.f, fontWeight: FontWeight.w500, borderRadius: 12.r, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, icon: AppAssets.directions_icon, iconColor: AppColors.whiteColor, - iconSize: 13.h, + iconSize: 14.h, ).paddingAll(12.h), ), ), @@ -199,7 +200,7 @@ class _AppointmentDetailsPageState extends State { Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, - borderRadius: 20.h, + borderRadius: 20.r, hasShadow: false, ), child: Row( @@ -211,12 +212,14 @@ class _AppointmentDetailsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), - "Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500), + "Notify me before the appointment" + .needTranslation + .toText11(color: AppColors.textColorLight, weight: FontWeight.w500), ], ), const Spacer(), Switch( - activeColor: AppColors.successColor, + activeThumbColor: AppColors.successColor, activeTrackColor: AppColors.successColor.withValues(alpha: .15), value: widget.patientAppointmentHistoryResponseModel.hasReminder!, onChanged: (newValue) { @@ -237,8 +240,12 @@ class _AppointmentDetailsPageState extends State { "Lab & Radiology".needTranslation.toText18(isBold: true), SizedBox(height: 16.h), GridView( + padding: EdgeInsets.zero, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, crossAxisSpacing: 13.h, mainAxisSpacing: 13.h, childAspectRatio: 7 / 6), + crossAxisCount: isTablet || isFoldable ? 3 : 2, + crossAxisSpacing: 13.w, + mainAxisSpacing: 13.w, + ), physics: NeverScrollableScrollPhysics(), shrinkWrap: true, children: [ @@ -247,19 +254,36 @@ class _AppointmentDetailsPageState extends State { textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.lab_result_icon, - iconSize: 40, + iconSize: 40.w, + isLargeText: true, + ), + MedicalFileCard( + label: "Radiology Results".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.radiology_icon, + iconSize: 40.w, + isLargeText: true, + ), + MedicalFileCard( + label: LocaleKeys.labResults.tr(context: context), + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.lab_result_icon, + iconSize: 40.w, isLargeText: true, ), MedicalFileCard( - label: LocaleKeys.radiology.tr(context: context), + label: "Radiology Results".needTranslation, textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, svgIcon: AppAssets.radiology_icon, - iconSize: 40, + iconSize: 40.w, isLargeText: true, ), ], ), + SizedBox(height: 16.h), LocaleKeys.prescriptions.tr().toText18(isBold: true), SizedBox(height: 16.h), Consumer(builder: (context, prescriptionVM, child) { @@ -268,153 +292,178 @@ class _AppointmentDetailsPageState extends State { : Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: Colors.white, - borderRadius: 20.h, + borderRadius: 20.r, ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - ListView.separated( - itemCount: prescriptionVM.prescriptionDetailsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - physics: NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: Row( - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.prescription_item_icon, - width: 40.h, - height: 40.h, - ), - SizedBox(width: 8.h), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Column( - children: [ - SizedBox( - width: 150.h, - child: prescriptionVM.prescriptionDetailsList[index].itemDescription! - .toText12(isBold: true, maxLine: 1)), - SizedBox( - width: 150.h, - child: - "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}" - .needTranslation - .toText10( - weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4), - ), - ], - ), - SizedBox(width: 68.h), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.blackColor, - width: 18.h, - height: 13.h, - fit: BoxFit.contain, - ), + padding: EdgeInsets.all(16.w), + child: Column( + children: [ + ListView.separated( + itemCount: prescriptionVM.prescriptionDetailsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(right: 8.w), + physics: NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: Row( + children: [ + Utils.buildSvgWithAssets( + icon: AppAssets.prescription_item_icon, + width: 40.h, + height: 40.h, + ), + SizedBox(width: 8.h), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Column( + children: [ + prescriptionVM.prescriptionDetailsList[index].itemDescription! + .toText12(isBold: true, maxLine: 1), + "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}" + .needTranslation + .toText10( + weight: FontWeight.w500, + color: AppColors.greyTextColor, + letterSpacing: -0.4), + ], + ), + SizedBox(width: 68.w), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.blackColor, + width: 18.w, + height: 13.h, + fit: BoxFit.contain, ), - ], - ), - ], - ), + ), + ], + ), + ], ), ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), - ).onPress(() { - prescriptionVM.setPrescriptionsDetailsLoading(); - Navigator.of(context).push( - CustomPageRoute( - page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()), ), ); - }), - SizedBox(height: 16.h), - const Divider(color: AppColors.dividerColor), - SizedBox(height: 16.h), - Row( - children: [ - // Expanded( - // child: CustomButton( - // text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context), - // onPressed: () {}, - // backgroundColor: AppColors.secondaryLightRedColor, - // borderColor: AppColors.secondaryLightRedColor, - // textColor: AppColors.primaryRedColor, - // fontSize: 14, - // fontWeight: FontWeight.w500, - // borderRadius: 12.h, - // height: 40.h, - // icon: AppAssets.appointment_calendar_icon, - // iconColor: AppColors.primaryRedColor, - // iconSize: 16.h, - // ), - // ), - // SizedBox(width: 16.h), - Expanded( - child: CustomButton( - text: "All Prescriptions".needTranslation, - onPressed: () { - Navigator.of(context) - .push( - CustomPageRoute( - page: PrescriptionsListPage(), - ), - ) - .then((val) { - prescriptionsViewModel.setPrescriptionsDetailsLoading(); - prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel()); - }); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12.h, - height: 40.h, - icon: AppAssets.requests, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ), + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), + ).onPress(() { + prescriptionVM.setPrescriptionsDetailsLoading(); + Navigator.of(context).push( + CustomPageRoute( + page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()), + ), + ); + }), + SizedBox(height: 16.h), + const Divider(color: AppColors.dividerColor), + SizedBox(height: 16.h), + Wrap( + runSpacing: 6.w, + children: [ + // Expanded( + // child: CustomButton( + // text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context), + // onPressed: () {}, + // backgroundColor: AppColors.secondaryLightRedColor, + // borderColor: AppColors.secondaryLightRedColor, + // textColor: AppColors.primaryRedColor, + // fontSize: 14, + // fontWeight: FontWeight.w500, + // borderRadius: 12.h, + // height: 40.h, + // icon: AppAssets.appointment_calendar_icon, + // iconColor: AppColors.primaryRedColor, + // iconSize: 16.h, + // ), + // ), + // SizedBox(width: 16.h), + Expanded( + child: CustomButton( + text: "Refill & Delivery".needTranslation, + onPressed: () { + Navigator.of(context) + .push( + CustomPageRoute( + page: PrescriptionsListPage(), + ), + ) + .then((val) { + prescriptionsViewModel.setPrescriptionsDetailsLoading(); + prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel()); + }); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + height: 40.h, + icon: AppAssets.requests, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, ), - ], - ), - ], - ), + ), + + SizedBox(width: 16.w), + Expanded( + child: CustomButton( + text: "All Prescriptions".needTranslation, + onPressed: () { + Navigator.of(context) + .push( + CustomPageRoute( + page: PrescriptionsListPage(), + ), + ) + .then((val) { + prescriptionsViewModel.setPrescriptionsDetailsLoading(); + prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel()); + }); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + height: 40.h, + icon: AppAssets.requests, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ), + ), + ], + ), + ], ), ); }), ], ), ], - ).paddingSymmetrical(24.h, 24.h), + ).paddingAll(24.w), ), ), ), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, - borderRadius: 24.h, + borderRadius: 24.r, hasShadow: true, ), child: SizedBox( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20) + if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || + widget.patientAppointmentHistoryResponseModel.nextAction == 20) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -423,7 +472,9 @@ class _AppointmentDetailsPageState extends State { children: [ "Amount before tax".needTranslation.toText18(isBold: true), Utils.getPaymentAmountWithSymbol( - widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, + widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), + AppColors.blackColor, + 13, isSaudiCurrency: true), ], ), @@ -432,8 +483,9 @@ class _AppointmentDetailsPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: - LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), + child: LocaleKeys.upcomingPaymentNow + .tr(context: context) + .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})" .needTranslation .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -2), @@ -470,10 +522,10 @@ class _AppointmentDetailsPageState extends State { backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, - fontSize: 16, + fontSize: 16.f, fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), height: 50.h, icon: AppAssets.add_icon, iconColor: AppColors.whiteColor, @@ -486,13 +538,14 @@ class _AppointmentDetailsPageState extends State { handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); }, backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), + borderColor: + AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : AppColors.whiteColor, - fontSize: 16, + fontSize: 16.f, fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 50.h, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 56.h, icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), iconColor: AppColors.whiteColor, iconSize: 18.h, diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index 8d9adec..7481bf2 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -16,18 +16,16 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/AppointmentFilter.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; -import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; +import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; -import '../../widgets/common_bottom_sheet.dart' - show showCommonBottomSheetWithoutHeight; +import '../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; class MyAppointmentsPage extends StatefulWidget { const MyAppointmentsPage({super.key}); @@ -86,18 +84,14 @@ class _MyAppointmentsPageState extends State { } Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) { - return getAppointList( - myAppointmentsVM, myAppointmentsVM.filteredAppointmentList); + return getAppointList(myAppointmentsVM, myAppointmentsVM.filteredAppointmentList); } - Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, - List filteredAppointmentList) { + Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, List filteredAppointmentList) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Visibility( - visible: myAppointmentsVM.availableFilters.isNotEmpty, - child: getAppointmentFilters(myAppointmentsVM)), + Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)), ListView.separated( padding: EdgeInsets.only(top: 24.h), shrinkWrap: true, @@ -110,14 +104,9 @@ class _MyAppointmentsPageState extends State { itemBuilder: (context, index) { return myAppointmentsVM.isMyAppointmentsLoading ? Container( - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: AppointmentCard( - patientAppointmentHistoryResponseModel: - PatientAppointmentHistoryResponseModel(), + patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), myAppointmentsViewModel: myAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: true, @@ -134,16 +123,10 @@ class _MyAppointmentsPageState extends State { child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: AppointmentCard( - patientAppointmentHistoryResponseModel: - filteredAppointmentList[index], - myAppointmentsViewModel: - myAppointmentsViewModel, + patientAppointmentHistoryResponseModel: filteredAppointmentList[index], + myAppointmentsViewModel: myAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: false, @@ -154,8 +137,7 @@ class _MyAppointmentsPageState extends State { ) : Utils.getNoDataWidget( context, - noDataText: "You don't have any appointments yet." - .needTranslation, + noDataText: "You don't have any appointments yet.".needTranslation, callToActionButton: CustomButton( text: LocaleKeys.bookAppo.tr(context: context), onPressed: () { @@ -168,18 +150,17 @@ class _MyAppointmentsPageState extends State { backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), + height: 40.h, icon: AppAssets.add_icon, iconColor: AppColors.primaryRedColor, ).paddingSymmetrical(48.h, 0.h), ); }, - separatorBuilder: (BuildContext cxt, int index) => - SizedBox(height: 16.h), + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), SizedBox(height: 24.h), ], @@ -189,45 +170,41 @@ class _MyAppointmentsPageState extends State { Widget getAppointmentFilters(MyAppointmentsViewModel myAppointmentsVM) { return SizedBox( child: Row( - children: [ - Expanded( - child: ListView.separated( - separatorBuilder: (_, index) => SizedBox( - width: 8.h, - ), - scrollDirection: Axis.horizontal, - itemCount: myAppointmentsVM.availableFilters.length, - itemBuilder: (_, index) => AppointmentFilters( - selectedFilter: myAppointmentsVM.selectedFilter, - item: myAppointmentsVM.availableFilters[index], - onClicked: () { - if (myAppointmentsVM.availableFilters[index] == - AppointmentListingFilters.DATESELECTION) { - showCommonBottomSheetWithoutHeight( - title: "Set The Date Range".needTranslation, - context, - child: DateRangeSelector( - onRangeSelected: (start, end) { - // if (start != null) { - myAppointmentsVM.getSelectedDateRange( - start, end); - // } - }, - ), - isFullScreen: false, - isCloseButtonVisible: true, - callBackFunc: () {}, - ); - } else { - myAppointmentsVM.setSelectedFilter( - myAppointmentsVM.availableFilters[index]); - myAppointmentsVM.filterTheListAsPerSelection(); - } - }, - )), - ), - ], - )).paddingOnly(top: 24.h, left: 24.h, right: 24.h); + children: [ + Expanded( + child: ListView.separated( + separatorBuilder: (_, index) => SizedBox( + width: 8.h, + ), + scrollDirection: Axis.horizontal, + itemCount: myAppointmentsVM.availableFilters.length, + itemBuilder: (_, index) => AppointmentFilters( + selectedFilter: myAppointmentsVM.selectedFilter, + item: myAppointmentsVM.availableFilters[index], + onClicked: () { + if (myAppointmentsVM.availableFilters[index] == AppointmentListingFilters.DATESELECTION) { + showCommonBottomSheetWithoutHeight( + title: "Set The Date Range".needTranslation, + context, + child: DateRangeSelector( + onRangeSelected: (start, end) { + // if (start != null) { + myAppointmentsVM.getSelectedDateRange(start, end); + // } + }, + ), + isFullScreen: false, + isCloseButtonVisible: true, + callBackFunc: () {}, + ); + } else { + myAppointmentsVM.setSelectedFilter(myAppointmentsVM.availableFilters[index]); + myAppointmentsVM.filterTheListAsPerSelection(); + } + }, + )), + ), + ], + )).paddingOnly(top: 24.h, left: 24.h, right: 24.h); } } - diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 1451bcb..627d527 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -23,339 +23,312 @@ 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/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; -import 'package:smooth_corner/smooth_corner.dart'; -class AppointmentCard extends StatefulWidget { - AppointmentCard( - {super.key, - required this.patientAppointmentHistoryResponseModel, - required this.myAppointmentsViewModel, - required this.bookAppointmentsViewModel, - this.isLoading = false, - this.isFromHomePage = false, - this.isFromMedicalReport = false, - this.medicalFileViewModel}); +class AppointmentCard extends StatelessWidget { + final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; + final MyAppointmentsViewModel myAppointmentsViewModel; + final bool isLoading; + final bool isFromHomePage; + final bool isFromMedicalReport; + final MedicalFileViewModel? medicalFileViewModel; + final BookAppointmentsViewModel bookAppointmentsViewModel; - PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; - MyAppointmentsViewModel myAppointmentsViewModel; - bool isLoading; - bool isFromHomePage; - bool isFromMedicalReport; - MedicalFileViewModel? medicalFileViewModel; - BookAppointmentsViewModel bookAppointmentsViewModel; + const AppointmentCard({ + super.key, + required this.patientAppointmentHistoryResponseModel, + required this.myAppointmentsViewModel, + required this.bookAppointmentsViewModel, + this.isLoading = false, + this.isFromHomePage = false, + this.isFromMedicalReport = false, + this.medicalFileViewModel, + }); - @override - State createState() => _AppointmentCardState(); -} - -class _AppointmentCardState extends State { @override Widget build(BuildContext context) { - AppState appState = getIt.get(); + final appState = getIt.get(); return InkWell( - onTap: () { - Navigator.of(context) - .push( - CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - ), - ) - .then((val) { - widget.myAppointmentsViewModel.initAppointmentsViewModel(); - widget.myAppointmentsViewModel.getPatientAppointments(true, false); - }); - }, + onTap: () => _goToDetails(context), child: Padding( padding: EdgeInsets.all(14.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Wrap( - alignment: WrapAlignment.start, - direction: Axis.horizontal, - spacing: 6.h, - runSpacing: 6.h, - children: [ - AppCustomChipWidget( - icon: widget.isLoading - ? AppAssets.walkin_appointment_icon - : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon), - iconColor: widget.isLoading - ? AppColors.textColor - : !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! - ? AppColors.textColor - : AppColors.whiteColor, - labelText: widget.isLoading - ? "Walk In" - : widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! - ? LocaleKeys.livecare.tr(context: context) - : "Walk In".needTranslation, - backgroundColor: - widget.isLoading ? AppColors.greyColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor), - textColor: widget.isLoading ? AppColors.textColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor), - ).toShimmer2(isShow: widget.isLoading), - AppCustomChipWidget( - labelText: widget.isLoading - ? "OutPatient" - : appState.isArabic() - ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! - : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!, - backgroundColor: AppColors.primaryRedColor.withOpacity(0.1), - textColor: AppColors.primaryRedColor, - ).toShimmer2(isShow: widget.isLoading), - AppCustomChipWidget( - labelText: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!), - backgroundColor: AppColors.successColor.withOpacity(0.1), - textColor: AppColors.successColor, - ).toShimmer2(isShow: widget.isLoading), - ], - ).toShimmer2(isShow: widget.isLoading), - ), - // TODO: Implement the logic to enable/disable the switch based on reminder status - // AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - // ? SizedBox().toShimmer2(isShow: widget.isLoading) - // : Switch( - // activeColor: AppColors.successColor, - // activeTrackColor: AppColors.successColor.withValues(alpha: .15), - // thumbIcon: WidgetStateProperty.resolveWith( - // (Set states) { - // if (states.contains(WidgetState.selected)) { - // return const Icon(Icons.check); // Icon when switch is ON - // } - // return const Icon(Icons.close); // Icon when switch is OFF - // }, - // ), - // value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!, - // onChanged: (newValue) { - // setState(() { - // widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel); - // }); - // }, - // ).toShimmer2(isShow: widget.isLoading), - ], - ), + _buildHeader(context, appState), SizedBox(height: 16.h), - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.network( - widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, - width: 63.h, - height: 63.h, - fit: BoxFit.cover, - ).circle(100).toShimmer2(isShow: widget.isLoading), - SizedBox(width: 16.h), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - (widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorNameObj!) - .toText16(isBold: true, maxlines: 1) - .toShimmer2(isShow: widget.isLoading), - SizedBox(height: 8.h), - Wrap( - direction: Axis.horizontal, - spacing: 3.h, - runSpacing: 4.h, - children: [ - widget.isFromHomePage - ? SizedBox.shrink() - : AppCustomChipWidget(labelText: widget.isLoading ? "Cardiology" : widget.patientAppointmentHistoryResponseModel.clinicName!).toShimmer2(isShow: widget.isLoading), - widget.isFromHomePage - ? SizedBox.shrink() - : AppCustomChipWidget(labelText: widget.isLoading ? "Olaya" : widget.patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: widget.isLoading), - AppCustomChipWidget( - icon: AppAssets.appointment_calendar_icon, - labelText: - widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)) - .toShimmer2(isShow: widget.isLoading), - widget.isFromMedicalReport - ? SizedBox.shrink() - : AppCustomChipWidget( - icon: AppAssets.appointment_time_icon, - labelText: widget.isLoading - ? "Cardiology" - : DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)) - .toShimmer2(isShow: widget.isLoading), - ], - ), - ], - ), - ), - ], - ), + _buildDoctorRow(context), SizedBox(height: 16.h), - widget.isFromMedicalReport - ? CustomButton( - text: "Select appointment".needTranslation, - onPressed: () { - widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel); - Navigator.pop(context, false); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.checkmark_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ) - : Row( - children: [ - Expanded( - flex: 6, - child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) - ? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading) - : CustomButton( - text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), - onPressed: () { - Navigator.of(context) - .push(CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - )) - .then((val) { - widget.myAppointmentsViewModel.initAppointmentsViewModel(); - widget.myAppointmentsViewModel.getPatientAppointments(true, false); - }); - }, - backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15), - borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), - textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), - iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction), - iconSize: 15.h, - ).toShimmer2(isShow: widget.isLoading), - ), - SizedBox(width: 8.h), - Expanded( - flex: 1, - child: Container( - height: 40.h, - width: 40.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.textColor, - borderRadius: 10.h, - ), - child: Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon, - iconColor: AppColors.whiteColor, - width: 40.h, - height: 40.h, - fit: BoxFit.cover, - ), - ), - ).toShimmer2(isShow: widget.isLoading).onPress(() { - Navigator.of(context) - .push( - CustomPageRoute( - page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), - ), - ) - .then((val) { - widget.myAppointmentsViewModel.initAppointmentsViewModel(); - widget.myAppointmentsViewModel.getPatientAppointments(true, false); - }); - }), - ), - ], - ), + _buildActionArea(context, appState), ], ), ), ); } - Widget getArrivedAppointmentButton() { - return DateTime.now().difference(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15 - ? CustomButton( - text: LocaleKeys.askDoctor.tr(context: context), - onPressed: () {}, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.ask_doctor_icon, - iconColor: AppColors.primaryRedColor, - iconSize: 16.h, - ) - : CustomButton( - text: "Rebook with same doctor".needTranslation, - onPressed: () { - openDoctorScheduleCalendar(); - }, - backgroundColor: AppColors.greyColor, - borderColor: AppColors.greyColor, - textColor: AppColors.blackColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - icon: AppAssets.rebook_appointment_icon, - iconColor: AppColors.blackColor, - iconSize: 16.h, - ); + Widget _buildHeader(BuildContext context, AppState appState) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded(child: _buildChips(context, appState)), + ], + ); } - openDoctorScheduleCalendar() async { - DoctorsListResponseModel doctor = DoctorsListResponseModel( - clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, - projectID: widget.patientAppointmentHistoryResponseModel.projectID, - doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, - doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, - doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, - name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", - speciality: [], - clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, - projectName: widget.patientAppointmentHistoryResponseModel.projectName, + Widget _buildChips(BuildContext context, AppState appState) { + final isLiveCare = !isLoading && patientAppointmentHistoryResponseModel.isLiveCareAppointment!; + + return Wrap( + alignment: WrapAlignment.start, + direction: Axis.horizontal, + spacing: 6.w, + runSpacing: 6.h, + children: [ + AppCustomChipWidget( + icon: isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon), + iconColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor), + labelText: isLoading ? 'Walk In'.needTranslation : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : 'Walk In'.needTranslation), + backgroundColor: isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor), + textColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor), + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: isLoading + ? 'OutPatient'.needTranslation + : (appState.isArabic() + ? patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! + : patientAppointmentHistoryResponseModel.isInOutPatientDescription!), + backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1), + textColor: AppColors.primaryRedColor, + ).toShimmer2(isShow: isLoading), + AppCustomChipWidget( + labelText: isLoading + ? 'Booked'.needTranslation + : AppointmentType.getAppointmentStatusType(patientAppointmentHistoryResponseModel.patientStatusType!), + backgroundColor: AppColors.successColor.withValues(alpha: 0.1), + textColor: AppColors.successColor, + ).toShimmer2(isShow: isLoading), + ], + ).toShimmer2(isShow: isLoading); + } + + Widget _buildDoctorRow(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + isLoading + ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' + : patientAppointmentHistoryResponseModel.doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.cover, + ).circle(100).toShimmer2(isShow: isLoading), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + (isLoading + ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' + : patientAppointmentHistoryResponseModel.doctorNameObj!) + .toText16(isBold: true, maxlines: 1) + .toShimmer2(isShow: isLoading), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + if (!isFromHomePage) + AppCustomChipWidget(labelText: isLoading ? 'Cardiology' : patientAppointmentHistoryResponseModel.clinicName!) + .toShimmer2(isShow: isLoading), + if (!isFromHomePage) + AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!) + .toShimmer2(isShow: isLoading), + AppCustomChipWidget( + icon: AppAssets.appointment_calendar_icon, + labelText: isLoading + ? 'Cardiology' + : DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false), + ).toShimmer2(isShow: isLoading), + if (!isFromMedicalReport) + AppCustomChipWidget( + icon: AppAssets.appointment_time_icon, + labelText: isLoading + ? 'Cardiology' + : DateUtil.formatDateToTimeLang( + DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false), + ).toShimmer2(isShow: isLoading), + ], + ), + ], + ), + ), + ], ); - widget.bookAppointmentsViewModel.setSelectedDoctor(doctor); - LoaderBottomSheet.showLoader(); - await widget.bookAppointmentsViewModel.getDoctorFreeSlots( - isBookingForLiveCare: false, - onSuccess: (dynamic respData) async { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - title: "Pick a Date".needTranslation, - context, - child: AppointmentCalendar(), - isFullScreen: false, - isCloseButtonVisible: true, - callBackFunc: () {}, - ); + } + + Widget _buildActionArea(BuildContext context, AppState appState) { + if (isFromMedicalReport) { + return CustomButton( + text: 'Select appointment'.needTranslation, + onPressed: () { + medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel); + Navigator.pop(context, false); }, - onError: (err) { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getErrorWidget(loadingText: err), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); - }); + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: isTablet || isFoldable ? 46.h : 40.h, + icon: AppAssets.checkmark_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ); + } + + return Row( + children: [ + Expanded( + flex: 6, + child: (AppointmentType.isArrived(patientAppointmentHistoryResponseModel) + ? _getArrivedButton(context) + : CustomButton( + text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction), + onPressed: () => _goToDetails(context), + backgroundColor: + AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15), + borderColor: + AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), + textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: isTablet || isFoldable ? 46.h : 40.h, + icon: AppointmentType.getNextActionIcon(patientAppointmentHistoryResponseModel.nextAction), + iconColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), + iconSize: 15.h, + )) + .toShimmer2(isShow: isLoading), + ), + SizedBox(width: 8.h), + Expanded( + flex: 1, + child: Container( + height: (isFoldable || isTablet) ? 50.h : 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.textColor, borderRadius: 10.h), + child: Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, + iconColor: AppColors.whiteColor, + width: 24.w, + height: 24.h, + fit: BoxFit.contain, + ), + ), + ).toShimmer2(isShow: isLoading).onPress(() { + _goToDetails(context); + }), + ), + ], + ); + } + + Widget _getArrivedButton(BuildContext context) { + final isRecent = DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15; + + if (isRecent) { + return CustomButton( + text: LocaleKeys.askDoctor.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: isTablet || isFoldable ? 46.h : 40.h, + icon: AppAssets.ask_doctor_icon, + iconColor: AppColors.primaryRedColor, + iconSize: 16.h, + ); + } + + return CustomButton( + text: 'Rebook with same doctor'.needTranslation, + onPressed: () => openDoctorScheduleCalendar(context), + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: isTablet || isFoldable ? 46.h : 40.h, + icon: AppAssets.rebook_appointment_icon, + iconColor: AppColors.blackColor, + iconSize: 16.h, + ); + } + + void _goToDetails(BuildContext context) { + Navigator.of(context) + .push( + CustomPageRoute( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel), + ), + ) + .then((_) { + myAppointmentsViewModel.initAppointmentsViewModel(); + myAppointmentsViewModel.getPatientAppointments(true, false); + }); } - void performAppointmentNextAction(nextAction) {} + void openDoctorScheduleCalendar(BuildContext context) async { + final doctor = DoctorsListResponseModel( + clinicID: patientAppointmentHistoryResponseModel.clinicID, + projectID: patientAppointmentHistoryResponseModel.projectID, + doctorID: patientAppointmentHistoryResponseModel.doctorID, + doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, + doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, + name: patientAppointmentHistoryResponseModel.doctorNameObj, + nationalityFlagURL: 'https://hmgwebservices.com/Images/flag/SYR.png', + speciality: [], + clinicName: patientAppointmentHistoryResponseModel.clinicName, + projectName: patientAppointmentHistoryResponseModel.projectName, + ); + + bookAppointmentsViewModel.setSelectedDoctor(doctor); + LoaderBottomSheet.showLoader(); + + await bookAppointmentsViewModel.getDoctorFreeSlots( + isBookingForLiveCare: false, + onSuccess: (respData) async { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: AppointmentCalendar(), + callBackFunc: () {}, + title: 'Pick a Date'.needTranslation, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }, + ); + } } diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index efc5a6b..2ea75d1 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -13,12 +13,17 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; class AppointmentDoctorCard extends StatelessWidget { - AppointmentDoctorCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.onRescheduleTap, required this.onCancelTap, required this.onAskDoctorTap}); + AppointmentDoctorCard( + {super.key, + required this.patientAppointmentHistoryResponseModel, + required this.onRescheduleTap, + required this.onCancelTap, + required this.onAskDoctorTap}); - PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; - late Function onRescheduleTap; - late Function onCancelTap; - late Function onAskDoctorTap; + final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; + final VoidCallback onRescheduleTap; + final VoidCallback onCancelTap; + final VoidCallback onAskDoctorTap; @override Widget build(BuildContext context) { @@ -38,11 +43,11 @@ class AppointmentDoctorCard extends StatelessWidget { children: [ Image.network( patientAppointmentHistoryResponseModel.doctorImageURL!, - width: 63.h, + width: 63.w, height: 63.h, fit: BoxFit.cover, - ).circle(100), - SizedBox(width: 16.h), + ).circle(100.r), + SizedBox(width: 16.w), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -50,23 +55,41 @@ class AppointmentDoctorCard extends StatelessWidget { patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true), Wrap( direction: Axis.horizontal, - spacing: 3.h, - runSpacing: 4.h, + spacing: 6.w, + runSpacing: 6.h, children: [ - AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.clinicName!), - AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.projectName!), AppCustomChipWidget( - icon: AppAssets.doctor_calendar_icon, - labelText: - "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"), + labelText: patientAppointmentHistoryResponseModel.clinicName!, + labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), + ), + AppCustomChipWidget( + labelText: patientAppointmentHistoryResponseModel.projectName!, + labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), + ), AppCustomChipWidget( - icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon, + icon: AppAssets.doctor_calendar_icon, + labelText: + "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang( + DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), + false, + )}", + ), + AppCustomChipWidget( + icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? AppAssets.walkin_appointment_icon + : AppAssets.small_livecare_icon, iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, - labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation, - backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor, + labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! + ? LocaleKeys.livecare.tr(context: context) + : "Walk In".needTranslation, + backgroundColor: + !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor, textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, ), - AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), + AppCustomChipWidget( + icon: AppAssets.rating_icon, + iconColor: AppColors.ratingColorYellow, + labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), ], ), ], @@ -93,10 +116,10 @@ class AppointmentDoctorCard extends StatelessWidget { backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 16.h, icon: AppAssets.ask_doctor_icon, @@ -110,10 +133,10 @@ class AppointmentDoctorCard extends StatelessWidget { backgroundColor: AppColors.greyColor, borderColor: AppColors.greyColor, textColor: AppColors.blackColor, - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, icon: AppAssets.rebook_appointment_icon, iconColor: AppColors.blackColor, @@ -129,9 +152,9 @@ class AppointmentDoctorCard extends StatelessWidget { backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12.h, + borderRadius: 12.r, height: 40.h, icon: AppAssets.cancel, iconColor: AppColors.whiteColor, @@ -148,9 +171,9 @@ class AppointmentDoctorCard extends StatelessWidget { backgroundColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12.h, + borderRadius: 12.r, height: 40.h, icon: AppAssets.appointment_calendar_icon, iconColor: AppColors.primaryRedColor, @@ -167,9 +190,9 @@ class AppointmentDoctorCard extends StatelessWidget { backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12.h, + borderRadius: 12.r, height: 40.h, icon: AppAssets.cancel, iconColor: AppColors.whiteColor, diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index cfa4437..93b26c7 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -1,6 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; @@ -12,7 +11,6 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; -import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; @@ -21,7 +19,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:provider/provider.dart'; class SavedLogin extends StatefulWidget { - const SavedLogin({Key? key}) : super(key: key); + const SavedLogin({super.key}); @override _SavedLogin createState() => _SavedLogin(); @@ -65,248 +63,255 @@ class _SavedLogin extends State { body: SafeArea( child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.h), - child: appState.getSelectDeviceByImeiRespModelElement != null ? Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Spacer(flex: 2), - // Welcome back text - LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor), - SizedBox(height: 16.h), - appState.getSelectDeviceByImeiRespModelElement != null - ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor) - : SizedBox(), - SizedBox(height: 24.h), - Container( - padding: EdgeInsets.all(16.h), - decoration: BoxDecoration( - color: AppColors.whiteColor, - border: Border.all(color: AppColors.whiteColor), - borderRadius: BorderRadius.circular(24.h), - boxShadow: [ - BoxShadow(color: Color(0x0D000000), blurRadius: 16, offset: Offset(0, 0), spreadRadius: 5), - ], - ), - child: Column( + child: appState.getSelectDeviceByImeiRespModelElement != null + ? Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - // Last login info - - ("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1), + const Spacer(flex: 2), + // Welcome back text + LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor), + SizedBox(height: 16.h), appState.getSelectDeviceByImeiRespModelElement != null - ? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null - ? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm") - : '--') - .toText16(isBold: true, color: AppColors.textColor) + ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase + .toText26(isBold: true, height: 26 / 36, color: AppColors.textColor) : SizedBox(), + SizedBox(height: 24.h), + Container( + padding: EdgeInsets.all(16.h), + decoration: BoxDecoration( + color: AppColors.whiteColor, + border: Border.all(color: AppColors.whiteColor), + borderRadius: BorderRadius.circular(24.h), + boxShadow: [ + BoxShadow(color: Color(0x0D000000), blurRadius: 16.h, offset: Offset(0, 0), spreadRadius: 5.h), + ], + ), + child: Column( + children: [ + // Last login info - appState.getSelectDeviceByImeiRespModelElement != null - ? Container( - margin: EdgeInsets.all(16.h), - child: Utils.buildSvgWithAssets( - icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), - height: 54, - width: 54, - iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)) - : SizedBox(), - // Face ID login button - SizedBox( - height: 45, - child: CustomButton( - text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}", - onPressed: () { - if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { - authVm.loginWithFingerPrintFace(() {}); - } else { - authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp); - } - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: Colors.white, - fontSize: 12, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(0, 10, 0, 10), - icon: getTypeIcons(loginType.toInt), - //loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp, - iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null, + ("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}") + .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1), + appState.getSelectDeviceByImeiRespModelElement != null + ? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null + ? DateUtil.getFormattedDate( + DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), + "d MMMM, y 'at' HH:mm") + : '--') + .toText16(isBold: true, color: AppColors.textColor) + : SizedBox(), + + appState.getSelectDeviceByImeiRespModelElement != null + ? Container( + margin: EdgeInsets.all(16.h), + child: Utils.buildSvgWithAssets( + icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), + height: 54.h, + width: 54.w, + iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)) + : SizedBox(), + // Face ID login button + CustomButton( + text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}", + onPressed: () { + if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { + authVm.loginWithFingerPrintFace(() {}); + } else { + authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp); + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: Colors.white, + fontSize: 12.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + height: 40.h, + padding: EdgeInsets.symmetric(vertical: 10.h), + icon: getTypeIcons(loginType.toInt), + //loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp, + iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null, + ), + ], ), ), - ], - ), - ), - const SizedBox(height: 24), - Padding( - padding: EdgeInsets.symmetric(horizontal: 16.0), - child: Text( - LocaleKeys.oR.tr(), - style: context.dynamicTextStyle(fontSize: 16, fontWeight: FontWeight.w500), - ), - ), - const SizedBox(height: 24), - // OTP login button - loginType.toInt != 1 - ? Column( - children: [ - loginType.toInt != 1 - ? CustomButton( - text: LocaleKeys.loginByOTP.tr(), - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - isDismissible: false, - useSafeArea: true, - backgroundColor: Colors.transparent, - enableDrag: false, - // Prevent dragging to avoid focus conflicts - builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { - return Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), - child: SingleChildScrollView( - child: GenericBottomSheet( - countryCode: "966", - initialPhoneNumber: "", - textController: TextEditingController(), - isFromSavedLogin: true, - isEnableCountryDropdown: true, - onCountryChange: (value) {}, - onChange: (String? value) {}, - buttons: [ - Padding( - padding: EdgeInsets.only(bottom: 10.h), - child: CustomButton( - text: LocaleKeys.sendOTPSMS.tr(), - onPressed: () { - Navigator.of(context).pop(); - loginType = LoginTypeEnum.sms; - authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - icon: AppAssets.sms), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), - ], - ), - Padding( - padding: const EdgeInsets.only(bottom: 10, top: 10), - child: CustomButton( - text: LocaleKeys.sendOTPWHATSAPP.tr(), - onPressed: () { - Navigator.of(context).pop(); - loginType = LoginTypeEnum.whatsapp; - authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); - }, - backgroundColor: AppColors.transparent, - borderColor: AppColors.textColor, - textColor: AppColors.textColor, - icon: AppAssets.whatsapp, - iconColor: null, + SizedBox(height: 24.h), + Padding( + padding: EdgeInsets.symmetric(horizontal: 16.w), + child: Text( + LocaleKeys.oR.tr(), + style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w500), + ), + ), + SizedBox(height: 24.h), + // OTP login button + loginType.toInt != 1 + ? Column( + children: [ + loginType.toInt != 1 + ? CustomButton( + text: LocaleKeys.loginByOTP.tr(), + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + isDismissible: false, + useSafeArea: true, + backgroundColor: Colors.transparent, + enableDrag: false, + // Prevent dragging to avoid focus conflicts + builder: (bottomSheetContext) => + StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), + child: SingleChildScrollView( + child: GenericBottomSheet( + countryCode: "966", + initialPhoneNumber: "", + textController: TextEditingController(), + isFromSavedLogin: true, + isEnableCountryDropdown: true, + onCountryChange: (value) {}, + onChange: (String? value) {}, + buttons: [ + Padding( + padding: EdgeInsets.only(bottom: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPSMS.tr(), + onPressed: () { + Navigator.of(context).pop(); + loginType = LoginTypeEnum.sms; + authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + icon: AppAssets.sms), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: 8.h), + child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), + ], + ), + Padding( + padding: EdgeInsets.only(bottom: 10.h, top: 10.h), + child: CustomButton( + text: LocaleKeys.sendOTPWHATSAPP.tr(), + onPressed: () { + Navigator.of(context).pop(); + loginType = LoginTypeEnum.whatsapp; + authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); + }, + backgroundColor: AppColors.transparent, + borderColor: AppColors.textColor, + textColor: AppColors.textColor, + icon: AppAssets.whatsapp, + iconColor: null, + ), + ), + ], ), ), - ], - ), - ), - ); - }), - ); - }, - backgroundColor: AppColors.whiteColor, - borderColor: AppColors.borderOnlyColor, - textColor: AppColors.textColor, - borderWidth: 2, - padding: EdgeInsets.fromLTRB(0, 14, 0, 14), - icon: AppAssets.sms, - iconColor: AppColors.textColor, - ) - : Container(), + ); + }), + ); + }, + backgroundColor: AppColors.whiteColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.textColor, + borderWidth: 2, + padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h), + icon: AppAssets.sms, + iconColor: AppColors.textColor, + ) + : Container(), + SizedBox( + height: 20.h, + ), + ], + ) + : CustomButton( + text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}", + icon: AppAssets.whatsapp, + iconColor: null, + onPressed: () { + if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { + authVm.loginWithFingerPrintFace(() {}); + } else { + loginType = LoginTypeEnum.whatsapp; + authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); + } + }, + backgroundColor: AppColors.whiteColor, + borderColor: Color(0xFF2E3039), + textColor: Color(0xFF2E3039), + borderWidth: 2.w, + padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h), + ), + const Spacer(flex: 2), + // OR divider + + SizedBox(height: 24.h), + // Guest and Switch account + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: SizedBox( + height: 56.h, + child: CustomButton( + text: LocaleKeys.guest.tr(), + onPressed: () { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 16.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h), + // icon: "assets/images/svg/apple-finder.svg", + ), + ), + ), SizedBox( - height: 20, + width: MediaQuery.of(context).size.width * 0.05, + ), + Expanded( + child: SizedBox( + height: 56.h, + child: CustomButton( + text: LocaleKeys.switchAccount.tr(), + onPressed: () async { + await authVm.clearDefaultInputValues(); + Navigator.of(context).push( + MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 15.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h), + // icon: "assets/images/svg/apple-finder.svg", + ), + ), ), ], - ) - : CustomButton( - text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}", - icon: AppAssets.whatsapp, - iconColor: null, - onPressed: () { - if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { - authVm.loginWithFingerPrintFace(() {}); - } else { - loginType = LoginTypeEnum.whatsapp; - authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); - } - }, - backgroundColor: AppColors.whiteColor, - borderColor: Color(0xFF2E3039), - textColor: Color(0xFF2E3039), - borderWidth: 2, - padding: EdgeInsets.fromLTRB(0, 14, 0, 14), ), - const Spacer(flex: 2), - // OR divider - - const SizedBox(height: 24), - // Guest and Switch account - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: SizedBox( - height: 56, - child: CustomButton( - text: LocaleKeys.guest.tr(), - onPressed: () { - Navigator.of(context).pushReplacement( - MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()), - ); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(0, 10, 0, 10), - // icon: "assets/images/svg/apple-finder.svg", - ), - ), - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.05, - ), - Expanded( - child: SizedBox( - height: 56, - child: CustomButton( - text: LocaleKeys.switchAccount.tr(), - onPressed: () async { - await authVm.clearDefaultInputValues(); - Navigator.of(context).push( - MaterialPageRoute(builder: (BuildContext context) => LoginScreen()), - ); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 15, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(0, 10, 0, 10), - // icon: "assets/images/svg/apple-finder.svg", - ), - ), - ), - ], - ), - const SizedBox(height: 20), - ], - ) : SizedBox.shrink(), + SizedBox(height: 20.h), + ], + ) + : SizedBox.shrink(), ), ), ); diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index b39c51e..02f9f22 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -13,7 +13,6 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; -import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; @@ -21,14 +20,13 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immed import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; import '../appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; @@ -130,7 +128,9 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsClinicsListLoading(true); @@ -163,7 +163,9 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); @@ -194,7 +196,9 @@ class _BookAppointmentPageState extends State { ], ), Transform.flip( - flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ], ).onPress(() { bookAppointmentsViewModel.setProjectID(null); @@ -214,115 +218,121 @@ class _BookAppointmentPageState extends State { Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: false, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), - "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - ], - ), - ], - ), - Transform.flip( - flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), - ], - ).onPress(() async { - //TODO Implement API to check for existing LiveCare Requests + borderRadius: 24.h, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + ], + ).onPress(() async { + //TODO Implement API to check for existing LiveCare Requests - LoaderBottomSheet.showLoader(); - await immediateLiveCareViewModel.getPatientLiveCareHistory(); - LoaderBottomSheet.hideLoader(); + LoaderBottomSheet.showLoader(); + await immediateLiveCareViewModel.getPatientLiveCareHistory(); + LoaderBottomSheet.hideLoader(); - if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { - Navigator.of(context).push( - CustomPageRoute( - page: ImmediateLiveCarePendingRequestPage(), - ), - ); - } else { - Navigator.of(context).push( - CustomPageRoute( - page: SelectImmediateLiveCareClinicPage(), - ), - ); - } - }), - SizedBox(height: 16.h), - Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), - "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - ], - ), - ], - ), - Transform.flip( - flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), - ], - ).onPress(() { - bookAppointmentsViewModel.setIsClinicsListLoading(true); - bookAppointmentsViewModel.setIsLiveCareSchedule(true); - Navigator.of(context).push( - CustomPageRoute( - page: SelectClinicPage(), - ), - ); - }), - SizedBox(height: 16.h), - Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), - SizedBox(width: 12.h), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), - "".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - ], - ), - ], - ), - Transform.flip( - flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), - ], - ).onPress(() { - openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); - }), - ], - ), - ), - ), - ], + if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { + Navigator.of(context).push( + CustomPageRoute( + page: ImmediateLiveCarePendingRequestPage(), + ), + ); + } else { + Navigator.of(context).push( + CustomPageRoute( + page: SelectImmediateLiveCareClinicPage(), + ), + ); + } + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + ], + ).onPress(() { + bookAppointmentsViewModel.setIsClinicsListLoading(true); + bookAppointmentsViewModel.setIsLiveCareSchedule(true); + Navigator.of(context).push( + CustomPageRoute( + page: SelectClinicPage(), + ), + ); + }), + SizedBox(height: 16.h), + Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), + SizedBox(width: 12.h), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), + "".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + ], + ), + ], + ), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), + ], + ).onPress(() { + openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); + }), + ], + ), + ), + ), + ], ).paddingSymmetrical(24.h, 0.h) : getLiveCareNotLoggedInUI(); default: @@ -335,8 +345,10 @@ class _BookAppointmentPageState extends State { regionalViewModel.flush(); regionalViewModel.setBottomSheetType(type); // AppointmentViaRegionViewmodel? viewmodel = null; - showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, - child: Consumer(builder: (_, data, __) { + showCommonBottomSheetWithoutHeight(context, + title: "", + titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), + isDismissible: false, child: Consumer(builder: (_, data, __) { return getRegionalSelectionWidget(data); }), callBackFunc: () {}); } @@ -395,7 +407,9 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true), - "No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + "No need to wait, you will get medical consultation immediately via video call" + .needTranslation + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ), @@ -427,7 +441,9 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true), - "A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + "A specialised doctor will contact you and will be able to view your medical history" + .needTranslation + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ), @@ -443,7 +459,9 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true), - "Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + "Offers free medicine delivery for the LiveCare appointment" + .needTranslation + .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), ], ), ), diff --git a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart index 28a55ba..9df0558 100644 --- a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart +++ b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart @@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart'; -import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -21,49 +20,52 @@ import 'package:provider/provider.dart'; class TrackingScreen extends StatelessWidget { final OrderTrackingState state = OrderTrackingState.ended; + + const TrackingScreen({super.key}); + @override Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: Visibility( - visible: state == OrderTrackingState.ended, + visible: state == OrderTrackingState.ended, child: SafeArea( child: CustomButton( - height: 56.h, - backgroundColor: AppColors.bgGreenColor, - borderColor: Colors.transparent, - text: "Close".needTranslation, - textColor: AppColors.whiteColor, - onPressed: () {}, - ).paddingOnly(left:16.h,right:16.h), + height: 56.h, + backgroundColor: AppColors.bgGreenColor, + borderColor: Colors.transparent, + text: "Close".needTranslation, + textColor: AppColors.whiteColor, + onPressed: () {}, + ).paddingOnly(left: 16.h, right: 16.h), ), ), body: CollapsingListView( title: "Tracking Details".needTranslation, child: SingleChildScrollView( child: Column( - children: [ - animationSection(), - Column( - spacing: 16.h, - children: [OrderStatus(context), OrderTrackingId(), ContactSection()], - ).paddingAll(16.h), - ], - ))), + children: [ + animationSection(), + Column( + spacing: 16.h, + children: [orderStatus(context), orderTrackingId(), contactSection()], + ).paddingAll(16.h), + ], + ))), ); } - animationSection(){ + + animationSection() { switch (state) { case OrderTrackingState.waitingForCall: case OrderTrackingState.dispactched: case OrderTrackingState.returning: return Lottie.asset(AppAnimations.ambulance, repeat: true, reverse: false, width: 260.h, height: 200.h); case OrderTrackingState.ended: - default: - return Lottie.asset(AppAnimations.checkmark, repeat: true, width: 260.h, height: 200.h); + return Lottie.asset(AppAnimations.checkmark, repeat: true, width: 260.h, height: 200.h); } } - OrderStatus(BuildContext context) { + orderStatus(BuildContext context) { return Container( padding: EdgeInsets.all(16.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -95,7 +97,9 @@ class TrackingScreen extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(height: 16,), + SizedBox( + height: 16, + ), CustomButton( text: "Cancel Request".needTranslation, onPressed: () async {}, @@ -131,14 +135,9 @@ class TrackingScreen extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 12.h, - children: [ - dividerSection, - doctorSection(), - mapSection(context) - ], + children: [dividerSection, doctorSection(), mapSection(context)], ); case OrderTrackingState.ended: - default: return Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 12.h, @@ -170,17 +169,14 @@ class TrackingScreen extends StatelessWidget { return Row( spacing: 16.h, children: [ - Utils.buildImgWithNetwork(url: "",) - .circle(52.h), + Utils.buildImgWithNetwork(url: "", iconColor: Colors.transparent).circle(52.h), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Dr. John Doe".toText16( - color: AppColors.textColor, weight: FontWeight.w600), + "Dr. John Doe".toText16(color: AppColors.textColor, weight: FontWeight.w600), SizedBox(height: 4.h), - "MBBS, MD - General Medicine".toText12( - color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + "MBBS, MD - General Medicine".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), ], ), ), @@ -204,23 +200,17 @@ class TrackingScreen extends StatelessWidget { Widget mapForRequestedCoordinate(BuildContext context) { // return SizedBox.shrink(); - if (context.read().isGMSAvailable || - Platform.isIOS) - return GMSMap( - myLocationEnabled: false, - currentLocation: - context.read().getGMSLocation(), - onCameraMoved: (value) => context - .read() - .handleGMSMapCameraMoved(value)); - else + if (context.read().isGMSAvailable || Platform.isIOS) { + return GMSMap( + myLocationEnabled: false, + currentLocation: context.read().getGMSLocation(), + onCameraMoved: (value) => context.read().handleGMSMapCameraMoved(value)); + } else { return HMSMap( myLocationEnabled: false, - currentLocation: - context.read().getHMSLocation(), - onCameraMoved: (value) => context - .read() - .handleHMSMapCameraMoved(value)); + currentLocation: context.read().getHMSLocation(), + onCameraMoved: (value) => context.read().handleHMSMapCameraMoved(value)); + } } informationRegardingTrackingSection() { @@ -229,74 +219,70 @@ class TrackingScreen extends StatelessWidget { children: [ currentStatus(), SizedBox(height: 4.h), - "You will receive a call from HMG for confirmation ".toText12( - color: AppColors.greyTextColor, fontWeight: FontWeight.w500) + "You will receive a call from HMG for confirmation ".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500) ], ); } currentStatus() { - switch (state) { - case OrderTrackingState.waitingForCall: - case OrderTrackingState.dispactched: - return RichText( - text: TextSpan(children: [ - TextSpan( - text: "Please wait for the call".needTranslation, - style: TextStyle( - fontSize: 21.f, - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), - TextSpan( - text: "...".needTranslation, - style: TextStyle( - fontSize: 21.f, - fontWeight: FontWeight.w600, - color: AppColors.errorColor, - ), - ), - ]), - ); - case OrderTrackingState.returning: - return RichText( - text: TextSpan(children: [ - TextSpan( - text: "15:30".needTranslation, - style: TextStyle( - fontSize: 21.f, - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), - TextSpan( - text: " mins ".needTranslation, - style: TextStyle( - fontSize: 21.f, - fontWeight: FontWeight.w600, - color: AppColors.errorColor, - ), - ), - TextSpan( - text: "to hospital".needTranslation, - style: TextStyle( - fontSize: 21.f, - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), - ]), - ); - case OrderTrackingState.ended: - default: - return "Arrived" - .needTranslation - .toText21(color: AppColors.textColor, weight: FontWeight.w600); - } + switch (state) { + case OrderTrackingState.waitingForCall: + case OrderTrackingState.dispactched: + return RichText( + text: TextSpan(children: [ + TextSpan( + text: "Please wait for the call".needTranslation, + style: TextStyle( + fontSize: 21.f, + fontWeight: FontWeight.w600, + color: AppColors.textColor, + ), + ), + TextSpan( + text: "...".needTranslation, + style: TextStyle( + fontSize: 21.f, + fontWeight: FontWeight.w600, + color: AppColors.errorColor, + ), + ), + ]), + ); + case OrderTrackingState.returning: + return RichText( + text: TextSpan(children: [ + TextSpan( + text: "15:30".needTranslation, + style: TextStyle( + fontSize: 21.f, + fontWeight: FontWeight.w600, + color: AppColors.textColor, + ), + ), + TextSpan( + text: " mins ".needTranslation, + style: TextStyle( + fontSize: 21.f, + fontWeight: FontWeight.w600, + color: AppColors.errorColor, + ), + ), + TextSpan( + text: "to hospital".needTranslation, + style: TextStyle( + fontSize: 21.f, + fontWeight: FontWeight.w600, + color: AppColors.textColor, + ), + ), + ]), + ); + case OrderTrackingState.ended: + return "Arrived".needTranslation.toText21(color: AppColors.textColor, weight: FontWeight.w600); + } } - OrderTrackingId() { + orderTrackingId() { return Container( padding: EdgeInsets.all(16.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -308,25 +294,21 @@ class TrackingScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, children: [ - "Req ID:1234567890" - .toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Req ID:1234567890".toText16(color: AppColors.textColor, weight: FontWeight.w600), Row( spacing: 8.h, children: [ - Chip("location name".needTranslation, AppAssets.location_pickup, - AppColors.blackBgColor), - Chip("hotel name ".needTranslation, AppAssets.hospital, - AppColors.blackBgColor), + chip("location name".needTranslation, AppAssets.location_pickup, AppColors.blackBgColor), + chip("hotel name ".needTranslation, AppAssets.hospital, AppColors.blackBgColor), ], ), - Chip("hotel name ".needTranslation, AppAssets.hospital, - AppColors.blackBgColor), + chip("hotel name ".needTranslation, AppAssets.hospital, AppColors.blackBgColor), ], ), ); } - Chip(String title, String iconString, Color iconColor) { + chip(String title, String iconString, Color iconColor) { return AppCustomChipWidget( labelText: title, icon: iconString, @@ -335,7 +317,7 @@ class TrackingScreen extends StatelessWidget { ); } - ContactSection() { + contactSection() { return Container( padding: EdgeInsets.all(16.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -350,11 +332,8 @@ class TrackingScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 4.h, children: [ - "Contact Rapid Response Team (RRT)".needTranslation.toText14( - color: AppColors.textColor, weight: FontWeight.w600), - "1223456789".needTranslation.toText12( - color: AppColors.primaryRedColor, - fontWeight: FontWeight.w500), + "Contact Rapid Response Team (RRT)".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w600), + "1223456789".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), SizedBox(height: 8.h), ], ), @@ -390,27 +369,21 @@ class TrackingScreen extends StatelessWidget { getTitle(OrderTrackingState state) { switch (state) { case OrderTrackingState.waitingForCall: - return "Confirmation Call".needTranslation - .toText16(color: AppColors.textColor, weight: FontWeight.w600); + return "Confirmation Call".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); case OrderTrackingState.dispactched: - return "Pickup Up from Home".needTranslation - .toText16(color: AppColors.textColor, weight: FontWeight.w600); + return "Pickup Up from Home".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); case OrderTrackingState.returning: - return " On The Way To Hospital".needTranslation - .toText16(color: AppColors.textColor, weight: FontWeight.w600); + return " On The Way To Hospital".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); case OrderTrackingState.ended: - default: - return "Arrived at Hospital".needTranslation - .toText16(color: AppColors.textColor, weight: FontWeight.w600); + return "Arrived at Hospital".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600); } } mapSection(BuildContext context) { - return SizedBox( + return SizedBox( height: 170.h, width: double.infinity, - child: ClipRRect( - borderRadius: BorderRadius.circular(20.h),child: mapForRequestedCoordinate(context)), + child: ClipRRect(borderRadius: BorderRadius.circular(20.h), child: mapForRequestedCoordinate(context)), ); } } diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 0e9eb30..da95633 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -108,408 +108,390 @@ class _LandingPageState extends State { insuranceViewModel = Provider.of(context, listen: false); immediateLiveCareViewModel = Provider.of(context, listen: false); appState = getIt.get(); - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: SingleChildScrollView( - padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24), - child: Column( - spacing: 16.h, - children: [ - Row( - spacing: 8.h, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - appState.isAuthenticated - ? WelcomeWidget( - onTap: () { - Navigator.of(context).push(springPageRoute(ProfileSettings())); - }, - name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), - imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, - ).expanded - : CustomButton( - text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () async { - await authVM.onLoginPressed(); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 14.f, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), - height: 40.h, - ), - Row( - mainAxisSize: MainAxisSize.min, - spacing: 12.h, - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - // page: LoginScreen(), - ), - ); - }), - Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 18.h, width: 18.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - // page: LoginScreen(), - ), - ); - }), - Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - // page: LoginScreen(), + return PopScope( + canPop: false, + child: Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: SingleChildScrollView( + padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24), + child: Column( + spacing: 16.h, + children: [ + Row( + spacing: 8.h, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + appState.isAuthenticated + ? WelcomeWidget( + onTap: () { + Navigator.of(context).push(springPageRoute(ProfileSettings())); + }, + name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), + imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, + ).expanded + : CustomButton( + text: LocaleKeys.loginOrRegister.tr(context: context), + onPressed: () async { + await authVM.onLoginPressed(); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + height: 40.h, ), - ); - }), - ], - ) - ], - ).paddingSymmetrical(24.h, 0.h), - appState.isAuthenticated - ? Column( + Row( + mainAxisSize: MainAxisSize.min, + spacing: 12.h, children: [ - SizedBox(height: 12.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Appointments & Visits".toText16(isBold: true), - Row( - children: [ - LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], + Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: MedicalFilePage(), + // page: LoginScreen(), ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ); + }), + Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 18.h, width: 18.h).onPress(() { Navigator.of(context).push( CustomPageRoute( - page: MyAppointmentsPage(), + page: MedicalFilePage(), + // page: LoginScreen(), ), ); }), - SizedBox(height: 16.h), - Consumer(builder: (context, myAppointmentsVM, child) { - return myAppointmentsVM.isMyAppointmentsLoading - ? Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), - child: AppointmentCard( - patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), - myAppointmentsViewModel: myAppointmentsViewModel, - bookAppointmentsViewModel: bookAppointmentsViewModel, - isLoading: true, - isFromHomePage: true, - ), - ).paddingSymmetrical(24.h, 0.h) - : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty - ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 - ? Container( - decoration: - RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), - child: AppointmentCard( - patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, - myAppointmentsViewModel: myAppointmentsViewModel, - bookAppointmentsViewModel: bookAppointmentsViewModel, - isLoading: false, - isFromHomePage: true, - ), - ).paddingSymmetrical(24.h, 0.h) - : Swiper( - itemCount: myAppointmentsVM.isMyAppointmentsLoading - ? 3 - : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 - ? myAppointmentsVM.patientAppointmentsHistoryList.length - : 3, - layout: SwiperLayout.STACK, - loop: true, - itemWidth: MediaQuery.of(context).size.width - 48.h, - indicatorLayout: PageIndicatorLayout.COLOR, - axisDirection: AxisDirection.right, - controller: _controller, - itemHeight: 210 + 25, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - margin: EdgeInsets.only(top: 210 + 8 + 24), - builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), - ), - itemBuilder: (BuildContext context, int index) { - return Container( - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: MedicalFilePage(), + // page: LoginScreen(), + ), + ); + }), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h), + appState.isAuthenticated + ? Column( + children: [ + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Appointments & Visits".toText16(isBold: true), + Row( + children: [ + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage())); + }), + SizedBox(height: 16.h), + Consumer( + builder: (context, myAppointmentsVM, child) { + return myAppointmentsVM.isMyAppointmentsLoading + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), + myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, + isLoading: true, + isFromHomePage: true, + ), + ).paddingSymmetrical(24.h, 0.h) + : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty + ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), child: AppointmentCard( - patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], + patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, myAppointmentsViewModel: myAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: true, ), - ); - }, - ) - : Container( - width: double.infinity, - decoration: - RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true), - child: Padding( - padding: EdgeInsets.all(12.h), - child: Column( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), - SizedBox(height: 12.h), - "You do not have any upcoming appointment. Please book an appointment".needTranslation.toText12(isCenter: true), - SizedBox(height: 12.h), - CustomButton( - text: LocaleKeys.bookAppo.tr(context: context), - onPressed: () { - Navigator.of(context).push( - CustomPageRoute( - page: BookAppointmentPage(), + ).paddingSymmetrical(24.h, 0.h) + : Swiper( + itemCount: myAppointmentsVM.isMyAppointmentsLoading + ? 3 + : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 + ? myAppointmentsVM.patientAppointmentsHistoryList.length + : 3, + layout: SwiperLayout.STACK, + loop: true, + itemWidth: MediaQuery.of(context).size.width - 48.h, + indicatorLayout: PageIndicatorLayout.COLOR, + axisDirection: AxisDirection.right, + controller: _controller, + itemHeight: 210 + 25, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only(top: 210 + 8 + 24), + builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), + ), + itemBuilder: (BuildContext context, int index) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], + myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, + isLoading: false, + isFromHomePage: true, ), ); }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 14.f, - fontWeight: FontWeight.w500, - padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), - icon: AppAssets.add_icon, - iconColor: AppColors.primaryRedColor, - ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h); - }), - Consumer(builder: (context, immediateLiveCareVM, child) { - return immediateLiveCareVM.patientHasPendingLiveCareRequest - ? Column( - children: [ - SizedBox(height: 12.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, - side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), - ), - width: double.infinity, - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ) + : Container( + width: double.infinity, + decoration: RoundedRectangleBorder() + .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), + child: Padding( + padding: EdgeInsets.all(12.h), + child: Column( children: [ - AppCustomChipWidget( - labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, - backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), - textColor: AppColors.alertColor, + Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), + SizedBox(height: 12.h), + "You do not have any upcoming appointment. Please book an appointment" + .needTranslation + .toText12(isCenter: true), + SizedBox(height: 12.h), + CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage())); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14.f, + fontWeight: FontWeight.w500, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + height: 46.h, ), - Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), - // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 40.h, height: 40.h, fit: BoxFit.contain), ], ), - SizedBox(height: 8.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ), + ).paddingSymmetrical(24.h, 0.h); + }, + ), + Consumer( + builder: (context, immediateLiveCareVM, child) { + return immediateLiveCareVM.patientHasPendingLiveCareRequest + ? Column( + children: [ + SizedBox(height: 12.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.r, + hasShadow: true, + side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), + ), + width: double.infinity, + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - "You have a pending LiveCare request".needTranslation.toText12(isBold: true), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon_small, - iconColor: AppColors.blackColor, - width: 20.h, - height: 15.h, - fit: BoxFit.contain, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppCustomChipWidget( + labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, + backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), + textColor: AppColors.alertColor, + ), + Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), + // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 40.h, height: 40.h, fit: BoxFit.contain), + ], + ), + SizedBox(height: 8.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "You have a pending LiveCare request".needTranslation.toText12(isBold: true), + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, + width: 20.h, + height: 15.h, + fit: BoxFit.contain, + ), + ], ), ], ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: ImmediateLiveCarePendingRequestPage(), + ), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage())); + }), + SizedBox(height: 12.h), + ], + ) + : SizedBox(height: 12.h); + }, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Quick Links".needTranslation.toText16(isBold: true), + Row( + children: [ + "View medical file".needTranslation.toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage())); + }), + SizedBox(height: 16.h), + Container( + height: 120.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getLoggedInServiceCardsList[index].icon, + title: LandingPageData.getLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, + serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, + ), + ), ), ); - }), - SizedBox(height: 12.h), - ], - ) - : SizedBox(height: 12.h); - }), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CustomButton(text: "Quick Links".needTranslation, - onPressed: () { - Navigator.of(context).push( - CustomPageRoute( - page: ActiveMedicationPage(), + }, + separatorBuilder: (BuildContext cxt, int index) => 10.width, ), - ); - }, - ), - // "Quick Links".needTranslation.toText16(isBold: true), - Row( - children: [ - "View medical file".needTranslation.toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ), ], ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - ), - ); - }), - SizedBox(height: 16.h), - Container( - height: 120.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: EdgeInsets.only(left: 16.h, right: 16.h), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getLoggedInServiceCardsList[index].icon, - title: LandingPageData.getLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, - serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, - ), + ).paddingSymmetrical(24.h, 0.h), + ], + ) + : Container( + height: 127.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName, + icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, + title: LandingPageData.getNotLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, ), ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 10.width, - ), - ), - ], - ), - ).paddingSymmetrical(24.h, 0.h), - ], - ) - : Container( - height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: EdgeInsets.only(left: 16.h, right: 16.h), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName, - icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, - title: LandingPageData.getNotLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, - ), ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, - ), - ), - ], - ), - ).paddingSymmetrical(24.h, 0.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Services".toText16(isBold: true), - Row( - children: [ - "View all services".toText12(color: AppColors.primaryRedColor).onPress(() { - }), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], - ), - ], - ).paddingSymmetrical(24.h, 0.h), - SizedBox( - height: 325.h, - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getServiceCardsList.length, - shrinkWrap: true, - padding: EdgeInsets.only(left: 24.h, right: 24.h), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: LargeServiceCard( - image: LandingPageData.getServiceCardsList[index].icon, - title: LandingPageData.getServiceCardsList[index].title, - subtitle: LandingPageData.getServiceCardsList[index].subtitle, - icon: LandingPageData.getServiceCardsList[index].largeCardIcon, - ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 8.width, - ), + ], + ), + ).paddingSymmetrical(24.h, 0.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Services".toText16(isBold: true), + Row( + children: [ + "View all services".toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], ), ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox( + height: 340.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 24.w, right: 24.w), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: LargeServiceCard( + image: LandingPageData.getServiceCardsList[index].icon, + title: LandingPageData.getServiceCardsList[index].title, + subtitle: LandingPageData.getServiceCardsList[index].subtitle, + icon: LandingPageData.getServiceCardsList[index].largeCardIcon, + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.w), + ), ), - ), - appState.isAuthenticated ? HabibWalletCard() : SizedBox(), - ], + appState.isAuthenticated ? HabibWalletCard() : SizedBox(), + ], + ), ), ), ); @@ -521,19 +503,21 @@ class _LandingPageState extends State { title: "", isCloseButtonVisible: false, - child: StatefulBuilder(builder: (context, setState) { - return QuickLogin( - isDone: isDone, - onPressed: () { - // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); - authVM.loginWithFingerPrintFace(() { - isDone = true; - cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); - setState(() {}); - }); - }, - ); - }), + child: StatefulBuilder( + builder: (context, setState) { + return QuickLogin( + isDone: isDone, + onPressed: () { + // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); + authVM.loginWithFingerPrintFace(() { + isDone = true; + cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); + setState(() {}); + }); + }, + ); + }, + ), // height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3, isFullScreen: false, callBackFunc: () { diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index c2bfae7..cd95613 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -35,7 +35,6 @@ class _LandingNavigationState extends State { ], ), bottomNavigationBar: BottomNavigation( - context: context, currentIndex: _currentIndex, onTap: (index) { setState(() => _currentIndex = index); diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index 8473eff..3989523 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -16,7 +16,7 @@ class LargeServiceCard extends StatelessWidget { final String title; final String subtitle; - LargeServiceCard({ + const LargeServiceCard({ super.key, this.image = "", this.icon = "", @@ -26,51 +26,37 @@ class LargeServiceCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.symmetric(horizontal: 3.h), - child: Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: Colors.transparent, - borderRadius: 16, - ), - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 0.h), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + return Container( + width: 150.w, + padding: EdgeInsets.symmetric(horizontal: 3.w), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.transparent, borderRadius: 16.r), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(AppAssets.livecare_service, width: 220.w, fit: BoxFit.contain), + SizedBox(height: 10.h), + Row( children: [ - Image.asset(AppAssets.livecare_service, width: 220.h, height: 220.h, fit: BoxFit.contain), - SizedBox(height: 3.h), - Row( - children: [ - Utils.buildSvgWithAssets(icon: icon, width: 24.h, height: 24.h), - title.toText14(color: AppColors.blackColor, isBold: true), - ], - ), - SizedBox(width: 220.h, child: subtitle.toText11(color: AppColors.blackColor)), - SizedBox(height: 6.h), - SizedBox( - width: 220.h, - child: CustomButton( - text: LocaleKeys.bookNow.tr(context: context), - onPressed: () { - // Navigator.of(context).pushReplacement( - // MaterialPageRoute(builder: (BuildContext context) => LandingPage()), - // ); - }, - backgroundColor: AppColors.borderOnlyColor, - borderColor: AppColors.borderOnlyColor, - textColor: AppColors.whiteColor, - fontSize: 14, - fontWeight: FontWeight.bold, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, - ), - ), + Utils.buildSvgWithAssets(icon: icon, width: 24.w, height: 24.h), + Flexible(child: title.toText14(color: AppColors.blackColor, isBold: true, textOverflow: TextOverflow.clip, maxlines: 1)), ], ), - ), + subtitle.toText11(color: AppColors.blackColor), + SizedBox(height: 10.h), + CustomButton( + text: LocaleKeys.bookNow.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.borderOnlyColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.whiteColor, + fontSize: 14.f, + fontWeight: FontWeight.bold, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), + height: 40.h, + ), + ], ), ); } diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 4d65515..c1e5ba4 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -179,7 +179,8 @@ class _MedicalFilePageState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h), + Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, + width: 56.w, height: 56.h), SizedBox(width: 8.w), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -226,12 +227,13 @@ class _MedicalFilePageState extends State { children: [ AppCustomChipWidget( labelText: "${appState.getAuthenticatedUser()!.age} Years Old", + labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w), ), AppCustomChipWidget( icon: AppAssets.blood_icon, - labelText: "Blood: ${appState.getUserBloodGroup}", + labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}", iconColor: AppColors.primaryRedColor, - labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), + labelPadding: EdgeInsetsDirectional.only(end: 8.w), ), Consumer(builder: (context, insuranceVM, child) { return AppCustomChipWidget( @@ -242,7 +244,7 @@ class _MedicalFilePageState extends State { iconSize: 12.w, backgroundColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1), - labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), + labelPadding: EdgeInsetsDirectional.only(end: 8.w), ); }), ], @@ -347,7 +349,9 @@ class _MedicalFilePageState extends State { }), SizedBox(height: 16.h), Consumer(builder: (context, myAppointmentsVM, child) { + // Provide an explicit height so the horizontal ListView has a bounded height return SizedBox( + height: 190.h, child: myAppointmentsVM.isMyAppointmentsLoading ? MedicalFileAppointmentCard( patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), @@ -359,7 +363,8 @@ 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), @@ -381,8 +386,8 @@ class _MedicalFilePageState extends State { fontSize: 14.f, fontWeight: FontWeight.w500, borderRadius: 12.r, - padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), - height: 40.h, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: isFoldable || isTablet ? 50.h : 40.h, icon: AppAssets.add_icon, iconColor: AppColors.primaryRedColor, ), @@ -505,11 +510,13 @@ 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, ), ), @@ -532,7 +539,8 @@ class _MedicalFilePageState extends State { prescriptionVM.setPrescriptionsDetailsLoading(); Navigator.of(context).push( CustomPageRoute( - page: PrescriptionDetailPage(prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), + page: PrescriptionDetailPage( + prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), ), ); }), @@ -648,7 +656,10 @@ 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 @@ -731,7 +742,7 @@ class _MedicalFilePageState extends State { }, separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h), ), - ); + ).paddingSymmetrical(24.w, 0); }), SizedBox(height: 24.h), "Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h), @@ -820,10 +831,14 @@ 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()); + insuranceViewModel.getPatientInsuranceDetailsForUpdate(appState.getAuthenticatedUser()!.patientId.toString(), + appState.getAuthenticatedUser()!.patientIdentificationNo.toString()); showCommonBottomSheetWithoutHeight(context, - child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false); + child: PatientInsuranceCardUpdateCard(), + callBackFunc: () {}, + title: "", + isCloseButtonVisible: false, + isFullScreen: false); }, backgroundColor: AppColors.bgGreenColor.withOpacity(0.20), borderColor: AppColors.bgGreenColor.withOpacity(0.0), @@ -832,7 +847,7 @@ class _MedicalFilePageState extends State { fontWeight: FontWeight.w500, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), - height: 56.h, + height: isFoldable ? 50.h : 40.h, ).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h), ), ).paddingSymmetrical(24.w, 0.h); @@ -874,19 +889,21 @@ class _MedicalFilePageState extends State { ); }), MedicalFileCard( - label: "My Invoices List".needTranslation, - textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.eye_result_icon, - isLargeText: true, - iconSize: 36.w), + label: "My Invoices List".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: true, + iconSize: 36.w, + ), MedicalFileCard( - label: "Ancillary Orders List".needTranslation, - textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.eye_result_icon, - isLargeText: true, - iconSize: 36.w), + label: "Ancillary Orders List".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: true, + iconSize: 36.w, + ), ], ).paddingSymmetrical(24.w, 0.0), SizedBox(height: 16.h), diff --git a/lib/presentation/my_family/widget/family_cards.dart b/lib/presentation/my_family/widget/family_cards.dart index 9b1fb4b..2f9c871 100644 --- a/lib/presentation/my_family/widget/family_cards.dart +++ b/lib/presentation/my_family/widget/family_cards.dart @@ -54,7 +54,10 @@ class _FamilyCardsState extends State { children: [ Utils.buildSvgWithAssets(icon: AppAssets.alertSquare), SizedBox(width: 8.h), - "Who can view my medical file ?".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500).onPress(() { + "Who can view my medical file ?" + .needTranslation + .toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500) + .onPress(() { dialogService.showFamilyBottomSheetWithoutHWithChild( label: "Manage Family".needTranslation, message: "", @@ -110,11 +113,18 @@ class _FamilyCardsState extends State { ? AppColors.textGreenColor : AppColors.alertColor), SizedBox(height: 8.h), - Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [ - (profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600), - (getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)) - .toText12(isBold: false, isCenter: false, maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - ]), + Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.start, + runAlignment: WrapAlignment.start, + spacing: 0.h, + children: [ + (profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600), + (getStatusTextByRequest( + FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)) + .toText12( + isBold: false, isCenter: false, maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + ]), SizedBox(height: 8.h), CustomChipWidget( height: 30.h, @@ -141,7 +151,7 @@ class _FamilyCardsState extends State { itemCount: widget.profiles.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, - crossAxisSpacing: 10.h, + crossAxisSpacing: 10.w, mainAxisSpacing: 10.h, childAspectRatio: widget.isShowDetails ? 0.56.h : 0.65.h, ), @@ -162,13 +172,14 @@ class _FamilyCardsState extends State { mainAxisSize: MainAxisSize.min, children: [ Utils.buildImgWithAssets( - icon: profile.gender == null - ? AppAssets.dummy_user - : profile.gender == 1 - ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) - : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg), - width: 80.h, - height: 78.h), + icon: profile.gender == null + ? AppAssets.dummy_user + : profile.gender == 1 + ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) + : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg), + width: 80.h, + height: 78.h, + ), SizedBox(height: 8.h), (profile.patientName ?? "Unknown").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), SizedBox(height: 8.h), @@ -282,7 +293,9 @@ class _FamilyCardsState extends State { alignment: WrapAlignment.start, children: [ (profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), - (getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)).toText14( + (getStatusTextByRequest( + FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)) + .toText14( isBold: false, isCenter: true, maxlines: 1, diff --git a/lib/presentation/onboarding/onboarding_screen.dart b/lib/presentation/onboarding/onboarding_screen.dart index 2bf898b..265559b 100644 --- a/lib/presentation/onboarding/onboarding_screen.dart +++ b/lib/presentation/onboarding/onboarding_screen.dart @@ -53,7 +53,7 @@ class _OnboardingScreenState extends State { left: false, right: false, child: Column( - spacing: 24, + spacing: 24.h, children: [ PageView( controller: pageController, @@ -75,24 +75,24 @@ class _OnboardingScreenState extends State { }, ).expanded, Row( - spacing: 4.h, + spacing: 4.w, children: [ AnimatedContainer( duration: const Duration(milliseconds: 250), height: 6.h, - width: selectedIndex == 0 ? 18.h : 6.h, - decoration: - BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), + width: selectedIndex == 0 ? 18.w : 6.w, + decoration: BoxDecoration( + color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)), ), AnimatedContainer( duration: const Duration(milliseconds: 250), height: 6.h, - width: selectedIndex == 1 ? 18.h : 6.h, - decoration: - BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), + width: selectedIndex == 1 ? 18.w : 6.w, + decoration: BoxDecoration( + color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)), ), ], - ).paddingOnly(left: 24.h, right: 24.h), + ).paddingOnly(left: 24.w, right: 24.w), Row( children: [ AnimatedSwitcher( @@ -102,20 +102,20 @@ class _OnboardingScreenState extends State { ? CustomButton( text: "Skip".needTranslation, onPressed: () => goToHomePage(), - width: 86.h, + width: 86.w, height: 56.h, backgroundColor: Color(0xffFEE9EA), textColor: AppColors.primaryRedColor, borderColor: Colors.transparent, - ).paddingOnly(left: 24.h) + ).paddingOnly(left: 24.w) : const SizedBox.shrink(), ), const Spacer(), AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, - width: selectedIndex == 0 ? 86.h : MediaQuery.of(context).size.width - 48.h, - margin: EdgeInsets.only(left: 24.h, right: 24.h), + width: selectedIndex == 0 ? 86.w : MediaQuery.of(context).size.width - 48.w, + margin: EdgeInsets.only(left: 24.w, right: 24.w), decoration: BoxDecoration( color: AppColors.primaryRedColor, borderRadius: BorderRadius.circular(12.r), @@ -126,8 +126,8 @@ class _OnboardingScreenState extends State { child: selectedIndex == 0 ? CustomButton( icon: getIt.get().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, - iconSize: 32.h, - width: 86.h, + iconSize: 32.w, + width: 86.w, height: 56.h, text: "".needTranslation, backgroundColor: Colors.transparent, @@ -146,7 +146,7 @@ class _OnboardingScreenState extends State { ), ), ], - ), + ).paddingOnly(bottom: (isFoldable || isTablet) ? 20.h : 10.h), ], ), ), @@ -157,7 +157,7 @@ class _OnboardingScreenState extends State { return Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, - spacing: 12, + spacing: 12.h, children: [ Align( alignment: Alignment.bottomCenter, @@ -167,10 +167,9 @@ class _OnboardingScreenState extends State { repeat: true, reverse: false, frameRate: FrameRate(60), - width: MediaQuery.sizeOf(context).width - 50, - height: MediaQuery.sizeOf(context).width - 50))) + width: MediaQuery.sizeOf(context).width - 50.w, + height: MediaQuery.sizeOf(context).width - 50.w))) .expanded, - // 12.height, Text( heading, style: TextStyle(fontSize: 36.f, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1), @@ -180,6 +179,6 @@ class _OnboardingScreenState extends State { style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), ), ], - ).paddingOnly(left: 24.h, right: 24.h); + ).paddingOnly(left: 24.w, right: 24.w); } } diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index 59e59b1..f5e2ef8 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -51,6 +51,33 @@ class _ProfileSettingsState extends State { super.dispose(); } + double dynamicItemHeight(BuildContext context) { + final double w = SizeUtils.width; + final double h = SizeUtils.height; + + double longer = w > h ? w : h; + double shorter = w < h ? w : h; + + final double aspect = longer / (shorter == 0 ? 1 : shorter); + + // Choose multiplier based on aspect ratio (handles near-square / foldable) + double multiplier; + if (aspect < 1.05) { + multiplier = 0.28; // nearly square / foldable -> smaller card height + } else if (aspect > 1.8) { + multiplier = 0.40; // very tall/wide -> larger height + } else { + multiplier = 0.34; // normal phones/tablets + } + + // Compute and clamp using sensible bounds (uses .h extension) + final double minH = 210.h; + final double maxH = 380.h; + final double computed = (shorter * multiplier); + + return computed.clamp(minH, maxH); + } + int length = 3; final SwiperController _controller = SwiperController(); @@ -72,11 +99,11 @@ class _ProfileSettingsState extends State { itemCount: medicalVm.patientFamilyFiles.length, layout: SwiperLayout.STACK, loop: true, - itemWidth: SizeUtils.width - 42.w, + itemHeight: dynamicItemHeight(context), + itemWidth: SizeUtils.width - 30.w, indicatorLayout: PageIndicatorLayout.COLOR, axisDirection: AxisDirection.right, controller: _controller, - itemHeight: 220.h, pagination: SwiperPagination( alignment: Alignment.bottomCenter, margin: EdgeInsets.only(top: (210.h + 8.h + 24.h)), @@ -97,13 +124,14 @@ class _ProfileSettingsState extends State { onFamilySwitchPress: (FamilyFileResponseModelLists profile) { medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); }, - ).paddingOnly(right: 16.w); + ).paddingOnly(right: 16.w, left: 8.w); }, ), + SizedBox(height: 5.h), GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 9.w, mainAxisSpacing: 9.h), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: isTablet ? 3 : 2), physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 24.w, top: 10.w), + padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 24.h), shrinkWrap: true, children: [ Container( @@ -115,14 +143,14 @@ class _ProfileSettingsState extends State { ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - spacing: 4.h, + // spacing: 4.h, children: [ Row( spacing: 8.w, crossAxisAlignment: CrossAxisAlignment.center, children: [ Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h), - "Habib Wallet".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded, + "Habib Wallet".needTranslation.toText16(weight: FontWeight.w600, maxlines: 2).expanded, Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward), ], ), @@ -131,10 +159,11 @@ class _ProfileSettingsState extends State { return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false) .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); }), + Spacer(), CustomButton( height: 40.h, icon: AppAssets.recharge_icon, - iconSize: 24.h, + iconSize: 22.w, iconColor: AppColors.infoColor, textColor: AppColors.infoColor, text: "Recharge".needTranslation, @@ -152,7 +181,7 @@ class _ProfileSettingsState extends State { ).onPress(() { Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); }), - ) + ), ], ), "Quick Actions" @@ -220,7 +249,12 @@ class _ProfileSettingsState extends State { ], ), ), - CustomButton(icon: AppAssets.minus, text: "Deactivate account".needTranslation, onPressed: () {}).paddingAll(24.w), + CustomButton( + height: 56.h, + icon: AppAssets.minus, + text: "Deactivate account".needTranslation, + onPressed: () {}, + ).paddingAll(24.w), ], ); }, @@ -275,7 +309,11 @@ class FamilyCardWidget extends StatelessWidget { final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); return Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.r, hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), child: Column( children: [ Column( @@ -284,7 +322,6 @@ class FamilyCardWidget extends StatelessWidget { children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, - // children: [ Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h), Column( @@ -404,7 +441,7 @@ class FamilyCardWidget extends StatelessWidget { borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor, textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, - height: 56.h, + height: isFoldable ? 50.h : 40.h, fontSize: 14.f, ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); } @@ -420,7 +457,7 @@ class FamilyCardWidget extends StatelessWidget { textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {}, - height: 40.h, + height: isFoldable ? 50.h : 40.h, fontSize: 14.f, ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); } diff --git a/lib/widgets/appbar/app_bar_widget.dart b/lib/widgets/appbar/app_bar_widget.dart index 2c180ce..b3bc3eb 100644 --- a/lib/widgets/appbar/app_bar_widget.dart +++ b/lib/widgets/appbar/app_bar_widget.dart @@ -43,13 +43,13 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { quarterTurns: 90, child: Utils.buildSvgWithAssets( icon: AppAssets.arrow_back, - width: 32.h, + width: 32.w, height: 32.h, ), ) : Utils.buildSvgWithAssets( icon: AppAssets.arrow_back, - width: 32.h, + width: 32.w, height: 32.h, ), ), @@ -57,11 +57,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ), // Logo - if (!hideLogoAndLang) - Utils.buildSvgWithAssets( - icon: AppAssets.habiblogo, - height: 24.h, - ), + if (!hideLogoAndLang) Utils.buildSvgWithAssets(icon: AppAssets.habiblogo, height: 24.h), if (!hideLogoAndLang) Expanded( diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart index 1574a0a..f6db2f4 100644 --- a/lib/widgets/appbar/collapsing_list_view.dart +++ b/lib/widgets/appbar/collapsing_list_view.dart @@ -110,14 +110,18 @@ class CollapsingListView extends StatelessWidget { color: AppColors.blackColor, letterSpacing: -0.5), ).expanded, - if (logout != null) actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!), - if (report != null) actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!), + if (logout != null) + actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!), + if (report != null) + actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!), if (history != null) - actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(history!), + actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon) + .onPress(history!), if (instructions != null) actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!), if (requests != null) - actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon).onPress(requests!), + actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon) + .onPress(requests!), if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24), if (trailing != null) trailing!, ], @@ -151,7 +155,7 @@ class CollapsingListView extends StatelessWidget { margin: EdgeInsets.only(right: 24.w), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.secondaryLightRedColor, - borderRadius: 12.r, + borderRadius: 10.r, ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/widgets/bottom_navigation/bottom_navigation.dart b/lib/widgets/bottom_navigation/bottom_navigation.dart index 1163c66..a3e117f 100644 --- a/lib/widgets/bottom_navigation/bottom_navigation.dart +++ b/lib/widgets/bottom_navigation/bottom_navigation.dart @@ -12,9 +12,8 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; class BottomNavigation extends StatelessWidget { final int currentIndex; final ValueChanged onTap; - BuildContext? context; - BottomNavigation({super.key, required this.currentIndex, required this.onTap, this.context}); + const BottomNavigation({super.key, required this.currentIndex, required this.onTap}); @override Widget build(BuildContext context) { @@ -30,7 +29,9 @@ class BottomNavigation extends StatelessWidget { iconSize: 27, isSpecial: true, ), - appState.isAuthenticated ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr(context: context)) : BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()), + appState.isAuthenticated + ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr(context: context)) + : BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()), BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr(context: context)), ]; @@ -65,11 +66,7 @@ class BottomNavigation extends StatelessWidget { ), ), const SizedBox(height: 10), - item.label.toText12( - fontWeight: FontWeight.w500, - // color: Colors.black87, - // textAlign: TextAlign.center, - ), + item.label.toText12(fontWeight: FontWeight.w500), SizedBox(height: item.isSpecial ? 5 : 0) ], ), @@ -82,12 +79,14 @@ class BottomNavItem { final String label; final double iconSize; final bool isSpecial; + final Color? color; const BottomNavItem({ required this.icon, required this.label, this.iconSize = 21, this.isSpecial = false, + this.color, }); } diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index 28d6554..f236bd9 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -56,7 +56,11 @@ class CustomButton extends StatelessWidget { return GestureDetector( onTap: isDisabled ? null : onPressed, child: Container( - height: height ?? (50.h), + height: height == null + ? 56.h + : (isFoldable || isTablet) + ? height! + 10.h + : height, width: width, padding: padding, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index d720151..f090f4f 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -48,63 +48,71 @@ class AppCustomChipWidget extends StatelessWidget { final iconS = iconSize ?? 12.w; return GestureDetector( onTap: onChipTap, - child: SizedBox( - child: ChipTheme( - data: ChipThemeData( - padding: EdgeInsets.zero, - shape: SmoothRectangleBorder( - side: BorderSide( - width: 10.w, - color: Colors.transparent, // Crucially, set color to transparent - style: BorderStyle.none, - ), - borderRadius: BorderRadius.circular(8.r), // Apply a border radius of 16.0 + child: ChipTheme( + data: ChipThemeData( + padding: EdgeInsets.zero, + shape: SmoothRectangleBorder( + side: BorderSide( + width: 10.w, + color: Colors.transparent, // Crucially, set color to transparent + style: BorderStyle.none, ), + borderRadius: BorderRadius.circular(isFoldable || isTablet ? 6.r : 8.r), ), - child: icon.isNotEmpty - ? Chip( - avatar: icon.isNotEmpty - ? Utils.buildSvgWithAssets(icon: icon, width: iconS, height: iconS, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) - : SizedBox.shrink(), - label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), - padding: padding, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w), - backgroundColor: backgroundColor, - shape: shape ?? - SmoothRectangleBorder( - borderRadius: BorderRadius.circular(8.r), - smoothness: 10, - side: BorderSide(color: AppColors.transparent, width: 1.5), - ), - deleteIcon: deleteIcon?.isNotEmpty == true - ? Utils.buildSvgWithAssets( - icon: deleteIcon!, + ), + child: icon.isNotEmpty + ? Chip( + avatar: icon.isNotEmpty + ? Padding( + padding: EdgeInsets.only(left: 8.w, right: 6.w), + child: Utils.buildSvgWithAssets( + icon: icon, width: iconS, height: iconS, - iconColor: deleteIconHasColor ? deleteIconColor : null, - ) - : null, - onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, - ) - : Chip( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), - padding: EdgeInsets.zero, - backgroundColor: backgroundColor, - shape: shape ?? - SmoothRectangleBorder( - borderRadius: BorderRadius.circular(8.r), - smoothness: 10, - side: BorderSide(color: AppColors.transparent, width: 1.5), - ), - labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w), - deleteIcon: deleteIcon?.isNotEmpty == true - ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null) - : null, - onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, - ), - ), + iconColor: iconHasColor ? iconColor : null, + fit: BoxFit.contain, + ), + ) + : SizedBox.shrink(), + avatarBoxConstraints: BoxConstraints(), + label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), + padding: EdgeInsets.zero, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + labelPadding: EdgeInsetsDirectional.only(end: 8.w), + backgroundColor: backgroundColor, + shape: shape ?? + SmoothRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + smoothness: 10, + side: BorderSide(color: AppColors.transparent, width: 1.5), + ), + deleteIcon: deleteIcon?.isNotEmpty == true + ? Utils.buildSvgWithAssets( + icon: deleteIcon!, + width: iconS, + height: iconS, + iconColor: deleteIconHasColor ? deleteIconColor : null, + ) + : null, + onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, + ) + : Chip( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), + padding: EdgeInsets.zero, + backgroundColor: backgroundColor, + shape: shape ?? + SmoothRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + smoothness: 10, + side: BorderSide(color: AppColors.transparent, width: 1.5), + ), + labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w), + deleteIcon: deleteIcon?.isNotEmpty == true + ? Utils.buildSvgWithAssets(icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null) + : null, + onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, + ), ), ); } diff --git a/lib/widgets/custom_tab_bar.dart b/lib/widgets/custom_tab_bar.dart index a148cc7..c65cd75 100644 --- a/lib/widgets/custom_tab_bar.dart +++ b/lib/widgets/custom_tab_bar.dart @@ -62,7 +62,7 @@ class CustomTabBarState extends State { scrollDirection: Axis.horizontal, padding: EdgeInsets.zero, physics: const BouncingScrollPhysics(), - itemBuilder: (cxt, index) => myTab(widget.tabs[index], index).expanded, + itemBuilder: (cxt, index) => myTab(widget.tabs[index], index), separatorBuilder: (cxt, index) => 4.width, itemCount: widget.tabs.length, ); @@ -75,20 +75,20 @@ class CustomTabBarState extends State { } return Container( - height: 62.h, padding: EdgeInsets.all(4.w), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 12.r, ), + height: 62.h, child: Center(child: parentWidget)); } Widget myTab(CustomTabBarModel tabBar, int currentIndex) { bool isSelected = selectedIndex == currentIndex; return Container( - height: 54.h, - padding: EdgeInsets.only(top: 4.h, bottom: 4.h, left: 16.w, right: 16.w), + height: (isTablet || isFoldable) ? 60.h : 54.h, + padding: EdgeInsets.only(top: 4.h, bottom: 4.h, left: 14.w, right: 14.w), alignment: Alignment.center, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor, diff --git a/lib/widgets/language_switcher.dart b/lib/widgets/language_switcher.dart index ee54015..91e45b2 100644 --- a/lib/widgets/language_switcher.dart +++ b/lib/widgets/language_switcher.dart @@ -42,7 +42,7 @@ class _LanguageSelectorState extends State { return Stack(clipBehavior: Clip.none, children: [ Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular((12.r)), + borderRadius: BorderRadius.circular((10.r)), color: AppColors.secondaryLightRedColor, ), child: InkWell(