diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ffa3a908..f703aeb8 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -50,6 +50,8 @@ android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove" /> + + @@ -92,7 +94,6 @@ - diff --git a/android/app/src/main2/AndroidManifest.xml b/android/app/src/main2/AndroidManifest.xml index e07739b9..535bd4ab 100644 --- a/android/app/src/main2/AndroidManifest.xml +++ b/android/app/src/main2/AndroidManifest.xml @@ -45,6 +45,7 @@ + - diff --git a/assets/fonts/cairo/Cairo-Bold.ttf b/assets/fonts/cairo/Cairo-Bold.ttf new file mode 100644 index 00000000..041abaf6 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold.ttf differ diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.eot b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.eot new file mode 100644 index 00000000..ed5f0c34 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.eot differ diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.otf b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.otf new file mode 100644 index 00000000..8bc25fbb Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.otf differ diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.ttf b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.ttf new file mode 100644 index 00000000..4f026897 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.ttf differ diff --git a/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.woff b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.woff new file mode 100644 index 00000000..91e3d063 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Bold/Cairo-Bold.woff differ diff --git a/assets/fonts/cairo/Cairo-ExtraBold.ttf b/assets/fonts/cairo/Cairo-ExtraBold.ttf new file mode 100644 index 00000000..4fc547de Binary files /dev/null and b/assets/fonts/cairo/Cairo-ExtraBold.ttf differ diff --git a/assets/fonts/cairo/Cairo-Light.ttf b/assets/fonts/cairo/Cairo-Light.ttf new file mode 100644 index 00000000..7e0f9427 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light.ttf differ diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.eot b/assets/fonts/cairo/Cairo-Light/Cairo-Light.eot new file mode 100644 index 00000000..d76c5391 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.eot differ diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.otf b/assets/fonts/cairo/Cairo-Light/Cairo-Light.otf new file mode 100644 index 00000000..f8812a9c Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.otf differ diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.ttf b/assets/fonts/cairo/Cairo-Light/Cairo-Light.ttf new file mode 100644 index 00000000..840cbc3e Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.ttf differ diff --git a/assets/fonts/cairo/Cairo-Light/Cairo-Light.woff b/assets/fonts/cairo/Cairo-Light/Cairo-Light.woff new file mode 100644 index 00000000..9534bb4b Binary files /dev/null and b/assets/fonts/cairo/Cairo-Light/Cairo-Light.woff differ diff --git a/assets/fonts/cairo/Cairo-Medium.ttf b/assets/fonts/cairo/Cairo-Medium.ttf new file mode 100644 index 00000000..5b8d0f70 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Medium.ttf differ diff --git a/assets/fonts/cairo/Cairo-Regular.ttf b/assets/fonts/cairo/Cairo-Regular.ttf new file mode 100644 index 00000000..36ac46bf Binary files /dev/null and b/assets/fonts/cairo/Cairo-Regular.ttf differ diff --git a/assets/fonts/cairo/Cairo-Regular/Cairo-Regular.ttf b/assets/fonts/cairo/Cairo-Regular/Cairo-Regular.ttf new file mode 100644 index 00000000..3eb09f86 Binary files /dev/null and b/assets/fonts/cairo/Cairo-Regular/Cairo-Regular.ttf differ diff --git a/assets/fonts/cairo/Cairo-SemiBold.ttf b/assets/fonts/cairo/Cairo-SemiBold.ttf new file mode 100644 index 00000000..fd01d422 Binary files /dev/null and b/assets/fonts/cairo/Cairo-SemiBold.ttf differ diff --git a/assets/images/png/Carparking_services_image.png b/assets/images/png/Carparking_services_image.png new file mode 100644 index 00000000..9ce08c78 Binary files /dev/null and b/assets/images/png/Carparking_services_image.png differ diff --git a/assets/images/png/ereferral_services_image.png b/assets/images/png/ereferral_services_image.png index 15194b59..10d0d14a 100644 Binary files a/assets/images/png/ereferral_services_image.png and b/assets/images/png/ereferral_services_image.png differ diff --git a/assets/images/png/full_body_back_female.png b/assets/images/png/full_body_back_female.png index 9b3aa74d..b1ea5cac 100644 Binary files a/assets/images/png/full_body_back_female.png and b/assets/images/png/full_body_back_female.png differ diff --git a/assets/images/png/full_body_back_male.png b/assets/images/png/full_body_back_male.png index 2eda5e8f..73a0b8b3 100644 Binary files a/assets/images/png/full_body_back_male.png and b/assets/images/png/full_body_back_male.png differ diff --git a/assets/images/png/full_body_front_female.png b/assets/images/png/full_body_front_female.png index c89608b0..68ce3738 100644 Binary files a/assets/images/png/full_body_front_female.png and b/assets/images/png/full_body_front_female.png differ diff --git a/assets/images/png/full_body_front_male.png b/assets/images/png/full_body_front_male.png index d1f34b4e..01e603a4 100644 Binary files a/assets/images/png/full_body_front_male.png and b/assets/images/png/full_body_front_male.png differ diff --git a/assets/images/png/healthtrackers_services_image.png b/assets/images/png/healthtrackers_services_image.png new file mode 100644 index 00000000..2d0c3f8a Binary files /dev/null and b/assets/images/png/healthtrackers_services_image.png differ diff --git a/assets/images/png/livechat_services_image.png b/assets/images/png/livechat_services_image.png new file mode 100644 index 00000000..7e33a933 Binary files /dev/null and b/assets/images/png/livechat_services_image.png differ diff --git a/assets/images/png/smartwatch_services_image.png b/assets/images/png/smartwatch_services_image.png new file mode 100644 index 00000000..303737bf Binary files /dev/null and b/assets/images/png/smartwatch_services_image.png differ diff --git a/assets/images/svg/add_new_family_icon.svg b/assets/images/svg/add_new_family_icon.svg new file mode 100644 index 00000000..ad7ee356 --- /dev/null +++ b/assets/images/svg/add_new_family_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/svg/appointment_rating_icon.svg b/assets/images/svg/appointment_rating_icon.svg new file mode 100644 index 00000000..83d89feb --- /dev/null +++ b/assets/images/svg/appointment_rating_icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/svg/calendar_filled_icon.svg b/assets/images/svg/calendar_filled_icon.svg new file mode 100644 index 00000000..116ce8ef --- /dev/null +++ b/assets/images/svg/calendar_filled_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index fe4c04d8..92f202f0 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -3,6 +3,8 @@ "arabic": "العربية", "login": "تسجيل الدخول", "noDataAvailable": "لا توجد بيانات متاحة", + "noRatingAvailable": "لا يوجد تقييم متاح", + "doctorDoesNotHaveRating": "لم يحصل هذا الطبيب على أي تقييمات بعد.", "confirm": "تأكيد", "loadingText": "جاري التحميل، يرجى الانتظار...", "kilometerUnit": "كم", @@ -301,7 +303,7 @@ "vitalSignsSubTitle": "التقارير", "myMedical": "نشط", "myMedicalSubtitle": "الأدوية", - "myDoctor": "طبيبي", + "myDoctor": "أطبائي", "myDoctorSubtitle": "القائمة", "eye": "العين", "eyeSubtitle": "القياس", @@ -363,7 +365,7 @@ "paymentOnline": "الخدمة", "onlineCheckIn": "تسجيل الوصول عبر الإنترنت", "myBalances": "رصيدي", - "myWallet": "محف��تي", + "myWallet": "محفظتي", "balanceAmount": "مبلغ المحفظة", "totalBalance": "إجمالي الرصيد", "createAdvancedPayment": "إعادة شحن المحفظة", @@ -487,7 +489,7 @@ "services2": "الخدمات", "cantSeeProfile": "لرؤية ملفك الطبي، يرجى تسجيل الدخول أو التسجيل الآن", "loginRegisterNow": "تسجيل الدخول أو التسجيل الآن", - "hmgPharmacy": "صيدلية مجموعة الحبيب الطبية", + "hmgPharmacy": "صيدلية الحبيب", "ecommerceSolution": "حلول التجارة الإلكترونية", "comprehensive": "شامل", "onlineConsulting": "استشارات عبر الإنترنت", @@ -858,7 +860,7 @@ "onboardingBody1": "ببضع نقرات فقط يمكنك استشارة الطبيب الذي تختاره.", "onboardingHeading2": "الوصول إلى السجل الطبي بين يديك", "onboardingBody2": "تتبع تاريخك الطبي بما في ذلك الفحوصات المخبرية، الوصفات الطبية، التأمين، وغيرها.", - "hmgHospitals": "مستشفيات مجموعة الحبيب الطبية", + "hmgHospitals": "مستشفيات الحبيب", "hmcMedicalClinic": "مراكز مجموعة الحبيب الطبية", "applyFilter": "تطبيق الفلتر", "facilityAndLocation": "المرفق والموقع", @@ -934,7 +936,7 @@ "hospitalInformation": "معلومات المستشفى", "fetchingAppointmentShare": "جاري جلب تفاصيل الموعد...", "bookingYourAppointment": "جاري حجز موعدك...", - "selectLiveCareClinic": "اختر عيادة لايف كير", + "selectLiveCareClinic": "عيادة لايف كير", "checkingForExistingDentalPlan": "جاري التحقق من وجود خطة أسنان حالية، يرجى الانتظار...", "dentalTreatmentPlan": "خطة علاج الأسنان", "youHaveExistingTreatmentPlan": "لديك خطة علاج حالية: ", @@ -1618,5 +1620,74 @@ "sortByLocation": "الترتيب حسب الموقع", "timeForFirstReminder": "وقت التذكير الأول", "reminderRemovalNote": "يمكنك إزالتها من التقويم الخاص بك لاحقاً عن طريق إيقاف تشغيل التذكير", - "communicationLanguage": "لغة التواصل" + "communicationLanguage": "لغة التواصل", + + "cmcServiceHeader": "فحص صحي شامل: تشخيص متقدم، معلومات صحية مفصلة", + "cmcServiceDescription": "احصل على معلومات تفصيلية عن صحتك من خلال خدمات التشخيص المتقدمة لدينا. افهم جسمك بشكل أفضل لمستقبل صحي.", + "eReferralServiceHeader": "نظام الإحالة الإلكترونية في مستشفى حبيب: تبسيط عملية إحالة المرضى", + "eReferralServiceDescription": "نُسهّل عملية نقل المرضى بسلاسة إلى مستشفى حبيب من خلال نظام الإحالة الإلكترونية الآمن لدينا. نضمن استمرارية الرعاية لكل مريض.", + "bloodDonationServiceHeader": "تبرع بالدم، أنقذ الأرواح. تبرعك يُحدث فرقاً.", + "bloodDonationServiceDescription": "تبرّع بالدم، وأنقذ الأرواح. تبرعك يبعث الأمل. انضم لحملة التبرع بالدم وكن شريان حياة للمحتاجين. كل قطرة تُحدث فرقًا!", + "healthTrackersServiceHeader": "تتبّع مؤشراتك الحيوية بسهولة ويسر", + "healthTrackersServiceDescription": "أدخل بياناتك لمراقبة معدل ضربات القلب وضغط الدم بشكل مستمر، بالإضافة إلى ملخصات دقيقة لأنشطتك اليومية. ابقَ على اطلاع وحسّن صحتك بسهولة.", + "waterConsumptionServiceHeader": "حافظ على رطوبتك، حافظ على صحتك. تتبع كمية الماء التي تشربها يومياً بكل سهولة.", + "waterConsumptionServiceDescription": "أروِ عطشك، وتابع صحتك. راقب كمية الماء التي تتناولها يومياً بكل سهولة باستخدام تطبيقنا سهل الاستخدام، مما يضمن لك الترطيب الأمثل والصحة الجيدة.", + "smartWatchServiceHeader": "قم بمزامنة ساعتك الذكية مع تطبيقات الصحة", + "smartWatchServiceDescription": "قم بتوصيل ساعتك الذكية بسلاسة بتطبيقنا الصحي لتتبع البيانات بسهولة والحصول على رؤى شخصية.", + "liveChatServiceHeader": "مساعدة الخبراء على مدار الساعة طوال أيام الأسبوع\n\nمساعدة", + "liveChatServiceDescription": "هل تحتاج إلى مساعدة؟ تتيح لك خدمة الدردشة المباشرة لدينا التواصل مع فريق دعم الخبراء للإجابة على أي أسئلة لديك حول الميزات أو الإعدادات أو استكشاف الأخطاء وإصلاحها.", + "emergencyServiceHeader": "تسجيل الوصول إلى قسم الطوارئ، أسرع من أي وقت مضى. اتصل بالإسعاف / فريق الاستجابة السريعة على الفور", + "emergencyServiceDescription": "هل تواجه حالة طبية طارئة؟ سيارات الإسعاف وفرق الاستجابة السريعة لدينا جاهزة على مدار الساعة. بالإضافة إلى ذلك، يمكنك تسجيل دخولك إلى قسم الطوارئ بسرعة لتلقي رعاية أسرع.", + "homeHealthCareServiceHeader": "صحتك، في أبهى صورها. رعاية فائقة الجودة، تصلك إلى عتبة دارك.", + "homeHealthCareServiceDescription": "نقدم لكم رعاية صحية عالية الجودة تصلكم إلى عتبة منزلكم. ممرضات ذوات خبرة يقدمون رعاية حانية في راحة منزلكم.", + "profileOnlyText": "الملف الشخصي", + "information": "معلومة", + "noFavouriteDoctors": "ليس لديك أي قائمة مفضلة حتى الآن", + "addDoctors": "إضافة الأطباء", + "favouriteList": "قائمة المفضلة", + "later": "لاحقاً", + "cancelAppointmentConfirmMessage": "هل أنت متأكد من رغبتك في إلغاء هذا الموعد؟", + "acknowledged": "معترف به", + "searchLabResults": "بحث نتائج المختبر", + "callForAssistance": "اتصل للحصول على المساعدة الفورية", + "oneWaySubtitle": "نقل من الموقع إلى المستشفى", + "twoWaySubtitle": "نقل من الموقع إلى المستشفى والعودة مرة أخرى", + "toHospitalSubtitle": "نقل من موقعك الحالي إلى المستشفى", + "fromHospitalSubtitle": "نقل من المستشفى إلى منزلك", + "clinicSchedule": "جدول العيادة", + "doctorAddedToFavourite": "تمت إضافة إلى قائمة المفضلة", + "doctorRemovedFromFavourite": "تمت إزالة من قائمة الأطباء المفضلين", + "addNewFavorite": "إضافة مفضلة جديدة", + "favourites": "المفضلة", + "pendingAncillaryOrders": "مدفوعات طلبات الاضافية", + "liveCarePermissions": "يتطلب لايف كير أذونات الكاميرا والميكروفون وإشعارات، يرجى السماح لها بالمتابعة.", + "containsImage": "يحتوي على صورة", + "servicesSelected": "الخدمة (الخدمات) المختارة", + "selectMedicalFileFromFamily": "اختر ملف طبي من عائلتك", + "anyFileFromHMG": "أي ملف طبي نشط من مجموعة حبيب الطبية", + "noDistance": "المسافة غير متوفرة", + "enterAmount": "يرجى إدخال المبلغ للمتابعة.", + "selectHospitalContinue": "يرجى اختيار المستشفى للمتابعة.", + "year": "سنة", + "notifyPrescription": "أبلغني قبل وقت الاستهلاك", + "doctorRatingAppointment": "طبيب\nتصنيف", + "ratingSubmitted": "تم إرسال التقييم", + "rateTheDoctor": "يرجى تقييم الطبيب", + "downloadInvoice": "تحميل الفاتورة", + "approvalDetails": "تفاصيل الموافقة", + "obese": "سمين", + "overWeight": "وزن زائد", + "underWeight": "وزن ناقص", + "nearestGate": "أقرب بوابة", + "RRTDDetails": "فريق الاستجابة السريعة يقوم بتقديم رعاية صحية متكاملة لجميع الحالات الطارئة و المستقرة، الخدمة تشمل جميع الأعمار بما في ذلك حالات الأطفال.", + "fromHospitalSubtitle": "نقل من المستشفى إلى منزلك", + "normalMapType": "افتراضي", + "satelliteMapType": "قمر صناعي", + "terrainMapType": "تضاريس", + "hybridMapType": "هجين", + "openCamera": "افتح الكاميرا", + "openGallery": "افتح المعرض", + "openFiles": "فتح الملفات", + "generateAiAnalysisRadResult": "بإنشاء تحليل الذكاء الاصطناعي لهذا التقرير", + "grantLocationPermission": "يرجى منح إذن الوصول إلى الموقع من إعدادات التطبيق للحصول على نتائج أفضل." } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 9419b5a3..384b16d5 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -3,6 +3,8 @@ "arabic": "Arabic", "login": "Login", "noDataAvailable": "No Data Available", + "noRatingAvailable": "No Rating Available", + "doctorDoesNotHaveRating": "This doctor does not have any ratings yet.", "confirm": "Confirm", "loadingText": "Loading, please wait...", "kilometerUnit": "KM", @@ -300,7 +302,7 @@ "vitalSignsSubTitle": "Reports", "myMedical": "Active", "myMedicalSubtitle": "Medications", - "myDoctor": "My Doctor", + "myDoctor": "My Doctors", "myDoctorSubtitle": "List", "eye": "Eye", "eyeSubtitle": "Measurement", @@ -928,7 +930,7 @@ "hospitalInformation": "Hospital Information", "fetchingAppointmentShare": "Fetching Appointment Share...", "bookingYourAppointment": "Booking your appointment...", - "selectLiveCareClinic": "Select LiveCare Clinic", + "selectLiveCareClinic": "LiveCare Clinic", "checkingForExistingDentalPlan": "Checking for an existing dental plan, Please wait...", "dentalTreatmentPlan": "Dental treatment plan", "youHaveExistingTreatmentPlan": "You have an existing treatment plan: ", @@ -1239,7 +1241,7 @@ "quickLinks": "Quick Links", "viewMedicalFileLandingPage": "View medical file", "immediateLiveCareRequest": "Immediate LiveCare Request", - "yourTurnIsAfterPatients": "Your turn is after {count} patients.", + "yourTurnIsAfterPatients": "Your turn is after {count} patients. Please wait for your turn, The doctor will call you shortly.", "dontHaveHHCOrders": "You don't have any Home Health Care orders yet.", "hhcOrders": "HHC Orders", "requestedServices": "Requested Services", @@ -1307,7 +1309,7 @@ "rejectedYourFamilyMemberRequest": "{status} your family member request", "notAvailable": "N/A", "selectAProfile": "Please select a profile", - "switchFamilyFile": "Switch from the below list of medical file", + "switchFamilyFile": "Switch from the below list of medical files", "medicalFiles": "Medical Files", "addANewFamilyMember": "Add a new family member", "viewInvoiceDetails": "View invoice details", @@ -1611,5 +1613,73 @@ "sortByLocation": "Sort by location", "sortByNearestLocation": "Sort by nearest to your location", "giveLocationPermissionForNearestList": "Please grant location permission from app settings to see the nearest locations.", - "communicationLanguage": "Communication Language" + "communicationLanguage": "Communication Language", + "cmcServiceHeader": "Complete Health Checkup: Advanced diagnostics, Detailed Health insights", + "cmcServiceDescription": "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.", + "eReferralServiceHeader": "HMG Hospital E-Referral: Streamlined patient referrals", + "eReferralServiceDescription": "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.", + "bloodDonationServiceHeader": "Give Blood, Save Lives. Your donation makes a difference.", + "bloodDonationServiceDescription": "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!", + "healthTrackersServiceHeader": "Track Your Vitals with Ease and effortlessly ", + "healthTrackersServiceDescription": "Input your metrics for continuous heart rate monitoring, blood pressure and precise daily activity summaries. Stay informed and optimize your well-being with ease.", + "waterConsumptionServiceHeader": "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.", + "waterConsumptionServiceDescription": "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.", + "smartWatchServiceHeader": "Sync Your Smartwatch with Health Apps", + "smartWatchServiceDescription": "Seamlessly connect your smartwatch to our health app for effortless data tracking and personalized insights.", + "liveChatServiceHeader": "24/7 Expert\nAssistance", + "liveChatServiceDescription": "Need help ? Our live chat connects you with expert support for any questions about features, settings, or troubleshooting.", + "emergencyServiceHeader": "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly", + "emergencyServiceDescription": "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.", + "homeHealthCareServiceHeader": "Your Health, Elevated. Premium care, delivered to your doorstep.", + "homeHealthCareServiceDescription": "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.", + "profileOnlyText": "Profile", + "information": "Information", + "noFavouriteDoctors": "You don't have any favourite list yet", + "addDoctors": "Add Doctors", + "favouriteList": "Favourite List", + "later": "Later", + "cancelAppointmentConfirmMessage": "Are you sure you want to cancel this appointment?", + "acknowledged": "Acknowledged", + "searchLabResults": "Search lab results", + "callForAssistance": "Call for immediate assistance", + "oneWaySubtitle": "Pickup from location to hospital", + "twoWaySubtitle": "Round trip from location to hospital and back", + "toHospitalSubtitle": "Transfer from your current location to the hospital", + "fromHospitalSubtitle": "Transfer from the hospital back to your home", + "clinicSchedule": "Clinic Schedule", + "doctorAddedToFavourite": "Added to favorites list", + "doctorRemovedFromFavourite": "Removed from favorites list", + "favourites": "Favourites", + "addNewFavorite": "Add new favourite", + "pendingAncillaryOrders": "Pending ancillary orders", + "liveCarePermissions": "LiveCare requires Camera, Microphone & Notifications permissions to enable virtual consultation between patient & doctor, Open app settings to allow them.", + "containsImage": "Contains Image", + "servicesSelected": "Service(s) Selected", + "selectMedicalFileFromFamily": "Select a medical file from your family", + "anyFileFromHMG": "Any active medical file from HMG", + "noDistance": "Distance not available", + "enterAmount": "Please enter amount to continue.", + "selectHospitalContinue": "Please select hospital to continue.", + "year": "Year", + "notifyPrescription": "Notify me before the consumption time", + "doctorRatingAppointment": "Doctor\nRating", + "ratingSubmitted": "Rating Submitted", + "rateTheDoctor": "Please rate the doctor", + "downloadInvoice": "Download Invoice", + "approvalDetails": "Approval Details", + "obese": "Obese", + "overWeight": "OverWeight", + "underWeight": "UnderWeight", + "nearestGate": "Nearest Gate", + "RRTDDetails": "The Rapid response team provides a comprehensive medical service for all sorts of urgent and stable cases of all ages, including adult and paediatric patients.", + "fromHospitalSubtitle": "Transfer from the hospital back to your home", + "normalMapType": "Default", + "satelliteMapType": "Satellite", + "terrainMapType": "Terrain", + "hybridMapType": "Hybrid", + "openCamera": "Open Camera", + "openGallery": "Open Gallery", + "openFiles": "Open Files", + "generateAiAnalysisRadResult": "Generate AI analysis for this result", + "grantLocationPermission": "Please grant location permission from app settings to see better results." } diff --git a/ios/Runner/ring_30Sec.mp3 b/ios/Runner/ring_30Sec.mp3 new file mode 100644 index 00000000..b5bb4b88 Binary files /dev/null and b/ios/Runner/ring_30Sec.mp3 differ diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 2de4ce5c..374ac9d1 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -200,7 +200,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 1231755; + // body['PatientID'] = 1018977; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; // body['SessionID'] = "45786230487560q"; diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 73cb287f..b0df11f2 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -233,6 +233,11 @@ class ApiConsts { static String getAiOverViewLabOrders = "Services/Patients.svc/REST/HMGAI_Lab_Analyze_Orders_API"; static String getAiOverViewLabOrder = "Services/Patients.svc/REST/HMGAI_Lab_Analyzer_API"; + static String googleCloudStorageARTranslationFileURL = "https://storage.googleapis.com/hmg-patientapp-translations/ar-SA.json"; + static String googleCloudStorageENTranslationFileURL = "https://storage.googleapis.com/hmg-patientapp-translations/en-US.json"; + + static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; + // ************ static values for Api **************** static final double appVersionID = 20.9; @@ -450,6 +455,7 @@ var IS_LAST_APPOITMENT_RATED = 'Services/Doctors.svc/REST/IsLastAppoitmentRated_ var GET_APPOINTMENT_DETAILS_BY_NO = 'Services/MobileNotifications.svc/REST/GetAppointmentDetailsByApptNo'; var NEW_RATE_APPOINTMENT_URL = "Services/Doctors.svc/REST/AppointmentsRating_InsertAppointmentRate"; var NEW_RATE_DOCTOR_URL = "Services/Doctors.svc/REST/DoctorsRating_InsertDoctorRate"; +var GET_DOCTORS_RATING_CHECK = "Services/Doctors.svc/REST/GetDoctorsRatingCheck"; //var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID'; @@ -934,6 +940,8 @@ const PROFILE_SETTING = 'Services/Patients.svc/REST/GetPateintInfoForUpdate'; const SAVE_SETTING = 'Services/Patients.svc/REST/UpdatePateintInfo'; +const DOWNLOAD_INVOICE_PDF = 'Services/Notifications.svc/REST/DownloadInvoiceReport'; + class ApiKeyConstants { static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng'; } diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index c4c3c366..e4308bd5 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -174,7 +174,7 @@ class AppAssets { static const String warning = '$svgBasePath/warning.svg'; static const String share_location = '$svgBasePath/share_location.svg'; static const String to_arrow = '$svgBasePath/to_arrow.svg'; - static const String dual_arrow = '$svgBasePath/to_arrow.svg'; + static const String dual_arrow = '$svgBasePath/dual_arrow.svg'; static const String forward_arrow_medium = '$svgBasePath/forward_arrow_medium.svg'; static const String eReferral = '$svgBasePath/e-referral.svg'; static const String comprehensiveCheckup = '$svgBasePath/comprehensive_checkup.svg'; @@ -220,6 +220,7 @@ class AppAssets { static const String ibw = '$svgBasePath/ibw.svg'; static const String general_health = '$svgBasePath/general_health.svg'; static const String women_health = '$svgBasePath/women_health.svg'; + static const String add_new_family_icon = '$svgBasePath/add_new_family_icon.svg'; static const String height = '$svgBasePath/height.svg'; static const String weight = '$svgBasePath/weight.svg'; @@ -236,6 +237,7 @@ class AppAssets { static const String forward_top_nav_icon = '$svgBasePath/forward_top_nav_icon.svg'; static const String back_top_nav_icon = '$svgBasePath/back_top_nav_icon.svg'; static const String bluetooth = '$svgBasePath/bluetooth.svg'; + static const String calendar_filled_icon = '$svgBasePath/calendar_filled_icon.svg'; //smartwatch static const String watchActivity = '$svgBasePath/watch_activity.svg'; @@ -354,6 +356,7 @@ class AppAssets { static const String darkModeIcon = '$svgBasePath/dark_mode_icon.svg'; static const String biometricLockIcon = '$svgBasePath/biometric_lock_icon.svg'; static const String imageIcon = '$svgBasePath/image_icon.svg'; + static const String appointmentRatingIcon = '$svgBasePath/appointment_rating_icon.svg'; // PNGS // static const String hmgLogo = '$pngBasePath/hmg_logo.png'; @@ -366,6 +369,10 @@ class AppAssets { static const String emergencyService = '$pngBasePath/emergency_services_image.png'; static const String cmcService = '$pngBasePath/cmc_services_image.png'; static const String eReferralService = '$pngBasePath/ereferral_services_image.png'; + static const String carParkingService = '$pngBasePath/Carparking_services_image.png'; + static const String smartWatchService = '$pngBasePath/smartwatch_services_image.png'; + static const String healthTrackersService = '$pngBasePath/healthtrackers_services_image.png'; + static const String livechatService = '$pngBasePath/livechat_services_image.png'; static const String maleImg = '$pngBasePath/male_img.png'; static const String femaleImg = '$pngBasePath/female_img.png'; diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 7d3ab1a4..a5a8d72a 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -175,4 +175,78 @@ class AppState { setRatedVisible(bool value) { isRatedVisible = value; } + + Map _userSwitchPayloads = {}; + int _childSwitchCounter = 0; + + Map get getUserSwitchPayloads => _userSwitchPayloads; + + void addUserSwitchPayload(dynamic payload, {bool isSuperUser = false}) { + var patientID = payload is Map ? payload['patientid'] : null; + + if (patientID == null) { + if (isSuperUser) { + _userSwitchPayloads['superUser'] = payload; + _childSwitchCounter = 0; + } else { + _childSwitchCounter++; + if (_childSwitchCounter == 1) { + _userSwitchPayloads['child'] = payload; + } else { + _userSwitchPayloads['child$_childSwitchCounter'] = payload; + } + } + return; + } + + String? existingKey; + for (var entry in _userSwitchPayloads.entries) { + if (entry.value is Map && entry.value['PatientID'] == patientID) { + existingKey = entry.key; + break; + } + } + + if (isSuperUser) { + if (existingKey != null && existingKey != 'superUser') { + _userSwitchPayloads.remove(existingKey); + } + _userSwitchPayloads['superUser'] = payload; + _childSwitchCounter = 0; + } else { + if (existingKey != null) { + _userSwitchPayloads[existingKey] = payload; + } else { + _childSwitchCounter++; + if (_childSwitchCounter == 1) { + _userSwitchPayloads['child'] = payload; + } else { + _userSwitchPayloads['child$_childSwitchCounter'] = payload; + } + } + } + } + + + dynamic getUserSwitchPayload({required String userType}) { + return _userSwitchPayloads[userType]; + } + + dynamic getUserSwitchPayloadByIndex({bool isSuperUser = false, int? childIndex}) { + if (isSuperUser) { + return _userSwitchPayloads['superUser']; + } else if (childIndex != null) { + if (childIndex == 1) { + return _userSwitchPayloads['child']; + } else { + return _userSwitchPayloads['child$childIndex']; + } + } + return null; + } + + void clearUserSwitchPayloads() { + _userSwitchPayloads.clear(); + _childSwitchCounter = 0; + } } diff --git a/lib/core/location_util.dart b/lib/core/location_util.dart index 95b154ba..c56c63d4 100644 --- a/lib/core/location_util.dart +++ b/lib/core/location_util.dart @@ -99,7 +99,7 @@ class LocationUtils { title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, child: Utils.getWarningWidget( - loadingText: "Please grant location permission from app settings to see better results", + loadingText: LocaleKeys.grantLocationPermission.tr(), isShowActionButtons: true, onCancelTap: () { navigationService.pop(); @@ -124,7 +124,7 @@ class LocationUtils { title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, child: Utils.getWarningWidget( - loadingText: "Please grant location permission from app settings to see better results", + loadingText: LocaleKeys.grantLocationPermission.tr(), isShowActionButtons: true, onCancelTap: () { navigationService.pop(); @@ -285,7 +285,7 @@ class LocationUtils { title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, child: Utils.getWarningWidget( - loadingText: "Please grant location permission from app settings to see better results", + loadingText: LocaleKeys.grantLocationPermission.tr(), isShowActionButtons: true, onCancelTap: () { navigationService.pop(); diff --git a/lib/core/utils/loading_utils.dart b/lib/core/utils/loading_utils.dart index bd24d30c..48d4f2dd 100644 --- a/lib/core/utils/loading_utils.dart +++ b/lib/core/utils/loading_utils.dart @@ -15,37 +15,37 @@ class LoadingUtils { static bool get isLoading => _isLoadingVisible; - static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) { - if (!_isLoadingVisible) { - _isLoadingVisible = true; - final context = _navigationService.navigatorKey.currentContext; - log("got the context in showFullScreenLoading"); - if (context == null) return; - - showDialog( - barrierDismissible: barrierDismissible, - context: context, - barrierColor: AppColors.blackColor.withOpacity(0.5), - useRootNavigator: false, - useSafeArea: false, - builder: (BuildContext context) { - return Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - ), - child: Material( - child: Center( - child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText), - ).paddingSymmetrical(24.w, 0), - ), - ); - }).then((value) { - _isLoadingVisible = false; - }); - } - } + // static showFullScreenLoader({bool barrierDismissible = true, isSuccessDialog = false, String loadingText = "Loading, Please wait..."}) { + // if (!_isLoadingVisible) { + // _isLoadingVisible = true; + // final context = _navigationService.navigatorKey.currentContext; + // log("got the context in showFullScreenLoading"); + // if (context == null) return; + // + // showDialog( + // barrierDismissible: barrierDismissible, + // context: context, + // barrierColor: AppColors.blackColor.withOpacity(0.5), + // useRootNavigator: false, + // useSafeArea: false, + // builder: (BuildContext context) { + // return Container( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, + // borderRadius: 20.h, + // hasShadow: false, + // ), + // child: Material( + // child: Center( + // child: isSuccessDialog ? Utils.getSuccessWidget(loadingText: loadingText) : Utils.getLoadingWidget(loadingText: loadingText), + // ).paddingSymmetrical(24.w, 0), + // ), + // ); + // }).then((value) { + // _isLoadingVisible = false; + // }); + // } + // } static hideFullScreenLoader() { if (!_isLoadingVisible) return; diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart index d438d449..e2925c29 100644 --- a/lib/core/utils/push_notification_handler.dart +++ b/lib/core/utils/push_notification_handler.dart @@ -396,10 +396,10 @@ class PushNotificationHandler { final deviceInfo = DeviceInfoPlugin(); final androidInfo = await deviceInfo.androidInfo; - int sdkInt = androidInfo.version.sdkInt ?? 0; - if (sdkInt >= 33) { - await FlutterCallkitIncoming.requestFullIntentPermission(); - } + // int sdkInt = androidInfo.version.sdkInt ?? 0; + // if (sdkInt >= 33) { + // await FlutterCallkitIncoming.requestFullIntentPermission(); + // } } } diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index f63b160b..feca055a 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -152,9 +152,11 @@ class Utils { static String getDayMonthYearDateFormatted(DateTime? dateTime) { if (dateTime == null) return ""; - return appState.isArabic() - ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}" - : "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}"; + return + // appState.isArabic() + // ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}" + // : + "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}"; } /// get month by @@ -353,7 +355,7 @@ class Utils { repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill), SizedBox(height: 16.h), (noDataText ?? LocaleKeys.noDataAvailable.tr()) - .toText14(weight: FontWeight.w500, color: AppColors.greyTextColor, isCenter: true) + .toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true) .paddingSymmetrical(64.w, 0.h), SizedBox(height: 16.h), callToActionButton @@ -369,7 +371,7 @@ class Utils { Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill), SizedBox(height: 8.h), - (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true), + (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true, isBold: true), SizedBox(height: 8.h), ], ).center; @@ -382,7 +384,7 @@ class Utils { children: [ Lottie.asset(AppAnimations.checkmark, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox(height: 8.h), - (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h), + (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isBold: true).paddingSymmetrical(24.h, 0.h), SizedBox(height: 8.h), ], ).center; @@ -768,6 +770,7 @@ class Utils { return Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, + spacing: 5.w, children: [ Image.asset(AppAssets.mada, width: 35.h, height: 35.h), Image.asset( diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 3ad0ad92..5db5b289 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -24,7 +24,7 @@ extension CapExtension on String { extension EmailValidator on String { Widget get toWidget => Text(this); - Widget toText8({Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text( + Widget toText8({bool isEnglishOnly = false, Color? color, FontWeight? fontWeight, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text( this, maxLines: maxlines, overflow: textOverflow, @@ -34,6 +34,7 @@ extension EmailValidator on String { fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: 0, + fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', ), ); @@ -60,7 +61,7 @@ extension EmailValidator on String { color: color ?? AppColors.blackColor, letterSpacing: letterSpacing, decoration: isUnderLine ? TextDecoration.underline : null, - fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins', + fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', decorationColor: color ?? AppColors.blackColor), ); @@ -73,13 +74,14 @@ extension EmailValidator on String { int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow, - double letterSpacing = 0}) => + double letterSpacing = 0, bool isEnglishOnly = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: maxlines, overflow: textOverflow, style: TextStyle( + fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', fontSize: 9.f, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), @@ -89,7 +91,7 @@ extension EmailValidator on String { decorationColor: color ?? AppColors.blackColor), ); - Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0}) => Text( + Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0, bool isEnglishOnly = false,}) => Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: (maxLine > 0) ? maxLine : null, @@ -100,6 +102,7 @@ extension EmailValidator on String { color: color ?? AppColors.blackColor, letterSpacing: letterSpacing, decoration: isUnderLine ? TextDecoration.underline : null, + fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', ), ); @@ -125,7 +128,7 @@ extension EmailValidator on String { height: height, decorationColor: isUnderLine ? AppColors.blackColor : null, decoration: isUnderLine ? TextDecoration.underline : null, - fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins', + fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', ), ); @@ -208,7 +211,7 @@ extension EmailValidator on String { height: height, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null, - fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'), + fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'), decorationColor: color ?? AppColors.blackColor), ); @@ -249,14 +252,14 @@ extension EmailValidator on String { overflow: textOverflow, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null, - fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'), + fontFamily: fontFamily ?? (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'), decorationColor: decorationColor ?? AppColors.errorColor), ); Widget toText17({bool isEnglishOnly = false, Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'), + style: TextStyle(color: color ?? AppColors.blackColor, fontSize: 17.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'), ); Widget toText18({Color? color, FontWeight? weight, bool isBold = false, bool isCenter = false, int? maxlines, TextOverflow? textOverflow, bool isEnglishOnly = false,}) => Text( @@ -264,7 +267,7 @@ extension EmailValidator on String { textAlign: isCenter ? TextAlign.center : null, this, overflow: textOverflow, - style: TextStyle(fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),), + style: TextStyle(fontSize: 18.f, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),), ); Widget toText19({Color? color, bool isBold = false}) => Text( @@ -294,36 +297,38 @@ extension EmailValidator on String { style: TextStyle(height: 1, color: color ?? AppColors.blackColor, fontSize: 22.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); - Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing}) => Text( + Widget toText24({Color? color, bool isBold = false, bool isCenter = false, FontWeight? fontWeight, double? letterSpacing, bool isEnglishOnly = false,}) => Text( this, textAlign: isCenter ? TextAlign.center : null, style: TextStyle( + fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'), height: 23 / 24, color: color ?? AppColors.blackColor, fontSize: 24.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.normal), ); - Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing}) => Text( + Widget toText26({Color? color, bool isBold = false, double? height, bool isCenter = false, FontWeight? weight, double? letterSpacing, bool isEnglishOnly = false,}) => Text( this, textAlign: isCenter ? TextAlign.center : null, style: TextStyle( + fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'), height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.f, letterSpacing: letterSpacing ?? -1, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), ); Widget toText28({bool isEnglishOnly = false, Color? color, bool isBold = false, double? height, bool isCenter = false, double? letterSpacing}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'), + style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.f, letterSpacing: letterSpacing ?? -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'), ); Widget toText32({bool isEnglishOnly = false, FontWeight? weight, Color? color, bool isBold = false, bool isCenter = false}) => Text( this, textAlign: isCenter ? TextAlign.center : null, style: TextStyle( - height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontFamily: isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins', fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal), + height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 32.f, letterSpacing: -1, fontFamily: getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins', fontWeight: isBold ? FontWeight.bold : weight ?? FontWeight.normal), ); Widget toText44({Color? color, bool isBold = false, bool isEnglishOnly = false,}) => Text( this, - style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'),), + style: TextStyle(height: 32 / 32, color: color ?? AppColors.blackColor, fontSize: 44.f, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, fontFamily: (isEnglishOnly ? "Poppins" : getIt.get().getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'),), ); Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { @@ -429,7 +434,7 @@ extension DynamicTextStyleExtension on BuildContext { String? fontFamily, bool isLanguageSwitcher = false}) { AppState appState = getIt.get(); - final family = appState.getLanguageCode() == "ar" ? 'GESSTwo' : 'Poppins'; + final family = appState.getLanguageCode() == "ar" ? 'CairoArabic' : 'Poppins'; return TextStyle( fontFamily: fontFamily ?? family, fontSize: fontSize, @@ -460,7 +465,7 @@ class FontUtils { /// Get the appropriate font family for a specific language static String getFontFamilyForLanguage(bool isArabic) { - return isArabic ? 'GESSTwo' : 'Poppins'; + return isArabic ? 'CairoArabic' : 'Poppins'; } } diff --git a/lib/features/authentication/authentication_repo.dart b/lib/features/authentication/authentication_repo.dart index cd8ad70d..41664758 100644 --- a/lib/features/authentication/authentication_repo.dart +++ b/lib/features/authentication/authentication_repo.dart @@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart'; @@ -214,56 +215,6 @@ class AuthenticationRepoImp implements AuthenticationRepo { int? patientID, int? loginType}) async { AppState appState = getIt.get(); - // if (isRegister) { - // newRequest["activationCode"] = activationCode ?? "0000"; - // newRequest["isSilentLogin"] = activationCode != null ? false : true; - // } else { - // newRequest.activationCode = activationCode ?? "0000"; - // newRequest.isSilentLogin = activationCode != null ? false : true; - // newRequest.projectOutSA = newRequest.zipCode == '966' ? false : true; - // newRequest.isDentalAllowedBackend = false; - // newRequest.forRegisteration = newRequest.isRegister ?? false; - // newRequest.isRegister = false; - // } - // Map familyRequest = {}; - // if (isFormFamilyFile) { - // AppState appState = getIt.get(); - // familyRequest = {}; - // familyRequest['PatientShareRequestID'] = patientShareRequestID; - // familyRequest['ResponseID'] = responseID; - // familyRequest['Status'] = 3; - // familyRequest["PatientID"] = appState.getAuthenticatedUser()!.patientId ?? 0; - // familyRequest["LogInTokenID"] = appState.getFamilyFileTokenID; - // familyRequest["activationCode"] = activationCode ?? "0000"; - // familyRequest["PatientMobileNumber"] = newRequest.patientMobileNumber; - // familyRequest["PatientIdentificationID"] = newRequest.patientIdentificationID; - // } - // Map switchRequest = {}; - // if (isSwitchUser) { - // switchRequest = newRequest.toJson(); - // - // switchRequest['PatientID'] = responseID; - // switchRequest['IsSilentLogin'] = true; - // switchRequest['LogInTokenID'] = null; - // switchRequest['SearchType'] = 2; - // if (loginType != 0) { - // switchRequest['SuperUser'] = patientID; - // switchRequest['DeviceToken'] = null; - // } else { - // switchRequest["LoginType"] = 2; - // } - // - // if (appState.getSuperUserID == responseID) { - // // switchRequest['LoginType'] = 3; - // switchRequest['PatientIdentificationID'] = ""; - // // switchRequest['ProjectOutSA'] = newRequest.zipCode == '966' ? false : true; - // switchRequest.remove('NationalID'); - // switchRequest.remove('isDentalAllowedBackend'); - // switchRequest.remove('ProjectOutSA'); - // switchRequest.remove('ForRegisteration'); - // appState.setSuperUserID = null; - // } - // } if (isRegister) { newRequest["activationCode"] = activationCode ?? "0000"; @@ -321,7 +272,6 @@ class AuthenticationRepoImp implements AuthenticationRepo { } } - // Determine which API to use based on zipCode final zipCode = _getZipCodeFromRequest(newRequest); final isOthersCountry = zipCode == '0'; final endpoint = isFormFamilyFile @@ -336,15 +286,40 @@ class AuthenticationRepoImp implements AuthenticationRepo { GenericApiModel? apiResponse; Failure? failure; + // Determine the payload to send + var requestBody = isFormFamilyFile + ? familyRequest + : isRegister + ? newRequest + : isSwitchUser + ? switchRequest + : newRequest.toJson(); + + if (isSwitchUser) { + Map allUsersPayload; + var switchingPatientID; + var superUserPatientID; + bool isSwitchingToSuperUser = false; + + allUsersPayload = appState.getUserSwitchPayloads; + switchingPatientID = switchRequest["PatientID"]; + superUserPatientID = allUsersPayload['superUser']['PatientID']; + isSwitchingToSuperUser = (appState.getSuperUserID == responseID) || (allUsersPayload.containsKey('superUser') && superUserPatientID == switchingPatientID); + + if (isSwitchingToSuperUser) { + var superUserPayload = appState.getUserSwitchPayloadByIndex(isSuperUser: true); + if (superUserPayload != null) { + requestBody['DeviceToken'] = newRequest.deviceToken; + requestBody['ProjectOutSA'] = 0; + requestBody['PatientIdentificationID'] = ""; + requestBody.removeWhere((key, value) => ['NationalID', 'isDentalAllowedBackend', 'ForRegisteration'].contains(key)); + } + } + } + await apiClient.post( endpoint, - body: isFormFamilyFile - ? familyRequest - : isRegister - ? newRequest - : isSwitchUser - ? switchRequest - : newRequest.toJson(), + body: requestBody, onFailure: (error, statusCode, {messageStatus, failureType}) { failure = failureType; }, @@ -611,7 +586,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { } @override - Future>> getServicePrivilege() { + Future> getServicePrivilege() { Map mapDevice = {}; try { GenericApiModel? apiResponse; @@ -692,6 +667,7 @@ class AuthenticationRepoImp implements AuthenticationRepo { @override Future>> getPatientBloodType() async { + // Map requestBody = {"PatientType": 1, "PatientTypeID": 1, "TokenID": "@dm!n", "PatientID": 5957754}; Map requestBody = {}; try { GenericApiModel? apiResponse; diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart index c0bc4dd1..2055deea 100644 --- a/lib/features/authentication/authentication_view_model.dart +++ b/lib/features/authentication/authentication_view_model.dart @@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/features/authentication/models/resp_models/s import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_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/authentication/saved_login_screen.dart'; @@ -100,7 +101,10 @@ class AuthenticationViewModel extends ChangeNotifier { var healthId; int getDeviceLastLogin = 1; + bool isInitialLoginNavigated = false; + Future onLoginPressed() async { + setIsInitialLoginNavigated(false); try { LoaderBottomSheet.showLoader(); //TODO: We will remove this delay @@ -110,7 +114,7 @@ class AuthenticationViewModel extends ChangeNotifier { if (data != null) { await _handleExistingImeiData(data); } else { - await _handleNewImeiRegistration(); + await handleNewImeiRegistration(); } } catch (e) { log("Error in onLoginPressed: $e"); @@ -131,6 +135,8 @@ class AuthenticationViewModel extends ChangeNotifier { pickedCountryByUAEUser = null; _appState.setUserRegistrationPayload = RegistrationDataModelPayload(); _appState.setNHICUserData = CheckUserStatusResponseNHIC(); + getIt.get().setSelectedHeight(0); + getIt.get().setSelectedWeight(0); } void onCountryChange(CountryEnum country) { @@ -196,6 +202,11 @@ class AuthenticationViewModel extends ChangeNotifier { } } + setIsInitialLoginNavigated(bool isInitialLoginNavigated) { + this.isInitialLoginNavigated = isInitialLoginNavigated; + notifyListeners(); + } + void onTermAccepted() { isTermsAccepted = !isTermsAccepted; notifyListeners(); @@ -213,7 +224,7 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken); } - Future selectDeviceImei({required Function(dynamic data) onSuccess, Function(String)? onError}) async { + Future selectDeviceImei({bool isNavigate = true, required Function(dynamic data) onSuccess, Function(String)? onError}) async { // LoadingUtils.showFullScreenLoading(); // String firebaseToken = _appState.deviceToken; String firebaseToken = await Utils.getStringFromPrefs(CacheConst.pushToken); @@ -228,7 +239,9 @@ class AuthenticationViewModel extends ChangeNotifier { // LoadingUtils.hideFullScreenLoader(); // await _errorHandlerService.handleError(failure: failure); LoaderBottomSheet.hideLoader(); - _navigationService.pushPage(page: LoginScreen()); + if (isNavigate) { + _navigationService.pushPage(page: LoginScreen()); + } }, (apiResponse) { // LoadingUtils.hideFullScreenLoader(); @@ -260,10 +273,13 @@ class AuthenticationViewModel extends ChangeNotifier { } } - Future _handleNewImeiRegistration() async { - await selectDeviceImei(onSuccess: (dynamic respData) async { - try { - if (respData != null) { + Future handleNewImeiRegistration({bool isNavigate = true}) async { + print("handleNewImeiRegistration called"); + await selectDeviceImei( + isNavigate: isNavigate, + onSuccess: (dynamic respData) async { + try { + if (respData != null) { dynamic data = await SelectDeviceByImeiRespModelElement.fromJson(respData.toJson()); _appState.setSelectDeviceByImeiRespModelElement(data); LoaderBottomSheet.hideLoader(); @@ -271,19 +287,24 @@ class AuthenticationViewModel extends ChangeNotifier { // TODO: Navigate to SavedLogin when available // SelectDeviceByImeiRespModelElement savedData = // SelectDeviceByImeiRespModelElement.fromJson(respData); - _navigationService.pushPage(page: SavedLogin()); - // _navigationService.pushPage(page: LoginScreen()); - } else { - print("print login........"); - LoaderBottomSheet.hideLoader(); - _navigationService.pushPage(page: LoginScreen()); - } - } catch (e) { - log("Error processing IMEI registration response: $e"); - LoaderBottomSheet.hideLoader(); - _navigationService.pushPage(page: LoginScreen()); - } - }, onError: (String error) { + if (!isInitialLoginNavigated) { + _navigationService.pushPage(page: SavedLogin()); + } + // _navigationService.pushPage(page: LoginScreen()); + } else { + if (isNavigate) { + LoaderBottomSheet.hideLoader(); + _navigationService.pushPage(page: LoginScreen()); + } + } + } catch (e) { + if (isNavigate) { + log("Error processing IMEI registration response: $e"); + LoaderBottomSheet.hideLoader(); + _navigationService.pushPage(page: LoginScreen()); + } + } + }, onError: (String error) { LoaderBottomSheet.hideLoader(); _dialogService.showErrorBottomSheet(message: error, onOkPressed: () {}); }); @@ -382,7 +403,7 @@ class AuthenticationViewModel extends ChangeNotifier { mobileNumber: phoneNumber, selectedLoginType: otpTypeEnum.toInt(), zipCode: selectedCountrySignup.countryCode, - nationalId:nationalIdOrFileNumber, + nationalId: nationalIdOrFileNumber, isFileNo: isForRegister ? isPatientHasFile(request: payload) : false, patientId: isFormFamilyFile ? _appState.getAuthenticatedUser()!.patientId : 0, isForRegister: isForRegister, @@ -612,13 +633,21 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setSuperUserID = null; _appState.setIsChildLoggedIn = false; activation.list!.first.isParentUser = true; + var allUsersPayload = _appState.getUserSwitchPayloads; + if ((allUsersPayload.containsKey('superUser') && allUsersPayload['superUser'] != null)) { + _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: false); + } else { + _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: true); + } } else { _appState.setSuperUserID = _appState.getAuthenticatedUser()?.patientId; _appState.setIsChildLoggedIn = true; activation.list!.first.isParentUser = false; + _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: false); } } else { activation.list!.first.isParentUser = true; + _appState.addUserSwitchPayload(activation.list!.first.toJson(), isSuperUser: true); } activation.list!.first.bloodGroup = activation.patientBloodType; activation.list!.first.zipCode = selectedCountrySignup == CountryEnum.others ? '0' : selectedCountrySignup.countryCode; @@ -627,7 +656,7 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setUserBloodGroup = activation.patientBlodType ?? "N/A"; // Fetch patient blood type from new API - await getPatientBloodTypeNew(); + // await getPatientBloodTypeNew(); } // _appState.setUserBloodGroup = (activation.patientBlodType ?? ""); _appState.setAppAuthToken = activation.authenticationTokenId; @@ -719,6 +748,8 @@ class AuthenticationViewModel extends ChangeNotifier { } Future navigateToHomeScreen() async { + setIsInitialLoginNavigated(true); + getPatientBloodTypeNew(); Navigator.pushAndRemoveUntil( _navigationService.navigatorKey.currentContext!, CustomPageRoute( @@ -862,17 +893,17 @@ class AuthenticationViewModel extends ChangeNotifier { resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { if (apiResponse.data is String) { //TODO: This Section Need to Be Testing. - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); _dialogService.showExceptionBottomSheet(message: apiResponse.data, onOkPressed: () {}, onCancelPressed: () {}); //TODO: Here We Need to Show a Dialog Of Something in the case of Fail With OK and Cancel and the Display Variable WIll be result. } else { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); if (apiResponse.data["MessageStatus"] == 1) { - LoadingUtils.showFullScreenLoader(isSuccessDialog: true, loadingText: "Your medical file has been created successfully. \nPlease proceed to login."); + LoaderBottomSheet.showLoader(loadingText: "Your medical file has been created successfully. \nPlease proceed to login."); //TODO: Here We Need to Show a Dialog Of Something in the case of Success. await clearDefaultInputValues(); // This will Clear All Default Values Of User. Future.delayed(Duration(seconds: 1), () { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); // _navigationService.pushAndReplace(AppRoutes.loginScreen); _navigationService.pushAndRemoveUntil(CustomPageRoute(page: LandingNavigation()), (r) => false); _navigationService.push(CustomPageRoute(page: LoginScreen())); @@ -1015,7 +1046,9 @@ class AuthenticationViewModel extends ChangeNotifier { nationalId: _appState.getAuthenticatedUser()!.patientIdentificationNo, gender: _appState.getAuthenticatedUser()!.gender) .toJson()); - resultEither.fold((failure) async => await _errorHandlerService.handleError(failure: failure), (apiResponse) async { + resultEither.fold((failure) async { + // await _errorHandlerService.handleError(failure: failure); + }, (apiResponse) async { if (apiResponse.messageStatus == 2) {} if (apiResponse.messageStatus == 1) { log("Insert Device Data Success"); @@ -1149,6 +1182,7 @@ class AuthenticationViewModel extends ChangeNotifier { _appState.setVidaPlusProjectList([]); _appState.setHMCProjectList([]); _appState.setProjectsDetailList([]); + _appState.clearUserSwitchPayloads(); await clearDefaultInputValues(); _navigationService.pushAndReplace(AppRoutes.landingScreen); } catch (e) { @@ -1168,7 +1202,7 @@ class AuthenticationViewModel extends ChangeNotifier { (apiResponse) { if (apiResponse.messageStatus == 1 && apiResponse.data != null) { // Extract blood type from response - String? bloodType = apiResponse.data['GetPatientBloodType']; + String? bloodType = apiResponse.data['GetPatientBloodType'][0]["BloodType"]; if (bloodType != null && bloodType.isNotEmpty) { _appState.setUserBloodGroup = bloodType; log("Blood type updated from new API: $bloodType"); diff --git a/lib/features/blood_donation/widgets/hospital_selection.dart b/lib/features/blood_donation/widgets/hospital_selection.dart index f49b42d6..c7313228 100644 --- a/lib/features/blood_donation/widgets/hospital_selection.dart +++ b/lib/features/blood_donation/widgets/hospital_selection.dart @@ -26,7 +26,7 @@ class HospitalBottomSheetBodySelection extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if(!isHideTitle) LocaleKeys.selectHospital.tr(context: context).toText16(weight: FontWeight.w500, color: AppColors.greyTextColor), + if(!isHideTitle) LocaleKeys.selectHospital.tr(context: context).toText16(isBold: true, color: AppColors.greyTextColor), SizedBox(height: 16.h), SizedBox( height: MediaQuery.sizeOf(context).height * .4, @@ -75,7 +75,8 @@ class HospitalBottomSheetBodySelection extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.hmg).paddingOnly(right: 10), Expanded( - child: Text(hospital.projectName ?? "", style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16.f, color: AppColors.blackColor)), + child: (hospital.projectName ?? "").toString().toText16(isBold: true, color: AppColors.blackColor), + // Text(hospital.projectName ?? "", style: TextStyle(isBold: true, fontSize: 16.f, color: AppColors.blackColor)), ) ], ); diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index afc9b568..6f2092f9 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/location_util.dart'; @@ -11,6 +12,7 @@ import 'package:hmg_patient_app_new/core/utils/loading_utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; 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/book_appointments/book_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/LaserCategoryType.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/free_slot.dart'; @@ -139,6 +141,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { bool applyFilters = false; bool isWaitingAppointmentAvailable = false; + bool isPatientRescheduleAppointment = false; bool isWaitingAppointmentSelected = false; int waitingAppointmentProjectID = 0; @@ -150,6 +153,72 @@ class BookAppointmentsViewModel extends ChangeNotifier { AppointmentNearestGateResponseModel? appointmentNearestGateResponseModel; ///variables for laser clinic + bool isLaserHospitalsLoading = false; + List laserHospitalsList = []; + int laserHospitalHmgCount = 0; + int laserHospitalHmcCount = 0; + + Future getLaserHospitals({Function(dynamic)? onSuccess, Function(String)? onError}) async { + isLaserHospitalsLoading = true; + laserHospitalsList.clear(); + laserHospitalHmgCount = 0; + laserHospitalHmcCount = 0; + notifyListeners(); + + final result = await bookAppointmentsRepo.getDoctorsList(253, 0, false, 0, ''); + + result.fold( + (failure) async { + isLaserHospitalsLoading = false; + notifyListeners(); + onError?.call(failure.message); + }, + (apiResponse) async { + if (apiResponse.messageStatus == 1) { + var doctorList = apiResponse.data!; + var regionList = await DoctorMapper.getMappedDoctor( + doctorList, + isArabic: _appState.isArabic(), + lat: _appState.userLat, + long: _appState.userLong, + ); + + var isLocationEnabled = (_appState.userLat != 0) && (_appState.userLong != 0); + regionList = await DoctorMapper.sortList(isLocationEnabled, regionList); + + // Flatten all hospitals across all regions into a single list + laserHospitalsList.clear(); + Set addedHospitals = {}; + regionList.registeredDoctorMap?.forEach((region, regionData) { + for (var hospital in regionData?.hmgDoctorList ?? []) { + if (!addedHospitals.contains(hospital.filterName)) { + addedHospitals.add(hospital.filterName ?? ''); + laserHospitalsList.add(hospital); + } + } + for (var hospital in regionData?.hmcDoctorList ?? []) { + if (!addedHospitals.contains(hospital.filterName)) { + addedHospitals.add(hospital.filterName ?? ''); + laserHospitalsList.add(hospital); + } + } + }); + + laserHospitalHmgCount = laserHospitalsList.where((h) => h.isHMC != true).length; + laserHospitalHmcCount = laserHospitalsList.where((h) => h.isHMC == true).length; + + isLaserHospitalsLoading = false; + notifyListeners(); + onSuccess?.call(apiResponse); + } else { + isLaserHospitalsLoading = false; + notifyListeners(); + onError?.call(apiResponse.errorMessage ?? 'Unknown error'); + } + }, + ); + } + List femaleLaserCategory = [ LaserCategoryType(1, 'bodyString'), LaserCategoryType(2, 'face'), @@ -157,7 +226,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { LaserCategoryType(11, 'retouch'), ]; List maleLaserCategory = [ - LaserCategoryType(1, 'body'), + LaserCategoryType(1, 'bodyString'), LaserCategoryType(2, 'face'), LaserCategoryType(11, 'retouch'), ]; @@ -262,6 +331,42 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + void filterDoctors(String query) { + if (query.isEmpty) { + filteredDoctorList = List.from(doctorsList); + _groupDoctorsList(); + setIsNearestAppointmentSelected(isNearestAppointmentSelected); + notifyListeners(); + return; + } + + filteredDoctorList = doctorsList.where((doctor) { + final name = (doctor.name ?? '').toLowerCase(); + final clinicName = (doctor.clinicName ?? '').toLowerCase(); + final projectName = (doctor.projectName ?? '').toLowerCase(); + final q = query.toLowerCase(); + return name.contains(q) || clinicName.contains(q) || projectName.contains(q); + }).toList(); + + // Re-group filtered list + final clinicMap = >{}; + final hospitalMap = >{}; + + for (var doctor in filteredDoctorList) { + final clinicKey = (doctor.clinicName ?? 'Unknown').trim(); + clinicMap.putIfAbsent(clinicKey, () => []).add(doctor); + + final hospitalKey = (doctor.projectName ?? 'Unknown').trim(); + hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor); + } + + doctorsListByClinic = clinicMap.values.toList(); + doctorsListByHospital = hospitalMap.values.toList(); + doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital; + + notifyListeners(); + } + void filterClinics(String? query) { if (query!.isEmpty) { _filteredClinicsList = List.from(clinicsList); @@ -289,14 +394,19 @@ class BookAppointmentsViewModel extends ChangeNotifier { isContinueDentalPlan = false; isChiefComplaintsListLoading = true; isWaitingAppointmentSelected = false; + isPatientRescheduleAppointment = false; bodyTypes = [maleLaserCategory, femaleLaserCategory]; // getLocation(); notifyListeners(); } + refreshDoctorsList() { + setIsDoctorsListLoading(true); + getDoctorsList(isNearest: isNearestAppointmentSelected); + } + setIsNearestAppointmentSelected(bool isNearestAppointmentSelected) { this.isNearestAppointmentSelected = isNearestAppointmentSelected; - if (isNearestAppointmentSelected) { for (var group in doctorsListGrouped) { group.sort((a, b) { @@ -427,6 +537,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); } + setIsPatientRescheduleAppointment(bool isPatientRescheduleAppointment) { + this.isPatientRescheduleAppointment = isPatientRescheduleAppointment; + notifyListeners(); + } + void onTabChanged(int index) { calculationID = null; isGetDocForHealthCal = false; @@ -535,8 +650,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { } //TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName - Future getDoctorsList({int projectID = 0, bool isNearest = true, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async { + Future getDoctorsList({int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", Function(dynamic)? onSuccess, Function(String)? onError}) async { doctorsList.clear(); + filteredDoctorList.clear(); + doctorsListGrouped.clear(); + notifyListeners(); projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID; final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, doctorName.isNotEmpty ? false : isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan); @@ -560,7 +678,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { clearSearchFilters(); getFiltersFromDoctorList(); _groupDoctorsList(); - setIsNearestAppointmentSelected(true); + setIsNearestAppointmentSelected(isNearest); notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -652,12 +770,14 @@ class BookAppointmentsViewModel extends ChangeNotifier { notifyListeners(); // Check if doctor is favorite after getting profile - checkIsFavouriteDoctor( - patientID: _appState.getAuthenticatedUser()!.patientId!, - projectID: doctorsProfileResponseModel.projectID ?? 0, - clinicID: doctorsProfileResponseModel.clinicID ?? 0, - doctorID: doctorsProfileResponseModel.doctorID ?? 0, - ); + if(_appState.isAuthenticated) { + checkIsFavouriteDoctor( + patientID: _appState.getAuthenticatedUser()!.patientId!, + projectID: doctorsProfileResponseModel.projectID ?? 0, + clinicID: doctorsProfileResponseModel.clinicID ?? 0, + doctorID: doctorsProfileResponseModel.doctorID ?? 0, + ); + } if (onSuccess != null) { onSuccess(apiResponse); @@ -741,8 +861,8 @@ class BookAppointmentsViewModel extends ChangeNotifier { } initialSlotDuration = apiResponse.data["InitialSlotDuration"]; freeSlotsResponse = apiResponse.data['FreeTimeSlots']; - isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"]; - // isWaitingAppointmentAvailable = true; + // isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"]; + isWaitingAppointmentAvailable = true; freeSlotsResponse.forEach((element) { // date = (isLiveCareSchedule != null && isLiveCareSchedule) @@ -852,64 +972,114 @@ class BookAppointmentsViewModel extends ChangeNotifier { print(failure); onError!(failure.message); }, - (apiResponse) { + (apiResponse) async { if (apiResponse.messageStatus == 2) { // onError!(apiResponse); - LoadingUtils.hideFullScreenLoader(); - showCommonBottomSheetWithoutHeight( - title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), - navigationService.navigatorKey.currentContext!, - child: Utils.getWarningWidget( - loadingText: apiResponse.data["ErrorEndUserMessage"], - isShowActionButtons: true, - onCancelTap: () { - navigationService.pop(); - }, - onConfirmTap: () async { - navigationService.pop(); - PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel( - appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'], - clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'], - projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'], - endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'], - startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'], - doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'], - isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'], - originalClinicID: 0, - originalProjectID: 0, - appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'], - ); - - LoaderBottomSheet.showLoader( - loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!), - ); - await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + if (isPatientRescheduleAppointment) { + PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel( + appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'], + clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'], + projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'], + endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'], + startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'], + doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'], + isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'], + originalClinicID: 0, + originalProjectID: 0, + appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'], + ); + + await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + // LoaderBottomSheet.hideLoader(); + Future.delayed(Duration(milliseconds: 50)).then((value) async {}); + // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr()); + await insertSpecificAppointment( + onError: (err) {}, + onSuccess: (apiResp) async { LoaderBottomSheet.hideLoader(); - Future.delayed(Duration(milliseconds: 50)).then((value) async {}); - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr()); - await insertSpecificAppointment( - onError: (err) {}, - onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); - await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - Navigator.pushAndRemoveUntil( - navigationService.navigatorKey.currentContext!, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); + await Future.delayed(Duration(milliseconds: 50)).then((value) async { + showCommonBottomSheetWithoutHeight( + GetIt.instance().navigatorKey.currentContext!, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), + callBackFunc: () { + setIsPatientRescheduleAppointment(false); + Navigator.pushAndRemoveUntil( + navigationService.navigatorKey.currentContext!, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); + }); + }); + }); + } else { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), + navigationService.navigatorKey.currentContext!, + child: Utils.getWarningWidget( + loadingText: apiResponse.data["ErrorEndUserMessage"], + isShowActionButtons: true, + onCancelTap: () { + navigationService.pop(); + }, + onConfirmTap: () async { + navigationService.pop(); + PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel( + appointmentNo: apiResponse.data["SameClinicApptList"][0]['AppointmentNo'], + clinicID: apiResponse.data["SameClinicApptList"][0]['ClinicID'], + projectID: apiResponse.data["SameClinicApptList"][0]['ProjectID'], + endDate: apiResponse.data["SameClinicApptList"][0]['EndTime'], + startTime: apiResponse.data["SameClinicApptList"][0]['StartTime'], + doctorID: apiResponse.data["SameClinicApptList"][0]['DoctorID'], + isLiveCareAppointment: apiResponse.data["SameClinicApptList"][0]['IsLiveCareAppointment'], + originalClinicID: 0, + originalProjectID: 0, + appointmentDate: apiResponse.data["SameClinicApptList"][0]['AppointmentDate'], + ); + + LoaderBottomSheet.showLoader( + loadingText: LocaleKeys.reschedulingAppo.tr(context: navigationService.navigatorKey.currentContext!), + ); + await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { + LoaderBottomSheet.hideLoader(); + Future.delayed(Duration(milliseconds: 50)).then((value) async {}); + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr()); + await insertSpecificAppointment( + onError: (err) {}, + onSuccess: (apiResp) async { + LoaderBottomSheet.hideLoader(); + await Future.delayed(Duration(milliseconds: 50)).then((value) async { + showCommonBottomSheetWithoutHeight( + GetIt.instance().navigatorKey.currentContext!, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), + callBackFunc: () { + setIsPatientRescheduleAppointment(false); + Navigator.pushAndRemoveUntil( + navigationService.navigatorKey.currentContext!, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); }); }); - }); - }); - }), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); + }); + }), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } } else if (apiResponse.messageStatus == 1) { if (apiResponse.data == null || apiResponse.data!.isEmpty) { onError!("No free slots available".tr()); @@ -946,7 +1116,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { (apiResponse) { if (apiResponse.messageStatus == 2) { // onError!(apiResponse); - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, @@ -982,22 +1152,28 @@ class BookAppointmentsViewModel extends ChangeNotifier { await cancelAppointment(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel).then((val) async { navigationService.pop(); Future.delayed(Duration(milliseconds: 50)).then((value) async {}); - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr()); + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.bookingYourAppointment.tr()); await insertSpecificAppointment( onError: (err) {}, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - Navigator.pushAndRemoveUntil( - navigationService.navigatorKey.currentContext!, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); + showCommonBottomSheetWithoutHeight( + GetIt.instance().navigatorKey.currentContext!, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), + callBackFunc: () { + setIsPatientRescheduleAppointment(false); + Navigator.pushAndRemoveUntil( + navigationService.navigatorKey.currentContext!, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); }); }); }); @@ -1491,7 +1667,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { }, (apiResponse) { if (apiResponse.messageStatus == 2) { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), navigationService.navigatorKey.currentContext!, diff --git a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart index 75ed1229..a9aa273e 100644 --- a/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart +++ b/lib/features/book_appointments/models/resp_models/doctors_list_response_model.dart @@ -193,7 +193,7 @@ class DoctorsListResponseModel { serviceID = json['ServiceID']; setupID = json['SetupID']; speciality = json['Speciality'] != null ? json['Speciality'].cast() : []; - // specialityN = json['SpecialityN'].cast(); + specialityN = json['SpecialityN'] != null ? json['SpecialityN'].cast() : []; transactionType = json['TransactionType']; virtualEmploymentType = json['VirtualEmploymentType']; workingHours = json['WorkingHours']; diff --git a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart index d7d3fbd1..dcb8a70e 100644 --- a/lib/features/book_appointments/models/resp_models/laser_body_parts.dart +++ b/lib/features/book_appointments/models/resp_models/laser_body_parts.dart @@ -61,4 +61,14 @@ class LaserBodyPart { data['CategoryNameN'] = this.categoryNameN; return data; } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LaserBodyPart && + runtimeType == other.runtimeType && + id == other.id; + + @override + int get hashCode => id.hashCode; } diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index ec211793..bfe226f8 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -530,6 +530,7 @@ class EmergencyServicesViewModel extends ChangeNotifier { void setTransportationOption(PatientERTransportationMethod item) { selectedTransportOption = item; + notifyListeners(); } void updateCallingPlace(AmbulanceCallingPlace? value) { diff --git a/lib/features/hmg_services/hmg_services_repo.dart b/lib/features/hmg_services/hmg_services_repo.dart index 89285339..b70e024c 100644 --- a/lib/features/hmg_services/hmg_services_repo.dart +++ b/lib/features/hmg_services/hmg_services_repo.dart @@ -919,8 +919,6 @@ class HmgServicesRepoImp implements HmgServicesRepo { @override Future>>> getPatientVitalSign() async { Map requestBody = { - - }; try { diff --git a/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart b/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart index 66f5d77b..90c513c4 100644 --- a/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart +++ b/lib/features/hmg_services/models/ui_models/vital_sign_ui_model.dart @@ -1,4 +1,6 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; /// UI-only helper model for Vital Sign cards. @@ -49,7 +51,7 @@ class VitalSignUiModel { } // Warning for both low and overweight/underweight BMI, since they can indicate potential health issues. - if (s.contains('low') || s.contains('underweight') || s.contains('overweight')) { + if (s.contains('low') || s.contains("OverWeight") || s.contains("وزن زائد") || s.contains("UnderWeight") || s.contains("وزن ناقص")) { final Color yellowBg = AppColors.highAndLow.withValues(alpha: 0.12); return VitalSignUiModel( iconBg: yellowBg, @@ -96,13 +98,13 @@ class VitalSignUiModel { final double bmiResult = double.tryParse(bmi.toString()) ?? 0; if (bmiResult >= 30) { - bmiStatus = "High"; + bmiStatus = LocaleKeys.obese.tr(); } else if (bmiResult < 30 && bmiResult >= 25) { - bmiStatus = "Overweight"; + bmiStatus = LocaleKeys.overWeight.tr(); } else if (bmiResult < 25 && bmiResult >= 18.5) { - bmiStatus = "Normal"; + bmiStatus = LocaleKeys.normal.tr(); } else if (bmiResult < 18.5) { - bmiStatus = "Underweight"; + bmiStatus = LocaleKeys.underWeight.tr(); } // if (bmi == null) return 'N/A'; diff --git a/lib/features/immediate_livecare/immediate_livecare_repo.dart b/lib/features/immediate_livecare/immediate_livecare_repo.dart index cbce4ea2..8775378b 100644 --- a/lib/features/immediate_livecare/immediate_livecare_repo.dart +++ b/lib/features/immediate_livecare/immediate_livecare_repo.dart @@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart'; @@ -13,6 +14,9 @@ import 'package:hmg_patient_app_new/services/logger_service.dart'; abstract class ImmediateLiveCareRepo { Future>>> getLiveCareImmediateClinicsList(int age, int genderID, {Function(dynamic)? onSuccess, Function(String)? onError}); + Future>>> getLiveCareImmediateClinicSchedule(int age, int genderID, int serviceID, + {Function(dynamic)? onSuccess, Function(String)? onError}); + Future>> getLiveCareImmediateAppointmentFees(int age, int genderID, int serviceID, {Function(dynamic)? onSuccess, Function(String)? onError}); @@ -203,4 +207,47 @@ class ImmediateLiveCareRepoImp implements ImmediateLiveCareRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> getLiveCareImmediateClinicSchedule(int age, int genderID, int serviceID, + {Function(dynamic p1)? onSuccess, Function(String p1)? onError}) async { + Map mapDevice = { + "Age": age, + "Gender": genderID, + "ServiceID": serviceID, + }; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + GET_LIVECARE_CLINIC_TIMING, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + // onError!(error); + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final respObject = response['PatientER_GetClinicsServiceTimingsList']; + final liveCareScheduleObj = respObject.map((item) => LiveCareClinicScheduleResponse.fromJson(item as Map)).toList().cast(); + + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: liveCareScheduleObj, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/immediate_livecare/immediate_livecare_view_model.dart b/lib/features/immediate_livecare/immediate_livecare_view_model.dart index 46f5a652..6613b481 100644 --- a/lib/features/immediate_livecare/immediate_livecare_view_model.dart +++ b/lib/features/immediate_livecare/immediate_livecare_view_model.dart @@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; +import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_clinics_response_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_livecare_immediate_fees_response_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_models/get_patient_livecare_history_response_model.dart'; @@ -46,6 +47,8 @@ class ImmediateLiveCareViewModel extends ChangeNotifier { List patientLiveCareHistoryList = []; bool patientHasPendingLiveCareRequest = false; + List liveCareClinicScheduleList = []; + late AppState _appState; Duration countdownDuration = Duration(minutes: 1, seconds: 0); @@ -265,6 +268,33 @@ class ImmediateLiveCareViewModel extends ChangeNotifier { ); } + Future getLiveCareImmediateClinicSchedule({int? serviceID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + liveCareClinicScheduleList.clear(); + notifyListeners(); + + final result = + await immediateLiveCareRepo.getLiveCareImmediateClinicSchedule(_appState.getAuthenticatedUser()!.age!, _appState.getAuthenticatedUser()!.gender!, serviceID!); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + liveCareClinicScheduleList = apiResponse.data!; + + // immediateLiveCareClinicsList.sort((a, b) => b.isOnline!.compareTo(a.isOnline!)); + + // isImmediateLiveCareClinicsLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + void startTimer() { timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime()); notifyListeners(); diff --git a/lib/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart b/lib/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart new file mode 100644 index 00000000..787167de --- /dev/null +++ b/lib/features/immediate_livecare/models/resp_models/get_livecare_clinic_schedule_response_model.dart @@ -0,0 +1,71 @@ +class LiveCareClinicScheduleResponse { + int? iD; + int? serviceID; + int? dayOfWeek; + String? dayOfWeekStr; + bool? isActive; + String? createdOn; + String? createdBy; + bool? projectOutSA; + String? dayOfWeekStrN; + List? shiftTimings; + + LiveCareClinicScheduleResponse({this.iD, this.serviceID, this.dayOfWeek, this.dayOfWeekStr, this.isActive, this.createdOn, this.createdBy, this.projectOutSA, this.dayOfWeekStrN, this.shiftTimings}); + + LiveCareClinicScheduleResponse.fromJson(Map json) { + iD = json['ID']; + serviceID = json['ServiceID']; + dayOfWeek = json['DayOfWeek']; + dayOfWeekStr = json['DayOfWeekStr']; + isActive = json['IsActive']; + createdOn = json['CreatedOn']; + createdBy = json['CreatedBy']; + projectOutSA = json['ProjectOutSA']; + dayOfWeekStrN = json['DayOfWeekStrN']; + if (json['ShiftTimings'] != null) { + shiftTimings = []; + json['ShiftTimings'].forEach((v) { + shiftTimings!.add(new ShiftTimings.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['ServiceID'] = this.serviceID; + data['DayOfWeek'] = this.dayOfWeek; + data['DayOfWeekStr'] = this.dayOfWeekStr; + data['IsActive'] = this.isActive; + data['CreatedOn'] = this.createdOn; + data['CreatedBy'] = this.createdBy; + data['ProjectOutSA'] = this.projectOutSA; + data['DayOfWeekStrN'] = this.dayOfWeekStrN; + if (this.shiftTimings != null) { + data['ShiftTimings'] = this.shiftTimings!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class ShiftTimings { + String? endTime; + int? shiftID; + String? startTime; + + ShiftTimings({this.endTime, this.shiftID, this.startTime}); + + ShiftTimings.fromJson(Map json) { + endTime = json['EndTime']; + shiftID = json['ShiftID']; + startTime = json['StartTime']; + } + + Map toJson() { + final Map data = new Map(); + data['EndTime'] = this.endTime; + data['ShiftID'] = this.shiftID; + data['StartTime'] = this.startTime; + return data; + } +} diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart index 0e7efdb3..6835badb 100644 --- a/lib/features/lab/lab_view_model.dart +++ b/lib/features/lab/lab_view_model.dart @@ -4,6 +4,7 @@ import 'dart:core'; import 'package:flutter/material.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/common_models/data_points.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; @@ -71,6 +72,9 @@ class LabViewModel extends ChangeNotifier { List uniqueTestsList = []; List indexedCharacterForUniqueTest = []; + List filteredUniqueTestsList = []; + List filteredIndexedCharacterForUniqueTest = []; + double maxY = 0.0; double minY = double.infinity; double maxX = double.infinity; @@ -84,6 +88,9 @@ class LabViewModel extends ChangeNotifier { LabOrderResponseByAi? labOrderResponseByAi; LabOrdersResponseByAi? labOrdersResponseByAi; + bool isLabAIAnalysisNeedsToBeShown = true; + bool isLabResultsHistoryShowMore = false; + LabViewModel({required this.labRepo, required this.errorHandlerService, required this.navigationService}); initLabProvider() { @@ -93,6 +100,8 @@ class LabViewModel extends ChangeNotifier { labOrderTests.clear(); isLabOrdersLoading = true; isLabResultsLoading = true; + isLabAIAnalysisNeedsToBeShown = true; + isLabResultsHistoryShowMore = false; patientLabOrdersByClinic.clear(); patientLabOrdersByHospital.clear(); patientLabOrdersViewList.clear(); @@ -111,6 +120,16 @@ class LabViewModel extends ChangeNotifier { notifyListeners(); } + setIsLabAIAnalysisNeedsToBeShown(bool isLabAIAnalysisNeedsToBeShown) { + this.isLabAIAnalysisNeedsToBeShown = isLabAIAnalysisNeedsToBeShown; + notifyListeners(); + } + + setIsLabResultsHistoryShowMore() { + isLabResultsHistoryShowMore = !isLabResultsHistoryShowMore; + notifyListeners(); + } + void setIsSortByClinic(bool value) { isSortByClinic = value; patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital; @@ -218,7 +237,8 @@ class LabViewModel extends ChangeNotifier { final clinicMap = >{}; final hospitalMap = >{}; if (query.isEmpty) { - // filteredLabOrders = List.from(patientLabOrders); // reset + filteredLabOrders = List.from(patientLabOrders); // reset + filteredUniqueTestsList = List.from(uniqueTestsList); for (var order in patientLabOrders) { final clinicKey = (order.clinicDescription ?? 'Unknown').trim(); clinicMap.putIfAbsent(clinicKey, () => []).add(order); @@ -234,7 +254,10 @@ class LabViewModel extends ChangeNotifier { final descriptions = order.testDetails?.map((d) => d.description?.toLowerCase()).toList() ?? []; return descriptions.any((desc) => desc != null && desc.contains(query.toLowerCase())); }).toList(); - // patientLabOrders = filteredLabOrders; + filteredUniqueTestsList = uniqueTestsList.where((test) { + final desc = test.description?.toLowerCase() ?? ''; + return desc.contains(query.toLowerCase()); + }).toList(); for (var order in filteredLabOrders) { final clinicKey = (order.clinicDescription ?? 'Unknown').trim(); clinicMap.putIfAbsent(clinicKey, () => []).add(order); @@ -246,6 +269,14 @@ class LabViewModel extends ChangeNotifier { patientLabOrdersByHospital = hospitalMap.values.toList(); patientLabOrdersViewList = isSortByClinic ? patientLabOrdersByClinic : patientLabOrdersByHospital; } + // Rebuild filtered indexed characters + filteredIndexedCharacterForUniqueTest = []; + for (var test in filteredUniqueTestsList) { + String label = test.description ?? ""; + if (label.isEmpty) continue; + if (filteredIndexedCharacterForUniqueTest.contains(label[0].toLowerCase())) continue; + filteredIndexedCharacterForUniqueTest.add(label[0].toLowerCase()); + } notifyListeners(); } @@ -280,6 +311,10 @@ class LabViewModel extends ChangeNotifier { for (var element in uniqueTests) { labOrderTests.add(element.description ?? ""); } + + // Initialize filtered lists with full data + filteredUniqueTestsList = List.from(uniqueTestsList); + filteredIndexedCharacterForUniqueTest = List.from(indexedCharacterForUniqueTest); } Future getLabResultsByAppointmentNo( @@ -389,15 +424,17 @@ class LabViewModel extends ChangeNotifier { } else if (apiResponse.messageStatus == 1) { var sortedResult = sort(apiResponse.data!); var recentThree = sortedResult.take(3).toList(); - mainLabResults = recentThree; + mainLabResults = sortedResult; double highRefrenceValue = double.negativeInfinity; String? flagForHighReferenceRange; double lowRefenceValue = double.infinity; String? flagForLowReferenceRange; - sortedResult.take(3).toList().reversed.forEach((element) { + sortedResult.toList().reversed.forEach((element) { try { + shouldShowGraph = isNumeric(element.resultValue!); + // if(isNumeric(element.resultValue!)){ var dateTime = DateUtil.convertStringToDate(element.verifiedOnDateTime!); var resultValue = double.parse(element.resultValue!); // var transformedValue = transformValueInRange(double.parse(element.resultValue!), element.calculatedResultFlag ?? ""); @@ -409,13 +446,22 @@ class LabViewModel extends ChangeNotifier { if (resultValue < minY) { minY = resultValue; } - if (highRefrenceValue < double.parse(element.referenceHigh ?? "0.0")) { + // if (highRefrenceValue < double.parse(element.referenceHigh ?? "0.0")) { highRefrenceValue = double.parse(element.referenceHigh ?? "0.0"); flagForHighReferenceRange = element.calculatedResultFlag; - } - if (lowRefenceValue > double.parse(element.referenceLow ?? "0.0")) { + // } + // if (lowRefenceValue > double.parse(element.referenceLow ?? "0.0")) { lowRefenceValue = double.parse(element.referenceLow ?? "0.0"); flagForLowReferenceRange = element.calculatedResultFlag; + // } + + if(double.parse(element.referenceHigh ?? "0.0") == 0 ){ + highRefrenceValue = double.negativeInfinity; + } + + + if(double.parse(element.referenceLow ?? "0.0") == 0 ){ + lowRefenceValue = double.negativeInfinity; } filteredGraphValues.add(DataPoint( @@ -427,28 +473,38 @@ class LabViewModel extends ChangeNotifier { time: DateUtil.convertStringToDate(element.verifiedOnDateTime), referenceValue: element.calculatedResultFlag ?? "", )); - } catch (e) {} + + } catch (e) { + } }); if (flagForLowReferenceRange == null && flagForHighReferenceRange == null) { highRefrenceValue = maxY; lowRefenceValue = minY; } // - if (minY > lowRefenceValue) { + if (lowRefenceValue != double.negativeInfinity &&minY > lowRefenceValue) { minY = lowRefenceValue - getInterval(); + }else if (highRefrenceValue != double.negativeInfinity && minY > highRefrenceValue) { + minY = highRefrenceValue - getInterval(); } this.flagForHighReferenceRange = flagForHighReferenceRange; this.flagForLowReferenceRange = flagForLowReferenceRange; highTransformedReferenceValue = double.parse(transformValueInRange(highRefrenceValue, flagForHighReferenceRange ?? "").toStringAsFixed(1)); lowTransformedReferenceValue = double.parse(transformValueInRange(lowRefenceValue, flagForLowReferenceRange ?? "").toStringAsFixed(1)); - this.highRefrenceValue = double.parse(highRefrenceValue.toStringAsFixed(1)); - this.lowRefenceValue = double.parse(lowRefenceValue.toStringAsFixed(1)); - if(maxY getAiOverviewLabOrders({required PatientLabOrdersResponseModel labOrder, required String loadingText}) async { // LoadingUtils.showFullScreenLoader(loadingText: "Loading and analysing your data,\nPlease be patient and let the AI do the magic. \nPlease be patient, This might take some time."); - LoadingUtils.showFullScreenLoader(loadingText: loadingText); + // LoadingUtils.showFullScreenLoader(loadingText: loadingText); + isLabAIAnalysisNeedsToBeShown = true; + notifyListeners(); + + LoaderBottomSheet.showLoader( + loadingText: loadingText, + showCloseButton: true, + onCloseTap: () { + setIsLabAIAnalysisNeedsToBeShown(false); + }); List> results = []; Map orderData = {"order_date": labOrder.orderDate ?? "", "clinic": labOrder.clinicDescription ?? "", "doctor": labOrder.doctorName ?? "", "results": []}; List> testResults = []; @@ -809,26 +878,38 @@ class LabViewModel extends ChangeNotifier { final result = await labRepo.getPatientAiOverViewLabOrders(results); result.fold( (failure) async { - LoadingUtils.hideFullScreenLoader(); - await errorHandlerService.handleError(failure: failure); + LoaderBottomSheet.hideLoader(); + if (isLabAIAnalysisNeedsToBeShown) { + await errorHandlerService.handleError(failure: failure); + } }, (apiResponse) { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { - labOrdersResponseByAi = apiResponse.data; - navigationService.push( - MaterialPageRoute(builder: (_) => LabAiAnalysisDetailedPage()), - ); + if (isLabAIAnalysisNeedsToBeShown) { + labOrdersResponseByAi = apiResponse.data; + navigationService.push( + CustomPageRoute( + direction: AxisDirection.down, + page: LabAiAnalysisDetailedPage(), + ), + ); + } } }, ); - LoadingUtils.hideFullScreenLoader(); + // LoadingUtils.hideFullScreenLoader(); } Future getAiOverviewSingleLabResult({required String langId, required LabResult recentLabResult, required String loadingText}) async { - LoaderBottomSheet.showLoader(loadingText: loadingText); + LoaderBottomSheet.showLoader( + loadingText: loadingText, + showCloseButton: true, + onCloseTap: () { + setIsLabAIAnalysisNeedsToBeShown(false); + }); List> results = []; results.add({ "Description": recentLabResult.description ?? '', @@ -838,19 +919,24 @@ class LabViewModel extends ChangeNotifier { "ReferanceRange": recentLabResult.referanceRange ?? '', }); - var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results}; + // var payload = {"patient_id": currentlySelectedPatientOrder!.patientID, "language_id": langId, "lab_results": results}; + var payload = {"patient_id": getIt.get().getAuthenticatedUser()!.patientId, "language_id": langId, "lab_results": results}; final result = await labRepo.getPatientAiOverViewLabOrder(payload); result.fold( (failure) async { LoaderBottomSheet.hideLoader(); - await errorHandlerService.handleError(failure: failure); + if (isLabAIAnalysisNeedsToBeShown) { + await errorHandlerService.handleError(failure: failure); + } }, (apiResponse) { LoaderBottomSheet.hideLoader(); if (apiResponse.messageStatus == 2) { } else if (apiResponse.messageStatus == 1) { - labOrderResponseByAi = apiResponse.data; - notifyListeners(); + if (isLabAIAnalysisNeedsToBeShown) { + labOrderResponseByAi = apiResponse.data; + notifyListeners(); + } } }, ); diff --git a/lib/features/my_appointments/appointment_via_region_viewmodel.dart b/lib/features/my_appointments/appointment_via_region_viewmodel.dart index 10da4e3d..1144be82 100644 --- a/lib/features/my_appointments/appointment_via_region_viewmodel.dart +++ b/lib/features/my_appointments/appointment_via_region_viewmodel.dart @@ -87,6 +87,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier { hmcCount = registeredDoctorMap.hmcSize; hmgCount = registeredDoctorMap.hmgSize; + hospitalList!.sort((a, b) => num.parse(a.distanceInKMs!).compareTo(num.parse(b.distanceInKMs!))); + getDisplayList(); } diff --git a/lib/features/my_appointments/models/resp_models/appointment_rated_response_model.dart b/lib/features/my_appointments/models/resp_models/appointment_rated_response_model.dart new file mode 100644 index 00000000..2096a1e1 --- /dev/null +++ b/lib/features/my_appointments/models/resp_models/appointment_rated_response_model.dart @@ -0,0 +1,30 @@ +class AppointmentRatedResponseModel { + int? appointmentNo; + int? clinicID; + int? doctorID; + bool? isAllowedToRate; + bool? isAppointmentRated; + int? projectID; + + AppointmentRatedResponseModel({this.appointmentNo, this.clinicID, this.doctorID, this.isAllowedToRate, this.isAppointmentRated, this.projectID}); + + AppointmentRatedResponseModel.fromJson(Map json) { + appointmentNo = json['AppointmentNo']; + clinicID = json['ClinicID']; + doctorID = json['DoctorID']; + isAllowedToRate = json['IsAllowedToRate']; + isAppointmentRated = json['IsAppointmentRated']; + projectID = json['ProjectID']; + } + + Map toJson() { + final Map data = new Map(); + data['AppointmentNo'] = this.appointmentNo; + data['ClinicID'] = this.clinicID; + data['DoctorID'] = this.doctorID; + data['IsAllowedToRate'] = this.isAllowedToRate; + data['IsAppointmentRated'] = this.isAppointmentRated; + data['ProjectID'] = this.projectID; + return data; + } +} diff --git a/lib/features/my_appointments/models/resp_models/hospital_model.dart b/lib/features/my_appointments/models/resp_models/hospital_model.dart index a807b99a..031b3fd4 100644 --- a/lib/features/my_appointments/models/resp_models/hospital_model.dart +++ b/lib/features/my_appointments/models/resp_models/hospital_model.dart @@ -75,9 +75,7 @@ class HospitalsModel { } String? getName(bool isArabic) { - if (isArabic) { - return "$nameN"; - } + return name; } diff --git a/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart b/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart index e1ae67d6..cea2e64b 100644 --- a/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart +++ b/lib/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart @@ -51,6 +51,7 @@ class PatientAppointmentHistoryResponseModel { bool? isExecludeDoctor; dynamic isFollowup; bool? isLiveCareAppointment; + bool? isLiveCareClinic; bool? isInOutPatient; bool? isMedicalReportRequested; bool? isOnlineCheckedIN; @@ -73,6 +74,7 @@ class PatientAppointmentHistoryResponseModel { num? patientShare; num? patientShareWithTax; num? patientTaxAmount; + String? doctorNationalityFlagURL; PatientAppointmentHistoryResponseModel({ this.setupID, @@ -127,6 +129,7 @@ class PatientAppointmentHistoryResponseModel { this.isExecludeDoctor, this.isFollowup, this.isLiveCareAppointment, + this.isLiveCareClinic, this.isMedicalReportRequested, this.isOnlineCheckedIN, this.latitude, @@ -148,6 +151,7 @@ class PatientAppointmentHistoryResponseModel { this.patientShare, this.patientShareWithTax, this.patientTaxAmount, + this.doctorNationalityFlagURL, }); PatientAppointmentHistoryResponseModel.fromJson(Map json) { @@ -203,6 +207,7 @@ class PatientAppointmentHistoryResponseModel { isExecludeDoctor = json['IsExecludeDoctor']; isFollowup = json['IsFollowup']; isLiveCareAppointment = json['IsLiveCareAppointment']; + isLiveCareClinic = json['IsLiveCareClinic']; isInOutPatient = json['IsInOutPatient']; isMedicalReportRequested = json['IsMedicalReportRequested']; isOnlineCheckedIN = json['IsOnlineCheckedIN']; @@ -235,6 +240,7 @@ class PatientAppointmentHistoryResponseModel { patientShare = json['PatientShare']; patientShareWithTax = json['PatientShareWithTax']; patientTaxAmount = json['PatientTaxAmount']; + doctorNationalityFlagURL = json['DoctorNationalityFlagURL']; } Map toJson() { diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index ccf76ce0..45c28c45 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_rated_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/rate_appointment_resp_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart'; @@ -81,6 +82,13 @@ abstract class MyAppointmentsRepo { Future>> convertPatientToCash({required int projectID}); Future>> sendPatientUpdateRequest(); + + Future>> getDoctorsRatingCheck({ + required int doctorID, + required int projectID, + required int clinicID, + required int appointmentNo, + }); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -513,7 +521,10 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { try { final list = response['PatientDoctorAppointmentResultList']; - final appointmentsList = list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map)).toList().cast(); + List appointmentsList = + list.map((item) => PatientAppointmentHistoryResponseModel.fromJson(item as Map)).toList().cast(); + + // appointmentsList.removeWhere((element) => element.isActiveDoctorProfile == false); apiResponse = GenericApiModel>( messageStatus: messageStatus, @@ -563,6 +574,8 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { final appointmentsList = (list as List).map((item) => GetFavoriteDoctorsListModel.fromJson(item as Map)).toList().cast(); + appointmentsList.removeWhere((element) => element.isActive == false); + apiResponse = GenericApiModel>( messageStatus: messageStatus, statusCode: statusCode, @@ -1072,4 +1085,50 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>> getDoctorsRatingCheck({ + required int doctorID, + required int projectID, + required int clinicID, + required int appointmentNo, + }) async { + Map mapDevice = { + "DoctorID": doctorID, + "ProjectID": projectID, + "ClinicID": clinicID, + "AppointmentNo": appointmentNo, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GET_DOCTORS_RATING_CHECK, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + AppointmentRatedResponseModel appointmentRatedResponseModel = AppointmentRatedResponseModel.fromJson(response['DoctorsRatingResponseModel'][0]); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: errorMessage, + data: appointmentRatedResponseModel, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index aa56d440..ede63ca9 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_favorite_doctors_list.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/appointemnet_filters.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_rated_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/ask_doctor_request_type_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/get_tamara_installments_details_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; @@ -87,6 +88,11 @@ class MyAppointmentsViewModel extends ChangeNotifier { bool isShowNotEligibleDialog = false; String notEligibleErrorMsg = ""; + bool isAppointmentAlreadyRated = false; + + AppointmentRatedResponseModel? appointmentRatedResponseModel; + bool isAppointmentRatedResponseLoading = false; + MyAppointmentsViewModel({required this.myAppointmentsRepo, required this.errorHandlerService, required this.appState}); void onTabChange(int index) { @@ -94,6 +100,10 @@ class MyAppointmentsViewModel extends ChangeNotifier { selectedTabIndex = index; start = null; end = null; + // if (index == 0) { + // filteredAppointmentList.clear(); + // filteredAppointmentList.addAll(patientAppointmentsHistoryList); + // } notifyListeners(); } @@ -666,9 +676,9 @@ class MyAppointmentsViewModel extends ChangeNotifier { Future getPatientFavouriteDoctors({bool forceRefresh = false, Function(dynamic)? onSuccess, Function(String)? onError}) async { // If data is already fetched and not forcing refresh, skip API call - if (isFavouriteDoctorsDataFetched && !forceRefresh) { - return; - } + // if (isFavouriteDoctorsDataFetched && !forceRefresh) { + // return; + // } isPatientFavouriteDoctorsLoading = true; patientFavouriteDoctorsList.clear(); @@ -920,6 +930,7 @@ class MyAppointmentsViewModel extends ChangeNotifier { isPatientHasQueueAppointment = false; isAppointmentQueueDetailsLoading = true; notifyListeners(); + final result = await myAppointmentsRepo.getPatientAppointmentQueueDetails( appointmentNo: patientArrivedAppointmentsHistoryList.first.appointmentNo, patientID: patientArrivedAppointmentsHistoryList.first.patientID); @@ -991,4 +1002,79 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future getDoctorsRatingCheck({ + required int doctorID, + required int projectID, + required int clinicID, + required int appointmentNo, + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + isAppointmentRatedResponseLoading = true; + notifyListeners(); + + final result = await myAppointmentsRepo.getDoctorsRatingCheck( + doctorID: doctorID, + projectID: projectID, + clinicID: clinicID, + appointmentNo: appointmentNo, + ); + + result.fold( + (failure) async { + // await errorHandlerService.handleError(failure: failure); + if (onError != null) onError(failure.message); + isAppointmentRatedResponseLoading = false; + notifyListeners(); + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + if (onError != null) onError(apiResponse.errorMessage ?? "Unknown error"); + isAppointmentRatedResponseLoading = false; + notifyListeners(); + } else if (apiResponse.messageStatus == 1) { + appointmentRatedResponseModel = apiResponse.data!; + isAppointmentRatedResponseLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse.data); + } + } + }, + ); + } + + Future submitDoctorRating( + {required PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel, + required int docRate, + required String docNote, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + final result = await myAppointmentsRepo.sendDoctorRate( + docRate, + patientAppointmentHistoryResponseModel.appointmentNo!, + patientAppointmentHistoryResponseModel.projectID!, + patientAppointmentHistoryResponseModel.doctorID!, + patientAppointmentHistoryResponseModel.clinicID!, + docNote, + patientAppointmentHistoryResponseModel.appointmentDate!, + patientAppointmentHistoryResponseModel.doctorNameObj!, + patientAppointmentHistoryResponseModel.projectName!, + patientAppointmentHistoryResponseModel.clinicName!); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + onError?.call(apiResponse.errorMessage ?? 'Unknown error'); + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + // onSuccess(apiResponse.data); + } + } + }, + ); + } } diff --git a/lib/features/my_invoices/my_invoices_repo.dart b/lib/features/my_invoices/my_invoices_repo.dart index 68eee6e5..79136ba0 100644 --- a/lib/features/my_invoices/my_invoices_repo.dart +++ b/lib/features/my_invoices/my_invoices_repo.dart @@ -13,6 +13,8 @@ abstract class MyInvoicesRepo { Future>> getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID}); Future>> sendInvoiceEmail({required num appointmentNo, required int projectID}); + + Future>> downloadInvoice({required String setupId, required int invoiceNo, required int projectID}); } class MyInvoicesRepoImp implements MyInvoicesRepo { @@ -138,4 +140,42 @@ class MyInvoicesRepoImp implements MyInvoicesRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future> downloadInvoice({required String setupId, required int invoiceNo, required int projectID}) async { + Map mapDevice = { + "SetupID": setupId, + "ProjectID": projectID, + "InvoiceNo": invoiceNo, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + DOWNLOAD_INVOICE_PDF, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["InvoiceReportPDFContent"], + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_invoices/my_invoices_view_model.dart b/lib/features/my_invoices/my_invoices_view_model.dart index d51ea086..cd18026c 100644 --- a/lib/features/my_invoices/my_invoices_view_model.dart +++ b/lib/features/my_invoices/my_invoices_view_model.dart @@ -26,6 +26,8 @@ class MyInvoicesViewModel extends ChangeNotifier { TextEditingController filterSearchController = TextEditingController(); String? selectedFilterItem; + String? downloadInvoicePDFBase64 = ""; + MyInvoicesViewModel({required this.myInvoicesRepo, required this.errorHandlerService, required this.navServices}); setInvoicesListLoading() { @@ -68,7 +70,6 @@ class MyInvoicesViewModel extends ChangeNotifier { Future getInvoiceDetails({required num appointmentNo, required num invoiceNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await myInvoicesRepo.getInvoiceDetails(appointmentNo: appointmentNo, invoiceNo: invoiceNo, projectID: projectID); - result.fold( (failure) async { isInvoiceDetailsLoading = false; @@ -93,6 +94,32 @@ class MyInvoicesViewModel extends ChangeNotifier { ); } + Future downloadInvoicePDF({required String setupId, required int invoiceNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myInvoicesRepo.downloadInvoice(setupId: setupId, invoiceNo: invoiceNo, projectID: projectID); + + result.fold( + (failure) async { + if (onError != null) { + onError(failure.message); + } + }, + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + if (apiResponse.data != null && apiResponse.data!.isNotEmpty) { + downloadInvoicePDFBase64 = apiResponse.data!; + } else { + downloadInvoicePDFBase64 = ""; + } + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + Future sendInvoiceEmail({required num appointmentNo, required int projectID, Function(dynamic)? onSuccess, Function(String)? onError}) async { final result = await myInvoicesRepo.sendInvoiceEmail(appointmentNo: appointmentNo, projectID: projectID); diff --git a/lib/features/notifications/notifications_repo.dart b/lib/features/notifications/notifications_repo.dart index 853a9792..a4588042 100644 --- a/lib/features/notifications/notifications_repo.dart +++ b/lib/features/notifications/notifications_repo.dart @@ -12,6 +12,10 @@ abstract class NotificationsRepo { required int pagingSize, required int currentPage, }); + + Future>>> markAsRead({ + required int notificationID, + }); } class NotificationsRepoImp implements NotificationsRepo { @@ -75,4 +79,38 @@ class NotificationsRepoImp implements NotificationsRepo { return Left(UnknownFailure(e.toString())); } } + + @override + Future>>> markAsRead({required int notificationID}) async { + Map mapDevice = {"NotificationPoolID": notificationID}; + + try { + GenericApiModel>? apiResponse; + Failure? failure; + await apiClient.post( + PUSH_NOTIFICATION_SET_MESSAGES_FROM_POOL_AS_READ, + body: mapDevice, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel>( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: [], + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/notifications/notifications_view_model.dart b/lib/features/notifications/notifications_view_model.dart index e15e267c..85049ec9 100644 --- a/lib/features/notifications/notifications_view_model.dart +++ b/lib/features/notifications/notifications_view_model.dart @@ -7,13 +7,15 @@ class NotificationsViewModel extends ChangeNotifier { bool isNotificationsLoading = false; bool hasMoreNotifications = true; + int unreadNotificationsCount = 0; + NotificationsRepo notificationsRepo; ErrorHandlerService errorHandlerService; List notificationsList = []; int currentPage = 0; - int pagingSize = 14; + int pagingSize = 50; int notificationStatusID = 2; // Default to status 2 (e.g., unread) NotificationsViewModel({ @@ -46,6 +48,7 @@ class NotificationsViewModel extends ChangeNotifier { Function(String)? onError, }) async { isNotificationsLoading = true; + unreadNotificationsCount = 0; notifyListeners(); final result = await notificationsRepo.getAllNotifications( @@ -78,6 +81,11 @@ class NotificationsViewModel extends ChangeNotifier { } notificationsList.addAll(newNotifications); + for (var notification in notificationsList) { + if (notification.isRead == false) { + unreadNotificationsCount++; + } + } currentPage++; notifyListeners(); @@ -89,6 +97,21 @@ class NotificationsViewModel extends ChangeNotifier { ); } + Future markAsRead({ + int notificationID = 0, + Function(dynamic)? onSuccess, + Function(String)? onError, + }) async { + final result = await notificationsRepo.markAsRead(notificationID: notificationID); + + result.fold( + (failure) async {}, + (apiResponse) { + refreshNotifications(); + }, + ); + } + Future refreshNotifications({ Function(dynamic)? onSuccess, Function(String)? onError, diff --git a/lib/features/symptoms_checker/symptoms_checker_repo.dart b/lib/features/symptoms_checker/symptoms_checker_repo.dart index 33bcbd8f..7fee030b 100644 --- a/lib/features/symptoms_checker/symptoms_checker_repo.dart +++ b/lib/features/symptoms_checker/symptoms_checker_repo.dart @@ -54,6 +54,7 @@ abstract class SymptomsCheckerRepo { Future>>> getClinicDetails({ required String categoryName, + required String language, required String userSessionToken, }); } @@ -400,6 +401,7 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo { @override Future>>> getClinicDetails({ required String categoryName, + required String language, required String userSessionToken, }) async { Map headers = { @@ -413,7 +415,7 @@ class SymptomsCheckerRepoImp implements SymptomsCheckerRepo { Failure? failure; await apiClient.get( - "${ApiConsts.getClinicFromCondition}$categoryName", + "${ApiConsts.getClinicFromCondition}$categoryName&language=$language", apiHeaders: headers, isExternal: true, isAllowAny: true, diff --git a/lib/features/symptoms_checker/symptoms_checker_view_model.dart b/lib/features/symptoms_checker/symptoms_checker_view_model.dart index 215b6921..d06b2538 100644 --- a/lib/features/symptoms_checker/symptoms_checker_view_model.dart +++ b/lib/features/symptoms_checker/symptoms_checker_view_model.dart @@ -1079,6 +1079,7 @@ class SymptomsCheckerViewModel extends ChangeNotifier { final result = await symptomsCheckerRepo.getClinicDetails( categoryName: categoryName, + language: appState.isArabic() ? 'ar' : 'en', userSessionToken: currentSessionAuthToken, ); diff --git a/lib/features/todo_section/todo_section_view_model.dart b/lib/features/todo_section/todo_section_view_model.dart index 0f6cc873..b14e4ead 100644 --- a/lib/features/todo_section/todo_section_view_model.dart +++ b/lib/features/todo_section/todo_section_view_model.dart @@ -13,13 +13,13 @@ class TodoSectionViewModel extends ChangeNotifier { String? notificationsCount = "0"; initializeTodoSectionViewModel() async { - if (isAncillaryOrdersNeedReloading) { - patientAncillaryOrdersList.clear(); - isAncillaryOrdersLoading = true; + // if (isAncillaryOrdersNeedReloading) { + patientAncillaryOrdersList.clear(); + isAncillaryOrdersLoading = true; isAncillaryDetailsProceduresLoading = true; notificationsCount = "0"; getPatientOnlineAncillaryOrderList(); - } + // } getPatientDashboard(); } @@ -63,13 +63,14 @@ class TodoSectionViewModel extends ChangeNotifier { } Future getPatientOnlineAncillaryOrderList({Function(dynamic)? onSuccess, Function(String)? onError}) async { - if (!isAncillaryOrdersNeedReloading) { - return; - } + // if (!isAncillaryOrdersNeedReloading) { + // return; + // } patientAncillaryOrdersList.clear(); isAncillaryOrdersLoading = true; notifyListeners(); + final result = await todoSectionRepo.getOnlineAncillaryOrderList(); result.fold( diff --git a/lib/features/translations/custom_asset_loader.dart b/lib/features/translations/custom_asset_loader.dart new file mode 100644 index 00000000..8cf9e7c9 --- /dev/null +++ b/lib/features/translations/custom_asset_loader.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:ui'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; +import 'package:path_provider/path_provider.dart'; + +class RemoteFileLoader extends AssetLoader { + @override + Future?> load(String path, Locale locale) async { + final directory = await getApplicationDocumentsDirectory(); + + // easy_localization uses toLanguageTag() which gives 'en-US' or 'ar-SA' + final fileName = "${locale.toLanguageTag()}.json"; + final file = File("${directory.path}/$fileName"); + + if (await file.exists()) { + final String jsonString = await file.readAsString(); + return jsonDecode(jsonString); + } else { + // If the download failed or hasn't happened yet, load from local assets + // path is the 'path' property from the EasyLocalization widget + final localData = await rootBundle.loadString('$path/$fileName'); + return jsonDecode(localData); + } + } +} diff --git a/lib/features/translations/translation_service.dart b/lib/features/translations/translation_service.dart new file mode 100644 index 00000000..b02a49cd --- /dev/null +++ b/lib/features/translations/translation_service.dart @@ -0,0 +1,36 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:http/http.dart' as http; +import 'package:path_provider/path_provider.dart'; + +class TranslationService { +// Replace with your actual hosting URL + static final String baseUrl = ApiConsts.googleCloudStorageENTranslationFileBaseURL; + + static Future downloadTranslations() async { + final directory = await getApplicationDocumentsDirectory(); + +// Ensure these match your locale codes exactly (en-US, ar-SA) + List files = ["en-US.json", "ar-SA.json"]; + + for (String fileName in files) { + try { + final String timestamp = DateTime.now().millisecondsSinceEpoch.toString(); + String url = "$baseUrl/$fileName?v=$timestamp"; + debugPrint(url); + // final response = await http.get(Uri.parse("$baseUrl/$fileName")); + final response = await http.get(Uri.parse(url)); + + if (response.statusCode == 200) { + final file = File("${directory.path}/$fileName"); + await file.writeAsBytes(response.bodyBytes); + } else { + print("Failed to download $fileName: Status ${response.statusCode}"); + } + } catch (e) { + print("Network error while downloading $fileName: $e"); + } + } + } +} diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index d3fcd24a..2a999ae7 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -7,6 +7,8 @@ abstract class LocaleKeys { static const arabic = 'arabic'; static const login = 'login'; static const noDataAvailable = 'noDataAvailable'; + static const noRatingAvailable = 'noRatingAvailable'; + static const doctorDoesNotHaveRating = 'doctorDoesNotHaveRating'; static const confirm = 'confirm'; static const loadingText = 'loadingText'; static const kilometerUnit = 'kilometerUnit'; @@ -1597,6 +1599,7 @@ abstract class LocaleKeys { static const timeFor = 'timeFor'; static const hmgPolicies = 'hmgPolicies'; static const darkMode = 'darkMode'; + static const featureComingSoonDescription = 'featureComingSoonDescription'; static const generateAiAnalysisResult = 'generateAiAnalysisResult'; static const ratings = 'ratings'; static const hmgPharmacyText = 'hmgPharmacyText'; @@ -1611,6 +1614,72 @@ abstract class LocaleKeys { static const timeForFirstReminder = 'timeForFirstReminder'; static const reminderRemovalNote = 'reminderRemovalNote'; static const communicationLanguage = 'communicationLanguage'; - static const featureComingSoonDescription = 'featureComingSoonDescription'; + static const cmcServiceHeader = 'cmcServiceHeader'; + static const cmcServiceDescription = 'cmcServiceDescription'; + static const eReferralServiceHeader = 'eReferralServiceHeader'; + static const eReferralServiceDescription = 'eReferralServiceDescription'; + static const bloodDonationServiceHeader = 'bloodDonationServiceHeader'; + static const bloodDonationServiceDescription = 'bloodDonationServiceDescription'; + static const healthTrackersServiceHeader = 'healthTrackersServiceHeader'; + static const healthTrackersServiceDescription = 'healthTrackersServiceDescription'; + static const waterConsumptionServiceHeader = 'waterConsumptionServiceHeader'; + static const waterConsumptionServiceDescription = 'waterConsumptionServiceDescription'; + static const smartWatchServiceHeader = 'smartWatchServiceHeader'; + static const smartWatchServiceDescription = 'smartWatchServiceDescription'; + static const liveChatServiceHeader = 'liveChatServiceHeader'; + static const liveChatServiceDescription = 'liveChatServiceDescription'; + static const emergencyServiceHeader = 'emergencyServiceHeader'; + static const emergencyServiceDescription = 'emergencyServiceDescription'; + static const homeHealthCareServiceHeader = 'homeHealthCareServiceHeader'; + static const homeHealthCareServiceDescription = 'homeHealthCareServiceDescription'; + static const profileOnlyText = 'profileOnlyText'; + static const information = 'information'; + static const noFavouriteDoctors = 'noFavouriteDoctors'; + static const addDoctors = 'addDoctors'; + static const favouriteList = 'favouriteList'; + static const later = 'later'; + static const cancelAppointmentConfirmMessage = 'cancelAppointmentConfirmMessage'; + static const acknowledged = 'acknowledged'; + static const searchLabResults = 'searchLabResults'; + static const callForAssistance = 'callForAssistance'; + static const oneWaySubtitle = 'oneWaySubtitle'; + static const twoWaySubtitle = 'twoWaySubtitle'; + static const toHospitalSubtitle = 'toHospitalSubtitle'; + static const fromHospitalSubtitle = 'fromHospitalSubtitle'; + static const clinicSchedule = 'clinicSchedule'; + static const doctorAddedToFavourite = 'doctorAddedToFavourite'; + static const doctorRemovedFromFavourite = 'doctorRemovedFromFavourite'; + static const addNewFavorite = 'addNewFavorite'; + static const favourites = 'favourites'; + static const pendingAncillaryOrders = 'pendingAncillaryOrders'; + static const liveCarePermissions = 'liveCarePermissions'; + static const containsImage = 'containsImage'; + static const servicesSelected = 'servicesSelected'; + static const selectMedicalFileFromFamily = 'selectMedicalFileFromFamily'; + static const anyFileFromHMG = 'anyFileFromHMG'; + static const noDistance = 'noDistance'; + static const enterAmount = 'enterAmount'; + static const selectHospitalContinue = 'selectHospitalContinue'; + static const year = 'year'; + static const notifyPrescription = 'notifyPrescription'; + static const doctorRatingAppointment = 'doctorRatingAppointment'; + static const ratingSubmitted = 'ratingSubmitted'; + static const rateTheDoctor = 'rateTheDoctor'; + static const downloadInvoice = 'downloadInvoice'; + static const approvalDetails = 'approvalDetails'; + static const obese = 'obese'; + static const overWeight = 'overWeight'; + static const underWeight = 'underWeight'; + static const nearestGate = 'nearestGate'; + static const RRTDDetails = 'RRTDDetails'; + static const normalMapType = 'normalMapType'; + static const satelliteMapType = 'satelliteMapType'; + static const terrainMapType = 'terrainMapType'; + static const hybridMapType = 'hybridMapType'; + static const openCamera = 'openCamera'; + static const openGallery = 'openGallery'; + static const openFiles = 'openFiles'; + static const generateAiAnalysisRadResult = 'generateAiAnalysisRadResult'; + static const grantLocationPermission = 'grantLocationPermission'; } diff --git a/lib/main.dart b/lib/main.dart index d38c9aab..c44a3a45 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,6 +41,8 @@ import 'package:hmg_patient_app_new/features/services_price_list/services_price_ import 'package:hmg_patient_app_new/features/smartwatch_health_data/health_provider.dart'; import 'package:hmg_patient_app_new/features/symptoms_checker/symptoms_checker_view_model.dart'; import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart'; +import 'package:hmg_patient_app_new/features/translations/custom_asset_loader.dart'; +import 'package:hmg_patient_app_new/features/translations/translation_service.dart'; import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart'; import 'package:hmg_patient_app_new/presentation/health_calculators_and_converts/health_calculator_view_model.dart'; import 'package:hmg_patient_app_new/features/health_trackers/health_trackers_view_model.dart'; @@ -95,6 +97,9 @@ Future callInitializations() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + // Download the latest JSONs from your server + // await TranslationService.downloadTranslations(); + // await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); try { @@ -125,6 +130,7 @@ void main() async { Locale('ar', 'SA'), ], path: 'assets/langs', + // assetLoader: RemoteFileLoader(), fallbackLocale: Locale('en', 'US'), child: MultiProvider(providers: [ ChangeNotifierProvider( diff --git a/lib/presentation/active_medication/active_medication_page.dart b/lib/presentation/active_medication/active_medication_page.dart index ae46d402..a893d7c0 100644 --- a/lib/presentation/active_medication/active_medication_page.dart +++ b/lib/presentation/active_medication/active_medication_page.dart @@ -137,7 +137,7 @@ class _ActiveMedicationPageState extends State { style: TextStyle( color: AppColors.textColor, fontSize: 27.f, - fontWeight: FontWeight.w600)), + fontWeight: FontWeight.w600,)), SizedBox(height: 16.h), SizedBox( height: 65.h, @@ -163,7 +163,7 @@ class _ActiveMedicationPageState extends State { style: TextStyle( color: AppColors.textColor, fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), WidgetSpan( @@ -174,7 +174,7 @@ class _ActiveMedicationPageState extends State { style: TextStyle( fontSize: 12, color: AppColors.textColor, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), ), @@ -184,7 +184,7 @@ class _ActiveMedicationPageState extends State { style: TextStyle( color: AppColors.textColor, fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), ], @@ -194,7 +194,7 @@ class _ActiveMedicationPageState extends State { style: TextStyle( color: AppColors.primaryRedBorderColor, fontSize: 12.f, - fontWeight: FontWeight.w500)), + fontWeight: FontWeight.w600,)), SizedBox(height: 16.h), Expanded( child: SingleChildScrollView( @@ -630,7 +630,7 @@ class _ActiveMedicationPageState extends State { style: TextStyle( fontSize: 12.f, color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), ], @@ -722,7 +722,7 @@ class _ActiveMedicationPageState extends State { ? AppColors.primaryRedBorderColor : AppColors.greyTextColor, fontSize: 11.f, - fontWeight: FontWeight.w500), + fontWeight: FontWeight.w600,), ), SizedBox(height: 5.h), Text("${date.day}", diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index 176b3771..10f616e8 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -31,12 +31,15 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_doctor_card.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_rating_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/ask_doctor/ask_doctor_page.dart'; import 'package:hmg_patient_app_new/presentation/ask_doctor/doctor_response_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart'; +import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.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'; @@ -72,11 +75,12 @@ class _AppointmentDetailsPageState extends State { @override void initState() { scheduleMicrotask(() async { - if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { - CalenderUtilsNew calendarUtils = await CalenderUtilsNew.instance; - var doesExist = await calendarUtils.checkIfEventExist("${widget.patientAppointmentHistoryResponseModel.appointmentNo}"); - myAppointmentsViewModel.setAppointmentReminder(doesExist, widget.patientAppointmentHistoryResponseModel); - setState(() {}); + if (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) { + myAppointmentsViewModel.getDoctorsRatingCheck( + doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo); } }); @@ -148,41 +152,57 @@ class _AppointmentDetailsPageState extends State { }); }, onCancelTap: () async { - LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context)); - await myAppointmentsViewModel.cancelAppointment( - patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, - onSuccess: (apiResponse) { - LoaderBottomSheet.hideLoader(); - myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); - myAppointmentsViewModel.getPatientAppointments(true, false); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentCancelledSuccessfully.tr(context: context)), - callBackFunc: () { - Navigator.of(context).pop(); - }, - title: "", - isCloseButtonVisible: true, - isDismissible: false, - isFullScreen: false, - ); - }, - onError: (err) { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getErrorWidget(loadingText: err.toString()), - callBackFunc: () {}, - title: "", - isCloseButtonVisible: true, - isDismissible: false, - isFullScreen: false, - ); - }); - var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", ); - setState(() { - myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); - }); + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: context), + context, + child: Utils.getWarningWidget( + loadingText: LocaleKeys.cancelAppointmentConfirmMessage.tr(context: context), + isShowActionButtons: true, + onCancelTap: () { + Navigator.of(context).pop(); + }, + onConfirmTap: () async { + Navigator.of(context).pop(); + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.cancellingAppointmentPleaseWait.tr(context: context)); + myAppointmentsViewModel.onTabChange(0); + myAppointmentsViewModel.updateListWRTTab(0); + await myAppointmentsViewModel.cancelAppointment( + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + onSuccess: (apiResponse) async { + LoaderBottomSheet.hideLoader(); + myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); + myAppointmentsViewModel.getPatientAppointments(true, false); + showCommonBottomSheet( + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentCancelledSuccessfully.tr(context: context)), + callBackFunc: (str) { + Navigator.of(context).pop(); + }, + title: "", + isCloseButtonVisible: false, + isDismissible: false, + isFullScreen: false, + isAutoDismiss: true, + height: ResponsiveExtension.screenHeight * 0.3, + ); + }, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err.toString()), + callBackFunc: () {}, + title: "", isCloseButtonVisible: true, isDismissible: false, isFullScreen: false); + }); + }), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + // var isEventAddedOrRemoved = await CalenderUtilsNew.instance.checkAndRemove( id:"${widget.patientAppointmentHistoryResponseModel.appointmentNo}", ); + // setState(() { + // myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel); + // }); }, onRescheduleTap: () async { openDoctorScheduleCalendar(); @@ -213,47 +233,53 @@ class _AppointmentDetailsPageState extends State { LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true), SizedBox(height: 4.h), (!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) - ? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500) - : LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, fontWeight: FontWeight.w500)), + ? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true) + : LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, isBold: true)), SizedBox(height: 16.h), ], ), - ((!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.nextAction != 10) - ? CustomButton( - text: LocaleKeys.confirm.tr(), - onPressed: () async { - LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingAppointmentPleaseWait.tr(context: context)); - await myAppointmentsViewModel.confirmAppointment( - patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, - onSuccess: (apiResponse) { - LoaderBottomSheet.hideLoader(); - myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); - myAppointmentsViewModel.initAppointmentsViewModel(); - myAppointmentsViewModel.getPatientAppointments(true, false); - showCommonBottomSheetWithoutHeight( - title: "", - context, - child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), - callBackFunc: () { - Navigator.of(context).pop(); - }, - isFullScreen: false, - isCloseButtonVisible: true, - ); - }); - }, - backgroundColor: AppColors.successColor, - borderColor: AppColors.successColor, - textColor: Colors.white, - fontSize: 14.f, - fontWeight: FontWeight.w500, - borderRadius: 12.r, - height: 40.h, - icon: AppAssets.confirm_appointment_icon, - iconColor: Colors.white, - iconSize: 16.h, - ) - : SizedBox.shrink()) + // ((!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.nextAction != 10) + // ? CustomButton( + // text: LocaleKeys.confirm.tr(), + // onPressed: () async { + // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.confirmingAppointmentPleaseWait.tr(context: context)); + // await myAppointmentsViewModel.confirmAppointment( + // patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + // onSuccess: (apiResponse) { + // LoaderBottomSheet.hideLoader(); + // myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); + // myAppointmentsViewModel.initAppointmentsViewModel(); + // // myAppointmentsViewModel.getPatientAppointments(true, false); + // showCommonBottomSheetWithoutHeight( + // title: "", + // context, + // child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), + // callBackFunc: () { + // Navigator.pushAndRemoveUntil( + // context, + // CustomPageRoute( + // page: LandingNavigation(), + // ), + // (r) => false); + // }, + // isFullScreen: false, + // isCloseButtonVisible: false, + // isAutoDismiss: true + // ); + // }); + // }, + // backgroundColor: AppColors.successColor, + // borderColor: AppColors.successColor, + // textColor: Colors.white, + // fontSize: 14.f, + // isBold: true, + // borderRadius: 12.r, + // height: 40.h, + // icon: AppAssets.confirm_appointment_icon, + // iconColor: Colors.white, + // iconSize: 16.h, + // ) + // : SizedBox.shrink()) ], ), //TODO Add countdown timer in case of LiveCare Appointment @@ -266,7 +292,7 @@ class _AppointmentDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.doctorWillCallYou.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.doctorWillCallYou.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -312,7 +338,7 @@ class _AppointmentDetailsPageState extends State { borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), textColor: Colors.white, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -333,12 +359,12 @@ class _AppointmentDetailsPageState extends State { AppCustomChipWidget( labelText: bookAppointmentsVM.isAppointmentNearestGateLoading ? "Floor: Ground Floor" - : "Floor: ${getIt.get().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}", - ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), + : "${LocaleKeys.floor.tr(context: context)}: ${getIt.get().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}", + ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), AppCustomChipWidget( labelText: - "Nearest Gate: ${getIt.get().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}") - .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), + "${LocaleKeys.nearestGate.tr(context: context)}: ${getIt.get().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}") + .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), ], ) : SizedBox.shrink(), ], @@ -362,7 +388,7 @@ class _AppointmentDetailsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), - LocaleKeys.notifyMeBeforeAppointment.tr(context: context).toText11(color: AppColors.textColorLight, weight: FontWeight.w500), + LocaleKeys.notifyMeBeforeAppointment.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), ], ), const Spacer(), @@ -427,15 +453,16 @@ class _AppointmentDetailsPageState extends State { ], ) // : SizedBox.shrink() - : GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: 16.h, - mainAxisSpacing: 16.w, - mainAxisExtent: 115.h, - ), - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, + : Consumer(builder: (context, myAppointmentsVM, child) { + return GridView( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + crossAxisSpacing: 16.h, + mainAxisSpacing: 16.w, + mainAxisExtent: 115.h, + ), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, shrinkWrap: true, children: [ MedicalFileCard( @@ -573,9 +600,48 @@ class _AppointmentDetailsPageState extends State { page: DoctorResponsePage(), ), ); - }), - ], - ), + }), + myAppointmentsVM.isAppointmentRatedResponseLoading + ? MedicalFileCard( + label: "Medical File", + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.appointmentRatingIcon, + isLargeText: true, + iconSize: 36.w, + isLocked: false, + ).toShimmer2() + : MedicalFileCard( + label: !((myAppointmentsVM.appointmentRatedResponseModel!).isAppointmentRated ?? true) + ? LocaleKeys.ratingSubmitted.tr(context: context) + : LocaleKeys.doctorRatingAppointment.tr(context: context), + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.appointmentRatingIcon, + isLargeText: true, + iconSize: 36.w, + isLocked: !(myAppointmentsVM.appointmentRatedResponseModel!.isAppointmentRated ?? true), + ).onPress(() { + if(!(myAppointmentsVM.appointmentRatedResponseModel!.isAppointmentRated ?? true)) { + showCommonBottomSheetWithoutHeight( + context, + title: LocaleKeys.doctorRating.tr(context: context), + isCloseButtonVisible: true, + child: StatefulBuilder( + builder: (context, setState) { + return AppointmentRatingWidget( + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + ); + }, + ), + isFullScreen: false, + ); + } + }), + ], + ); + }), + // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ @@ -662,7 +728,7 @@ class _AppointmentDetailsPageState extends State { // // "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}" // // .needTranslation // // .toText10( - // // weight: FontWeight.w500, + // // weight: FontWeight.w600, // // color: AppColors.greyTextColor, // // letterSpacing: -0.4), // // ], @@ -709,7 +775,7 @@ class _AppointmentDetailsPageState extends State { // // // borderColor: AppColors.secondaryLightRedColor, // // // textColor: AppColors.primaryRedColor, // // // fontSize: 14, - // // // fontWeight: FontWeight.w500, + // // // isBold: true, // // // borderRadius: 12.h, // // // height: 40.h, // // // icon: AppAssets.appointment_calendar_icon, @@ -737,7 +803,7 @@ class _AppointmentDetailsPageState extends State { // // borderColor: AppColors.secondaryLightRedColor, // // textColor: AppColors.primaryRedColor, // // fontSize: 14.f, - // // fontWeight: FontWeight.w500, + // // isBold: true, // // borderRadius: 12.r, // // height: 40.h, // // icon: AppAssets.requests, @@ -766,7 +832,7 @@ class _AppointmentDetailsPageState extends State { // // borderColor: AppColors.secondaryLightRedColor, // // textColor: AppColors.primaryRedColor, // // fontSize: 14.f, - // // fontWeight: FontWeight.w500, + // // isBold: true, // // borderRadius: 12.r, // // height: 40.h, // // icon: AppAssets.requests, @@ -819,8 +885,7 @@ class _AppointmentDetailsPageState extends State { children: [ Expanded( child: LocaleKeys.upcomingPaymentNow - .tr(context: context) - .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), + .tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)), "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64), ], ), @@ -850,8 +915,9 @@ class _AppointmentDetailsPageState extends State { ? (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! || widget.patientAppointmentHistoryResponseModel.isExecludeDoctor! || - Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)) - ? CustomButton( + !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)) + ? SizedBox.shrink() + : CustomButton( text: LocaleKeys.rebookAppointment.tr(context: context), onPressed: () { openDoctorScheduleCalendar(); @@ -860,15 +926,14 @@ class _AppointmentDetailsPageState extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, - borderRadius: 12.r, + fontWeight: FontWeight.w600, + borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 50.h, icon: AppAssets.add_icon, iconColor: Colors.white, iconSize: 18.h, ).paddingSymmetrical(16.h, 24.h) - : SizedBox.shrink() : CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), onPressed: () { @@ -879,7 +944,7 @@ class _AppointmentDetailsPageState extends State { AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 56.h, @@ -909,7 +974,7 @@ class _AppointmentDetailsPageState extends State { SizedBox(height: 4.h), LocaleKeys.confirmYourLocationDesc.tr(context: context).toText12( color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + isBold: true, ), SizedBox(height: 24.h), CustomButton( @@ -923,7 +988,7 @@ class _AppointmentDetailsPageState extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 56.h, @@ -949,7 +1014,7 @@ class _AppointmentDetailsPageState extends State { borderColor: AppColors.chipSecondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 56.h, @@ -987,12 +1052,13 @@ class _AppointmentDetailsPageState extends State { doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", + nationalityFlagURL: "", speciality: [], clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, projectName: widget.patientAppointmentHistoryResponseModel.projectName, ); bookAppointmentsViewModel.setSelectedDoctor(doctor); + bookAppointmentsViewModel.setIsPatientRescheduleAppointment(true); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingDoctorSchedulePleaseWait.tr(context: context)); await bookAppointmentsViewModel.getDoctorFreeSlots( isBookingForLiveCare: false, @@ -1036,17 +1102,19 @@ class _AppointmentDetailsPageState extends State { myAppointmentsViewModel.initAppointmentsViewModel(); myAppointmentsViewModel.getPatientAppointments(true, false); Navigator.of(context).pop(); - }, - title: "", - height: ResponsiveExtension.screenHeight * 0.3, - isCloseButtonVisible: true, - isDismissible: false, - isFullScreen: false, + }, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, isSuccessDialog: true); }); // LoaderBottomSheet.hideLoader(); case 15: - break; + getIt.get().showCommonBottomSheetWithoutH( + message: LocaleKeys.upcomingPaymentPending.tr(context: context), + label: LocaleKeys.notice.tr(), + onOkPressed: () {}, + okLabel: "confirm", + cancelLabel: LocaleKeys.acknowledged.tr(context: context), + isConfirmButton: true, + ); case 20: myAppointmentsViewModel.setIsPatientAppointmentShareLoading(true); Navigator.of(context).push( diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index d762f795..54d7991a 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -111,7 +111,7 @@ class _AppointmentPaymentPageState extends State { Image.asset(AppAssets.mada, width: 72.h, height: 25.h) .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), - "Mada".toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), + LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], ), SizedBox(width: 8.h), @@ -153,7 +153,7 @@ class _AppointmentPaymentPageState extends State { ], ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), - "Visa or Mastercard" + LocaleKeys.visaOrMastercard.tr(context: context) .toText16(isBold: true) .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], @@ -193,7 +193,7 @@ class _AppointmentPaymentPageState extends State { Image.asset(AppAssets.tamaraEng, width: 72.h, height: 25.h) .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), - "Tamara" + LocaleKeys.tamara.tr(context: context) .toText16(isBold: true) .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], @@ -249,7 +249,7 @@ class _AppointmentPaymentPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.insuranceExpiredOrInactive.tr(context: context) - .toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500) + .toText14(color: AppColors.primaryRedColor, isBold: true) .paddingSymmetrical(24.h, 0.h), CustomButton( text: LocaleKeys.updateInsurance.tr(context: context), @@ -258,13 +258,14 @@ class _AppointmentPaymentPageState extends State { CustomPageRoute( page: InsuranceHomePage(), ), - ); + ) + .then((val) {}); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.secondaryLightRedBorderColor, textColor: AppColors.whiteColor, fontSize: 10.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 8, padding: EdgeInsets.fromLTRB(15, 0, 15, 0), height: 30.h, diff --git a/lib/presentation/appointments/appointment_queue_page.dart b/lib/presentation/appointments/appointment_queue_page.dart index f205aeb9..cc1c7fd1 100644 --- a/lib/presentation/appointments/appointment_queue_page.dart +++ b/lib/presentation/appointments/appointment_queue_page.dart @@ -33,178 +33,162 @@ class AppointmentQueuePage extends StatelessWidget { Expanded( child: CollapsingListView( title: LocaleKeys.queueing.tr(context: context), - child: SingleChildScrollView( - child: Padding( - padding: EdgeInsets.all(24.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: false, - side: BorderSide( - color: myAppointmentsVM.isAppointmentQueueDetailsLoading - ? AppColors.whiteColor - : Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), - width: 2.w), - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: RefreshIndicator( + color: AppColors.primaryRedColor, + onRefresh: () async { + await myAppointmentsVM.getPatientAppointmentQueueDetails(); + }, + child: SingleChildScrollView( + child: Padding( + padding: EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + side: BorderSide( + color: myAppointmentsVM.isAppointmentQueueDetailsLoading ? AppColors.whiteColor : Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), width: 2.w), + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppCustomChipWidget( - labelText: myAppointmentsVM.currentQueueStatus == 0 ? LocaleKeys.inQueue.tr(context: context) : LocaleKeys.yourTurn.tr(context: context), - backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20), - textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), - ), - Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppCustomChipWidget( + labelText: myAppointmentsVM.currentQueueStatus == 0 ? LocaleKeys.inQueue.tr(context: context) : LocaleKeys.yourTurn.tr(context: context), + backgroundColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.20), + textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), + ), + Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), + ], + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + SizedBox(height: 10.h), + "Hala ${appState!.getAuthenticatedUser()!.firstName}!!!".toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + SizedBox(height: 8.h), + LocaleKeys.thankYouForPatience + .tr(context: context) + .toText12(isBold: true, color: AppColors.textColorLight) + .toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + SizedBox(height: 8.h), + myAppointmentsVM.currentPatientQueueDetails.queueNo!.toText32(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + SizedBox(height: 8.h), + CustomButton( + text: Utils.getCardButtonText(myAppointmentsVM.currentQueueStatus, myAppointmentsVM.currentPatientQueueDetails.roomNo ?? ""), + onPressed: () {}, + backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus), + borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01), + textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus), + fontSize: 12.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), ], - ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - SizedBox(height: 10.h), - "Hala ${appState!.getAuthenticatedUser()!.firstName}!!!" - .toText16(isBold: true) - .toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - SizedBox(height: 8.h), - LocaleKeys.thankYouForPatience.tr(context: context) - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight) - .toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - SizedBox(height: 8.h), - myAppointmentsVM.currentPatientQueueDetails.queueNo! - .toText32(isBold: true) - .toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - SizedBox(height: 8.h), - CustomButton( - text: Utils.getCardButtonText( - myAppointmentsVM.currentQueueStatus, myAppointmentsVM.currentPatientQueueDetails.roomNo ?? ""), - onPressed: () {}, - backgroundColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus), - borderColor: Utils.getCardButtonColor(myAppointmentsVM.currentQueueStatus).withValues(alpha: 0.01), - textColor: Utils.getCardButtonTextColor(myAppointmentsVM.currentQueueStatus), - fontSize: 12.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - iconColor: AppColors.whiteColor, - iconSize: 18.h, - ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - ], - ), - ), - ), - SizedBox(height: 16.h), - myAppointmentsVM.patientQueueDetailsList.isNotEmpty - ? Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.servingNow.tr(context: context) - .toText16(isBold: true) - .toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - SizedBox(height: 18.h), - ListView.separated( - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: myAppointmentsVM.patientQueueDetailsList.length, - physics: NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - myAppointmentsVM.patientQueueDetailsList[index].queueNo!.toText17(isBold: true), - Row( - crossAxisAlignment: CrossAxisAlignment.center, + ), + ), + SizedBox(height: 16.h), + myAppointmentsVM.patientQueueDetailsList.isNotEmpty + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.servingNow.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + SizedBox(height: 18.h), + ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: myAppointmentsVM.patientQueueDetailsList.length, + physics: NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Room: ${myAppointmentsVM.patientQueueDetailsList[index].roomNo}" - .toText12(fontWeight: FontWeight.w500), - SizedBox(width: 8.w), - AppCustomChipWidget( - deleteIcon: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 - ? AppAssets.call_for_vitals - : AppAssets.call_for_doctor, - labelText: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 - ? LocaleKeys.callForVitalSigns.tr(context: context) - : LocaleKeys.callForDoctor.tr(context: context), - iconColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 - ? AppColors.primaryRedColor - : AppColors.successColor, - textColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 - ? AppColors.primaryRedColor - : AppColors.successColor, - iconSize: 14.w, - backgroundColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 - ? AppColors.primaryRedColor.withValues(alpha: 0.1) - : AppColors.successColor.withValues(alpha: 0.1), - labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h), + myAppointmentsVM.patientQueueDetailsList[index].queueNo!.toText17(isBold: true), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + "Room: ${myAppointmentsVM.patientQueueDetailsList[index].roomNo}".toText12(isBold: true), + SizedBox(width: 8.w), + AppCustomChipWidget( + deleteIcon: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 ? AppAssets.call_for_vitals : AppAssets.call_for_doctor, + labelText: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 + ? LocaleKeys.callForVitalSigns.tr(context: context) + : LocaleKeys.callForDoctor.tr(context: context), + iconColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 ? AppColors.primaryRedColor : AppColors.successColor, + textColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 ? AppColors.primaryRedColor : AppColors.successColor, + iconSize: 14.w, + backgroundColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 + ? AppColors.primaryRedColor.withValues(alpha: 0.1) + : AppColors.successColor.withValues(alpha: 0.1), + labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h), + ), + ], ), ], - ), - ], - ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h), - ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - ], - ), - ), - ) - : SizedBox.shrink(), - SizedBox(height: 16.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.4, - hasShadow: true, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 8.h), + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + ], + ), + ), + ) + : SizedBox.shrink(), + SizedBox(height: 16.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.4, + hasShadow: true, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.buildSvgWithAssets(icon: AppAssets.bulb_icon, width: 24.w, height: 24.h), - SizedBox(width: 8.w), - LocaleKeys.thingsToAskDoctor.tr(context: context).toText16(isBold: true), + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.bulb_icon, width: 24.w, height: 24.h), + SizedBox(width: 8.w), + LocaleKeys.thingsToAskDoctor.tr(context: context).toText16(isBold: true), + ], + ), + SizedBox(height: 8.h), + "• ${LocaleKeys.improveOverallHealth.tr(context: context)}".toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${LocaleKeys.routineScreenings.tr(context: context)}".toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${LocaleKeys.whatIsThisMedicationFor.tr(context: context)}".toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${LocaleKeys.sideEffectsToKnow.tr(context: context)}".toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 4.h), + "• ${LocaleKeys.whenFollowUp.tr(context: context)}".toText12(isBold: true, color: AppColors.textColorLight), + SizedBox(height: 16.h), ], - ), - SizedBox(height: 8.h), - "• ${LocaleKeys.improveOverallHealth.tr(context: context)}" - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${LocaleKeys.routineScreenings.tr(context: context)}" - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${LocaleKeys.whatIsThisMedicationFor.tr(context: context)}" - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${LocaleKeys.sideEffectsToKnow.tr(context: context)}" - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - SizedBox(height: 4.h), - "• ${LocaleKeys.whenFollowUp.tr(context: context)}" - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), - - SizedBox(height: 16.h), - ], - ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), - ), + ).toShimmer2(isShow: myAppointmentsVM.isAppointmentQueueDetailsLoading), + ), + ), + ], ), - ], - ), + ), ), ), ), @@ -229,7 +213,7 @@ class AppointmentQueuePage extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 50.h, diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index c9e70487..1f54e3d1 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -55,36 +55,40 @@ class _MyAppointmentsPageState extends State { bookAppointmentsViewModel = Provider.of(context, listen: false); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, - body: CollapsingListView( - title: LocaleKeys.appointmentsList.tr(context: context), - child: SingleChildScrollView( - child: Column( - children: [ - SizedBox(height: 16.h), - CustomTabBar( - activeTextColor: Color(0xffED1C2B), - activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), - tabs: [ - CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)), - CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)), - CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)), - ], - onTabChange: (index) { - setState(() { - expandedIndex = null; - }); - myAppointmentsViewModel.onTabChange(index); - myAppointmentsViewModel.updateListWRTTab(index); - context.read().flush(); - }, - ).paddingSymmetrical(24.h, 0.h), - Consumer(builder: (context, myAppointmentsVM, child) { - return getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM); - }), - ], + body: Consumer(builder: (context, myAppointmentsVM, child) { + return CollapsingListView( + title: LocaleKeys.appointmentsList.tr(context: context), + child: SingleChildScrollView( + child: Column( + children: [ + SizedBox(height: 16.h), + CustomTabBar( + initialIndex: myAppointmentsVM.selectedTabIndex, + activeTextColor: Color(0xffED1C2B), + activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), + tabs: [ + CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)), + CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)), + CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)), + ], + onTabChange: (index) { + setState(() { + expandedIndex = null; + }); + myAppointmentsViewModel.onTabChange(index); + myAppointmentsViewModel.updateListWRTTab(index); + context.read().flush(); + }, + ).paddingSymmetrical(24.h, 0.h), + // Consumer(builder: (context, myAppointmentsVM, child) { + // return + getSelectedTabData(myAppointmentsVM.selectedTabIndex, myAppointmentsVM), + // }), + ], ), - ), - ), + ), + ); + }), ); } @@ -109,7 +113,7 @@ class _MyAppointmentsPageState extends State { borderColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -124,7 +128,7 @@ class _MyAppointmentsPageState extends State { borderColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, textColor: myAppointmentsVM.isAppointmentsSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -144,7 +148,7 @@ class _MyAppointmentsPageState extends State { ? myAppointmentsVM.patientAppointmentsViewList.length : 1, itemBuilder: (context, index) { - final isExpanded = expandedIndex == index; + final isExpanded = myAppointmentsVM.selectedTabIndex == 1 ? true : expandedIndex == index; return myAppointmentsVM.isMyAppointmentsLoading ? Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), @@ -187,7 +191,7 @@ class _MyAppointmentsPageState extends State { children: [ AppCustomChipWidget( labelText: - "${myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.length} Appointments"), + "${myAppointmentsVM.patientAppointmentsViewList[index].patientDoctorAppointmentList!.length} ${LocaleKeys.appointments.tr(context: context)}", isEnglishOnly: true,), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), @@ -262,7 +266,7 @@ class _MyAppointmentsPageState extends State { borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart index 002f537c..93733770 100644 --- a/lib/presentation/appointments/my_doctors_page.dart +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -89,7 +89,7 @@ class _MyDoctorsPageState extends State { borderColor: isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -107,7 +107,7 @@ class _MyDoctorsPageState extends State { borderColor: isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, textColor: isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -218,14 +218,23 @@ class _MyDoctorsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - AppCustomChipWidget(labelText: "${group.length} ${'doctors'}"), - Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + AppCustomChipWidget( + richText: Row( + children: [ + "${group.length} ".toText10(isEnglishOnly: true), + "${LocaleKeys.doctors.tr(context: context)} ".toText10() + ], + ), + // labelText: "${group.length} ${'doctors'}", + isEnglishOnly: true, + ), + Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Text( displayName, - style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600), + style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600,), overflow: TextOverflow.ellipsis, ), ], @@ -267,7 +276,7 @@ class _MyDoctorsPageState extends State { ).circle(100), SizedBox(width: 8.h), Expanded( - child: (doctor?.doctorName ?? "").toString().toText14(weight: FontWeight.w500), + child: (doctor?.doctorName ?? "").toString().toText14(isBold: true), ), ], ), @@ -289,10 +298,10 @@ class _MyDoctorsPageState extends State { flex: 2, child: CustomButton( icon: AppAssets.view_report_icon, - iconColor: AppColors.primaryRedColor, + iconColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 1.0) : AppColors.primaryRedColor, iconSize: 16.h, text: LocaleKeys.viewProfile.tr(context: context), - onPressed: () async { + onPressed: doctor?.isActiveDoctorProfile == false ? () {} : () async { bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel( clinicID: doctor?.clinicID ?? 0, projectID: doctor?.projectID ?? 0, @@ -309,7 +318,7 @@ class _MyDoctorsPageState extends State { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( CustomPageRoute( - page: DoctorProfilePage(), + page: DoctorProfilePage(isDoctorAllowedToBook: !doctor?.isLiveCareClinic), ), ); }, onError: (err) { @@ -323,11 +332,11 @@ class _MyDoctorsPageState extends State { ); }); }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, + backgroundColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : AppColors.secondaryLightRedColor, + borderColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 0.01) : AppColors.secondaryLightRedColor, + textColor: doctor?.isActiveDoctorProfile == false ? AppColors.inputLabelTextColor.withValues(alpha: 1.0) : AppColors.primaryRedColor, fontSize: 14, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 52a1c219..c69a2bc4 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -148,8 +148,8 @@ class AppointmentCard extends StatelessWidget { Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), SizedBox(height: 2.h), (isFoldable || isTablet) - ? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor) - : "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor), + ? "${patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true) + : "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), ], ), ).circle(100).toShimmer2(isShow: isLoading), @@ -165,7 +165,16 @@ class AppointmentCard extends StatelessWidget { children: [ (isLoading ? 'Dr' : "${patientAppointmentHistoryResponseModel.doctorTitle}").toText16(isBold: true, maxlines: 1), (isLoading ? 'John Doe' : " ${patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}") - .toText16(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")) + .toText16(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")), + SizedBox(width: 12.w), + (patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) + ? Image.network( + patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", + width: 20.h, + height: 15.h, + fit: BoxFit.cover, + ) + : SizedBox.shrink(), ], ).toShimmer2(isShow: isLoading), SizedBox(height: 8.h), @@ -261,7 +270,7 @@ class AppointmentCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, @@ -289,7 +298,7 @@ class AppointmentCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, @@ -314,7 +323,7 @@ class AppointmentCard extends StatelessWidget { borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01), textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -394,7 +403,7 @@ class AppointmentCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, @@ -413,7 +422,7 @@ class AppointmentCard extends StatelessWidget { textColor: AppColors.blackColor, borderWidth: 1.h, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -456,7 +465,7 @@ class AppointmentCard extends StatelessWidget { doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, name: patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: 'https://hmgwebservices.com/Images/flag/SYR.png', + nationalityFlagURL: '', speciality: [], clinicName: patientAppointmentHistoryResponseModel.clinicName, projectName: patientAppointmentHistoryResponseModel.projectName, diff --git a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart index 95fc09b8..da50b3c7 100644 --- a/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart +++ b/lib/presentation/appointments/widgets/appointment_checkin_bottom_sheet.dart @@ -71,20 +71,20 @@ class AppointmentCheckinBottomSheet extends StatelessWidget { } }); }), - SizedBox(height: 16.h), - checkInOptionCard( - AppAssets.checkin_nfc_icon, - LocaleKeys.nfcNearFieldCommunication.tr(context: context), - LocaleKeys.scanPhoneViaNFC.tr(context: context), - ).onPress(() { - Future.delayed(const Duration(milliseconds: 500), () { - showNfcReader(context, onNcfScan: (String nfcId) { - Future.delayed(const Duration(milliseconds: 100), () { - sendCheckInRequest(nfcId, 1, context); - }); - }, onCancel: () {}); - }); - }), + // SizedBox(height: 16.h), + // checkInOptionCard(O + // AppAssets.checkin_nfc_icon, + // LocaleKeys.nfcNearFieldCommunication.tr(context: context), + // LocaleKeys.scanPhoneViaNFC.tr(context: context), + // ).onPress(() { + // Future.delayed(const Duration(milliseconds: 500), () { + // showNfcReader(context, onNcfScan: (String nfcId) { + // Future.delayed(const Duration(milliseconds: 100), () { + // sendCheckInRequest(nfcId, 1, context); + // }); + // }, onCancel: () {}); + // }); + // }), SizedBox(height: 16.h), checkInOptionCard( AppAssets.checkin_qr_icon, @@ -120,7 +120,7 @@ class AppointmentCheckinBottomSheet extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText16(isBold: true, color: AppColors.textColor), - subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + subTitle.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index 5a47708e..5de06455 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -1,19 +1,27 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; 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/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'dart:ui' as ui; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; + class AppointmentDoctorCard extends StatelessWidget { const AppointmentDoctorCard( {super.key, required this.patientAppointmentHistoryResponseModel, required this.onRescheduleTap, required this.onCancelTap, required this.onAskDoctorTap, this.renderWidgetForERDisplay = false}); @@ -66,7 +74,7 @@ class AppointmentDoctorCard extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow), SizedBox(height: 2.h), - "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}".toText11(isBold: true, color: AppColors.textColor), + "${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), ], ), ).circle(100), @@ -75,10 +83,24 @@ class AppointmentDoctorCard extends StatelessWidget { ), SizedBox(width: 16.w), Expanded( + flex: 9, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? "")), + Row( + children: [ + patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? "")), + SizedBox(width: 12.w), + (patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) + ? Image.network( + patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", + width: 20.h, + height: 15.h, + fit: BoxFit.cover, + ) + : SizedBox.shrink(), + ], + ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, @@ -120,6 +142,41 @@ class AppointmentDoctorCard extends StatelessWidget { ], ), ), + Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).onPress(() async { + DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel(); + selectedDoctor.doctorID = patientAppointmentHistoryResponseModel.doctorID; + selectedDoctor.doctorImageURL = patientAppointmentHistoryResponseModel.doctorImageURL; + selectedDoctor.name = patientAppointmentHistoryResponseModel.doctorNameObj; + selectedDoctor.doctorTitle = patientAppointmentHistoryResponseModel.doctorTitle; + selectedDoctor.nationalityFlagURL = ""; + selectedDoctor.speciality = patientAppointmentHistoryResponseModel.doctorSpeciality; + selectedDoctor.clinicName = patientAppointmentHistoryResponseModel.clinicName; + selectedDoctor.projectName = patientAppointmentHistoryResponseModel.projectName; + selectedDoctor.clinicID = patientAppointmentHistoryResponseModel.clinicID; + selectedDoctor.projectID = patientAppointmentHistoryResponseModel.projectID; + + getIt.get().setSelectedDoctor(selectedDoctor); + LoaderBottomSheet.showLoader(); + await getIt.get().getDoctorProfile(onSuccess: (dynamic respData) { + LoaderBottomSheet.hideLoader(); + Navigator.of(context).push( + CustomPageRoute( + page: DoctorProfilePage( + isDoctorAllowedToBook: !(patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false), + ), + ), + ); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + }) ], ), SizedBox(height: 8.h), @@ -147,7 +204,7 @@ class AppointmentDoctorCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, @@ -166,7 +223,7 @@ class AppointmentDoctorCard extends StatelessWidget { // borderColor: AppColors.greyColor, // textColor: AppColors.blackColor, // fontSize: 12.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), // height: 40.h, @@ -187,7 +244,7 @@ class AppointmentDoctorCard extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, icon: AppAssets.cancel, @@ -206,7 +263,7 @@ class AppointmentDoctorCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, icon: AppAssets.appointment_calendar_icon, @@ -225,7 +282,7 @@ class AppointmentDoctorCard extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, icon: AppAssets.cancel, diff --git a/lib/presentation/appointments/widgets/appointment_rating_widget.dart b/lib/presentation/appointments/widgets/appointment_rating_widget.dart new file mode 100644 index 00000000..735895a0 --- /dev/null +++ b/lib/presentation/appointments/widgets/appointment_rating_widget.dart @@ -0,0 +1,181 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_export.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/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/widgets/doctor_row_appointment_rating.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:provider/provider.dart'; + +class AppointmentRatingWidget extends StatefulWidget { + const AppointmentRatingWidget({super.key, required this.patientAppointmentHistoryResponseModel}); + + final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; + + @override + State createState() => _AppointmentRatingWidgetState(); +} + +class _AppointmentRatingWidgetState extends State { + late MyAppointmentsViewModel myAppointmentsViewModel; + late BookAppointmentsViewModel bookAppointmentsViewModel; + + int rating = 0; + String notes = ""; + + @override + Widget build(BuildContext context) { + myAppointmentsViewModel = Provider.of(context, listen: false); + bookAppointmentsViewModel = Provider.of(context, listen: false); + return SizedBox( + height: MediaQuery.of(context).size.height * 0.6, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Scrollable main content + Expanded( + child: Padding( + padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Doctor row + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: BuildDoctorRowAppointmentRating( + patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, + )), + + SizedBox(height: 16), + + // Rating box + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + width: double.infinity, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.rateTheDoctor.tr(context: context).toText16(isBold: true), + SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...List.generate( + 5, + (index) => AnimatedSwitcher( + duration: Duration(milliseconds: 1000), + switchInCurve: Curves.elasticOut, + switchOutCurve: Curves.elasticIn, + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition(child: child, scale: animation); + }, + child: Container( + key: ValueKey(rating), + child: IconButton( + iconSize: 45.0, + onPressed: () { + setState(() { + rating = index + 1; + }); + }, + color: rating >= (index + 1) ? Color.fromRGBO(255, 186, 0, 1.0) : Colors.grey[400], + icon: Icon(rating >= (index + 1) ? Icons.star : Icons.star)), + ), + ), + ) + ], + ), + ], + ), + ), + ), + + SizedBox(height: 12), + + // Note text field + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.0), + child: TextField( + maxLines: 4, + decoration: InputDecoration.collapsed( + hintText: LocaleKeys.notes.tr(context: context), + hintStyle: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16)), + onChanged: (value) { + setState(() { + notes = value; + }); + }, + ))), + ], + ), + ), + ), + // Bottom action buttons pinned to bottom + SafeArea( + top: false, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.later.tr(context: context), + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + onPressed: () { + Navigator.pop(context); + }, + ), + ), + SizedBox(width: 10), + Expanded( + child: CustomButton( + text: LocaleKeys.submit.tr(context: context), + onPressed: () async { + // Set up clinic rating and show clinic rating view + // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); + // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); + // appointmentRatingViewModel!.setClinicOrDoctor(true); + + LoaderBottomSheet.showLoader(); + await myAppointmentsViewModel.submitDoctorRating(docRate: rating, docNote: notes, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel); + // await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note); + LoaderBottomSheet.hideLoader(); + Navigator.pop(context); + + setState(() {}); + }, + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart b/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart index 3002c1c9..35b15399 100644 --- a/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart +++ b/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart @@ -47,7 +47,7 @@ class AskDoctorRequestTypeSelect extends StatelessWidget { listTileTheme: ListTileThemeData(horizontalTitleGap: 4), ), child: RadioListTile( - title: (askDoctorRequestTypeList[index].description ?? '').toText14(weight: FontWeight.w500), + title: (askDoctorRequestTypeList[index].description ?? '').toText14(isBold: true), value: index, fillColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { diff --git a/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart b/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart new file mode 100644 index 00000000..8872f4ea --- /dev/null +++ b/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/size_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/my_appointments/models/resp_models/appointment_details_resp_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; +import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; + +import 'dart:ui' as ui; + +class BuildDoctorRowAppointmentRating extends StatelessWidget { + final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; + + BuildDoctorRowAppointmentRating({super.key, required this.patientAppointmentHistoryResponseModel}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.all(16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.network( + patientAppointmentHistoryResponseModel.doctorImageURL!, + width: 63.h, + height: 63.h, + fit: BoxFit.cover, + ).circle(100), + SizedBox(width: 16.h), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + patientAppointmentHistoryResponseModel.doctorNameObj!.toString().toText16(isBold: true, maxlines: 1), + SizedBox(height: 8.h), + Wrap( + direction: Axis.horizontal, + spacing: 3.h, + runSpacing: 4.h, + children: [ + AppCustomChipWidget( + labelText: patientAppointmentHistoryResponseModel.projectName.toString(), + ), + AppCustomChipWidget( + labelText: patientAppointmentHistoryResponseModel.clinicName.toString(), + ), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -8.w, end: 6.w), + icon: AppAssets.ic_date_filter, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false), + isEnglishOnly: true, + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart b/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart index 2f5589c8..a6e3e8d2 100644 --- a/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart +++ b/lib/presentation/appointments/widgets/faculity_selection/facility_selection_item.dart @@ -82,7 +82,7 @@ class FacilitySelectionItem extends StatelessWidget { subTitle, style: TextStyle( fontSize: 12.h, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.greyTextColor, ), ), diff --git a/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart b/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart index b6366dbf..eb084ca7 100644 --- a/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart +++ b/lib/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart @@ -39,7 +39,7 @@ class FacilityTypeSelectionWidget extends StatelessWidget { LocaleKeys.selectFacilitiesSubTitle.tr(), style: TextStyle( fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.greyTextColor, ), ), diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart index 8904e585..4264ba17 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart @@ -36,7 +36,7 @@ import '../../../emergency_services/call_ambulance/widgets/type_selection_widget // LocaleKeys.selectHospital.tr(context: context), // style: TextStyle( // fontSize: 21, -// fontWeight: FontWeight.w600, +// isBold: true, // color: AppColors.blackColor, // ), // ), @@ -44,7 +44,7 @@ import '../../../emergency_services/call_ambulance/widgets/type_selection_widget // LocaleKeys.selectHospitalSubTitle.tr(context: context), // style: TextStyle( // fontSize: 16, -// fontWeight: FontWeight.w500, +// isBold: true, // color: AppColors.greyTextColor, // ), // ), @@ -186,7 +186,7 @@ class HospitalBottomSheetBody extends StatelessWidget { children: [ LocaleKeys.sortByLocation.tr(context: context).toText14(isBold: true), // SizedBox(height: 4.h), - LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, weight: FontWeight.w500), + LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), ], ), ], diff --git a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart index abe1641a..8b395e7b 100644 --- a/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart +++ b/lib/presentation/appointments/widgets/hospital_bottom_sheet/hospital_list_items.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; @@ -7,9 +8,12 @@ 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/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/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'dart:ui' as ui; + class HospitalListItem extends StatelessWidget { final PatientDoctorAppointmentList? hospitalData; final bool isLocationEnabled; @@ -76,12 +80,16 @@ class HospitalListItem extends StatelessWidget { children: [ Visibility( visible: (hospitalData?.distanceInKMs != "0"), - child: AppCustomChipWidget( - labelText: "${hospitalData?.distanceInKMs ?? ""} km", - deleteIcon: AppAssets.location_red, - deleteIconSize: Size(9, 12), - backgroundColor: AppColors.secondaryLightRedColor, - textColor: AppColors.errorColor, + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: "${hospitalData?.distanceInKMs ?? ""} km", + deleteIcon: AppAssets.location_red, + deleteIconSize: Size(9, 12), + backgroundColor: AppColors.secondaryLightRedColor, + textColor: AppColors.errorColor, + isEnglishOnly: true, + ), ), ), Visibility( @@ -89,7 +97,7 @@ class HospitalListItem extends StatelessWidget { child: Row( children: [ AppCustomChipWidget( - labelText: "Distance not available", + labelText: LocaleKeys.noDistance.tr(), textColor: AppColors.blackColor, ), // SizedBox( diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart index 99f31be6..7f88074c 100644 --- a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart +++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_item.dart @@ -68,7 +68,7 @@ class RegionListItem extends StatelessWidget { text: TextSpan( text: count, style: TextStyle(fontSize: 12.h, fontWeight: FontWeight.w700, color: AppColors.blackColor), - children: [TextSpan(text: title, style: TextStyle(fontSize: 12.h, fontWeight: FontWeight.w500, color: AppColors.blackColor))])), + children: [TextSpan(text: title, style: TextStyle(fontSize: 12.h, fontWeight: FontWeight.w600, color: AppColors.blackColor))])), ); } @@ -93,7 +93,7 @@ class RegionListItem extends StatelessWidget { subTitle, style: TextStyle( fontSize: 12.h, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.greyTextColor, ), ), diff --git a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart index 2b3a6ffc..b689162c 100644 --- a/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart +++ b/lib/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart @@ -117,38 +117,38 @@ class _RegionBottomSheetBodyState extends State { height: MediaQuery.of(context).size.height * 0.5, child: Column( children: [ - Consumer( - builder: (context, regionVM, _) { - return Padding( - padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 8.h), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.location, iconColor: AppColors.greyTextColor, width: 24.h, height: 24.h), - SizedBox(width: 12.w), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.sortByLocation.tr(context: context).toText14(isBold: true), - // SizedBox(height: 4.h), - LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, weight: FontWeight.w500), - ], - ), - ], - ), - Switch( - value: regionVM.sortByLocation, - onChanged: (value) => _handleSortByLocationToggle(value), - activeThumbColor: AppColors.successColor, - activeTrackColor: AppColors.successColor.withValues(alpha: 0.15), - ), - ], - ), - ); - }, - ), + // Consumer( + // builder: (context, regionVM, _) { + // return Padding( + // padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 8.h), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // children: [ + // Utils.buildSvgWithAssets(icon: AppAssets.location, iconColor: AppColors.greyTextColor, width: 24.h, height: 24.h), + // SizedBox(width: 12.w), + // Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // LocaleKeys.sortByLocation.tr(context: context).toText14(isBold: true), + // // SizedBox(height: 4.h), + // LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), + // ], + // ), + // ], + // ), + // Switch( + // value: regionVM.sortByLocation, + // onChanged: (value) => _handleSortByLocationToggle(value), + // activeThumbColor: AppColors.successColor, + // activeTrackColor: AppColors.successColor.withValues(alpha: 0.15), + // ), + // ], + // ), + // ); + // }, + // ), Expanded( child: ListView.separated( itemCount: myAppointmentsVM.hospitalList?.registeredDoctorMap?.length ?? 0, diff --git a/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart b/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart index d58b75e9..4ebd9b74 100644 --- a/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart +++ b/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart @@ -203,7 +203,7 @@ class AskDoctorAppointmentCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // height: isTablet || isFoldable ? 46.h : 40.h, diff --git a/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart b/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart index 33b544d7..59516d15 100644 --- a/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart +++ b/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart @@ -77,7 +77,7 @@ class DoctorResponseTransactions extends StatelessWidget { borderColor: AppColors.greyColor, textColor: AppColors.textColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10.r, height: 40.h, ).paddingSymmetrical(16.w, 0.h), @@ -93,7 +93,7 @@ class DoctorResponseTransactions extends StatelessWidget { color: AppColors.infoColor.withAlpha(20), borderRadius: BorderRadius.circular(8.r), ), - child: doctorResponseModel.requestTypeDescription!.toText12(color: AppColors.infoColor, fontWeight: FontWeight.w500), + child: doctorResponseModel.requestTypeDescription!.toText12(color: AppColors.infoColor, isBold: true), ), ], ), @@ -115,7 +115,7 @@ class DoctorResponseTransactions extends StatelessWidget { color: AppColors.infoColor.withAlpha(20), borderRadius: BorderRadius.circular(8.r), ), - child: doctorResponseModel.transactions![index].doctorResponse!.toText12(color: AppColors.textColor, fontWeight: FontWeight.w500), + child: doctorResponseModel.transactions![index].doctorResponse!.toText12(color: AppColors.textColor, isBold: true), ), SizedBox(width: 8.w), Image.network("https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", fit: BoxFit.cover, width: 42.w, height: 42.h).circle(100.r), diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart index 62d0c9e2..d559ebbc 100644 --- a/lib/presentation/authentication/login.dart +++ b/lib/presentation/authentication/login.dart @@ -116,7 +116,7 @@ class LoginScreenState extends State { child: RichText( textAlign: TextAlign.center, text: TextSpan( - style: context.dynamicTextStyle(color: AppColors.textColor, fontSize: 14.f, height: 26 / 16, fontWeight: FontWeight.w500), + style: context.dynamicTextStyle(color: AppColors.textColor, fontSize: 14.f, height: 26 / 16, fontWeight: FontWeight.w600,), children: [ TextSpan(text: LocaleKeys.dontHaveAccount.tr(context: context), style: context.dynamicTextStyle()), TextSpan(text: " "), @@ -126,7 +126,7 @@ class LoginScreenState extends State { color: AppColors.primaryRedColor, fontSize: 14.f, // Adjusted to sizer unit height: 26 / 16, // Ratio - fontWeight: FontWeight.w500), + fontWeight: FontWeight.w600,), recognizer: TapGestureRecognizer() ..onTap = () { Navigator.of(context).push( diff --git a/lib/presentation/authentication/quick_login.dart b/lib/presentation/authentication/quick_login.dart index 19e90d64..319402b0 100644 --- a/lib/presentation/authentication/quick_login.dart +++ b/lib/presentation/authentication/quick_login.dart @@ -49,13 +49,13 @@ class QuickLoginState extends State { ], ), Utils.showLottie(context: context, assetPath: AppAnimations.checkmark, width: 120, height: 120, repeat: true), - LocaleKeys.allSet.tr(context: context).toText16(textAlign: TextAlign.center, weight: FontWeight.w500) + LocaleKeys.allSet.tr(context: context).toText16(textAlign: TextAlign.center, isBold: true) // Text( // ' TranslationBase.of(context).allSet', // textAlign: TextAlign.center, // style: context.dynamicTextStyle( // fontSize: 16, - // fontWeight: FontWeight.w500, + // isBold: true, // color: Colors.black, // ), // ), diff --git a/lib/presentation/authentication/register.dart b/lib/presentation/authentication/register.dart index 535cdff1..2f181e16 100644 --- a/lib/presentation/authentication/register.dart +++ b/lib/presentation/authentication/register.dart @@ -160,7 +160,7 @@ class _RegisterNew extends State { children: [ Text( LocaleKeys.iAcceptThe.tr(context: context), - style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, color: Color(0xFF2E3039)), ), GestureDetector( onTap: () { @@ -173,7 +173,7 @@ class _RegisterNew extends State { " ${LocaleKeys.termsConditoins.tr(context: context)}", style: context.dynamicTextStyle( fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.primaryRedColor, decoration: TextDecoration.underline, decorationColor: AppColors.primaryRedBorderColor, @@ -185,7 +185,7 @@ class _RegisterNew extends State { // Expanded( // child: Text( // LocaleKeys.iAcceptTermsConditions.tr(context: context).split("the").first, - // style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + // style: context.dynamicTextStyle(fontSize: 14.fSize, isBold: true, color: Color(0xFF2E3039)), // ), // ), ], @@ -222,7 +222,7 @@ class _RegisterNew extends State { color: Colors.black, fontSize: 16.f, height: 26 / 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), children: [ TextSpan(text: LocaleKeys.alreadyHaveAccount.tr(context: context), style: context.dynamicTextStyle()), @@ -233,7 +233,7 @@ class _RegisterNew extends State { color: AppColors.primaryRedColor, fontSize: 16.f, height: 26 / 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), recognizer: TapGestureRecognizer() ..onTap = () { diff --git a/lib/presentation/authentication/register_step2.dart b/lib/presentation/authentication/register_step2.dart index dc87aa1d..f01dd036 100644 --- a/lib/presentation/authentication/register_step2.dart +++ b/lib/presentation/authentication/register_step2.dart @@ -140,7 +140,7 @@ class _RegisterNew extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.personalDetailsVerification.tr(context: context).toText26(color: AppColors.textColor, weight: FontWeight.w600, letterSpacing: -2), + LocaleKeys.personalDetailsVerification.tr(context: context).toText26(color: AppColors.textColor, isBold: true, letterSpacing: -2), SizedBox(height: 24.h), // Insurance Card Section diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart index 96db5187..bd29e3d2 100644 --- a/lib/presentation/authentication/saved_login_screen.dart +++ b/lib/presentation/authentication/saved_login_screen.dart @@ -66,6 +66,7 @@ class _SavedLogin extends State { appBar: CustomAppBar( onBackPressed: () { Navigator.of(context).pop(); + authVm.setIsInitialLoginNavigated(true); }, onLanguageChanged: (value) { context.setLocale(value == 'en' ? Locale('en', 'US') : Locale('ar', 'SA')); @@ -84,7 +85,7 @@ class _SavedLogin extends State { SizedBox(height: 16.h), appState.getSelectDeviceByImeiRespModelElement != null ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase - .toText26(isBold: true, height: 26 / 36, color: AppColors.textColor) + .toText26(isBold: true, height: 26 / 36, color: AppColors.textColor, isEnglishOnly: true) : SizedBox(), SizedBox(height: 24.h), Container( @@ -144,7 +145,7 @@ class _SavedLogin extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, height: 40.h, padding: EdgeInsets.symmetric(vertical: 10.h), @@ -161,7 +162,7 @@ class _SavedLogin extends State { padding: EdgeInsets.symmetric(horizontal: 16.w), child: Text( LocaleKeys.oR.tr(), - style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w500), + style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w600,), ), ), SizedBox(height: 24.h), @@ -206,7 +207,7 @@ class _SavedLogin extends State { }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, - textColor: AppColors.textColor, + textColor: Colors.white, icon: AppAssets.sms), ), Row( @@ -304,7 +305,7 @@ class _SavedLogin extends State { borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h), // icon: "assets/images/svg/apple-finder.svg", @@ -329,7 +330,7 @@ class _SavedLogin extends State { borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), fontSize: 15.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h), // icon: "assets/images/svg/apple-finder.svg", diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart index 7c6c625f..277d78c0 100644 --- a/lib/presentation/blood_donation/blood_donation_page.dart +++ b/lib/presentation/blood_donation/blood_donation_page.dart @@ -146,11 +146,11 @@ class _BloodDonationPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.city.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), + LocaleKeys.city.tr().toText16(color: AppColors.textColor, isBold: true), (appState.isArabic() ? (bloodDonationVM.selectedCity?.descriptionN ?? LocaleKeys.select.tr()) : bloodDonationVM.selectedCity?.description ?? LocaleKeys.select.tr(context: context)) - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + .toText14(color: AppColors.greyTextColor, isBold: true), ], ), ], @@ -174,11 +174,11 @@ class _BloodDonationPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.gender.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), + LocaleKeys.gender.tr().toText16(color: AppColors.textColor, isBold: true), (appState.isArabic() ? (bloodDonationVM.selectedGender?.typeAr ?? LocaleKeys.select.tr()) : bloodDonationVM.selectedGender?.type ?? LocaleKeys.select.tr(context: context)) - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + .toText14(color: AppColors.greyTextColor, isBold: true), ], ), ], @@ -205,12 +205,12 @@ class _BloodDonationPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), - // bloodDonationVM.selectedBloodType?.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, isBold: true), + // bloodDonationVM.selectedBloodType?.toText14(color: AppColors.greyTextColor, isBold: true), (appState.isArabic() ? (bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr()) : bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr(context: context)) - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500) + .toText14(color: AppColors.greyTextColor, isBold: true) ], ), ], @@ -264,7 +264,7 @@ class _BloodDonationPageState extends State { children: [ Text( "${LocaleKeys.iAcceptThe.tr()} ", - style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + style: context.dynamicTextStyle(fontSize: 14.f, fontWeight: FontWeight.w600, color: Color(0xFF2E3039)), ), GestureDetector( onTap: () { @@ -282,7 +282,7 @@ class _BloodDonationPageState extends State { LocaleKeys.termsConditoins.tr(), style: context.dynamicTextStyle( fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.primaryRedColor, decoration: TextDecoration.underline, decorationColor: AppColors.primaryRedBorderColor, @@ -294,7 +294,7 @@ class _BloodDonationPageState extends State { // Expanded( // child: Text( // LocaleKeys.iAcceptTermsConditions.tr().split("the").first, - // style: context.dynamicTextStyle(fontSize: 14.fSize, fontWeight: FontWeight.w500, color: Color(0xFF2E3039)), + // style: context.dynamicTextStyle(fontSize: 14.fSize, isBold: true, color: Color(0xFF2E3039)), // ), // ), ], @@ -365,7 +365,7 @@ class _BloodDonationPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 50.h, diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index cfdd9d6a..bbc88214 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_liveca import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.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; import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart'; @@ -119,18 +120,30 @@ class _BookAppointmentPageState extends State { if (appState.isAuthenticated) ...[ Consumer(builder: (context, myAppointmentsVM, child) { return myAppointmentsVM.isPatientMyDoctorsLoading - ? Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.network( + ? SizedBox( + height: 110.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: 5, + shrinkWrap: true, + padding: EdgeInsets.only(left: 24.w, right: 24.w), + itemBuilder: (context, index) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.network( "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 64.w, height: 64.h, 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").toString().toText12(isBold: true, isCenter: true, maxLine: 2).toShimmer2(isShow: true), + ], + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.h), + ), ) : myAppointmentsVM.patientMyDoctorsList.isEmpty ? SizedBox() @@ -138,7 +151,25 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ if (appState.isAuthenticated) ...[], - LocaleKeys.recentVisits.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.recentVisits.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), + Row( + children: [ + LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: MyDoctorsPage(), + ), + ); + }), + ], + ), SizedBox(height: 16.h), SizedBox( height: 110.h, @@ -170,7 +201,7 @@ class _BookAppointmentPageState extends State { width: 80.w, child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName) .toString() - .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2) + .toText12(isBold: true, isCenter: true, maxLine: 2) .toShimmer2(isShow: false), ), ], @@ -186,7 +217,7 @@ class _BookAppointmentPageState extends State { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( CustomPageRoute( - page: DoctorProfilePage(), + page: DoctorProfilePage(isDoctorAllowedToBook: !(myAppointmentsVM.patientMyDoctorsList[index].isLiveCareClinic ?? false)), ), ); }, onError: (err) { @@ -218,13 +249,14 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 24.h), - LocaleKeys.favouriteDoctors.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), + LocaleKeys.favouriteList.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), SizedBox(height: 16.h), SizedBox( height: 110.h, child: ListView.separated( scrollDirection: Axis.horizontal, - itemCount: 3, // Show 3 shimmer items + itemCount: 3, + // Show 3 shimmer items shrinkWrap: true, padding: EdgeInsets.only(left: 24.w, right: 24.w), itemBuilder: (context, index) { @@ -264,21 +296,79 @@ class _BookAppointmentPageState extends State { // Show empty state or actual list return myAppointmentsVM.patientFavouriteDoctorsList.isEmpty - ? SizedBox() + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 16.h), + LocaleKeys.favouriteList.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), + SizedBox(height: 16.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Center( + child: LocaleKeys.noFavouriteDoctors.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), + ), + SizedBox(height: 12.h), + CustomButton( + text: LocaleKeys.add.tr(context: context), + onPressed: () { + Navigator.of(context).push(CustomPageRoute(page: SearchDoctorByName())); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14.f, + isBold: true, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + height: 40.h, + ), + ], + ), + ).paddingSymmetrical(24.w, 0.h), + SizedBox(height: 24.h), + ], + ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 24.h), - LocaleKeys.favouriteDoctors.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), - SizedBox(height: 16.h), - SizedBox( + LocaleKeys.favourites.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.w, 0.h), + SizedBox(height: 16.h), + SizedBox( height: 110.h, child: ListView.separated( scrollDirection: Axis.horizontal, - itemCount: myAppointmentsVM.patientFavouriteDoctorsList.length, + itemCount: myAppointmentsVM.patientFavouriteDoctorsList.length + 1, shrinkWrap: true, padding: EdgeInsets.only(left: 24.w, right: 24.w), itemBuilder: (context, index) { + // Last item: static "Add" button + if (index == myAppointmentsVM.patientFavouriteDoctorsList.length) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.add_new_family_icon, height: 64.h, width: 64.h), + SizedBox( + width: 80.w, + child: LocaleKeys.add.tr(context: context).toText12( + color: AppColors.textColor, + isBold: true, + isCenter: true, + ).paddingOnly(top: 4.h), + ), + ], + ).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: SearchDoctorByName())); + }); + } return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 1000), @@ -300,7 +390,7 @@ class _BookAppointmentPageState extends State { width: 80.w, child: (myAppointmentsVM.patientFavouriteDoctorsList[index].doctorName) .toString() - .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2) + .toText12(isBold: true, isCenter: true, maxLine: 2) .toShimmer2(isShow: false), ), ], @@ -316,7 +406,7 @@ class _BookAppointmentPageState extends State { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( CustomPageRoute( - page: DoctorProfilePage(), + page: DoctorProfilePage(isDoctorAllowedToBook: true), ), ); }, onError: (err) { @@ -337,8 +427,8 @@ class _BookAppointmentPageState extends State { separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h), ), ), - ], - ); + ], + ); }), ], ], @@ -381,8 +471,8 @@ class _BookAppointmentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.searchByClinic.tr(context: context).toText14(color: AppColors.textColor, weight: FontWeight.w500), - LocaleKeys.tapToSelectClinic.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.searchByClinic.tr(context: context).toText14(color: AppColors.textColor, isBold: true), + LocaleKeys.tapToSelectClinic.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), ], ), ], @@ -413,8 +503,8 @@ class _BookAppointmentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.searchByDoctor.tr(context: context).toText14(color: AppColors.textColor, weight: FontWeight.w500), - LocaleKeys.tapToSelect.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.searchByDoctor.tr(context: context).toText14(color: AppColors.textColor, isBold: true), + LocaleKeys.tapToSelect.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), ], ), ], @@ -443,8 +533,8 @@ class _BookAppointmentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.searchByRegion.tr(context: context).toText14(color: AppColors.textColor, weight: FontWeight.w500), - LocaleKeys.centralRegion.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.searchByRegion.tr(context: context).toText14(color: AppColors.textColor, isBold: true), + LocaleKeys.centralRegion.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), ], ), ], @@ -487,8 +577,8 @@ class _BookAppointmentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.immediateConsultation.tr(context: context).toText14(color: AppColors.textColor, weight: FontWeight.w500), - LocaleKeys.tapToSelectClinic.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.immediateConsultation.tr(context: context).toText14(color: AppColors.textColor, isBold: true), + LocaleKeys.tapToSelectClinic.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), ], ), ], @@ -529,8 +619,8 @@ class _BookAppointmentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.scheduledConsultation.tr(context: context).toText14(color: AppColors.textColor, weight: FontWeight.w500), - LocaleKeys.tapToSelectClinic.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.scheduledConsultation.tr(context: context).toText14(color: AppColors.textColor, isBold: true), + LocaleKeys.tapToSelectClinic.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), ], ), ], @@ -570,10 +660,10 @@ class _BookAppointmentPageState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.notSureHelpMeChooseClinic.tr(context: context).toText16(weight: FontWeight.w600, color: AppColors.textColor), + LocaleKeys.notSureHelpMeChooseClinic.tr(context: context).toText16(isBold: true, color: AppColors.textColor), SizedBox(height: 8.h), LocaleKeys.mentionYourSymptomsAndFindDoctors.tr(context: context).toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, ), ], @@ -659,7 +749,7 @@ class _BookAppointmentPageState extends State { return SizedBox.shrink(); } - void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) { + void _handleSortByLocationToggle(bool value, AppointmentViaRegionViewmodel regionVM) { if (value) { final locationUtils = getIt.get(); locationUtils.getLocation( @@ -733,7 +823,7 @@ class _BookAppointmentPageState extends State { getTitle(AppointmentViaRegionViewmodel data) { if (data.selectedRegionId == null) { - return LocaleKeys.selectRegion.tr(context: context).toText20(weight: FontWeight.w600); + return LocaleKeys.selectRegion.tr(context: context).toText20(isBold: true); } else { return Transform.flip( flipX: data.isArabic ? true : false, @@ -761,7 +851,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.immediateService.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.noNeedToWaitGetMedicalConsultation.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.noNeedToWaitGetMedicalConsultation.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -777,7 +867,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.noVisitRequired.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -793,7 +883,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.doctorWillContact.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.specialisedDoctorWillContactYou.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.specialisedDoctorWillContactYou.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -809,7 +899,7 @@ class _BookAppointmentPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.freeMedicineDelivery.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.offersFreeMedicineDelivery.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.offersFreeMedicineDelivery.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -825,7 +915,7 @@ class _BookAppointmentPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -848,7 +938,7 @@ class _BookAppointmentPageState extends State { children: [ LocaleKeys.notSureHelpMeChooseClinic.tr(context: context).toText28(color: AppColors.textColor, isBold: true, height: 1.5), SizedBox(height: 4.h), - LocaleKeys.mentionYourSymptomsAndFindDoctors.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500,), + LocaleKeys.mentionYourSymptomsAndFindDoctors.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true,), SizedBox(height: 24.h), CustomButton( text: LocaleKeys.yesPleaseINeedHelp.tr(context: context), @@ -860,7 +950,7 @@ class _BookAppointmentPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 56.h, @@ -875,7 +965,7 @@ class _BookAppointmentPageState extends State { borderColor: AppColors.chipSecondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 56.h, diff --git a/lib/presentation/book_appointment/doctor_filter/doctors_filter.dart b/lib/presentation/book_appointment/doctor_filter/doctors_filter.dart index 9cfcc1f8..02dd9b97 100644 --- a/lib/presentation/book_appointment/doctor_filter/doctors_filter.dart +++ b/lib/presentation/book_appointment/doctor_filter/doctors_filter.dart @@ -60,7 +60,7 @@ class DoctorsFilters extends StatelessWidget{ LocaleKeys.filters.tr(), style:TextStyle( fontFamily: 'Poppins', - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, fontSize: 27.f, color: AppColors.textColor, letterSpacing: -1 @@ -70,7 +70,7 @@ class DoctorsFilters extends StatelessWidget{ LocaleKeys.clearAllFilters.tr(), style:TextStyle( fontFamily: 'Poppins', - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontSize: 14.f, color: AppColors.errorColor ) @@ -181,7 +181,7 @@ class DoctorsFilters extends StatelessWidget{ textColor: Colors.white, fontSize: 16.f, padding: EdgeInsets.zero, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10.r, icon: AppAssets.add_icon, iconColor: AppColors.primaryRedColor, diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 4c824a05..dbb885b3 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart' show NumberFormat; 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'; @@ -21,7 +22,9 @@ import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; class DoctorProfilePage extends StatelessWidget { - const DoctorProfilePage({super.key}); + const DoctorProfilePage({super.key, required this.isDoctorAllowedToBook}); + + final bool isDoctorAllowedToBook; @override Widget build(BuildContext context) { @@ -33,42 +36,41 @@ class DoctorProfilePage extends StatelessWidget { children: [ Expanded( child: CollapsingListView( - title: LocaleKeys.doctorProfile.tr(), - trailing: Consumer( - builder: (context, viewModel, child) { - return SizedBox( - width: 24.h, - height: 24.h, - child: Utils.buildSvgWithAssets( - icon: viewModel.isFavouriteDoctor ? AppAssets.bookmark_filled_icon : AppAssets.bookmark_icon, - width: 24.h, - height: 24.h, - iconColor: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor, - ).onPress(() async { - viewModel.toggleFavouriteDoctor(); - await viewModel.insertFavouriteDoctor( - patientID: appState.getAuthenticatedUser()!.patientId!, - projectID: viewModel.doctorsProfileResponseModel.projectID ?? 0, - clinicID: viewModel.doctorsProfileResponseModel.clinicID ?? 0, - doctorID: viewModel.doctorsProfileResponseModel.doctorID ?? 0, - isActive: viewModel.isFavouriteDoctor, - onSuccess: (response) { - // Successfully added/removed favorite - refresh the favorites list - getIt.get().refreshFavouriteDoctors(); - print( - viewModel.isFavouriteDoctor ? "Doctor added to favorites" : "Doctor removed from favorites", - ); - }, - onError: (error) { - // Revert the state on error - viewModel.toggleFavouriteDoctor(); - Utils.showToast(error); - }, - ); - }), - ); - }, - ), + title: LocaleKeys.profileOnlyText.tr(), + trailing: appState.isAuthenticated + ? Consumer( + builder: (context, viewModel, child) { + return SizedBox( + width: 24.h, + height: 24.h, + child: Icon( + viewModel.isFavouriteDoctor ? Icons.bookmark : Icons.bookmark_border_outlined, + size: 36.h, + color: viewModel.isFavouriteDoctor ? AppColors.primaryRedColor : AppColors.textColor, + ).onPress(() async { + viewModel.toggleFavouriteDoctor(); + await viewModel.insertFavouriteDoctor( + patientID: appState.getAuthenticatedUser()!.patientId!, + projectID: viewModel.doctorsProfileResponseModel.projectID ?? 0, + clinicID: viewModel.doctorsProfileResponseModel.clinicID ?? 0, + doctorID: viewModel.doctorsProfileResponseModel.doctorID ?? 0, + isActive: viewModel.isFavouriteDoctor, + onSuccess: (response) { + Utils.showToast(viewModel.isFavouriteDoctor ? LocaleKeys.doctorAddedToFavourite.tr(context: context) : LocaleKeys.doctorRemovedFromFavourite.tr(context: context)); + // Successfully added/removed favorite - refresh the favorites list + getIt.get().refreshFavouriteDoctors(); + }, + onError: (error) { + // Revert the state on error + viewModel.toggleFavouriteDoctor(); + Utils.showToast(error); + }, + ); + }), + ); + }, + ) + : SizedBox.shrink(), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -98,7 +100,7 @@ class DoctorProfilePage extends StatelessWidget { ), (bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty ? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first : "") .toString() - .toText18(weight: FontWeight.w500, color: AppColors.primaryRedColor), + .toText18(isBold: true, color: AppColors.primaryRedColor), ], ), ], @@ -137,7 +139,7 @@ class DoctorProfilePage extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_rating_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false), SizedBox(height: 16.h), - LocaleKeys.ratings.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + LocaleKeys.ratings.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor), bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate .toString() .toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor, fontFamily: "Poppins"), @@ -158,9 +160,8 @@ class DoctorProfilePage extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_reviews_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false), SizedBox(height: 16.h), - LocaleKeys.reviews.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate - .toString() + LocaleKeys.reviews.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor), + NumberFormat.decimalPattern().format(bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate ?? 0) .toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor, fontFamily: "Poppins"), ], ).onPress(() { @@ -179,15 +180,16 @@ class DoctorProfilePage extends StatelessWidget { SizedBox(height: 16.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), SizedBox(height: 16.h), - LocaleKeys.docInfo.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor), + LocaleKeys.information.tr(context: context).toText14(isBold: true, color: AppColors.textColor), SizedBox(height: 6.h), - bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo!.toText12(fontWeight: FontWeight.w600, color: AppColors.greyTextColor), + (bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo ?? "").trim().toText12(isBold: true, color: AppColors.greyTextColor), + SizedBox(height: 24.h), ], ).paddingSymmetrical(24.h, 0.h), ), ), ), - Container( + isDoctorAllowedToBook ? Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, @@ -199,7 +201,11 @@ class DoctorProfilePage extends StatelessWidget { bookAppointmentsViewModel.selectedDoctor.speciality = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty; bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName; - bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName; + bookAppointmentsViewModel.selectedDoctor.doctorImageURL = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL; + bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL = bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL; + bookAppointmentsViewModel.selectedDoctor.clinicName = bookAppointmentsViewModel.doctorsProfileResponseModel.clinicDescription; + bookAppointmentsViewModel.selectedDoctor.projectName = bookAppointmentsViewModel.doctorsProfileResponseModel.projectName; + LoaderBottomSheet.showLoader(); bookAppointmentsViewModel.isLiveCareSchedule ? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots( @@ -253,7 +259,7 @@ class DoctorProfilePage extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -261,7 +267,7 @@ class DoctorProfilePage extends StatelessWidget { iconColor: Colors.white, iconSize: 20.h, ).paddingSymmetrical(24.h, 24.h), - ), + ) : SizedBox.shrink(), ], ), ); diff --git a/lib/presentation/book_appointment/laser/laser_appointment.dart b/lib/presentation/book_appointment/laser/laser_appointment.dart index 3b94b0ec..c8773933 100644 --- a/lib/presentation/book_appointment/laser/laser_appointment.dart +++ b/lib/presentation/book_appointment/laser/laser_appointment.dart @@ -84,7 +84,7 @@ class LaserAppointment extends StatelessWidget { activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.malE.tr()), - CustomTabBarModel(null, "Female"), + CustomTabBarModel(null, LocaleKeys.femaleGender.tr()), ], onTabChange: (index) { var viewmodel = context.read(); @@ -93,6 +93,7 @@ class LaserAppointment extends StatelessWidget { viewmodel.getLaserClinic(); }, ), + SizedBox(height: 8.h), Selector( selector: (_, vm) => vm.selectedBodyTypeIndex, builder: (_, bodyType, __) { @@ -110,6 +111,7 @@ class LaserAppointment extends StatelessWidget { }); }, ), + SizedBox(height: 8.h), Selector>(selector: (_, vm) => vm.laserBodyPartsList, builder:(_, parts,__){ return BodyPartsListing( parts: parts, diff --git a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart index c9578682..51d964eb 100644 --- a/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart +++ b/lib/presentation/book_appointment/laser/widgets/body_part_listing.dart @@ -1,11 +1,17 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.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/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/laser_body_parts_data.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/laser_body_parts.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/chip/app_custom_chip_widget.dart'; import 'package:provider/provider.dart'; +import 'package:smooth_corner/smooth_corner.dart'; class BodyPartsListing extends StatelessWidget { final List parts; @@ -46,11 +52,7 @@ class BodyPartsListing extends StatelessWidget { Visibility( visible: !isLoading, child: GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - childAspectRatio: 85 / 107, - crossAxisSpacing: 4.h, - mainAxisSpacing: 21.h), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 75 / 107, crossAxisSpacing: 4.h, mainAxisSpacing: 21.h), physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: parts.length, @@ -70,7 +72,7 @@ class BodyPartsListing extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ AspectRatio( - aspectRatio: 97 / 97, + aspectRatio: 150 / 140, child: FittedBox( fit: BoxFit.fitWidth, child: Stack( @@ -93,12 +95,12 @@ class BodyPartsListing extends StatelessWidget { Container( width: 18.h, height: 18.h, - child: Icon(Icons.done, - color: Colors.white, size: 12.h), decoration: BoxDecoration( color: AppColors.primaryRedColor, borderRadius: BorderRadius.circular(30.h), ), + child: Icon(Icons.done, + color: Colors.white, size: 12.h), ), ], ), @@ -106,17 +108,46 @@ class BodyPartsListing extends StatelessWidget { ), SizedBox(height: 6.h), Expanded( - child: Text( - context - .read() - .getLaserProcedureNameWRTLanguage(parts[index]), - style: TextStyle( - fontSize: 12.f, - fontWeight: FontWeight.w600, - color: Color(0xff2B353E), - letterSpacing: -0.48, + child: Center( + child: Column( + children: [ + context.read().getLaserProcedureNameWRTLanguage(parts[index]).toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 1), + SizedBox(height: 4.h), + AppCustomChipWidget( + backgroundColor: _isSelected ? AppColors.chipSecondaryLightRedColor : AppColors.whiteColor, + textColor: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.blackColor, + // labelText: "${parts[index].timeDuration!} ${LocaleKeys.mins.tr()}", + richText: "${parts[index].timeDuration!} ${LocaleKeys.mins.tr()}".toText12( + isBold: true, + color: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.blackColor, + ), + icon: AppAssets.waiting_time_clock, + iconHasColor: true, + iconColor: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.blackColor, + iconSize: 16, + labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.w), + deleteIconSize: Size(18.w, 18.h), + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + smoothness: 10, + side: BorderSide(color: _isSelected ? AppColors.chipPrimaryRedBorderColor : AppColors.borderGrayColor, width: 1), + )), + + // AppCustomChipWidget( + // labelText: "${parts[index].timeDuration!} ${LocaleKeys.mins.tr()}", + // backgroundColor: AppColors.whiteColor, + // // textColor: Utils.getCardBorderColor(myAppointmentsVM.currentQueueStatus), + // ), + // Row( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // "${LocaleKeys.mins.tr()}: ".toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + // parts[index].timeDuration!.toText12(isBold: true, color: AppColors.textColor, isCenter: true, maxLine: 2), + // ], + // ), + ], ), - maxLines: 1, ), ), ], diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart index c885b595..5f44448a 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -75,14 +75,20 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true), + "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true, isEnglishOnly: true), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 3.h, runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"), + AppCustomChipWidget( + richText: Row( + children: [ + "${appState.getAuthenticatedUser()!.age} ".toText10(color: AppColors.blackColor, isEnglishOnly: true), + LocaleKeys.yearsOld.tr(context: context).toText10(color: AppColors.blackColor), + ], + ),), AppCustomChipWidget(labelText: "${LocaleKeys.gender.tr(context: context)}: ${appState.getAuthenticatedUser()?.gender == 1 ? LocaleKeys.malE.tr(context: context) : LocaleKeys.femaleGender.tr(context: context)}"), ], ), @@ -163,7 +169,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), + LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true).paddingSymmetrical(24.h, 0.h), CustomButton( text: LocaleKeys.updateInsurance.tr(context: context), onPressed: () { @@ -171,13 +177,28 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { CustomPageRoute( page: InsuranceHomePage(), ), - ); + ) + .then((val) async { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingFeesPleaseWait.tr()); + await immediateLiveCareVM.getLiveCareImmediateAppointmentFees(onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + }); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.secondaryLightRedBorderColor, textColor: AppColors.whiteColor, fontSize: 10, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 8, padding: EdgeInsets.fromLTRB(15, 0, 15, 0), height: 30.h, @@ -193,7 +214,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), - Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.amount!.toText16(isBold: true), AppColors.blackColor, 13, + Utils.getPaymentAmountWithSymbol((immediateLiveCareVM.liveCareImmediateAppointmentFeesList.amount ?? "").toText16(isBold: true, isEnglishOnly: true), AppColors.blackColor, 13, isSaudiCurrency: immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "ريال"), ], @@ -203,7 +224,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { children: [ LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor), Utils.getPaymentAmountWithSymbol( - immediateLiveCareVM.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, + immediateLiveCareVM.liveCareImmediateAppointmentFeesList.tax!.toText14(isBold: true, color: AppColors.greyTextColor, isEnglishOnly: true), AppColors.greyTextColor, 13, isSaudiCurrency: (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "ريال")), ], @@ -213,7 +234,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox(width: 200.h, child: Utils.getPaymentMethods()), - Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true), AppColors.blackColor, 17, + Utils.getPaymentAmountWithSymbol(immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total!.toText24(isBold: true, isEnglishOnly: true), AppColors.blackColor, 17, isSaudiCurrency: (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "ريال")), ], @@ -274,7 +295,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { borderColor: AppColors.successColor, textColor: AppColors.whiteColor, fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -317,7 +338,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { borderColor: AppColors.infoColor, textColor: AppColors.whiteColor, fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -343,44 +364,48 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { bool cameraGranted = statuses[Permission.camera]?.isGranted ?? false; bool micGranted = statuses[Permission.microphone]?.isGranted ?? false; bool notifGranted = statuses[Permission.notification]?.isGranted ?? false; + // bool alertWindowGranted = Platform.isAndroid ? (statuses[Permission.systemAlertWindow]?.isGranted ?? false) : true; + bool alertWindowGranted = true; // If all required permissions are already granted - if (cameraGranted && micGranted && notifGranted) return true; + if (cameraGranted && micGranted && notifGranted && alertWindowGranted) return true; // Collect only the missing permissions final missing = []; if (!cameraGranted) missing.add(Permission.camera); if (!micGranted) missing.add(Permission.microphone); if (!notifGranted) missing.add(Permission.notification); + if (Platform.isAndroid && !alertWindowGranted) missing.add(Permission.systemAlertWindow); // If any of the missing permissions are permanently denied/restricted -> open settings (single dialog) final permanent = missing.where((p) => (statuses[p]?.isPermanentlyDenied ?? false) || (statuses[p]?.isRestricted ?? false)).toList(); if (permanent.isNotEmpty) { final names = permanent.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and '); - final message = permanent.length == 1 ? '$names permission is permanently denied. Open app settings to allow it.' : '$names permissions are permanently denied. Open app settings to allow them.'; + // final message = permanent.length == 1 ? '$names permission is permanently denied. Open app settings to allow it.' : '$names permissions are permanently denied. Open app settings to allow them.'; + final message = LocaleKeys.liveCarePermissions.tr(); await LiveCarePermissionService.instance.showOpenSettingsDialog( context, - title: "Permissions Required", + title: LocaleKeys.permissionsProfile.tr(), message: message, ); return false; } // Show one combined retry dialog for all missing permissions - final missingNames = missing.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and '); - final retry = await showDialog( - context: context, - builder: (c) => AlertDialog( - title: const Text('Permissions required'), - content: Text('$missingNames permission${missing.length > 1 ? 's are' : ' is'} required to start the video call. Retry now?'), - actions: [ - TextButton(onPressed: () => Navigator.of(c).pop(false), child: const Text('Cancel')), - TextButton(onPressed: () => Navigator.of(c).pop(true), child: const Text('Retry')), - ], - ), - ); - - if (retry != true) return false; + // final missingNames = missing.map((p) => LiveCarePermissionService.instance.friendlyName(p)).join(' and '); + // final retry = await showDialog( + // context: context, + // builder: (c) => AlertDialog( + // title: const Text('Permissions required'), + // content: Text('$missingNames permission${missing.length > 1 ? 's are' : ' is'} required to start the video call. Retry now?'), + // actions: [ + // TextButton(onPressed: () => Navigator.of(c).pop(false), child: const Text('Cancel')), + // TextButton(onPressed: () => Navigator.of(c).pop(true), child: const Text('Retry')), + // ], + // ), + // ); + // + // if (retry != true) return false; // Request only the missing permissions final newStatuses = {}; @@ -392,6 +417,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { cameraGranted = newStatuses[Permission.camera]?.isGranted ?? cameraGranted; micGranted = newStatuses[Permission.microphone]?.isGranted ?? micGranted; notifGranted = newStatuses[Permission.notification]?.isGranted ?? notifGranted; + alertWindowGranted = newStatuses[Permission.systemAlertWindow]?.isGranted ?? alertWindowGranted; // If any requested permission is now permanently denied -> open settings final newlyPermanent = missing.where((p) => (newStatuses[p]?.isPermanentlyDenied ?? false) || (newStatuses[p]?.isRestricted ?? false)).toList(); @@ -407,6 +433,7 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { return false; } + // return cameraGranted && micGranted && notifGranted && alertWindowGranted; return cameraGranted && micGranted && notifGranted; } diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index 4018b42a..0341796f 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -234,7 +234,7 @@ class _ImmediateLiveCarePaymentPageState extends State( - valueListenable: immediateLiveCareVM.durationNotifier, - builder: (context, duration, child) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, + LocaleKeys.immediateLiveCareRequest.tr(context: context).toText16(isBold: true), + SizedBox(height: 10.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Wrap( children: [ - buildTime(duration), + AppCustomChipWidget( + labelText: immediateLiveCareViewModel.patientLiveCareHistoryList.isNotEmpty ? immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus : "", + backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), + textColor: AppColors.alertColor, + ), + SizedBox(width: 8.w), + AppCustomChipWidget( + icon: AppAssets.appointment_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(immediateLiveCareViewModel.patientLiveCareHistoryList[0].arrivalTime), false)), ], - ); - }, + ), + 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: 80.h, height: 80.h, fit: BoxFit.cover), + ], ), SizedBox(height: 8.h), + SizedBox(height: 8.h), + LocaleKeys.yourTurnIsAfterPatients + .tr(namedArgs: {'count': (immediateLiveCareViewModel.patientLiveCareHistoryList[0].patCount ?? 0).toString()}, context: context).toText14(isBold: true), + // "Your turn is after ${immediateLiveCareViewModel.patientLiveCareHistoryList[0].patCount} patients.".toText16(isBold: true), + SizedBox(height: 8.h), ], ), ), @@ -106,38 +120,31 @@ class _ImmediateLiveCarePendingRequestPageState extends State( + valueListenable: immediateLiveCareVM.durationNotifier, + builder: (context, duration, child) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + buildTime(duration), + ], + ); + }, + ), SizedBox(height: 8.h), ], ), ), - ) + ), ], ), ), @@ -158,7 +165,7 @@ class _ImmediateLiveCarePendingRequestPageState extends State(); + appState = getIt.get(); return Container( padding: EdgeInsets.all(16.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -36,10 +45,13 @@ class LiveCareClinicCard extends StatelessWidget { Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 24.h, height: 24.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading), AppCustomChipWidget( icon: AppAssets.livecare_online_icon, - iconColor: AppColors.whiteColor, - richText: (liveCareClinicListResponseModel.isOnline == 1 ? LocaleKeys.online.tr(context: context) : LocaleKeys.offline.tr()).toText10(isBold: true, color: AppColors.whiteColor), - backgroundColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor, - textColor: AppColors.whiteColor, + iconColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor, + richText: (liveCareClinicListResponseModel.isOnline == 1 ? LocaleKeys.online.tr(context: context) : LocaleKeys.offline.tr()).toText12( + isBold: true, + color: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor, + ), + backgroundColor: AppColors.transparent, + textColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor, ), ]), SizedBox(height: 8.h), @@ -48,12 +60,194 @@ class LiveCareClinicCard extends StatelessWidget { child: (isLoading ? "Cardiology" : (appState.isArabic() ? liveCareClinicListResponseModel.serviceNameN : liveCareClinicListResponseModel.serviceName))! .toText16(isBold: true) .toShimmer2(isShow: isLoading)), - Transform.flip( - flipX: appState.isArabic(), - child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), + // Transform.flip( + // flipX: appState.isArabic(), + // child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, width: 40.h, height: 40.h, fit: BoxFit.contain, iconColor: AppColors.textColor).toShimmer2(isShow: isLoading)), ]), + SizedBox(height: 8.h), + Row( + children: [ + Expanded( + flex: 7, + child: CustomButton( + text: LocaleKeys.bookNow.tr(), + onPressed: () async { + onImmediateLiveCareClinicSelected(liveCareClinicListResponseModel); + }, + backgroundColor: AppColors.successLightBgColor, + borderColor: AppColors.successLightBgColor, + textColor: AppColors.successColor, + borderWidth: 1.h, + fontSize: (isFoldable || isTablet) ? 12.f : 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + icon: AppAssets.bookAppoBottom, + iconColor: liveCareClinicListResponseModel.isOnline == 0 ? AppColors.textColorLight : AppColors.successColor, + iconSize: 16.h, + isDisabled: liveCareClinicListResponseModel.isOnline == 0, + ), + ), + SizedBox(width: 8.w), + Expanded( + flex: 1, + child: Container( + height: (isFoldable || isTablet) ? 50.h : 40.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: Color(0xFFEFEFF0), + color: AppColors.transparent, + borderRadius: 10.h, + side: BorderSide( + color: AppColors.textColor, + width: 1.2, + ), + ), + child: Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.calendar, + iconColor: AppColors.textColor, + width: 5.w, + height: 5.h, + fit: BoxFit.scaleDown, + ), + ), + ).onPress(() { + onClinicScheduleTapped(liveCareClinicListResponseModel.serviceID!); + }), + ), + ], + ), ], ), ); } + + onClinicScheduleTapped(int serviceID) async { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr()); + await immediateLiveCareViewModel.getLiveCareImmediateClinicSchedule( + serviceID: serviceID, + onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight(GetIt.instance().navigatorKey.currentContext!, + child: ListView.separated( + shrinkWrap: true, + itemBuilder: (_, index) { + return DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 8.h, + children: [ + checkInOptionCard(AppAssets.calendarGrey, immediateLiveCareViewModel.liveCareClinicScheduleList[index].dayOfWeekStr!, + "${immediateLiveCareViewModel.liveCareClinicScheduleList[index].shiftTimings!.first.startTime} to ${immediateLiveCareViewModel.liveCareClinicScheduleList[index].shiftTimings!.first.endTime}"), + ], + ), + ), + ], + ), + ).paddingSymmetrical(0.h, 0.h).onPress(() {}); + }, + separatorBuilder: (_, __) => SizedBox(height: 16.h), + itemCount: immediateLiveCareViewModel.liveCareClinicScheduleList.length), + title: LocaleKeys.clinicSchedule.tr(), + isCloseButtonVisible: true, + useSafeArea: true, + isFullScreen: true); + }); + } + + onImmediateLiveCareClinicSelected(GetLiveCareClinicListResponseModel liveCareClinic) { + //TODO: add implementation to show clinic schedule + // if (liveCareClinic.isOnline == 1) { + showCommonBottomSheetWithoutHeight(GetIt.instance().navigatorKey.currentContext!, child: SelectLiveCareCallType(immediateLiveCareViewModel: immediateLiveCareViewModel), + callBackFunc: () async { + if (immediateLiveCareViewModel.liveCareSelectedCallType != 0) { + immediateLiveCareViewModel.setImmediateLiveCareSelectedClinic(liveCareClinic); + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingFeesPleaseWait.tr()); + await immediateLiveCareViewModel.getLiveCareImmediateAppointmentFees(onSuccess: (val) { + LoaderBottomSheet.hideLoader(); + Navigator.of(GetIt.instance().navigatorKey.currentContext!).push( + CustomPageRoute( + page: ImmediateLiveCarePaymentDetails(), + ), + ); + }, onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + GetIt.instance().navigatorKey.currentContext!, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }); + } + }, title: LocaleKeys.selectLiveCareCallType.tr(), isCloseButtonVisible: true, isFullScreen: false); + } + + Widget checkInOptionCard(String icon, String title, String subTitle) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.h, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText16(isBold: true, color: AppColors.textColor), + SizedBox(height: 8.h), + Row( + children: [ + AppCustomChipWidget( + icon: AppAssets.appointment_time_icon, + iconColor: AppColors.textColor, + labelText: subTitle.split("to").first.trim(), + isEnglishOnly: true, + ), + SizedBox(width: 8.w), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_forward, + height: 24.h, + width: 24.h, + iconColor: AppColors.textColor, + ), + ), + SizedBox(width: 8.w), + AppCustomChipWidget( + icon: AppAssets.appointment_time_icon, + iconColor: AppColors.textColor, + labelText: subTitle.split("to")[1].trim(), + isEnglishOnly: true, + ), + ], + ), + ], + ), + ), + ], + ), + ], + ).paddingAll(16.h), + ); + } } diff --git a/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart b/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart index 8bb07897..916e0486 100644 --- a/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart +++ b/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart @@ -71,7 +71,7 @@ class SelectLiveCareCallType extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText16(isBold: true, color: AppColors.textColor), - subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + subTitle.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart index 9069138f..a3138dff 100644 --- a/lib/presentation/book_appointment/review_appointment_page.dart +++ b/lib/presentation/book_appointment/review_appointment_page.dart @@ -90,18 +90,18 @@ class _ReviewAppointmentPageState extends State { .toString() .toText16(isBold: true, maxlines: 1), SizedBox(width: 12.w), - Image.network( + (bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null && bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty )? Image.network( bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", width: 20.h, height: 15.h, fit: BoxFit.cover, - ), + ) : SizedBox.shrink(), ], ), SizedBox(height: 2.h), (bookAppointmentsViewModel.selectedDoctor.speciality!.isNotEmpty ? bookAppointmentsViewModel.selectedDoctor.speciality!.first : "") .toString() - .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, maxLine: 1), + .toText12(isBold: true, color: AppColors.greyTextColor, maxLine: 1), ], ), ], @@ -161,7 +161,7 @@ class _ReviewAppointmentPageState extends State { spacing: 4.h, runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"), + AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} ${LocaleKeys.yearsOld.tr(context: context)}"), AppCustomChipWidget(labelText: "${LocaleKeys.gender.tr(context: context)}: ${appState.getAuthenticatedUser()?.gender == 1 ? LocaleKeys.malE.tr(context: context) : LocaleKeys.femaleGender.tr(context: context)}"), ], ), @@ -197,12 +197,12 @@ class _ReviewAppointmentPageState extends State { AppCustomChipWidget( labelText: bookAppointmentsVM.isAppointmentNearestGateLoading ? "Floor: Ground Floor" - : "Floor: ${getIt.get().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}", - ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), + : "${LocaleKeys.floor.tr(context: context)} ${getIt.get().isArabic() ? bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocationN : bookAppointmentsViewModel.appointmentNearestGateResponseModel!.clinicLocation}", + ).toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), AppCustomChipWidget( labelText: - "Nearest Gate: ${getIt.get().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}") - .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), + "${LocaleKeys.nearestGate.tr(context: context)} ${getIt.get().isArabic() ? bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumberN : bookAppointmentsVM.appointmentNearestGateResponseModel!.nearestGateNumber}") + .toShimmer2(isShow: bookAppointmentsVM.isAppointmentNearestGateLoading), ], ) : SizedBox.shrink(), @@ -235,7 +235,7 @@ class _ReviewAppointmentPageState extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -316,34 +316,42 @@ class _ReviewAppointmentPageState extends State { } void initiateBookAppointment() async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: LocaleKeys.bookingYourAppointment.tr(context: context)); + // LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: bookAppointmentsViewModel.isPatientRescheduleAppointment ? LocaleKeys.reschedulingAppo.tr(context: context) : LocaleKeys.bookingYourAppointment.tr(context: context)); + LoaderBottomSheet.showLoader(loadingText: bookAppointmentsViewModel.isPatientRescheduleAppointment ? LocaleKeys.reschedulingAppo.tr(context: context) : LocaleKeys.bookingYourAppointment.tr(context: context)); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); if (bookAppointmentsViewModel.isLiveCareSchedule) { await bookAppointmentsViewModel.insertSpecificAppointmentForLiveCare(onError: (err) { print(err.data["ErrorEndUserMessage"]); - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); }, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - bookAppointmentsViewModel.setIsLiveCareSchedule(false); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); + // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.appointmentSuccess.tr()); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()), + callBackFunc: () { + bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); }); }); } else { //TODO: Add patient Derma package check API Here await bookAppointmentsViewModel.insertSpecificAppointment(onError: (err) { // print(err.data["ErrorEndUserMessage"]); - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); showCommonBottomSheetWithoutHeight( title: LocaleKeys.notice.tr(context: context), context, @@ -357,19 +365,25 @@ class _ReviewAppointmentPageState extends State { isCloseButtonVisible: true, ); }, onSuccess: (apiResp) async { - LoadingUtils.hideFullScreenLoader(); + LoaderBottomSheet.hideLoader(); await Future.delayed(Duration(milliseconds: 50)).then((value) async { - LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: true, loadingText: LocaleKeys.appointmentSuccess.tr()); - await Future.delayed(Duration(milliseconds: 4000)).then((value) { - LoadingUtils.hideFullScreenLoader(); - bookAppointmentsViewModel.setIsLiveCareSchedule(false); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); - }); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentSuccess.tr()).paddingSymmetrical(0.h, 24.h), + callBackFunc: () { + bookAppointmentsViewModel.setIsLiveCareSchedule(false); + bookAppointmentsViewModel.setIsPatientRescheduleAppointment(false); + Navigator.pushAndRemoveUntil( + context, + CustomPageRoute( + page: LandingNavigation(), + ), + (r) => false); + }, + isFullScreen: false, + isCloseButtonVisible: false, + isAutoDismiss: true + ); }); }); } diff --git a/lib/presentation/book_appointment/search_doctor_by_name.dart b/lib/presentation/book_appointment/search_doctor_by_name.dart index 533229a0..3222fbd8 100644 --- a/lib/presentation/book_appointment/search_doctor_by_name.dart +++ b/lib/presentation/book_appointment/search_doctor_by_name.dart @@ -154,7 +154,7 @@ class _SearchDoctorByNameState extends State { // borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), // textColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -169,7 +169,7 @@ class _SearchDoctorByNameState extends State { // borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, // textColor: bookAppointmentsVM.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -218,7 +218,7 @@ class _SearchDoctorByNameState extends State { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( CustomPageRoute( - page: DoctorProfilePage(), + page: DoctorProfilePage(isDoctorAllowedToBook: true), ), ); }, @@ -306,7 +306,7 @@ class _SearchDoctorByNameState extends State { // borderColor: AppColors.greyColor, // textColor: AppColors.blackColor, // fontSize: 10, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 8, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 30.h), @@ -319,7 +319,7 @@ class _SearchDoctorByNameState extends State { // bookAppointmentsVM.isSortByClinic // ? (groupedDoctors[index].first.clinicName ?? 'Unknown') // : (groupedDoctors[index].first.projectName ?? 'Unknown'), - // style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600), + // style: TextStyle(fontSize: 16.h, isBold: true), // overflow: TextOverflow.ellipsis, // ), // // Expanded content - list of doctors in this group @@ -440,7 +440,7 @@ class _SearchDoctorByNameState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/book_appointment/select_clinic_page.dart b/lib/presentation/book_appointment/select_clinic_page.dart index aaa52217..b08564ca 100644 --- a/lib/presentation/book_appointment/select_clinic_page.dart +++ b/lib/presentation/book_appointment/select_clinic_page.dart @@ -16,6 +16,8 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_clinic_list_response_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_livecare_clinics_response_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/facility_selection.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/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; @@ -96,7 +98,7 @@ class _SelectClinicPageState extends State { backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor, borderRadius: 12.r, - fontWeight: FontWeight.w500), + fontWeight: FontWeight.w600,), ), child: getCalculatorResultWidget(type: widget.calculatorType!, calculatedResult: widget.calculatedResult).paddingSymmetrical(18.w, 24.h), ); @@ -617,7 +619,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 8.w, ), - calculatedResult["lastPeriodDate"].toString().toText16(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["lastPeriodDate"].toString().toText16(color: AppColors.successColor, isBold: true), ], ), ], @@ -639,7 +641,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 8.w, ), - calculatedResult["ovulationDate"].toString().toText16(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["ovulationDate"].toString().toText16(color: AppColors.successColor, isBold: true), ], ), SizedBox(height: 8.h), @@ -653,7 +655,7 @@ class _SelectClinicPageState extends State { borderRadius: 8.h, textColor: AppColors.textColor), SizedBox(height: 16.h), - "Fertility Window".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Fertility Window".toText16(color: AppColors.textColor, isBold: true), SizedBox(height: 8.h), Row( children: [ @@ -661,7 +663,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 8.w, ), - calculatedResult["fertileWindowStart"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["fertileWindowStart"].toString().toText13(color: AppColors.successColor, isBold: true), SizedBox( width: 4.w, ), @@ -669,12 +671,12 @@ class _SelectClinicPageState extends State { SizedBox( width: 4.w, ), - calculatedResult["fertileWindowEnd"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["fertileWindowEnd"].toString().toText13(color: AppColors.successColor, isBold: true), ], ), SizedBox(height: 8.h), "This is your fertile window. Maintaining a healthy calorie range supports your body during this period, considering your Basal Metabolic Rate and activity. Your Body Mass Index is within the healthy range." - .toText12(height: 1.5, color: AppColors.textColorLight, fontWeight: FontWeight.w500) + .toText12(height: 1.5, color: AppColors.textColorLight, isBold: true) ], ), ), @@ -695,7 +697,7 @@ class _SelectClinicPageState extends State { ), Row( children: [ - "Tips to keep in mind".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Tips to keep in mind".toText16(color: AppColors.textColor, isBold: true), ], ), ], @@ -733,7 +735,7 @@ class _SelectClinicPageState extends State { children: [ Utils.buildSvgWithAssets(icon: AppAssets.pregnancyDueDateAccordion, width: 30.h, height: 30.h), SizedBox(width: 8.w), - calculatedResult["dueDate"].toString().toText16(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["dueDate"].toString().toText16(color: AppColors.successColor, isBold: true), ], ), Expanded(child: SizedBox(width: 8.w)), @@ -760,7 +762,7 @@ class _SelectClinicPageState extends State { children: [ Row( children: [ - "First Trimester".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "First Trimester".toText16(color: AppColors.textColor, isBold: true), Expanded(child: SizedBox(width: 8.h)), CustomChipWidget( height: 30.h, @@ -780,7 +782,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 8.w, ), - calculatedResult["firstTrimester"]["start"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["firstTrimester"]["start"].toString().toText13(color: AppColors.successColor, isBold: true), SizedBox( width: 4.w, ), @@ -788,7 +790,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 4.w, ), - calculatedResult["firstTrimester"]["end"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["firstTrimester"]["end"].toString().toText13(color: AppColors.successColor, isBold: true), ], ), SizedBox(height: 8.h), @@ -804,7 +806,7 @@ class _SelectClinicPageState extends State { children: [ Row( children: [ - "Second Trimester".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Second Trimester".toText16(color: AppColors.textColor, isBold: true), Expanded(child: SizedBox(width: 8.h)), CustomChipWidget( height: 30.h, @@ -824,7 +826,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 8.w, ), - calculatedResult["secondTrimester"]["start"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["secondTrimester"]["start"].toString().toText13(color: AppColors.successColor, isBold: true), SizedBox( width: 4.w, ), @@ -832,7 +834,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 4.w, ), - calculatedResult["secondTrimester"]["end"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["secondTrimester"]["end"].toString().toText13(color: AppColors.successColor, isBold: true), ], ), SizedBox(height: 8.h), @@ -848,7 +850,7 @@ class _SelectClinicPageState extends State { children: [ Row( children: [ - "Third Trimester".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Third Trimester".toText16(color: AppColors.textColor, isBold: true), Expanded(child: SizedBox(width: 8.h)), CustomChipWidget( height: 30.h, @@ -868,7 +870,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 8.w, ), - calculatedResult["thirdTrimester"]["start"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["thirdTrimester"]["start"].toString().toText13(color: AppColors.successColor, isBold: true), SizedBox( width: 4.w, ), @@ -876,7 +878,7 @@ class _SelectClinicPageState extends State { SizedBox( width: 4.w, ), - calculatedResult["thirdTrimester"]["end"].toString().toText13(color: AppColors.successColor, weight: FontWeight.w600), + calculatedResult["thirdTrimester"]["end"].toString().toText13(color: AppColors.successColor, isBold: true), ], ), SizedBox(height: 8.h), @@ -902,7 +904,7 @@ class _SelectClinicPageState extends State { ), ), Expanded( - child: text.toText12(color: AppColors.textColorLight, fontWeight: FontWeight.w500, height: 1.2.h), + child: text.toText12(color: AppColors.textColorLight, isBold: true, height: 1.2.h), ), ], ); @@ -922,7 +924,7 @@ class _SelectClinicPageState extends State { children: [ ('${calories.toStringAsFixed(0)}').toText32(color: getTextColor(title), isBold: true), SizedBox(width: 10.w), - hide ? SizedBox() : 'kcal/day'.toText12(color: AppColors.inputLabelTextColor, isBold: true, fontWeight: FontWeight.w600) + hide ? SizedBox() : 'kcal/day'.toText12(color: AppColors.inputLabelTextColor, isBold: true) ], ), _buildStatusIcon(title) @@ -948,9 +950,9 @@ class _SelectClinicPageState extends State { ), SizedBox(height: 8.h), ], - title.toText16(color: AppColors.textColor, weight: FontWeight.w600), + title.toText16(color: AppColors.textColor, isBold: true), SizedBox(height: 8.h), - description.toText12(height: 1.2.h, color: AppColors.textColorLight, fontWeight: FontWeight.w500) + description.toText12(height: 1.2.h, color: AppColors.textColorLight, isBold: true) ], ), ); @@ -1027,6 +1029,7 @@ class _SelectClinicPageState extends State { //17 and 235 void handleDoctorScreen(GetClinicsListResponseModel clinic) async { + regionalViewModel.flush(); if (widget.isFromRegionFlow) { //Dental Clinic Flow if (clinic.clinicID == 17) { @@ -1061,10 +1064,27 @@ class _SelectClinicPageState extends State { ); } } else { + // if (clinic.clinicID == 253) { + // LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); + // await bookAppointmentsViewModel.getLaserHospitals( + // onSuccess: (_) { + // LoaderBottomSheet.hideLoader(); + // _showLaserHospitalBottomSheet(); + // }, + // onError: (err) { + // LoaderBottomSheet.hideLoader(); + // }, + // ); + // return; + // } var bottomSheetType = RegionBottomSheetType.FOR_CLINIIC; - if (clinic.clinicID == 17 || clinic.clinicID == 253) { + if (clinic.clinicID == 17) { + regionalViewModel.setBottomSheetState(AppointmentViaRegionState.HOSPITAL_SELECTION); bottomSheetType = RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER; } + // if (clinic.clinicID == 253) { + // bottomSheetType = RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER; + // } openRegionListBottomSheet(context, bottomSheetType); } } @@ -1072,7 +1092,6 @@ class _SelectClinicPageState extends State { void openRegionListBottomSheet(BuildContext context, RegionBottomSheetType type) { bookAppointmentsViewModel.setProjectID(null); - regionalViewModel.flush(); regionalViewModel.setBottomSheetType(type); // AppointmentViaRegionViewmodel? viewmodel = null; showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer(builder: (_, data, __) => getTitle(data)), isDismissible: false, @@ -1106,10 +1125,19 @@ class _SelectClinicPageState extends State { regionalViewModel.handleLastStepForRegion(); } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.FOR_CLINIIC) { regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION); - regionalViewModel.handleLastStepForClinic(); + var appointmentsViewModel = Provider.of(context, listen: false); + if (appointmentsViewModel.selectedClinic.clinicID == 253) { + appointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.patientDoctorAppointmentList!.first.iD?.toString() ?? ""); + appointmentsViewModel.resetLaserData(); + appointmentsViewModel.getLaserClinic(); + regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID ?? -1); + } else { + regionalViewModel.handleLastStepForClinic(); + } } else if (regionalViewModel.regionBottomSheetType == RegionBottomSheetType.REGION_FOR_DENTAL_AND_LASER) { var appointmentsViewModel = Provider.of(context, listen: false); regionalViewModel.setBottomSheetState(AppointmentViaRegionState.DOCTOR_SELECTION); + appointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.hospitalList.first.iD?.toString() ?? ""); regionalViewModel.handleLastStepForClinicForDentalAndLaser(appointmentsViewModel.selectedClinic.clinicID ?? -1); } }, @@ -1132,6 +1160,7 @@ class _SelectClinicPageState extends State { id = regionalViewModel.selectedHospital?.patientDoctorAppointmentList?.first.projectID?.toString() ?? ""; } if (bookAppointmentsViewModel.selectedClinic.clinicID == 17) { + bookAppointmentsViewModel.setProjectID(id); if (appState.isAuthenticated) { initDentalAppointment(); return SizedBox.shrink(); @@ -1140,12 +1169,18 @@ class _SelectClinicPageState extends State { } } - if (bookAppointmentsViewModel.selectedClinic.clinicID == 253) { - { - bookAppointmentsViewModel.resetLaserData(); - bookAppointmentsViewModel.getLaserClinic(); - } - } + // if (bookAppointmentsViewModel.selectedClinic.clinicID == 253) { + // // { + // // bookAppointmentsViewModel.resetLaserData(); + // // bookAppointmentsViewModel.getLaserClinic(); + // // Navigator.push( + // // context, + // // CustomPageRoute( + // // page: LaserAppointment(), + // // ), + // // ); + // // } + // } bookAppointmentsViewModel.setProjectID(id); return SizedBox.shrink(); } else { @@ -1156,7 +1191,7 @@ class _SelectClinicPageState extends State { getTitle(AppointmentViaRegionViewmodel data) { if (data.selectedRegionId == null) { - return LocaleKeys.selectRegion.tr(context: context).toText20(weight: FontWeight.w600); + return LocaleKeys.selectRegion.tr(context: context).toText20(isBold: true); } else { return Transform.flip( flipX: data.isArabic ? true : false, @@ -1184,7 +1219,7 @@ class _SelectClinicPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.youHaveExistingTreatmentPlan.tr(context: context).toText14(weight: FontWeight.w500), + LocaleKeys.youHaveExistingTreatmentPlan.tr(context: context).toText14(isBold: true), SizedBox(height: 8.h), Container( width: double.infinity, @@ -1239,7 +1274,7 @@ class _SelectClinicPageState extends State { ), ), SizedBox(height: 16.h), - LocaleKeys.wouldYouLikeToContinue.tr(context: context).toText14(weight: FontWeight.w500), + LocaleKeys.wouldYouLikeToContinue.tr(context: context).toText14(isBold: true), SizedBox(height: 16.h), Row( children: [ @@ -1349,4 +1384,60 @@ class _SelectClinicPageState extends State { // bookAppointmentsViewModel.getRegionMappedProjectList(); } + + void _showLaserHospitalBottomSheet() { + final TextEditingController laserHospitalSearchController = TextEditingController(); + List filteredList = List.from(bookAppointmentsViewModel.laserHospitalsList); + + showCommonBottomSheetWithoutHeight( + context, + title: LocaleKeys.selectHospital.tr(context: context), + isDismissible: true, + isCloseButtonVisible: true, + child: StatefulBuilder( + builder: (context, setModalState) { + return HospitalBottomSheetBody( + searchText: laserHospitalSearchController, + displayList: filteredList, + selectedFacility: FacilitySelection.ALL, + hmcCount: bookAppointmentsViewModel.laserHospitalHmcCount, + hmgCount: bookAppointmentsViewModel.laserHospitalHmgCount, + sortByLocation: false, + onFacilityClicked: (facility) { + setModalState(() { + if (facility == FacilitySelection.ALL) { + filteredList = List.from(bookAppointmentsViewModel.laserHospitalsList); + } else if (facility == FacilitySelection.HMG) { + filteredList = bookAppointmentsViewModel.laserHospitalsList.where((h) => h.isHMC != true).toList(); + } else { + filteredList = bookAppointmentsViewModel.laserHospitalsList.where((h) => h.isHMC == true).toList(); + } + }); + }, + onHospitalClicked: (hospital) { + Navigator.of(context).pop(); // close bottom sheet + final projectID = hospital.hospitalList.isNotEmpty ? hospital.hospitalList.first.iD?.toString() : hospital.patientDoctorAppointmentList?.first.projectID?.toString(); + bookAppointmentsViewModel.setProjectID(projectID); + bookAppointmentsViewModel.resetLaserData(); + bookAppointmentsViewModel.getLaserClinic(); + Navigator.push( + context, + CustomPageRoute(page: LaserAppointment()), + ); + }, + onHospitalSearch: (value) { + setModalState(() { + if (value.isEmpty) { + filteredList = List.from(bookAppointmentsViewModel.laserHospitalsList); + } else { + filteredList = bookAppointmentsViewModel.laserHospitalsList.where((h) => h.filterName != null && h.filterName!.toLowerCase().contains(value.toLowerCase())).toList(); + } + }); + }, + ); + }, + ), + callBackFunc: () {}, + ); + } } diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 7947fa05..de2e2ae8 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -88,7 +88,7 @@ class _SelectDoctorPageState extends State { // backgroundColor: AppColors.primaryRedColor, // borderColor: AppColors.primaryRedColor, // borderRadius: 12.r, - // fontWeight: FontWeight.w500), + // isBold: true), // ), child: SingleChildScrollView( controller: _scrollController, @@ -167,14 +167,14 @@ class _SelectDoctorPageState extends State { ? GestureDetector( onTap: () { searchEditingController.clear(); - bookAppointmentsViewModel.filterClinics(""); + bookAppointmentsViewModel.filterDoctors(""); textFocusNode.unfocus(); }, child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown), ) : null, onChange: (value) { - bookAppointmentsViewModel.filterClinics(value!); + bookAppointmentsViewModel.filterDoctors(value ?? ""); }, padding: EdgeInsets.symmetric( vertical: ResponsiveExtension(10).h, @@ -197,7 +197,7 @@ class _SelectDoctorPageState extends State { borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), textColor: bookAppointmentsVM.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -212,7 +212,7 @@ class _SelectDoctorPageState extends State { borderColor: bookAppointmentsVM.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, textColor: bookAppointmentsVM.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -220,15 +220,16 @@ class _SelectDoctorPageState extends State { ], ).paddingSymmetrical(0.h, 0.h), if (bookAppointmentsViewModel.isGetDocForHealthCal && bookAppointmentsVM.showSortFilterButtons) SizedBox(height: 16.h), - Row( - mainAxisSize: MainAxisSize.max, + bookAppointmentsVM.doctorsListGrouped.isNotEmpty + ? Row( + mainAxisSize: MainAxisSize.max, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.nearestAppo.tr(context: context).toText13(isBold: true), SizedBox(height: 4.h), - LocaleKeys.viewNearestAppos.tr(context: context).toText11(color: AppColors.textColorLight, weight: FontWeight.w500), + LocaleKeys.viewNearestAppos.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), ], ), const Spacer(), @@ -238,11 +239,13 @@ class _SelectDoctorPageState extends State { value: bookAppointmentsVM.isNearestAppointmentSelected, onChanged: (newValue) async { bookAppointmentsVM.setIsNearestAppointmentSelected(newValue); - }, - ), - ], - ), - ListView.separated( + bookAppointmentsVM.refreshDoctorsList(); + }, + ), + ], + ) + : SizedBox.shrink(), + ListView.separated( padding: EdgeInsets.only(top: 16.h), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), @@ -301,7 +304,7 @@ class _SelectDoctorPageState extends State { borderColor: AppColors.greyColor, textColor: AppColors.blackColor, fontSize: 10, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 8, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 30.h, @@ -315,7 +318,7 @@ class _SelectDoctorPageState extends State { bookAppointmentsVM.isSortByClinic ? (bookAppointmentsVM.doctorsListGrouped[index].first.clinicName ?? 'Unknown') : (bookAppointmentsVM.doctorsListGrouped[index].first.projectName ?? 'Unknown'), - style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600), + style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600,), overflow: TextOverflow.ellipsis, ), // Expanded content - list of doctors in this group @@ -344,7 +347,7 @@ class _SelectDoctorPageState extends State { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( CustomPageRoute( - page: DoctorProfilePage(), + page: DoctorProfilePage(isDoctorAllowedToBook: true,), ), ); }, onError: (err) { diff --git a/lib/presentation/book_appointment/select_livecare_clinic_page.dart b/lib/presentation/book_appointment/select_livecare_clinic_page.dart index 87ab0eb5..bd2b6462 100644 --- a/lib/presentation/book_appointment/select_livecare_clinic_page.dart +++ b/lib/presentation/book_appointment/select_livecare_clinic_page.dart @@ -55,7 +55,7 @@ class SelectLivecareClinicPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.immediateService.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.noNeedToWaitGetMedicalConsultation.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.noNeedToWaitGetMedicalConsultation.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -71,7 +71,7 @@ class SelectLivecareClinicPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.noVisitRequired.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.livecarePoint5.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -87,7 +87,7 @@ class SelectLivecareClinicPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.doctorWillContact.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.specialisedDoctorWillContactYou.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.specialisedDoctorWillContactYou.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -103,7 +103,7 @@ class SelectLivecareClinicPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.freeMedicineDelivery.tr(context: context).toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.offersFreeMedicineDelivery.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.offersFreeMedicineDelivery.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -152,7 +152,7 @@ class SelectLivecareClinicPage extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -171,7 +171,7 @@ class SelectLivecareClinicPage extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart index 19d0fb41..500167eb 100644 --- a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart +++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart @@ -98,7 +98,7 @@ class WaitingAppointmentInfo extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart index 6f3a773a..11c79c10 100644 --- a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart +++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart @@ -63,20 +63,20 @@ class WaitingAppointmentOnlineCheckinSheet extends StatelessWidget { }); }), SizedBox(height: 16.h), - checkInOptionCard( - AppAssets.checkin_nfc_icon, - LocaleKeys.nfcNearFieldCommunication.tr(), - LocaleKeys.scanPhoneViaNFC.tr(), - ).onPress(() { - Future.delayed(const Duration(milliseconds: 500), () { - showNfcReader(context, onNcfScan: (String nfcId) { - Future.delayed(const Duration(milliseconds: 100), () { - checkScannedNFCAndQRCode(nfcId, context); - }); - }, onCancel: () {}); - }); - }), - SizedBox(height: 16.h), + // checkInOptionCard( + // AppAssets.checkin_nfc_icon, + // LocaleKeys.nfcNearFieldCommunication.tr(), + // LocaleKeys.scanPhoneViaNFC.tr(), + // ).onPress(() { + // Future.delayed(const Duration(milliseconds: 500), () { + // showNfcReader(context, onNcfScan: (String nfcId) { + // Future.delayed(const Duration(milliseconds: 100), () { + // checkScannedNFCAndQRCode(nfcId, context); + // }); + // }, onCancel: () {}); + // }); + // }), + // SizedBox(height: 16.h), checkInOptionCard( AppAssets.checkin_qr_icon, LocaleKeys.qrCode.tr(), @@ -111,7 +111,7 @@ class WaitingAppointmentOnlineCheckinSheet extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText16(isBold: true, color: AppColors.textColor), - subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + subTitle.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart index 663fd5b4..4d136cdd 100644 --- a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart +++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart @@ -244,7 +244,7 @@ class _WaitingAppointmentPaymentPageState extends State { // Row( // crossAxisAlignment: CrossAxisAlignment.center, // children: [ - // "Pick a Date".toText20(weight: FontWeight.w600).expanded, + // "Pick a Date".toText20(isBold: true).expanded, // Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { // Navigator.of(context).pop(); // }), @@ -147,7 +147,7 @@ class _AppointmentCalendarState extends State { SizedBox(height: 10.h), Transform.translate( offset: const Offset(0.0, -10.0), - child: selectedDateDisplay.toText16(weight: FontWeight.w500, isEnglishOnly: true), + child: selectedDateDisplay.toText16(isBold: true, isEnglishOnly: true), ), //TODO: Add Next Day Span here dayEvents.isNotEmpty @@ -269,7 +269,7 @@ class _AppointmentCalendarState extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 50.h, @@ -349,14 +349,14 @@ class TimeSlotChip extends StatelessWidget { ? Container( padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h), decoration: ShapeDecoration( - color: isSelected ? AppColors.warningColorYellow : AppColors.textColor, + color: isSelected ? AppColors.warningColorYellow : AppColors.whiteColor, shape: SmoothRectangleBorder( borderRadius: BorderRadius.circular(8.h), smoothness: 1, side: BorderSide(color: isSelected ? AppColors.warningColorYellow : AppColors.borderOnlyColor.withOpacity(0.2), width: 1), ), ), - child: label.toText12(color: isSelected ? AppColors.whiteColor : Colors.black87, fontWeight: FontWeight.w500, isEnglishOnly: true), + child: label.toText12(color: isSelected ? AppColors.whiteColor : Colors.black87, isBold: true, isEnglishOnly: true), ) : Container( padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h), @@ -368,7 +368,7 @@ class TimeSlotChip extends StatelessWidget { side: BorderSide(color: isSelected ? AppColors.primaryRedColor : AppColors.borderOnlyColor.withOpacity(0.2), width: 1), ), ), - child: label.toText12(color: isSelected ? AppColors.primaryRedColor : AppColors.textColor, fontWeight: FontWeight.w500, isEnglishOnly: true), + child: label.toText12(color: isSelected ? AppColors.primaryRedColor : AppColors.textColor, isBold: true, isEnglishOnly: true), ), ); } diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart index 25856829..11042a57 100644 --- a/lib/presentation/book_appointment/widgets/doctor_card.dart +++ b/lib/presentation/book_appointment/widgets/doctor_card.dart @@ -1,6 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -76,7 +78,7 @@ class DoctorCard extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, applyThemeColor: false), SizedBox(height: 2.h), - "${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor), + "${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true), ], ), ).circle(100).toShimmer2(isShow: isLoading), @@ -103,13 +105,20 @@ class DoctorCard extends StatelessWidget { SizedBox( child: (isLoading ? "Consultant Cardiologist" - : doctorsListResponseModel.speciality!.isNotEmpty - ? (doctorsListResponseModel.speciality!.first.length > 32 - ? '${doctorsListResponseModel.speciality!.first.substring(0, 32)}...' - : doctorsListResponseModel.speciality!.first) + : (getIt.get().isArabic() && doctorsListResponseModel.specialityN != null) + ? doctorsListResponseModel.specialityN!.isNotEmpty + ? (doctorsListResponseModel.specialityN!.first.length > 32 + ? '${doctorsListResponseModel.specialityN!.first.substring(0, 32)}...' + : doctorsListResponseModel.specialityN!.first) + : "" + : doctorsListResponseModel.speciality != null ? + doctorsListResponseModel.speciality!.isNotEmpty + ? (doctorsListResponseModel.speciality!.first.length > 32 + ? '${doctorsListResponseModel.speciality!.first.substring(0, 32)}...' + : doctorsListResponseModel.speciality!.first) : "" : "") .toString() - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColor, maxLine: 2, isEnglishOnly: true) + .toText12(isBold: true, color: AppColors.textColor, maxLine: 2, isEnglishOnly: true) .toShimmer2(isShow: isLoading), ), SizedBox(width: 6.w), @@ -133,8 +142,9 @@ class DoctorCard extends StatelessWidget { labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)), // richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)) // .toText10(isEnglishOnly: true, color: AppColors.textColor), - // backgroundColor: AppColors.successColor, - textColor: AppColors.textColor, + backgroundColor: AppColors.successColor.withValues(alpha: .15), + isEnglishOnly: true, + textColor: AppColors.successColor, ).toShimmer2(isShow: isLoading) : SizedBox.shrink() : SizedBox.shrink(), @@ -252,7 +262,7 @@ class DoctorCard extends StatelessWidget { borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), height: 40.h, diff --git a/lib/presentation/book_appointment/widgets/doctor_rating_details.dart b/lib/presentation/book_appointment/widgets/doctor_rating_details.dart index 1a5eedfe..df88b366 100644 --- a/lib/presentation/book_appointment/widgets/doctor_rating_details.dart +++ b/lib/presentation/book_appointment/widgets/doctor_rating_details.dart @@ -1,9 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; 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/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -15,188 +15,304 @@ class DoctorRatingDetails extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer(builder: (context, bookAppointmentsVM, child) { - return bookAppointmentsVM.isDoctorRatingDetailsLoading - ? Utils.getLoadingWidget() - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.ceilToDouble().toString().toText44(isBold: true, isEnglishOnly: true), - SizedBox(height: 4.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - "${bookAppointmentsVM.doctorsProfileResponseModel.noOfPatientsRate} " - .toText16(weight: FontWeight.w500, color: AppColors.greyInfoTextColor, isEnglishOnly: true), - LocaleKeys.reviews.tr(context: context) - .toText16(weight: FontWeight.w500, color: AppColors.greyInfoTextColor,), - ], - ), + if (bookAppointmentsVM.isDoctorRatingDetailsLoading) { + return Utils.getLoadingWidget(); + } - RatingBar( - initialRating: bookAppointmentsVM.doctorsProfileResponseModel.actualDoctorRate!.toDouble(), - direction: Axis.horizontal, - allowHalfRating: true, - itemCount: 5, - itemSize: 20.h, - ignoreGestures: true, - ratingWidget: RatingWidget( - full: Icon( - Icons.star, - color: AppColors.ratingColorYellow, - size: 24.h, - ), - half: Icon( - Icons.star_half, - color: AppColors.ratingColorYellow, - ), - empty: Icon( - Icons.star, - color: AppColors.ratingColorYellow, - ), - ), - tapOnlyMode: true, - unratedColor: Colors.grey[500], - itemPadding: EdgeInsets.symmetric(horizontal: 4.0), - onRatingUpdate: (rating) { - print(rating); - }, - ), - ], + final rating = bookAppointmentsVM.doctorsProfileResponseModel.decimalDoctorRate?.toDouble() ?? 0.0; + if (rating == 0 || rating == 0.0) { + return NoRatingDialog(); + } + + return DoctorRatingDialog( + averageRating: rating, + totalReviews: (bookAppointmentsVM.doctorsProfileResponseModel.noOfPatientsRate ?? 0).toInt(), + fiveStarPercent: bookAppointmentsVM.doctorDetailsList.isNotEmpty ? bookAppointmentsVM.doctorDetailsList[0].ratio.round() : 0, + fourStarPercent: bookAppointmentsVM.doctorDetailsList.length > 1 ? bookAppointmentsVM.doctorDetailsList[1].ratio.round() : 0, + threeStarPercent: bookAppointmentsVM.doctorDetailsList.length > 2 ? bookAppointmentsVM.doctorDetailsList[2].ratio.round() : 0, + twoStarPercent: bookAppointmentsVM.doctorDetailsList.length > 3 ? bookAppointmentsVM.doctorDetailsList[3].ratio.round() : 0, + oneStarPercent: bookAppointmentsVM.doctorDetailsList.length > 4 ? bookAppointmentsVM.doctorDetailsList[4].ratio.round() : 0, + ); + }); + } +} + +class DoctorRatingDialog extends StatelessWidget { + final double averageRating; + final int totalReviews; + final int fiveStarPercent; + final int fourStarPercent; + final int threeStarPercent; + final int twoStarPercent; + final int oneStarPercent; + + const DoctorRatingDialog({ + super.key, + required this.averageRating, + required this.totalReviews, + required this.fiveStarPercent, + required this.fourStarPercent, + required this.threeStarPercent, + required this.twoStarPercent, + required this.oneStarPercent, + }); + + /// Returns the color for the rating badge and progress bar based on star level. + /// 5 stars -> bright green + /// 4 stars -> slightly darker green + /// 3 stars -> dark yellow / olive + /// 2 stars -> dark orange / brown-red + /// 1 star -> red + Color _getBarColor(int stars) { + switch (stars) { + case 5: + return AppColors.ratingFiveStarColor; + case 4: + return AppColors.ratingFourStarColor; + case 3: + return AppColors.ratingThreeStarColor; + case 2: + return AppColors.ratingTwoStarColor; + case 1: + return AppColors.ratingOneStarColor; + default: + return Colors.grey; + } + } + + /// Returns the background color for the large rating badge based on the + /// overall average rating. + Color _getBadgeColor(double rating) { + if (rating == 0 || rating == 0.0) { + return AppColors.greyLightColor; // Grey color for no rating + } else if (rating >= 4.5) { + return AppColors.ratingFiveStarColor; // Bright green + } else if (rating >= 4.0) { + return AppColors.ratingBadgeFiveStarColor; // Green + } else if (rating >= 3.0) { + return AppColors.ratingThreeStarColor; + } else if (rating >= 2.0) { + return AppColors.ratingTwoStarColor; + } else { + return AppColors.ratingOneStarColor; + } + } + + @override + Widget build(BuildContext context) { + final ratingRows = [ + _RatingRowData(stars: 5, percent: fiveStarPercent), + _RatingRowData(stars: 4, percent: fourStarPercent), + _RatingRowData(stars: 3, percent: threeStarPercent), + _RatingRowData(stars: 2, percent: twoStarPercent), + _RatingRowData(stars: 1, percent: oneStarPercent), + ]; + + return Container( + width: 420, + padding: const EdgeInsets.all(0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 130.w, + height: 140.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + borderRadius: 16.r, + color: _getBadgeColor(averageRating), ), - SizedBox(height: 8.h), - Container( - margin: EdgeInsets.only(top: 10.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Container( - width: 100.0, - margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), - child: Text(LocaleKeys.excellent.tr(context: context), style: TextStyle(fontSize: 13.0, color: AppColors.textColor, fontWeight: FontWeight.w600))), - getRatingLine(bookAppointmentsVM.doctorDetailsList[0].ratio, Colors.green[700]!), - ], - ), - Container( - margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), - child: Text("${getRatingWidth(bookAppointmentsVM.doctorDetailsList[0].ratio).round()}%", style: TextStyle(fontSize: 14.0, color: AppColors.textColor, fontWeight: FontWeight.w600, fontFamily: "Poppins")), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + averageRating.toString(), + style: TextStyle( + fontFamily: "Poppins", + fontSize: 48.f, + fontWeight: FontWeight.w700, + color: averageRating == 0 || averageRating == 0.0 ? AppColors.textColor : AppColors.whiteColor, + height: 1.1, ), - ], - ), - ), - Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Container( - width: 100.0, - margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), - child: Text(LocaleKeys.vGood.tr(context: context), style: TextStyle(fontSize: 13.0, color: AppColors.textColor, fontWeight: FontWeight.w600))), - getRatingLine(bookAppointmentsVM.doctorDetailsList[1].ratio, Color(0xffB7B723)), - ], + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 6, ), - Container( - margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), - child: Text("${bookAppointmentsVM.doctorDetailsList[1].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: AppColors.textColor, fontWeight: FontWeight.w600, fontFamily: "Poppins")), + decoration: BoxDecoration( + color: averageRating == 0 || averageRating == 0.0 ? AppColors.greyF7Color : AppColors.textColor.withAlpha(20), + borderRadius: BorderRadius.circular(8), ), - ], - ), - ), - Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( + child: Row( children: [ - Container( - width: 100.0, - margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), - child: Text(LocaleKeys.good.tr(context: context), style: TextStyle(fontSize: 13.0, color: AppColors.textColor, fontWeight: FontWeight.w600))), - getRatingLine(bookAppointmentsVM.doctorDetailsList[2].ratio, Color(0xffEBA727)), + NumberFormat.decimalPattern().format(totalReviews).toText12( + isBold: true, + color: averageRating == 0 || averageRating == 0.0 ? AppColors.textColor : AppColors.whiteColor, + isEnglishOnly: true + ), + ' ${LocaleKeys.reviews.tr(context: context)}'.toText12( + isBold: true, + color: averageRating == 0 || averageRating == 0.0 ? AppColors.textColor : AppColors.whiteColor, + ), ], ), - Container( - margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), - child: Text("${bookAppointmentsVM.doctorDetailsList[2].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: AppColors.textColor, fontWeight: FontWeight.w600, fontFamily: "Poppins")), - ), - ], - ), + // child: Text( + // '${NumberFormat.decimalPattern().format(totalReviews)} ${LocaleKeys.reviews.tr(context: context)}', + // style: TextStyle( + // fontSize: 12.f, + // isBold: true, + // color: averageRating == 0 || averageRating == 0.0 ? AppColors.textColor : AppColors.whiteColor, + // ), + // ), + ).paddingSymmetrical(12.h, 0.h), + ], ), - Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Container( - width: 100.0, - margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), - child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: AppColors.textColor, fontWeight: FontWeight.w600))), - getRatingLine(bookAppointmentsVM.doctorDetailsList[3].ratio, Color(0xffEB7227)), - ], - ), - Container( - margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), - child: Text("${bookAppointmentsVM.doctorDetailsList[3].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: AppColors.textColor, fontWeight: FontWeight.w600, fontFamily: "Poppins")), - ), - ], - ), + ), + + const SizedBox(width: 16), + + // ── Right: Star Breakdown ── + Expanded( + child: Column( + children: ratingRows + .map( + (row) => _buildRatingRow(row.stars, row.percent), + ) + .toList(), ), - Container( - margin: EdgeInsets.only(bottom: 30.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Container( - width: 100.0, - margin: EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0), - child: Text(LocaleKeys.average.tr(context: context), style: TextStyle(fontSize: 13.0, color: AppColors.textColor, fontWeight: FontWeight.w600))), - getRatingLine(bookAppointmentsVM.doctorDetailsList[4].ratio, Color(0xffE20C0C)), - ], - ), - Container( - margin: EdgeInsets.only(top: 10.0, left: 10.0, right: 10.0), - child: Text("${bookAppointmentsVM.doctorDetailsList[4].ratio.round()}%", style: TextStyle(fontSize: 14.0, color: AppColors.textColor, fontWeight: FontWeight.w600, fontFamily: "Poppins")), - ), - ], + ), + ], + ), + const SizedBox(height: 16), + ], + ), + ); + } + + Widget _buildRatingRow(int stars, int percent) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Row( + children: [ + // Star icons + SizedBox( + width: 70, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate( + stars, + (_) => Icon(Icons.star, size: 14, color: AppColors.ratingStarIconColor), + ), + ), + ), + + const SizedBox(width: 8), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: SizedBox( + height: 5.h, + child: LinearProgressIndicator( + value: percent / 100, + backgroundColor: AppColors.shimmerBaseColor, + valueColor: AlwaysStoppedAnimation( + _getBarColor(stars), ), ), - ], - ); - }); - } + ), + ), + ), + + const SizedBox(width: 8), - double getRatingWidth(num patientNumber) { - var width = patientNumber; - return width.roundToDouble(); + // Percentage text + SizedBox( + width: 36, + child: Text( + '$percent%', + textAlign: TextAlign.right, + style: TextStyle( + fontFamily: "Poppins", + fontSize: 14, + fontWeight: FontWeight.w600, + color: AppColors.ratingPercentageTextColor, + ), + ), + ), + ], + ), + ); } +} + +class _RatingRowData { + final int stars; + final int percent; + + _RatingRowData({required this.stars, required this.percent}); +} + +class NoRatingDialog extends StatelessWidget { + const NoRatingDialog({super.key}); - Widget getRatingLine(double patientNumber, Color color) { + @override + Widget build(BuildContext context) { return Container( - margin: EdgeInsets.only(top: 10.0), - child: Stack(children: [ - SizedBox( - width: 150.0, - height: 7.h, - child: Container( - color: Colors.grey[300], + width: 420, + padding: const EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Icon + Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: AppColors.lightRedButtonColor, + borderRadius: BorderRadius.circular(24), + ), + child: Icon( + Icons.star_border_rounded, + size: 48, + color: AppColors.primaryRedColor, + ), + ), + + const SizedBox(height: 20), + + // Title + Text( + LocaleKeys.noRatingAvailable.tr(context: context), + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w700, + color: AppColors.textColor, + ), + textAlign: TextAlign.center, ), - ), - SizedBox( - width: patientNumber * 1.55, - height: 7.h, - child: Container( - color: color, + + const SizedBox(height: 12), + + // Description + Text( + LocaleKeys.doctorDoesNotHaveRating.tr(context: context), + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w400, + color: AppColors.textColorLight, + ), + textAlign: TextAlign.center, ), - ), - ]), + + const SizedBox(height: 8), + ], + ), ); } } diff --git a/lib/presentation/child_vaccine/my_child_vaccine_page.dart b/lib/presentation/child_vaccine/my_child_vaccine_page.dart index 6a226ab4..3ec6a09e 100644 --- a/lib/presentation/child_vaccine/my_child_vaccine_page.dart +++ b/lib/presentation/child_vaccine/my_child_vaccine_page.dart @@ -84,11 +84,11 @@ // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ -// LocaleKeys.city.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), +// LocaleKeys.city.tr().toText16(color: AppColors.textColor, isBold: true), // (appState.isArabic() // ? (bloodDonationVM.selectedCity?.descriptionN ?? LocaleKeys.select.tr()) // : bloodDonationVM.selectedCity?.description ?? LocaleKeys.select.tr(context: context)) -// .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), +// .toText14(color: AppColors.greyTextColor, isBold: true), // ], // ), // ], @@ -112,11 +112,11 @@ // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ -// LocaleKeys.gender.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), +// LocaleKeys.gender.tr().toText16(color: AppColors.textColor, isBold: true), // (appState.isArabic() // ? (bloodDonationVM.selectedGender?.typeAr ?? LocaleKeys.select.tr()) // : bloodDonationVM.selectedGender?.type ?? LocaleKeys.select.tr(context: context)) -// .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), +// .toText14(color: AppColors.greyTextColor, isBold: true), // ], // ), // ], @@ -143,12 +143,12 @@ // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ -// LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), -// // bloodDonationVM.selectedBloodType?.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), +// LocaleKeys.bloodType.tr().toText16(color: AppColors.textColor, isBold: true), +// // bloodDonationVM.selectedBloodType?.toText14(color: AppColors.greyTextColor, isBold: true), // (appState.isArabic() // ? (bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr()) // : bloodDonationVM.selectedBloodType ?? LocaleKeys.select.tr(context: context)) -// .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500) +// .toText14(color: AppColors.greyTextColor, isBold: true) // ], // ), // ], @@ -307,7 +307,7 @@ class _MyChildVaccinePageState extends State { "${children[index]['name']} (${children[index]['gender']})", style: TextStyle( color: isSelected ? AppColors.primaryRedColor : AppColors.textColor, - fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.w600, fontSize: 14.f, ), ), @@ -343,7 +343,7 @@ class _MyChildVaccinePageState extends State { children: [ Utils.buildSvgWithAssets(icon: AppAssets.calendar, width: 16.h, height: 16.h, iconColor: AppColors.textColor), SizedBox(width: 8.w), - "Due Date : ${data['dueDate']}".toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Due Date : ${data['dueDate']}".toText12(isBold: true, color: AppColors.textColor), ], ), ), @@ -354,7 +354,7 @@ class _MyChildVaccinePageState extends State { children: [ Utils.buildSvgWithAssets(icon: AppAssets.tickIcon, width: 20.h, height: 20.h, iconColor: Colors.green), SizedBox(width: 12.w), - vaccine.toString().toText14(weight: FontWeight.w500, color: AppColors.textColor), + vaccine.toString().toText14(isBold: true, color: AppColors.textColor), ], ), )), diff --git a/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart b/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart index dd836a64..fa0fdcdb 100644 --- a/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart +++ b/lib/presentation/comprehensive_checkup/cmc_order_detail_page.dart @@ -105,7 +105,7 @@ class _CmcOrderDetailPageState extends State { child: (isLoading ? "Processing" : order.statusText ?? '') .toText12( color: statusColor, - fontWeight: FontWeight.w600, + isBold: true, ) .toShimmer2(isShow: isLoading), ), @@ -113,7 +113,7 @@ class _CmcOrderDetailPageState extends State { (isLoading ? "Jan 15, 2024" : _formatDate(order.created)) .toText12( color: AppColors.textColorLight, - fontWeight: FontWeight.w500, + isBold: true, ) .toShimmer2(isShow: isLoading), ], @@ -127,7 +127,7 @@ class _CmcOrderDetailPageState extends State { if (!isLoading) ...[ LocaleKeys.requestID.tr(context: context).toText14( color: AppColors.textColorLight, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(width: 4.w), ], @@ -171,7 +171,7 @@ class _CmcOrderDetailPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w600, + isBold: true, borderRadius: 10.r, height: 44.h, ).toShimmer2(isShow: isLoading), diff --git a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart index 840d38cf..94bc0e13 100644 --- a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart +++ b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart @@ -356,7 +356,7 @@ class _CmcSelectionReviewPageState extends State { children: [ LocaleKeys.hereIsYourRequestNumber.tr(context: context).toText14( color: AppColors.textColorLight, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(width: 4.w), ("$requestId").toText16(isBold: true), @@ -385,7 +385,7 @@ class _CmcSelectionReviewPageState extends State { isCloseButtonVisible: false, isDismissible: false, isFullScreen: false, - ); + isAutoDismiss: true); } void _handleConfirm() { diff --git a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart index da926cd2..589764c1 100644 --- a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart +++ b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart @@ -112,14 +112,14 @@ class _ComprehensiveCheckupPageState extends State { ), child: statusDisp.toText12( color: statusColor, - fontWeight: FontWeight.w600, + isBold: true, ), ), SizedBox(width: 8.w), if (order.created != null) DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12( color: AppColors.textColorLight, - fontWeight: FontWeight.w500, + isBold: true, ), ], ), @@ -129,7 +129,7 @@ class _ComprehensiveCheckupPageState extends State { // Request ID Row( children: [ - LocaleKeys.requestID.tr(context: context).toText14(color: AppColors.textColorLight, weight: FontWeight.w500), + LocaleKeys.requestID.tr(context: context).toText14(color: AppColors.textColorLight, isBold: true), SizedBox(width: 4.w), "${order.iD ?? '-'}".toText16(isBold: true), ], @@ -182,7 +182,7 @@ class _ComprehensiveCheckupPageState extends State { Expanded( child: LocaleKeys.pendingOrderWait.tr(context: context).toText12( color: AppColors.infoBannerTextColor, - fontWeight: FontWeight.w500, + isBold: true, ), ), ], @@ -200,7 +200,7 @@ class _ComprehensiveCheckupPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w600, + isBold: true, borderRadius: 10.r, height: 44.h, ), diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart index 93c8d5fd..6f24e5e4 100644 --- a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart +++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_bottom_sheet_body.dart @@ -85,7 +85,7 @@ class CmcHospitalBottomSheetBody extends StatelessWidget { ? _buildLoadingShimmer() : hmgServicesViewModel.filteredHospitalsList.isEmpty ? Center( - child: LocaleKeys.noHospitalsFound.tr(context: context).toText16(weight: FontWeight.w500, color: AppColors.greyTextColor), + child: LocaleKeys.noHospitalsFound.tr(context: context).toText16(isBold: true, color: AppColors.greyTextColor), ) : ListView.separated( itemCount: hmgServicesViewModel.filteredHospitalsList.length, diff --git a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart index 25cd5068..b8ac54cc 100644 --- a/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart +++ b/lib/presentation/comprehensive_checkup/widgets/cmc_hospital_list_item.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; @@ -9,6 +10,7 @@ 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/my_appointments/models/resp_models/hospital_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/chip/app_custom_chip_widget.dart'; @@ -107,7 +109,7 @@ class CmcHospitalListItem extends StatelessWidget { Visibility( visible: (hospital.distanceInKilometers == null || hospital.distanceInKilometers == 0), child: AppCustomChipWidget( - labelText: " Distance not available", + labelText: LocaleKeys.noDistance.tr(), textColor: AppColors.blackColor, ), ), diff --git a/lib/presentation/contact_us/contact_us.dart b/lib/presentation/contact_us/contact_us.dart index 602fc3b2..f4b16ec4 100644 --- a/lib/presentation/contact_us/contact_us.dart +++ b/lib/presentation/contact_us/contact_us.dart @@ -39,8 +39,8 @@ class ContactUs extends StatelessWidget { checkInOptionCard( AppAssets.call_fill, LocaleKeys.callNow.tr(), - // LocaleKeys.viewNearestHMGLocationsviewNearestHMGLocations.tr(), - "Call for immediate assistance", + LocaleKeys.callForAssistance.tr(), + // "Call for immediate assistance", ).onPress(() { launchUrl(Uri.parse("tel://" + "+966 92 006 6666")); }), @@ -83,6 +83,8 @@ class ContactUs extends StatelessWidget { LocaleKeys.liveChatWithHMG.tr(), ).onPress(() { if (getIt.get().isAuthenticated) { + Navigator.of(context).pop(); + contactUsViewModel.getLiveChatProjectsList(); Navigator.of(context).push( CustomPageRoute( page: LiveChatPage(), @@ -93,9 +95,9 @@ class ContactUs extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.liveChat.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, - serviceImage: AppAssets.eReferralService), + serviceHeader: LocaleKeys.liveChatServiceHeader.tr(), + serviceDescription: LocaleKeys.liveChatServiceDescription.tr(), + serviceImage: AppAssets.livechatService), ), ); } @@ -124,7 +126,7 @@ class ContactUs extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText16(isBold: true, color: AppColors.textColor), - subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + subTitle.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), diff --git a/lib/presentation/contact_us/feedback_page.dart b/lib/presentation/contact_us/feedback_page.dart index 93954af9..cc70f28e 100644 --- a/lib/presentation/contact_us/feedback_page.dart +++ b/lib/presentation/contact_us/feedback_page.dart @@ -125,7 +125,7 @@ class FeedbackPage extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, @@ -146,7 +146,7 @@ class FeedbackPage extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.likeToHear.tr().toText14(weight: FontWeight.w500), + LocaleKeys.likeToHear.tr().toText14(isBold: true), SizedBox(height: 16.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -167,9 +167,9 @@ class FeedbackPage extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.feedbackType.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), + LocaleKeys.feedbackType.tr().toText16(color: AppColors.textColor, isBold: true), (getIt.get().isArabic() ? contactUsViewModel.selectedFeedbackType.nameAR : contactUsViewModel.selectedFeedbackType.nameEN) - .toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + .toText14(color: AppColors.greyTextColor, isBold: true), ], ), ], @@ -197,7 +197,7 @@ class FeedbackPage extends StatelessWidget { getIt.get().isArabic() ? contactUsViewModel.feedbackTypeList[index].nameAR : contactUsViewModel.feedbackTypeList[index].nameEN, style: TextStyle( fontSize: 16.h, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), value: contactUsViewModel.feedbackTypeList[index], @@ -286,10 +286,13 @@ class FeedbackPage extends StatelessWidget { children: [ AppCustomChipWidget(labelText: contactUsViewModel.patientFeedbackSelectedAppointment!.clinicName!).toShimmer2(isShow: false), AppCustomChipWidget(labelText: contactUsViewModel.patientFeedbackSelectedAppointment!.projectName!).toShimmer2(isShow: false), - AppCustomChipWidget( - icon: AppAssets.appointment_calendar_icon, - labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(contactUsViewModel.patientFeedbackSelectedAppointment!.appointmentDate), false), - ).toShimmer2(isShow: false), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + icon: AppAssets.appointment_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(contactUsViewModel.patientFeedbackSelectedAppointment!.appointmentDate), false), + isEnglishOnly: true).toShimmer2(isShow: false), + ), ], ), ], @@ -349,7 +352,7 @@ class FeedbackPage extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -475,7 +478,7 @@ class FeedbackPage extends StatelessWidget { .toText16(weight: FontWeight.bold, fontFamily: Utils.isArabicText(contactUsViewModel.cocItemsList[index].cOCTitle ?? "") ? "GESSTwo" : "Poppins"), SizedBox(height: 8.h), "${contactUsViewModel.cocItemsList[index].detail}".toText14( - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.textColorLight, fontFamily: Utils.isArabicText(contactUsViewModel.cocItemsList[index].detail ?? "") ? "GESSTwo" : "Poppins"), ], diff --git a/lib/presentation/contact_us/find_us_page.dart b/lib/presentation/contact_us/find_us_page.dart index 0cabbf2b..e1dff5e7 100644 --- a/lib/presentation/contact_us/find_us_page.dart +++ b/lib/presentation/contact_us/find_us_page.dart @@ -45,7 +45,7 @@ class FindUsPage extends StatelessWidget { activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.hmgHospitals.tr()), - CustomTabBarModel(null, LocaleKeys.pharmaciesList.tr()), + CustomTabBarModel(null, LocaleKeys.hmgPharmacy.tr()), ], onTabChange: (index) { contactUsVM.setHMGHospitalsListSelected(index == 0); @@ -59,7 +59,7 @@ class FindUsPage extends StatelessWidget { children: [ LocaleKeys.sortByLocation.tr(context: context).toText14(isBold: true), SizedBox(height: 4.h), - LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, weight: FontWeight.w500), + LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), ], ), const Spacer(), diff --git a/lib/presentation/contact_us/live_chat_page.dart b/lib/presentation/contact_us/live_chat_page.dart index 0350c316..0fd14b6c 100644 --- a/lib/presentation/contact_us/live_chat_page.dart +++ b/lib/presentation/contact_us/live_chat_page.dart @@ -121,7 +121,9 @@ class LiveChatPage extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - ("${appState.isArabic() ? contactUsVM.liveChatProjectsList[index].projectNameN! : contactUsVM.liveChatProjectsList[index].projectName!}\n${contactUsVM.liveChatProjectsList[index].distanceInKilometers!} KM") + ("${appState.isArabic() ? contactUsVM.liveChatProjectsList[index].projectNameN! : contactUsVM.liveChatProjectsList[index].projectName!}" + // "\n${contactUsVM.liveChatProjectsList[index].distanceInKilometers!} KM" + ) .toText14(isBold: true, color: contactUsVM.selectedLiveChatProjectIndex == index ? Colors.white : AppColors.textColor), Transform.flip( flipX: getIt.get().isArabic(), @@ -237,7 +239,7 @@ class LiveChatPage extends StatelessWidget { borderColor: contactUsVM.selectedLiveChatProjectIndex == -1 ? AppColors.greyColor : AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/contact_us/widgets/find_us_item_card.dart b/lib/presentation/contact_us/widgets/find_us_item_card.dart index 521775bc..1f7f2443 100644 --- a/lib/presentation/contact_us/widgets/find_us_item_card.dart +++ b/lib/presentation/contact_us/widgets/find_us_item_card.dart @@ -18,6 +18,7 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:map_launcher/map_launcher.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'dart:ui' as ui; class FindUsItemCard extends StatelessWidget { FindUsItemCard({super.key, required this.getHMGLocationsModel}); @@ -57,13 +58,17 @@ class FindUsItemCard extends StatelessWidget { (getHMGLocationsModel.distanceInKilometers != 0 && contactUsViewModel.hasLocationEnabled) ? Column( children: [ - AppCustomChipWidget( - labelText: "${getHMGLocationsModel.distanceInKilometers ?? ""} km", - labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.w), - icon: AppAssets.location_red, - // iconColor: AppColors.primaryRedColor, - // backgroundColor: AppColors.secondaryLightRedColor, - // textColor: AppColors.errorColor, + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: "${getHMGLocationsModel.distanceInKilometers ?? ""} km", + labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.w), + icon: AppAssets.location_red, + isEnglishOnly: true, + // iconColor: AppColors.primaryRedColor, + // backgroundColor: AppColors.secondaryLightRedColor, + // textColor: AppColors.errorColor, + ), ), SizedBox( height: 16.h, @@ -118,7 +123,7 @@ class FindUsItemCard extends StatelessWidget { textColor: AppColors.blackColor, borderWidth: 1.h, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, diff --git a/lib/presentation/covid19test/covid19_landing_page.dart b/lib/presentation/covid19test/covid19_landing_page.dart index a241da60..63ebe8f5 100644 --- a/lib/presentation/covid19test/covid19_landing_page.dart +++ b/lib/presentation/covid19test/covid19_landing_page.dart @@ -102,7 +102,7 @@ class _Covid19LandingPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 50.h, @@ -259,7 +259,7 @@ class _Covid19LandingPageState extends State { : AppColors.greyTextColor.withValues(alpha: 0.3), textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w600, + isBold: true, borderRadius: 12.r, height: 56.h, ), diff --git a/lib/presentation/covid19test/covid_19_questionnaire.dart b/lib/presentation/covid19test/covid_19_questionnaire.dart index f7b1f4c0..bb12e448 100644 --- a/lib/presentation/covid19test/covid_19_questionnaire.dart +++ b/lib/presentation/covid19test/covid_19_questionnaire.dart @@ -71,7 +71,7 @@ class _Covid19QuestionnaireState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w600, + isBold: true, borderRadius: 12.r, height: 56.h, )), @@ -93,7 +93,7 @@ class _Covid19QuestionnaireState extends State { children: [ "Please answer below questionnaire:".toText14( color: AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(height: 20.h), // Question list diff --git a/lib/presentation/covid19test/covid_payment_screen.dart b/lib/presentation/covid19test/covid_payment_screen.dart index 40bf2863..a846828f 100644 --- a/lib/presentation/covid19test/covid_payment_screen.dart +++ b/lib/presentation/covid19test/covid_payment_screen.dart @@ -178,7 +178,7 @@ class _CovidPaymentScreenState extends State { // borderColor: AppColors.primaryRedColor, // textColor: AppColors.whiteColor, // fontSize: 16.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.r, // padding: EdgeInsets.symmetric(horizontal: 10.w), // icon: AppAssets.add_icon, diff --git a/lib/presentation/covid19test/covid_review_screen.dart b/lib/presentation/covid19test/covid_review_screen.dart index 34401298..be6bdb71 100644 --- a/lib/presentation/covid19test/covid_review_screen.dart +++ b/lib/presentation/covid19test/covid_review_screen.dart @@ -279,7 +279,7 @@ class _CovidReviewScreenState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), // icon: AppAssets.add_icon, @@ -305,7 +305,7 @@ class _CovidReviewScreenState extends State { "Please select the procedure:".toText14( color: AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(height: 16.h), diff --git a/lib/presentation/e_referral/e_referral_search_result.dart b/lib/presentation/e_referral/e_referral_search_result.dart index 6a4b8318..29c7a294 100644 --- a/lib/presentation/e_referral/e_referral_search_result.dart +++ b/lib/presentation/e_referral/e_referral_search_result.dart @@ -75,7 +75,7 @@ class _SearchResultPageState extends State { // style: TextStyle( // color: Colors.white, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // ), ), ), diff --git a/lib/presentation/e_referral/new_e_referral.dart b/lib/presentation/e_referral/new_e_referral.dart index 844ef5d5..61796dbd 100644 --- a/lib/presentation/e_referral/new_e_referral.dart +++ b/lib/presentation/e_referral/new_e_referral.dart @@ -227,7 +227,7 @@ class _NewReferralPageState extends State { children: [ LocaleKeys.hereIsYourReferralNumber.tr(context: context).toText14( color: AppColors.textColorLight, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(width: 4.w), ("$requestId").toText16(isBold: true), diff --git a/lib/presentation/e_referral/widget/e_referral_other_details.dart b/lib/presentation/e_referral/widget/e_referral_other_details.dart index 114b3b24..579f281f 100644 --- a/lib/presentation/e_referral/widget/e_referral_other_details.dart +++ b/lib/presentation/e_referral/widget/e_referral_other_details.dart @@ -183,7 +183,7 @@ class _OtherDetailsStepState extends State { color: Colors.black, weight: FontWeight.w600, ), - // style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600), + // style: TextStyle(fontSize: 14, isBold: true), //), ), ], diff --git a/lib/presentation/emergency_services/RRT/rrt_map_screen.dart b/lib/presentation/emergency_services/RRT/rrt_map_screen.dart index 530152b1..bd5407b6 100644 --- a/lib/presentation/emergency_services/RRT/rrt_map_screen.dart +++ b/lib/presentation/emergency_services/RRT/rrt_map_screen.dart @@ -31,25 +31,26 @@ class RrtMapScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: Visibility( - visible: context.watch().bottomSheetType == - BottomSheetType.FIXED, - child: Padding( - padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), - child: DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, borderRadius: 12.h), - child: Utils.buildSvgWithAssets( - icon: AppAssets.locate_me, width: 24.h, height: 24.h) - .paddingAll(12.h) - .onPress(() { - context - .read() - .moveToCurrentLocation(); - }), - ), - ), - ), + // floatingActionButton: Visibility( + // visible: context.watch().bottomSheetType == + // BottomSheetType.FIXED, + // child: Padding( + // padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), + // child: DecoratedBox( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, borderRadius: 12.h), + // child: Container() + // // Utils.buildSvgWithAssets( + // // icon: AppAssets.locate_me, width: 24.h, height: 24.h) + // // .paddingAll(12.h) + // // .onPress(() { + // // context + // // .read() + // // .moveToCurrentLocation(); + // // }), + // ), + // ), + // ), bottomSheet: ExpandableBottomSheet( bottomSheetType: context.watch().bottomSheetType, @@ -73,6 +74,7 @@ class RrtMapScreen extends StatelessWidget { inputController: context.read().gmsController, showCenterMarker: true, + bottomPaddingHeight: MediaQuery.of(context).size.height * 0.3, ) else HMSMap( @@ -139,7 +141,7 @@ class RrtMapScreen extends StatelessWidget { color: AppColors.textColor, ), LocaleKeys.pleaseSelectTheLocation.tr().toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, ) ], @@ -189,10 +191,7 @@ class RrtMapScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 16.h, children: [ - hospitalAndPickUpSection(context), - - ], ).paddingOnly(top: 24.h, bottom: 32.h,left: 24.h, right: 24.h), @@ -227,9 +226,8 @@ class RrtMapScreen extends StatelessWidget { shrinkWrap: true, itemCount: 3, itemBuilder: (__, index) { - if (index == - 2) // todo means the end of the list so handle as per the viewmodel - { + if (index == 2) // todo means the end of the list so handle as per the viewmodel + { return CustomButton( height: 40.h, backgroundColor: AppColors.lightRedButtonColor, @@ -243,7 +241,7 @@ class RrtMapScreen extends StatelessWidget { return AddressItem( isSelected: index == 0, address: - "Flat No 301, Building No 12, Palm Spring Apartment, Sector 45, Gurugram, Haryana 122003", + "", title: index == 0 ? LocaleKeys.home.tr() : LocaleKeys.work.tr(), onTap: () {}, ); @@ -370,7 +368,7 @@ class RrtMapScreen extends StatelessWidget { children: [ title.toText12( color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + isBold: true, ), SizedBox(height: 2.h), ], @@ -378,7 +376,7 @@ class RrtMapScreen extends StatelessWidget { ), subTitle.toText14( color: AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, ), ], ), @@ -423,7 +421,7 @@ class RrtMapScreen extends StatelessWidget { height: 18.h, width: 18.h), SizedBox(width: 4.h,), LocaleKeys.amountPaidAtHospital.tr().toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, ), ], @@ -437,7 +435,7 @@ class RrtMapScreen extends StatelessWidget { .getTotalPrice() ?? 0)) .toText24( - fontWeight: FontWeight.w600, + isBold: true, color: AppColors.textColor, letterSpacing: -2), AppColors.blackColor, diff --git a/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart b/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart index c17f3560..3b095ecf 100644 --- a/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart +++ b/lib/presentation/emergency_services/RRT/rrt_request_type_select.dart @@ -21,6 +21,7 @@ class RrtRequestTypeSelect extends StatelessWidget { return Column( children: [ Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.rapidResponseTeam.tr(context: context).toText20(color: AppColors.textColor, isBold: true), SizedBox( @@ -32,10 +33,9 @@ class RrtRequestTypeSelect extends StatelessWidget { borderRadius: 20.r, ), child: OptionSelection(context)), - SizedBox( - height: 24.h, - ), + SizedBox(height: 16.h), termsAndCondition(context, emergencyServicesVM), + SizedBox(height: 16.h), ], ).paddingSymmetrical(24.w, 0.h), bottomPriceContent(context, emergencyServicesVM) @@ -79,7 +79,7 @@ class RrtRequestTypeSelect extends StatelessWidget { ), Utils.getPaymentAmountWithSymbol( (Utils.formatNumberToInternationalFormat(context.read().selectedRRTProcedure?.patientShare ?? 0)) - .toText24(fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -2), + .toText24(isBold: true, color: AppColors.textColor, letterSpacing: -2), AppColors.blackColor, 17.h) ], @@ -101,7 +101,7 @@ class RrtRequestTypeSelect extends StatelessWidget { Row( children: [ LocaleKeys.vat15.tr(context: context).toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, ), "${emergencyServicesVM.selectedRRTProcedure?.patientTaxAmount})".toText14( @@ -122,15 +122,7 @@ class RrtRequestTypeSelect extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Row( - spacing: 6.w, - children: [ - Image.asset(AppAssets.mada, width: 24.h, height: 24.h), - Image.asset(AppAssets.visa, width: 24.h, height: 24.h), - Image.asset(AppAssets.mastercard, width: 24.h, height: 24.h), - Image.asset(AppAssets.applePay, width: 24.h, height: 24.h), - ], - ), + Utils.getPaymentMethods(), Column( children: [ Divider( @@ -139,7 +131,7 @@ class RrtRequestTypeSelect extends StatelessWidget { ), Utils.getPaymentAmountWithSymbol( (Utils.formatNumberToInternationalFormat(context.read().selectedRRTProcedure?.patientShareWithTax ?? 0)) - .toText24(fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -2), + .toText24(isBold: true, color: AppColors.textColor, letterSpacing: -2), AppColors.blackColor, 17.h), ], @@ -149,7 +141,9 @@ class RrtRequestTypeSelect extends StatelessWidget { CustomButton(text: LocaleKeys.next.tr(), onPressed: () { Navigator.pop(context); emergencyServicesVM.openRRT(); - }) + }, + isDisabled: !emergencyServicesVM.agreedToTermsAndCondition, + ) ], ).paddingAll(24.h), ), @@ -183,7 +177,7 @@ class RrtRequestTypeSelect extends StatelessWidget { final indexOfSelectedItem = selectedProcedure != null ? procedureList.indexOf(selectedProcedure) : -1; return RadioListTile( - title: (procedure.procedureName ?? "").toText16(color: AppColors.textColor, weight: FontWeight.w500), + title: (procedure.procedureName ?? "").toText16(color: AppColors.textColor, isBold: true), value: index, fillColor: WidgetStateProperty.resolveWith((states) { if (states.contains(WidgetState.selected)) { @@ -236,7 +230,6 @@ class RrtRequestTypeSelect extends StatelessWidget { value: emergencyServicesVM.agreedToTermsAndCondition, checkColor: AppColors.whiteColor, fillColor: MaterialStateProperty.resolveWith((Set states) { - print("the state is ${states}"); if (states.contains(WidgetState.selected)) { return AppColors.errorColor; } @@ -250,8 +243,8 @@ class RrtRequestTypeSelect extends StatelessWidget { spacing: 4.w, children: [ SizedBox.shrink(), - LocaleKeys.agreeTo.tr().toText16(color: AppColors.textColor, weight: FontWeight.w500), - LocaleKeys.termsConditoins.tr().toText16(color: AppColors.errorColor, isUnderLine: true, weight: FontWeight.w500).onPress(() { + LocaleKeys.agreeTo.tr().toText16(color: AppColors.textColor, isBold: true), + LocaleKeys.termsConditoins.tr().toText16(color: AppColors.errorColor, isUnderLine: true, isBold: true).onPress(() { emergencyServicesVM.getTermsAndConditions(); }), ], diff --git a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart index e5578a3e..55958048 100644 --- a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -33,18 +35,18 @@ class CallAmbulancePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: Visibility( - visible: context.watch().bottomSheetType == BottomSheetType.FIXED, - child: Padding( - padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), - child: DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), - child: Utils.buildSvgWithAssets(icon: AppAssets.locate_me, width: 24.h, height: 24.h).paddingAll(12.h).onPress(() { - context.read().moveToCurrentLocation(); - }), - ), - ), - ), + // floatingActionButton: Visibility( + // visible: context.watch().bottomSheetType == BottomSheetType.FIXED, + // child: Padding( + // padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), + // child: DecoratedBox( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), + // child: Utils.buildSvgWithAssets(icon: AppAssets.locate_me, width: 24.h, height: 24.h).paddingAll(12.h).onPress(() { + // context.read().moveToCurrentLocation(); + // }), + // ), + // ), + // ), bottomSheet: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -67,6 +69,7 @@ class CallAmbulancePage extends StatelessWidget { myLocationEnabled: true, inputController: context.read().gmsController, showCenterMarker: true, + bottomPaddingHeight: 400.h, ) else HMSMap( @@ -384,7 +387,7 @@ class CallAmbulancePage extends StatelessWidget { children: [ title.toText12( color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + isBold: true, ), SizedBox(height: 2.h), ], @@ -392,7 +395,7 @@ class CallAmbulancePage extends StatelessWidget { ), subTitle.toText14( color: AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, ), ], ), @@ -437,7 +440,7 @@ class CallAmbulancePage extends StatelessWidget { width: 4.h, ), LocaleKeys.amountPaidAtHospital.tr(context: context).toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, ), ], @@ -447,7 +450,7 @@ class CallAmbulancePage extends StatelessWidget { ), Utils.getPaymentAmountWithSymbol( (Utils.formatNumberToInternationalFormat(context.read().getTotalPrice() ?? 0)) - .toText24(fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -2), + .toText24(isBold: true, color: AppColors.textColor, letterSpacing: -2), AppColors.blackColor, 17.h) @@ -527,7 +530,7 @@ class CallAmbulancePage extends StatelessWidget { textPlaceInput(context) { return Consumer(builder: (_, vm, __) { // print("the data is ${vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description}"); - return (vm.geocodeResponse != null && vm.geocodeResponse!.results.isNotEmpty) ? SizedBox( + return SizedBox( width: MediaQuery.sizeOf(context).width, child: TextInputWidget( labelText: LocaleKeys.enterPickupLocationManually.tr(context: context), @@ -546,10 +549,11 @@ class CallAmbulancePage extends StatelessWidget { vertical: ResponsiveExtension(10).h, horizontal: ResponsiveExtension(15).h, ), + fontFamily: "Poppins", ).onPress(() { openLocationInputBottomSheet(context); }), - ) : SizedBox.shrink(); + ); }); } @@ -561,7 +565,7 @@ class CallAmbulancePage extends StatelessWidget { } openLocationInputBottomSheet(BuildContext context) { - context.read().flushSearchPredictions(); + // context.read().flushSearchPredictions(); showCommonBottomSheetWithoutHeight( title: "", context, diff --git a/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart b/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart index bc6d9b03..15d1d552 100644 --- a/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/requesting_services_page.dart @@ -30,7 +30,7 @@ class RequestingServicesPage extends StatelessWidget { Positioned( bottom: 1, child: LocaleKeys.submitRequest.tr(context: context) - .toText16(color: AppColors.textColor, weight: FontWeight.w500) + .toText16(color: AppColors.textColor, isBold: true) .paddingOnly(bottom: 100.h, left: 100.h, right: 100.h)) ], ), diff --git a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart index 9a49dd06..0f4c587b 100644 --- a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart +++ b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart @@ -220,9 +220,9 @@ class TrackingScreen extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Dr. John Doe".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Dr. John Doe".toText16(color: AppColors.textColor, isBold: true), SizedBox(height: 4.h), - "MBBS, MD - General Medicine".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + "MBBS, MD - General Medicine".toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -253,7 +253,9 @@ class TrackingScreen extends StatelessWidget { }, currentLocation: context.read().getGMSLocation(), - onCameraMoved: (value) => context.read().handleGMSMapCameraMoved(value)); + onCameraMoved: (value) => context.read().handleGMSMapCameraMoved(value), + bottomPaddingHeight: MediaQuery.of(context).size.height * 0.2, + ); } else { return HMSMap( myLocationEnabled: false, @@ -268,7 +270,7 @@ class TrackingScreen extends StatelessWidget { informationRegardingTrackingSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [currentStatus(), SizedBox(height: 4.h), "You will receive a call from HMG for confirmation ".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500)], + children: [currentStatus(), SizedBox(height: 4.h), "You will receive a call from HMG for confirmation ".toText12(color: AppColors.greyTextColor, isBold: true)], ); } @@ -293,7 +295,7 @@ class TrackingScreen extends StatelessWidget { text: "...", style: TextStyle( fontSize: 21.f, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, color: AppColors.errorColor, ), ), @@ -306,7 +308,7 @@ class TrackingScreen extends StatelessWidget { text: "15:30", style: TextStyle( fontSize: 21.f, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -314,7 +316,7 @@ class TrackingScreen extends StatelessWidget { text: LocaleKeys.mins.tr(), style: TextStyle( fontSize: 21.f, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, color: AppColors.errorColor, ), ), @@ -322,7 +324,7 @@ class TrackingScreen extends StatelessWidget { text: LocaleKeys.toHospitalLower.tr(), style: TextStyle( fontSize: 21.f, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -330,7 +332,7 @@ class TrackingScreen extends StatelessWidget { ); case OrderTrackingState.ended: - return LocaleKeys.arrived.tr().toText21(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.arrived.tr().toText21(color: AppColors.textColor, isBold: true); case OrderTrackingState.failed: case OrderTrackingState.cancel: return SizedBox.shrink(); @@ -354,7 +356,7 @@ class TrackingScreen extends StatelessWidget { spacing: 8.h, children: [ - Visibility(visible:(order != null || rrtOrder != null) ,child: "Req ID: ${order?.iD?? rrtOrder?.iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600)), + Visibility(visible:(order != null || rrtOrder != null) ,child: "Req ID: ${order?.iD?? rrtOrder?.iD}".toText16(color: AppColors.textColor, isBold: true)), if(order != null) ambulanceOrderData() // else rrtOrderData() @@ -388,8 +390,8 @@ class TrackingScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 4.h, children: [ - LocaleKeys.contact.tr().toText14(color: AppColors.textColor, weight: FontWeight.w600), - "0115259555".toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500).onPress((){ + LocaleKeys.contact.tr().toText14(color: AppColors.textColor, isBold: true), + "0115259555".toText12(color: AppColors.primaryRedColor, isBold: true).onPress((){ launchUrl( Uri.parse("tel://0115259555"), ); @@ -432,23 +434,23 @@ class TrackingScreen extends StatelessWidget { getTitle(OrderTrackingState? state) { if(state == null) { - return LocaleKeys.failed.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.failed.tr().toText16(color: AppColors.textColor, isBold: true); } switch (state) { case OrderTrackingState.waitingForCall: - return LocaleKeys.confirmationCall.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.confirmationCall.tr().toText16(color: AppColors.textColor, isBold: true); case OrderTrackingState.dispactched: - return LocaleKeys.pickupFromHome.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.pickupFromHome.tr().toText16(color: AppColors.textColor, isBold: true); case OrderTrackingState.returning: - return LocaleKeys.onTheWayToHospital.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.onTheWayToHospital.tr().toText16(color: AppColors.textColor, isBold: true); case OrderTrackingState.ended: - return LocaleKeys.arrivedAtHospital.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.arrivedAtHospital.tr().toText16(color: AppColors.textColor, isBold: true); case OrderTrackingState.failed: - return LocaleKeys.failed.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.failed.tr().toText16(color: AppColors.textColor, isBold: true); case OrderTrackingState.cancel: - return LocaleKeys.orderCancel.tr().toText16(color: AppColors.textColor, weight: FontWeight.w600); + return LocaleKeys.orderCancel.tr().toText16(color: AppColors.textColor, isBold: true); } } diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart b/lib/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart index 13e32561..f9d39434 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart @@ -48,7 +48,7 @@ class AddressItem extends StatelessWidget { children: [ title.toText12( color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + isBold: true, ), Row( mainAxisSize: MainAxisSize.max, @@ -56,7 +56,7 @@ class AddressItem extends StatelessWidget { Expanded( child: address.toText14( color: AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, textOverflow : TextOverflow.ellipsis, maxlines: 2 ), diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/WayPickup.dart b/lib/presentation/emergency_services/call_ambulance/widgets/WayPickup.dart new file mode 100644 index 00000000..ad28122c --- /dev/null +++ b/lib/presentation/emergency_services/call_ambulance/widgets/WayPickup.dart @@ -0,0 +1,172 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.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/features/emergency_services/models/AmbulanceCallingPlace.dart'; +import 'package:hmg_patient_app_new/features/emergency_services/models/ambulance_direction.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:provider/provider.dart'; + +import '../../../../generated/locale_keys.g.dart' show LocaleKeys; + +class WayPickup extends StatelessWidget { + final VoidCallback onTap; + const WayPickup({super.key, required this.onTap}); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.selectWay.tr(context: context) + .toText24(color: AppColors.textColor, isBold: true), + SizedBox( + height: 16.h, + ), + // LocaleKeys.selectDirection.tr(context: context) + // .toText16(color: AppColors.textColor, isBold: true), + // SizedBox( + // height: 12.h, + // ), + Column( + children: [ + SizedBox(height: 12.h), + Column( + mainAxisAlignment: MainAxisAlignment.start, + spacing:8.h, + children: [ + TransportOptionItem( + title: LocaleKeys.oneWay.tr(context: context), + subTitle: LocaleKeys.oneWaySubtitle.tr(context: context), + firstIcon: AppAssets.to_arrow, + middleIcon: AppAssets.to_arrow, + lastIcon: AppAssets.hospital, + shouldFlipIcon: getIt.get().isArabic(), + price: context.read().selectedTransportOption?.priceTotal?.toString()??"", + onTap: () { + context + .read() + .updateDirection( AmbulanceDirection.ONE_WAY); + onTap(); + }, + ), + + TransportOptionItem( + title: LocaleKeys.twoWay.tr(context: context), + subTitle: LocaleKeys.twoWaySubtitle.tr(context: context), + firstIcon: AppAssets.dual_arrow, + middleIcon: AppAssets.dual_arrow, + lastIcon: AppAssets.hospital, + price: ((context.read().selectedTransportOption?.priceTotal??0)*2).toString(), + onTap: () { + context + .read() + .updateDirection( AmbulanceDirection.TWO_WAY); + onTap(); + }, + ), + + // Visibility( + // visible: value == AmbulanceCallingPlace.TO_HOSPITAL, + // child: Selector( + // selector: (context, viewModel) => viewModel.ambulanceDirection, + // builder: (context, directionValue, _) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // SizedBox(height: 16.h), + // LocaleKeys.selectWay.tr(context: context) + // .toText16(color: AppColors.textColor, isBold: true), + // SizedBox(height: 12.h), + // Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Expanded( + // child: Row( + // children: [ + // Radio( + // value: AmbulanceDirection.ONE_WAY, + // groupValue: directionValue, + // onChanged: (AmbulanceDirection? newValue) { + // if (newValue != null) { + // context + // .read() + // .updateDirection(newValue); + // } + // }, + // activeColor: AppColors.primaryRedColor, + // fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + // ), + // LocaleKeys.oneWay.tr(context: context) + // .toText12(color: AppColors.textColor, isBold: true) + // ], + // ).onPress(() { + // context + // .read() + // .updateDirection(AmbulanceDirection.ONE_WAY); + // }), + // ), + // Expanded( + // child: Row( + // children: [ + // Radio( + // value: AmbulanceDirection.TWO_WAY, + // groupValue: directionValue, + // onChanged: (AmbulanceDirection? newValue) { + // if (newValue != null) { + // context + // .read() + // .updateDirection(newValue); + // } + // }, + // activeColor: AppColors.primaryRedColor, + // fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + // ), + // LocaleKeys.twoWay.tr(context: context) + // .toText14(color: AppColors.textColor, isBold: true) + // ], + // ).onPress(() { + // context + // + // .read() + // .updateDirection(AmbulanceDirection.TWO_WAY); + // }), + // ), + // ], + // ), + // ], + // ); + // }, + // ), + ], + ), + ], + ), + // Selector( + // selector: (context, viewModel) => viewModel.callingPlace, + // builder: (context, value, _) { + // return ; + // }, + // ), + SizedBox( + height: 16.h, + ), + // CustomButton( + // text: LocaleKeys.confirm.tr(context: context), + // onPressed: onTap, + // backgroundColor: AppColors.primaryRedColor, + // borderColor: AppColors.primaryRedColor, + // textColor: AppColors.whiteColor, + // iconColor: AppColors.whiteColor, + // ), + ], + ); + } +} \ No newline at end of file diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart b/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart index be1d3800..a99a7bbd 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart @@ -29,7 +29,7 @@ class AmbulanceOptionSelectionBottomSheet extends StatelessWidget { itemBuilder: (_, index) => TransportOptionItem( title: Utils.getTextWRTCurrentLanguage(data[index].text, data[index].textN), subTitle: "", - firstIcon: AppAssets.location_pickup, + firstIcon: AppAssets.ambulance, middleIcon: AppAssets.to_arrow, lastIcon: AppAssets.hospital, price: data[index].priceTotal.toString(), diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart index 56a94e0f..fa4eefe1 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart @@ -1,11 +1,13 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.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/features/emergency_services/models/AmbulanceCallingPlace.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/ambulance_direction.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:provider/provider.dart'; @@ -13,7 +15,7 @@ import 'package:provider/provider.dart'; import '../../../../generated/locale_keys.g.dart' show LocaleKeys; class PickupLocation extends StatelessWidget { - final VoidCallback onTap; + final Function(AmbulanceCallingPlace) onTap; const PickupLocation({super.key, required this.onTap}); @override @@ -26,159 +28,189 @@ class PickupLocation extends StatelessWidget { SizedBox( height: 16.h, ), - LocaleKeys.selectDirection.tr(context: context) - .toText16(color: AppColors.textColor, weight: FontWeight.w600), - SizedBox( - height: 12.h, - ), - Selector( - selector: (context, viewModel) => viewModel.callingPlace, - builder: (context, value, _) { - return Column( + // LocaleKeys.selectDirection.tr(context: context) + // .toText16(color: AppColors.textColor, isBold: true), + // SizedBox( + // height: 12.h, + // ), + Column( + children: [ + SizedBox(height: 12.h), + Column( + mainAxisAlignment: MainAxisAlignment.start, + spacing:8.h, children: [ - SizedBox(height: 12.h), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: Row( - children: [ - Radio( - value: AmbulanceCallingPlace.TO_HOSPITAL, - groupValue: value, - onChanged: (AmbulanceCallingPlace? newValue) { - if (newValue != null) { - context - .read() - .updateCallingPlace(newValue); - } - }, - activeColor: AppColors.primaryRedColor, - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - LocaleKeys.toHospital.tr(context: context) - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress(() { - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); - }), - ), - Expanded( - child: Row( - children: [ - Radio( - value: AmbulanceCallingPlace.FROM_HOSPITAL, - groupValue: value, - onChanged: (AmbulanceCallingPlace? newValue) { - if (newValue != null) { - context - .read() - .updateCallingPlace(newValue); - } - }, - activeColor: AppColors.primaryRedColor, - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - LocaleKeys.fromHospital.tr(context: context) - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress(() { - context - .read() - .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); - }), - ), - ], + TransportOptionItem( + title: LocaleKeys.toHospital.tr(context: context), + subTitle: LocaleKeys.toHospitalSubtitle.tr(context: context), + firstIcon: AppAssets.homeBottomFill, + middleIcon: AppAssets.to_arrow, + lastIcon: AppAssets.hospital, + price: context.read().selectedTransportOption?.priceTotal?.toString()??"", + onTap: () { + context.read() + .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); + onTap(AmbulanceCallingPlace.TO_HOSPITAL); + }, ), - Visibility( - visible: value == AmbulanceCallingPlace.TO_HOSPITAL, - child: Selector( - selector: (context, viewModel) => viewModel.ambulanceDirection, - builder: (context, directionValue, _) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - LocaleKeys.selectWay.tr(context: context) - .toText16(color: AppColors.textColor, weight: FontWeight.w600), - SizedBox(height: 12.h), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded( - child: Row( - children: [ - Radio( - value: AmbulanceDirection.ONE_WAY, - groupValue: directionValue, - onChanged: (AmbulanceDirection? newValue) { - if (newValue != null) { - context - .read() - .updateDirection(newValue); - } - }, - activeColor: AppColors.primaryRedColor, - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - LocaleKeys.oneWay.tr(context: context) - .toText12(color: AppColors.textColor, fontWeight: FontWeight.w500) - ], - ).onPress(() { - context - .read() - .updateDirection(AmbulanceDirection.ONE_WAY); - }), - ), - Expanded( - child: Row( - children: [ - Radio( - value: AmbulanceDirection.TWO_WAY, - groupValue: directionValue, - onChanged: (AmbulanceDirection? newValue) { - if (newValue != null) { - context - .read() - .updateDirection(newValue); - } - }, - activeColor: AppColors.primaryRedColor, - fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), - ), - LocaleKeys.twoWay.tr(context: context) - .toText14(color: AppColors.textColor, weight: FontWeight.w500) - ], - ).onPress(() { - context - .read() - .updateDirection(AmbulanceDirection.TWO_WAY); - }), - ), - ], - ), - ], - ); - }, - ), - ) + + TransportOptionItem( + title: LocaleKeys.fromHospital.tr(context: context), + subTitle: LocaleKeys.fromHospitalSubtitle.tr(context: context), + firstIcon: AppAssets.hospital, + middleIcon: AppAssets.to_arrow, + lastIcon: AppAssets.hospital, + price: context.read().selectedTransportOption?.priceTotal?.toString()??"", + onTap: () { + context.read().updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); + onTap(AmbulanceCallingPlace.FROM_HOSPITAL); + }, + ), + // Expanded( + // child: Row( + // children: [ + // Radio( + // value: AmbulanceCallingPlace.TO_HOSPITAL, + // groupValue: value, + // onChanged: (AmbulanceCallingPlace? newValue) { + // if (newValue != null) { + // context + // .read() + // .updateCallingPlace(newValue); + // } + // }, + // activeColor: AppColors.primaryRedColor, + // fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + // ), + // LocaleKeys.toHospital.tr(context: context) + // .toText14(color: AppColors.textColor, isBold: true) + // ], + // ).onPress(() { + // context + // .read() + // .updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL); + // }), + // ), + // Expanded( + // child: Row( + // children: [ + // Radio( + // value: AmbulanceCallingPlace.FROM_HOSPITAL, + // groupValue: value, + // onChanged: (AmbulanceCallingPlace? newValue) { + // if (newValue != null) { + // context + // .read() + // .updateCallingPlace(newValue); + // } + // }, + // activeColor: AppColors.primaryRedColor, + // fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + // ), + // LocaleKeys.fromHospital.tr(context: context) + // .toText14(color: AppColors.textColor, isBold: true) + // ], + // ).onPress(() { + // context + // .read() + // .updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL); + // }), + // ), + // ], + // ), + // Visibility( + // visible: value == AmbulanceCallingPlace.TO_HOSPITAL, + // child: Selector( + // selector: (context, viewModel) => viewModel.ambulanceDirection, + // builder: (context, directionValue, _) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // SizedBox(height: 16.h), + // LocaleKeys.selectWay.tr(context: context) + // .toText16(color: AppColors.textColor, isBold: true), + // SizedBox(height: 12.h), + // Row( + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Expanded( + // child: Row( + // children: [ + // Radio( + // value: AmbulanceDirection.ONE_WAY, + // groupValue: directionValue, + // onChanged: (AmbulanceDirection? newValue) { + // if (newValue != null) { + // context + // .read() + // .updateDirection(newValue); + // } + // }, + // activeColor: AppColors.primaryRedColor, + // fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + // ), + // LocaleKeys.oneWay.tr(context: context) + // .toText12(color: AppColors.textColor, isBold: true) + // ], + // ).onPress(() { + // context + // .read() + // .updateDirection(AmbulanceDirection.ONE_WAY); + // }), + // ), + // Expanded( + // child: Row( + // children: [ + // Radio( + // value: AmbulanceDirection.TWO_WAY, + // groupValue: directionValue, + // onChanged: (AmbulanceDirection? newValue) { + // if (newValue != null) { + // context + // .read() + // .updateDirection(newValue); + // } + // }, + // activeColor: AppColors.primaryRedColor, + // fillColor: MaterialStateProperty.all(AppColors.primaryRedColor), + // ), + // LocaleKeys.twoWay.tr(context: context) + // .toText14(color: AppColors.textColor, isBold: true) + // ], + // ).onPress(() { + // context + // + // .read() + // .updateDirection(AmbulanceDirection.TWO_WAY); + // }), + // ), + // ], + // ), + // ], + // ); + // }, + // ), ], - ); - }, + ), + ], ), + // Selector( + // selector: (context, viewModel) => viewModel.callingPlace, + // builder: (context, value, _) { + // return ; + // }, + // ), SizedBox( height: 16.h, ), - CustomButton( - text: LocaleKeys.confirm.tr(context: context), - onPressed: onTap, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: AppColors.whiteColor, - iconColor: AppColors.whiteColor, - ), + // CustomButton( + // text: LocaleKeys.confirm.tr(context: context), + // onPressed: onTap, + // backgroundColor: AppColors.primaryRedColor, + // borderColor: AppColors.primaryRedColor, + // textColor: AppColors.whiteColor, + // iconColor: AppColors.whiteColor, + // ), ], ); } diff --git a/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart b/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart index 9a959ebb..81085ed6 100644 --- a/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart +++ b/lib/presentation/emergency_services/call_ambulance/widgets/transport_option_Item.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.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'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; 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/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:provider/provider.dart'; class TransportOptionItem extends StatelessWidget { final String title; @@ -15,6 +18,7 @@ class TransportOptionItem extends StatelessWidget { final String lastIcon; final String price; final VoidCallback onTap; + final bool shouldFlipIcon; const TransportOptionItem( {super.key, @@ -24,6 +28,7 @@ class TransportOptionItem extends StatelessWidget { required this.middleIcon, required this.lastIcon, required this.price, + this.shouldFlipIcon = false, required this.onTap}); @override @@ -39,14 +44,14 @@ class TransportOptionItem extends StatelessWidget { spacing: 16.h, children: [ headerSection(), - titleSection(), + titleSection(context), ], )).onPress((){ onTap(); }); } - titleSection() { + titleSection(BuildContext context) { return Row( children: [ Expanded( @@ -54,14 +59,12 @@ class TransportOptionItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText16( - color: AppColors.textColor, weight: FontWeight.w600), - // subTitle.toText12( - // color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + color: AppColors.textColor, isBold: true), + subTitle.toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_medium, width: 24.h, height: 24.h) + Transform.flip(flipX: getIt.get().isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_medium, width: 24.h, height: 24.h)) ], ); } @@ -70,20 +73,19 @@ class TransportOptionItem extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - // Expanded( - // child: Row( - // children: [ - // buildIcon(firstIcon), - // Utils.buildSvgWithAssets( - // icon: middleIcon, width: 24.h, height: 24.h).paddingAll(8.h), - // buildIcon(lastIcon) - // ], - // ), - // ), - Utils.getPaymentAmountWithSymbol2( - num.tryParse(price) ?? 0.0, - fontSize: 18.f, - letterSpacing:-2 + Expanded( + child: Row( + children: [ + Transform.flip(flipX: getIt.get().isArabic() && shouldFlipIcon, child: buildIcon(firstIcon)), + // Utils.buildSvgWithAssets( + // icon: middleIcon, width: 24.h, height: 24.h).paddingAll(8.h), + // buildIcon(lastIcon) + ], + ), + ), + Visibility( + visible: price.isNotEmpty, + child: Utils.getPaymentAmountWithSymbol2(num.tryParse(price) ?? 0.0, fontSize: 18.f, letterSpacing: -2), ), ], ); @@ -98,7 +100,7 @@ class TransportOptionItem extends StatelessWidget { backgroundColor: AppColors.greyColor, iconColor: AppColors.greyTextColor, borderColor: Colors.transparent, - height: 40.h, + height: 42.h, ); } } diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index 801f6ca2..3d5470a8 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/OrderDisp import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/RRT/rrt_request_type_select.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/WayPickup.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/ambulance_option_selection_bottomsheet.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/pickup_location.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart'; @@ -26,6 +27,8 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:lottie/lottie.dart'; import 'package:provider/provider.dart'; +import '../../features/emergency_services/models/AmbulanceCallingPlace.dart' show AmbulanceCallingPlace; + class EmergencyServicesPage extends StatefulWidget { EmergencyServicesPage({super.key}); @@ -87,12 +90,15 @@ class _EmergencyServicesPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.callAmbulance.tr().toText16(isBold: true, color: AppColors.blackColor), - LocaleKeys.requestAmbulanceInEmergency.tr().toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.requestAmbulanceInEmergency.tr().toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), SizedBox(width: 12.h), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), + ), ], ).onPress(() { if(appState.isAuthenticated) { @@ -127,7 +133,7 @@ class _EmergencyServicesPageState extends State { SizedBox(height: 8.h), LocaleKeys.confirmation.tr().toText28(color: Colors.white, isBold: true), SizedBox(height: 8.h), - LocaleKeys.areYouSureYouWantToCallAmbulance.tr().toText14(color: Colors.white, weight: FontWeight.w500), + LocaleKeys.areYouSureYouWantToCallAmbulance.tr().toText14(color: Colors.white, isBold: true), SizedBox(height: 24.h), CustomButton( text: LocaleKeys.confirm.tr(context: context), @@ -158,8 +164,8 @@ class _EmergencyServicesPageState extends State { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.emergencyServices.tr(), - serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, - serviceDescription: "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.".needTranslation, + serviceHeader: LocaleKeys.emergencyServiceHeader.tr(), + serviceDescription: LocaleKeys.emergencyServiceDescription.tr(), serviceImage: AppAssets.emergencyService), ), ); @@ -183,12 +189,15 @@ class _EmergencyServicesPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.nearester.tr(context: context).toText16(isBold: true, color: AppColors.blackColor), - LocaleKeys.getDetailsOfNearestBranch.tr().toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.getDetailsOfNearestBranch.tr().toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), SizedBox(width: 12.h), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), + Transform.flip( + flipX: appState.isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), + ), ], ).onPress(() { context.read().navigateToNearestERPage(); @@ -211,12 +220,12 @@ class _EmergencyServicesPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.rapidResponseTeam.tr(context: context).toText16(isBold: true, color: AppColors.blackColor), - "Comprehensive medical service for all sorts of urgent and stable cases".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.RRTDDetails.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), SizedBox(width: 12.h), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h)), ], ).onPress(() { if(appState.isAuthenticated) { @@ -251,7 +260,7 @@ class _EmergencyServicesPageState extends State { SizedBox(height: 8.h), LocaleKeys.confirm.tr().toText28(color: Colors.white, isBold: true), SizedBox(height: 8.h), - LocaleKeys.areYouSureYouWantToCallRRT.tr().toText14(color: Colors.white, weight: FontWeight.w500), + LocaleKeys.areYouSureYouWantToCallRRT.tr().toText14(color: Colors.white, isBold: true), SizedBox(height: 24.h), CustomButton( text: LocaleKeys.confirm.tr(context: context), @@ -308,8 +317,8 @@ class _EmergencyServicesPageState extends State { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.emergencyServices.tr(), - serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, - serviceDescription: "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.".needTranslation, + serviceHeader: LocaleKeys.emergencyServiceHeader.tr(), + serviceDescription: LocaleKeys.emergencyServiceDescription.tr(), serviceImage: AppAssets.emergencyService), ), ); @@ -333,12 +342,12 @@ class _EmergencyServicesPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.emergencyCheckIn.tr(context: context).toText16(isBold: true, color: AppColors.blackColor), - LocaleKeys.priorERCheckInToSkipLine.tr().toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.priorERCheckInToSkipLine.tr().toText12(color: AppColors.greyTextColor, isBold: true), ], ), ), SizedBox(width: 12.h), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h)), ], ).onPress(() { if(appState.isAuthenticated) { @@ -373,7 +382,7 @@ class _EmergencyServicesPageState extends State { SizedBox(height: 8.h), LocaleKeys.confirm.tr().toText28(color: Colors.white, isBold: true), SizedBox(height: 8.h), - LocaleKeys.areYouSureYouWantToMakeERCheckIn.tr().toText14(color: Colors.white, weight: FontWeight.w500), + LocaleKeys.areYouSureYouWantToMakeERCheckIn.tr().toText14(color: Colors.white, isBold: true), SizedBox(height: 24.h), CustomButton( text: LocaleKeys.confirm.tr(context: context), @@ -408,8 +417,8 @@ class _EmergencyServicesPageState extends State { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.emergencyServices.tr(), - serviceHeader: "ER Check-in, Faster Than Ever. Call ambulance / Rapid Response Team instantly".needTranslation, - serviceDescription: "In a medical emergency? Our ambulances and rapid response teams are on standby 24/7. Plus, quick ER check-in for faster care.".needTranslation, + serviceHeader: LocaleKeys.emergencyServiceHeader.tr(), + serviceDescription: LocaleKeys.emergencyServiceDescription.tr(), serviceImage: AppAssets.emergencyService), ), ); @@ -440,7 +449,42 @@ class _EmergencyServicesPageState extends State { openTranportationSelectionBottomSheet(context); }), context, - child: PickupLocation(onTap: () { + child: PickupLocation(onTap: (fromLocation) { + Navigator.of(context).pop(); + if(fromLocation == AmbulanceCallingPlace.TO_HOSPITAL){ + openNumberWayOfSelectionBottomSheet(context); + return; + } + context.read().flushSearchPredictions(); + context.read().navigateTOAmbulancePage(); + }), + isFullScreen: false, + isCloseButtonVisible: true, + hasBottomPadding: false, + backgroundColor: AppColors.bottomSheetBgColor, + callBackFunc: () {}, + ); + } + + openNumberWayOfSelectionBottomSheet(BuildContext context) { + showCommonBottomSheetWithoutHeight( + onCloseClicked: () { + context.read().flushPickupInformation(); + }, + titleWidget: Transform.flip( + flipX: emergencyServicesViewModel.isArabic, + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_back, + iconColor: Color(0xff2B353E), + fit: BoxFit.contain, + ), + ).onPress(() { + context.read().flushPickupInformation(); + Navigator.pop(context); + openPickupDetailsBottomSheet(context); + }), + context, + child: WayPickup(onTap: () { Navigator.of(context).pop(); context.read().flushSearchPredictions(); context.read().navigateTOAmbulancePage(); @@ -459,7 +503,7 @@ class _EmergencyServicesPageState extends State { title: LocaleKeys.transportOptions.tr(), context, child: SizedBox( - height: 400.h, + height: 500.h, child: AmbulanceOptionSelectionBottomSheet(onTap: () { Navigator.of(context).pop(); openPickupDetailsBottomSheet(context); diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart index 7f206c17..bb9f3ffd 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_home.dart @@ -57,7 +57,7 @@ class ErOnlineCheckinHome extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.onlineCheckIn.tr().toText18(color: AppColors.textColor, isBold: true), - LocaleKeys.erOnlineCheckInDescription.tr().toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.erOnlineCheckInDescription.tr().toText14(color: AppColors.greyTextColor, isBold: true), ], ), ), @@ -136,7 +136,7 @@ class ErOnlineCheckinHome extends StatelessWidget { borderColor: emergencyServicesViewModel.patientHasAdvanceERBalance ? AppColors.alertColor : AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart index eee31da7..913fe562 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_details_page.dart @@ -117,7 +117,7 @@ class ErOnlineCheckinPaymentDetailsPage extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded(child: "".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), + Expanded(child: "".toText12(isBold: true, color: AppColors.greyTextColor)), LocaleKeys.vatWithAmount .tr(namedArgs: {"amount": emergencyServicesViewModel.erOnlineCheckInPaymentDetailsResponse.patientTaxAmount.toString()}) .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1), @@ -154,7 +154,7 @@ class ErOnlineCheckinPaymentDetailsPage extends StatelessWidget { borderColor: AppColors.infoColor.withOpacity(0.01), textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 56.h, diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart index bc0e7344..b47ccc33 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart @@ -230,7 +230,7 @@ class _ErOnlineCheckinPaymentPageState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), + LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true).paddingSymmetrical(24.h, 0.h), CustomButton( text: LocaleKeys.updateInsurance.tr(context: context), onPressed: () { @@ -244,7 +244,7 @@ class _ErOnlineCheckinPaymentPageState extends State borderColor: AppColors.secondaryLightRedBorderColor, textColor: AppColors.whiteColor, fontSize: 10, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 8, padding: EdgeInsets.fromLTRB(15, 0, 15, 0), height: 30.h, diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_select_checkin_bottom_sheet.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_select_checkin_bottom_sheet.dart index 3b2dc8ae..aec10522 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_select_checkin_bottom_sheet.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_select_checkin_bottom_sheet.dart @@ -60,20 +60,20 @@ class ErOnlineCheckinSelectCheckinBottomSheet extends StatelessWidget { } }); }), - SizedBox(height: 16.h), - checkInOptionCard( - AppAssets.checkin_nfc_icon, - LocaleKeys.nfcNearFieldCommunication.tr(context: context), - LocaleKeys.scanPhoneViaNFC.tr(context: context), - ).onPress(() { - Future.delayed(const Duration(milliseconds: 500), () { - showNfcReader(context, onNcfScan: (String nfcId) { - Future.delayed(const Duration(milliseconds: 100), () { - sendCheckInRequest(nfcId, context); - }); - }, onCancel: () {}); - }); - }), + // SizedBox(height: 16.h), + // checkInOptionCard( + // AppAssets.checkin_nfc_icon, + // LocaleKeys.nfcNearFieldCommunication.tr(context: context), + // LocaleKeys.scanPhoneViaNFC.tr(context: context), + // ).onPress(() { + // Future.delayed(const Duration(milliseconds: 500), () { + // showNfcReader(context, onNcfScan: (String nfcId) { + // Future.delayed(const Duration(milliseconds: 100), () { + // sendCheckInRequest(nfcId, context); + // }); + // }, onCancel: () {}); + // }); + // }), SizedBox(height: 16.h), checkInOptionCard( AppAssets.checkin_qr_icon, @@ -109,7 +109,7 @@ class ErOnlineCheckinSelectCheckinBottomSheet extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ title.toText16(isBold: true, color: AppColors.textColor), - subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + subTitle.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), diff --git a/lib/presentation/emergency_services/history/er_history_listing.dart b/lib/presentation/emergency_services/history/er_history_listing.dart index a60d4556..d72200c5 100644 --- a/lib/presentation/emergency_services/history/er_history_listing.dart +++ b/lib/presentation/emergency_services/history/er_history_listing.dart @@ -23,7 +23,6 @@ class ErHistoryListing extends StatelessWidget { return Scaffold( body: Column( children: [ - Expanded( child: CollapsingListView( title: LocaleKeys.history.tr(context: context), @@ -31,15 +30,12 @@ class ErHistoryListing extends StatelessWidget { physics: NeverScrollableScrollPhysics(), child: Column( children: [ - Selector( selector: (context, vm) => (vm.orderDisplayList, vm.historyLoading), builder: (context, data, _) { - return Column( children: [ orderChips(context, data.$2, data.$1), - Visibility( visible:data.$1.isNotEmpty == true, child: ListView.builder( diff --git a/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart b/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart index e1ebc83c..67bdcbd8 100644 --- a/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart +++ b/lib/presentation/emergency_services/history/widget/ambulance_history_item.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; import '../../../../core/utils/utils.dart'; @@ -37,11 +38,11 @@ class AmbulanceHistoryItem extends StatelessWidget { spacing: 8.h, children: [ RequestStatus(status: order.statusId ?? 0), - "Req ID: ${order.iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Req ID: ${order.iD}".toText16(color: AppColors.textColor, isBold: true, isEnglishOnly: true), Row( spacing: 4.w, children: [ - chip( Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(order.time)), AppAssets.calendar, AppColors.blackBgColor), + chip(Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(order.time)), AppAssets.calendar, AppColors.blackBgColor), chip(LocaleKeys.ambulancerequest.tr(context: context), AppAssets.ambulance, AppColors.blackBgColor), ], ), @@ -71,11 +72,15 @@ class AmbulanceHistoryItem extends StatelessWidget { } chip(String title, String iconString, Color iconColor) { - return AppCustomChipWidget( - labelText: title, - icon: iconString, - iconColor: iconColor, - iconSize: 12.h, + return Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: title, + icon: iconString, + iconColor: iconColor, + iconSize: 12.h, + isEnglishOnly: true, + ), ); } diff --git a/lib/presentation/emergency_services/history/widget/rrt_item.dart b/lib/presentation/emergency_services/history/widget/rrt_item.dart index c8787791..c85f12a5 100644 --- a/lib/presentation/emergency_services/history/widget/rrt_item.dart +++ b/lib/presentation/emergency_services/history/widget/rrt_item.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; import '../../../../core/utils/utils.dart'; @@ -37,11 +38,11 @@ class RRTItem extends StatelessWidget { spacing: 8.h, children: [ RequestStatus(status: order.statusId ?? 0), - "Req ID: ${order.iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Req ID: ${order.iD}".toText16(color: AppColors.textColor, isBold: true, isEnglishOnly: true), Row( spacing: 4.w, children: [ - chip( Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(order.time)), AppAssets.calendar, AppColors.blackBgColor), + chip(Utils.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(order.time)), AppAssets.calendar, AppColors.blackBgColor), chip(LocaleKeys.rapidResponseTeam.tr(context: context), AppAssets.ic_rrt_vehicle, AppColors.blackBgColor), ], ), @@ -65,11 +66,15 @@ class RRTItem extends StatelessWidget { } chip(String title, String iconString, Color iconColor) { - return AppCustomChipWidget( - labelText: title, - icon: iconString, - iconColor: iconColor, - iconSize: 12.h, + return Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: title, + icon: iconString, + iconColor: iconColor, + iconSize: 12.h, + isEnglishOnly: true, + ), ); } diff --git a/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart b/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart index db45654a..4b66abd9 100644 --- a/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart +++ b/lib/presentation/emergency_services/widgets/location_input_bottom_sheet.dart @@ -4,6 +4,8 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart' show AnimationConfiguration, SlideAnimation, FadeInAnimation; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/debouncer.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -49,6 +51,7 @@ class LocationInputBottomSheet extends StatelessWidget { vertical: 10.h, horizontal: 15.h, ), + fontFamily: "Poppins", ), SizedBox(height: 24.h), Selector>( @@ -82,16 +85,18 @@ class LocationInputBottomSheet extends StatelessWidget { Expanded( child: prediction.description.toText14( color: AppColors.textColor, - weight: FontWeight.w500), + weight: FontWeight.w600), ), - - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon_small, - iconColor: AppColors.blackColor, + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, width: 20.h, height: 15.h, fit: BoxFit.contain, - ), + ), + ) ], ).paddingAll(24.h),), ), diff --git a/lib/presentation/emergency_services/widgets/nearestERItem.dart b/lib/presentation/emergency_services/widgets/nearestERItem.dart index 0c4a5a6a..5579fd75 100644 --- a/lib/presentation/emergency_services/widgets/nearestERItem.dart +++ b/lib/presentation/emergency_services/widgets/nearestERItem.dart @@ -13,6 +13,8 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; + class NearestERItem extends StatelessWidget { final ProjectAvgERWaitingTime nearestERItem; final bool isLoading; @@ -68,20 +70,28 @@ class NearestERItem extends StatelessWidget { Row( spacing: 8.h, children: [ - AppCustomChipWidget( - labelText: "${nearestERItem.distanceInKilometers} km", - icon: AppAssets.location, - iconHasColor: false, - labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h), - padding: EdgeInsets.all(8.h), - ).toShimmer2(isShow: isLoading), - AppCustomChipWidget( - labelText: "Expected waiting time: ${nearestERItem.getTime()} mins", - icon: AppAssets.waiting_time_clock, - iconHasColor: false, - labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h), - padding: EdgeInsets.all(8.h), - ).toShimmer2(isShow: isLoading), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: "${nearestERItem.distanceInKilometers} km", + icon: AppAssets.location, + iconHasColor: false, + labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h), + padding: EdgeInsets.all(8.h), + isEnglishOnly: true, + ).toShimmer2(isShow: isLoading), + ), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: "Expected waiting time: ${nearestERItem.getTime()} mins", + icon: AppAssets.waiting_time_clock, + iconHasColor: false, + labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h), + padding: EdgeInsets.all(8.h), + isEnglishOnly: true, + ).toShimmer2(isShow: isLoading), + ), ], ), SizedBox(height: 16.h), @@ -101,7 +111,7 @@ class NearestERItem extends StatelessWidget { iconColor: AppColors.primaryRedColor, height: 40.h, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, ).toShimmer2(isShow: isLoading), ), SizedBox(width: 8.h), diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart index 2a3da660..8e6230e8 100644 --- a/lib/presentation/habib_wallet/habib_wallet_page.dart +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -74,17 +74,18 @@ class _HabibWalletState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ "${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}".toText19(isBold: true, color: Colors.white), - "MRN: ${_appState.getAuthenticatedUser()!.patientId!}".toText14(weight: FontWeight.w500, color: AppColors.greyTextColor), + "MRN: ${_appState.getAuthenticatedUser()!.patientId!}".toText14(isBold: true, color: AppColors.greyTextColor, isEnglishOnly: true), ], ).expanded, Utils.buildSvgWithAssets(icon: AppAssets.habiblogo, width: 24.h, height: 24.h, applyThemeColor: false), ], ), Spacer(), - LocaleKeys.balanceAmount.tr(context: context).toText14(weight: FontWeight.w500, color: Colors.white), + LocaleKeys.balanceAmount.tr(context: context).toText14(isBold: true, color: Colors.white), SizedBox(height: 4.h), Consumer(builder: (context, habibWalletVM, child) { - return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, textColor: Colors.white, iconColor: Colors.white, iconSize: 16, isExpanded: false) + return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)), + textColor: Colors.white, iconColor: Colors.white, iconSize: 16, isExpanded: false) .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 24.h); }), ], @@ -103,7 +104,7 @@ class _HabibWalletState extends State { textColor: Colors.white, text: LocaleKeys.recharge.tr(context: context), borderWidth: 0.w, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderColor: Colors.transparent, padding: EdgeInsets.fromLTRB(4, 0, 12, 0), fontSize: 14.f, @@ -137,8 +138,9 @@ class _HabibWalletState extends State { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded(child: habibWalletVM.habibWalletBalanceList[index].projectDescription!.toText16(weight: FontWeight.w500, color: AppColors.textColor)), - Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletBalanceList[index].patientAdvanceBalanceAmount!, textColor: AppColors.textColor, iconColor: AppColors.textColor, iconSize: 18.h, isExpanded: false, fontSize: 28.f, fontWeight: FontWeight.w500), + Expanded(child: habibWalletVM.habibWalletBalanceList[index].projectDescription!.toText16(isBold: true, color: AppColors.textColor)), + Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletBalanceList[index].patientAdvanceBalanceAmount!)), + textColor: AppColors.textColor, iconColor: AppColors.textColor, iconSize: 18.h, isExpanded: false, fontSize: 28.f, fontWeight: FontWeight.w600,), ], ).paddingSymmetrical(0, 12.h); }, diff --git a/lib/presentation/habib_wallet/recharge_wallet_page.dart b/lib/presentation/habib_wallet/recharge_wallet_page.dart index ef1642eb..c671152f 100644 --- a/lib/presentation/habib_wallet/recharge_wallet_page.dart +++ b/lib/presentation/habib_wallet/recharge_wallet_page.dart @@ -23,6 +23,8 @@ import 'package:provider/provider.dart'; import 'widgets/select-medical_file.dart'; +import 'dart:ui' as ui; + class RechargeWalletPage extends StatefulWidget { const RechargeWalletPage({super.key}); @@ -81,7 +83,7 @@ class _RechargeWalletPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ //TODO: Check with hussain to show AED or SAR - LocaleKeys.amount.tr(context: context).toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.amount.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), Spacer(), Row( crossAxisAlignment: CrossAxisAlignment.end, @@ -98,7 +100,7 @@ class _RechargeWalletPageState extends State { isBorderAllowed: false, isAllowLeadingIcon: true, autoFocus: true, - fontSize: 40.f, + fontSize: 25.f, padding: EdgeInsets.symmetric(horizontal: 8.h, vertical: 0.h), focusNode: textFocusNode, isWalletAmountInput: true, @@ -108,7 +110,7 @@ class _RechargeWalletPageState extends State { ), ), const Spacer(), - LocaleKeys.sar.tr(context: context).toText20(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.sar.tr(context: context).toText20(color: AppColors.greyTextColor, isBold: true), ], ), ], @@ -138,9 +140,9 @@ class _RechargeWalletPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (habibWalletVM.getSelectedRechargeTypeValue()).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + (habibWalletVM.getSelectedRechargeTypeValue()).toText12(color: AppColors.greyTextColor, isBold: true), "${LocaleKeys.medicalFile.tr(context: context)}: ${habibWalletVM.fileNumber}" - .toText14(color: AppColors.textColor, weight: FontWeight.w500, letterSpacing: -0.2), + .toText14(color: AppColors.textColor, isBold: true, letterSpacing: -0.2, isEnglishOnly: true), ], ), ], @@ -153,12 +155,12 @@ class _RechargeWalletPageState extends State { titleWidget: Consumer(builder: (context, habibWalletVM, child) { return habibWalletVM.currentIndex != 0 ? IconButton( - icon: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 24.h, height: 24.h), + icon: Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 24.h, height: 24.h)), padding: EdgeInsets.zero, onPressed: () => habibWalletVM.setCurrentIndex(0), highlightColor: Colors.transparent, ) - : LocaleKeys.medicalFile.tr(context: context).toText20(weight: FontWeight.w600); + : LocaleKeys.medicalFile.tr(context: context).toText20(isBold: true); }), child: Consumer(builder: (context, habibWalletVM, child) { return MultiPageBottomSheet(); }), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true); @@ -176,11 +178,11 @@ class _RechargeWalletPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.hospital.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.hospital.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true), SizedBox( width: MediaQuery.of(context).size.width * 0.55, child: (habibWalletVM.selectedHospital != null ? habibWalletVM.selectedHospital!.name : LocaleKeys.selectHospital.tr(context: context))! - .toText14(color: AppColors.textColor, weight: FontWeight.w500), + .toText14(color: AppColors.textColor, isBold: true), ), ], ), @@ -202,8 +204,8 @@ class _RechargeWalletPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), - "${appState.getAuthenticatedUser()!.emailAddress}".toText14(color: AppColors.textColor, weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true), + "${appState.getAuthenticatedUser()!.emailAddress}".toText14(color: AppColors.textColor, isBold: true, letterSpacing: -0.2, isEnglishOnly: true), ], ), ], @@ -250,7 +252,7 @@ class _RechargeWalletPageState extends State { if (amountTextController.text.isEmpty) { showCommonBottomSheetWithoutHeight( context, - child: Utils.getErrorWidget(loadingText: "Please enter amount to continue."), + child: Utils.getErrorWidget(loadingText: LocaleKeys.enterAmount.tr(context: context)), callBackFunc: () { textFocusNode.requestFocus(); }, @@ -260,7 +262,7 @@ class _RechargeWalletPageState extends State { } else if (habibWalletVM.selectedHospital == null) { showCommonBottomSheetWithoutHeight( context, - child: Utils.getErrorWidget(loadingText: "Please select hospital to continue."), + child: Utils.getErrorWidget(loadingText: LocaleKeys.selectHospitalForAdvancePayment.tr(context: context)), callBackFunc: () { textFocusNode.requestFocus(); }, @@ -268,7 +270,7 @@ class _RechargeWalletPageState extends State { isCloseButtonVisible: true, ); } else { - habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text)); + habibWalletVM.setWalletRechargeAmount(num.parse(amountTextController.text.replaceAll(',', ''))); habibWalletVM.setNotesText(notesTextController.text); // habibWalletVM.setDepositorDetails(appState.getAuthenticatedUser()!.patientId.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", // appState.getAuthenticatedUser()!.mobileNumber!); @@ -283,7 +285,7 @@ class _RechargeWalletPageState extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index 9efcb01b..151b3f21 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -62,7 +62,7 @@ class _WalletPaymentConfirmPageState extends State { children: [ Expanded( child: CollapsingListView( - title: "Select Payment Method", + title: LocaleKeys.selectPaymentOption.tr(context: context), child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -169,9 +169,17 @@ class _WalletPaymentConfirmPageState extends State { spacing: 4.h, runSpacing: 4.h, children: [ - AppCustomChipWidget(labelText: "${LocaleKeys.fileno.tr(context: context)}.: ${habibWalletVM.fileNumber}"), - AppCustomChipWidget(labelText: "${LocaleKeys.mobileNumber.tr(context: context)}: ${habibWalletVM.mobileNumber}"), - AppCustomChipWidget(labelText: "${habibWalletVM.selectedHospital!.name}"), + AppCustomChipWidget( + labelText: "${LocaleKeys.fileno.tr(context: context)}.: ${habibWalletVM.fileNumber}", + isEnglishOnly: true, + ), + AppCustomChipWidget( + labelText: "${LocaleKeys.mobileNumber.tr(context: context)}: ${habibWalletVM.mobileNumber}", + isEnglishOnly: true, + ), + AppCustomChipWidget( + labelText: "${habibWalletVM.selectedHospital!.name}", + ), ], ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 16.h), @@ -181,7 +189,9 @@ class _WalletPaymentConfirmPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.totalAmountToPay.tr(context: context).toText16(isBold: true), - Utils.getPaymentAmountWithSymbol(habibWalletVM.walletRechargeAmount.toString().toText24(isBold: true), AppColors.blackColor, 15.h, isSaudiCurrency: true), + Utils.getPaymentAmountWithSymbol( + NumberFormat.decimalPattern().format(habibWalletVM.walletRechargeAmount).toString().toText24(isBold: true, isEnglishOnly: true), AppColors.blackColor, 15.h, + isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 12.h), @@ -320,6 +330,7 @@ class _WalletPaymentConfirmPageState extends State { }, isFullScreen: false, isCloseButtonVisible: true, + isAutoDismiss: true ); }, onError: (err) { diff --git a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart index 67e174a3..fe5172e6 100644 --- a/lib/presentation/habib_wallet/widgets/hospital_list_item.dart +++ b/lib/presentation/habib_wallet/widgets/hospital_list_item.dart @@ -1,12 +1,18 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; 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/my_appointments/models/resp_models/hospital_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/chip/app_custom_chip_widget.dart'; + +import 'dart:ui' as ui; class HospitalListItemAdvancePayment extends StatelessWidget { final HospitalsModel hospitalModel; @@ -32,7 +38,7 @@ class HospitalListItemAdvancePayment extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, - children: [hospitalName], + children: [hospitalName, distanceInfo], ), ), Transform.flip( @@ -68,4 +74,45 @@ class HospitalListItemAdvancePayment extends StatelessWidget { ) ], ); + + Widget get distanceInfo => Row( + spacing: 4.w, + children: [ + Visibility( + visible: (hospitalModel.distanceInKilometers != 0), + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: "${hospitalModel.distanceInKilometers ?? ""} KMs", + deleteIcon: AppAssets.location_red, + deleteIconSize: Size(9, 12), + backgroundColor: AppColors.secondaryLightRedColor, + textColor: AppColors.errorColor, + isEnglishOnly: true, + ), + ), + ), + Visibility( + visible: (hospitalModel.distanceInKilometers == 0), + child: Row( + children: [ + AppCustomChipWidget( + labelText: LocaleKeys.noDistance.tr(), + textColor: AppColors.blackColor, + ), + // SizedBox( + // width: 8.h, + // ) + ], + )), + // Visibility( + // visible: !isLocationEnabled, + // child: AppCustomChipWidget( + // labelText: "Location turned off", + // deleteIcon: AppAssets.location_unavailable, + // deleteIconSize: Size(9.w, 12.h), + // textColor: AppColors.blackColor, + // )), + ], + ); } diff --git a/lib/presentation/habib_wallet/widgets/select-medical_file.dart b/lib/presentation/habib_wallet/widgets/select-medical_file.dart index 6fed6522..0a97988c 100644 --- a/lib/presentation/habib_wallet/widgets/select-medical_file.dart +++ b/lib/presentation/habib_wallet/widgets/select-medical_file.dart @@ -78,9 +78,10 @@ class _MultiPageBottomSheetState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Enter File Number".toText20(weight: FontWeight.w600), + LocaleKeys.pleaseEnterAFileNumber.tr(context: context).toText20(isBold: true), SizedBox(height: 12.h), TextInputWidget( + fontFamily: "Poppins", labelText: LocaleKeys.fileNumber.tr(), hintText: "xxxxxxxxx", controller: fileNumberEditingController, @@ -139,11 +140,16 @@ class _MultiPageBottomSheetState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.myMedicalFile.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500), - "${LocaleKeys.fileno.tr(context: context)}: ${appState.getAuthenticatedUser()!.patientId}".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.myMedicalFile.tr(context: context).toText16(color: AppColors.textColor, isBold: true), + Row( + children: [ + "${LocaleKeys.fileno.tr(context: context)}: ".toText12(color: AppColors.greyTextColor, isBold: true), + appState.getAuthenticatedUser()!.patientId.toString().toText12(color: AppColors.greyTextColor, isBold: true, isEnglishOnly: true), + ], + ), ], ), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), ], ).paddingAll(16.h), ).onPress(() { @@ -166,11 +172,11 @@ class _MultiPageBottomSheetState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.familyTitle.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500), - "Select a medical file from your family".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.familyTitle.tr(context: context).toText16(color: AppColors.textColor, isBold: true), + LocaleKeys.selectMedicalFileFromFamily.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), ], ).paddingAll(16.h), ).onPress(() { @@ -204,11 +210,11 @@ class _MultiPageBottomSheetState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.otherAccount.tr(context: context).toText16(color: AppColors.textColor, weight: FontWeight.w500), - "Any active medical file from HMG".toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), + LocaleKeys.otherAccount.tr(context: context).toText16(color: AppColors.textColor, isBold: true), + LocaleKeys.anyFileFromHMG.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true), ], ), - Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h), + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, iconColor: AppColors.textColor, width: 15.h, height: 15.h)), ], ).paddingAll(16.h), ).onPress(() { diff --git a/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart b/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart index da374a04..e93b9cef 100644 --- a/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart +++ b/lib/presentation/habib_wallet/widgets/select_hospital_bottom_sheet.dart @@ -1,85 +1,215 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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'; +import 'package:hmg_patient_app_new/core/location_util.dart'; +import 'package:hmg_patient_app_new/core/utils/doctor_response_mapper.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +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/habib_wallet/habib_wallet_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/widgets/hospital_list_item.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; -class SelectHospitalBottomSheet extends StatelessWidget { +class SelectHospitalBottomSheet extends StatefulWidget { + const SelectHospitalBottomSheet({super.key}); + + @override + State createState() => _SelectHospitalBottomSheetState(); +} + +class _SelectHospitalBottomSheetState extends State { late HabibWalletViewModel habibWalletVM; final TextEditingController searchText = TextEditingController(); + bool sortByLocation = false; + bool isLoading = false; + List displayList = []; - SelectHospitalBottomSheet({super.key}); + @override + void initState() { + super.initState(); + final appState = getIt.get(); + sortByLocation = (appState.userLat != 0.0) && (appState.userLong != 0.0); + WidgetsBinding.instance.addPostFrameCallback((_) { + _updateDisplayList(); + }); + } + + void _updateDisplayList() { + final appState = getIt.get(); + final hospitals = List.from(habibWalletVM.advancePaymentHospitals); + + if (sortByLocation && appState.userLat != 0.0 && appState.userLong != 0.0) { + hospitals.sort((a, b) { + final distA = (a.latitude != null && a.longitude != null) + ? DoctorMapper.calculateDistance(appState.userLat, appState.userLong, double.parse(a.latitude!), double.parse(a.longitude!)) + : double.infinity; + final distB = (b.latitude != null && b.longitude != null) + ? DoctorMapper.calculateDistance(appState.userLat, appState.userLong, double.parse(b.latitude!), double.parse(b.longitude!)) + : double.infinity; + return distA.compareTo(distB); + }); + } + + setState(() { + displayList = hospitals; + isLoading = false; + }); + } + + void _refreshHospitalListAfterApi() { + void listener() { + if (habibWalletVM.advancePaymentHospitals.isNotEmpty) { + habibWalletVM.removeListener(listener); + _updateDisplayList(); + } + } + habibWalletVM.addListener(listener); + habibWalletVM.getProjectsList(); + } + + void _handleSortByLocationToggle(bool value) { + if (value) { + final locationUtils = getIt.get(); + locationUtils.getLocation( + isShowConfirmDialog: true, + onSuccess: (latLng) { + setState(() { + sortByLocation = true; + isLoading = true; + }); + _refreshHospitalListAfterApi(); + }, + onFailure: () { + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: context), + context, + child: Utils.getWarningWidget( + loadingText: LocaleKeys.giveLocationPermissionForNearestList.tr(context: context), + isShowActionButtons: true, + onCancelTap: () { + Navigator.of(context).pop(); + }, + onConfirmTap: () async { + Navigator.of(context).pop(); + openAppSettings(); + }), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + setState(() { + sortByLocation = false; + }); + }, + onLocationDeniedForever: () { + showCommonBottomSheetWithoutHeight( + title: LocaleKeys.notice.tr(context: context), + context, + child: Utils.getWarningWidget( + loadingText: LocaleKeys.giveLocationPermissionForNearestList.tr(context: context), + isShowActionButtons: true, + onCancelTap: () { + Navigator.of(context).pop(); + }, + onConfirmTap: () async { + Navigator.of(context).pop(); + openAppSettings(); + }), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + setState(() { + sortByLocation = false; + }); + }, + ); + } else { + final appState = getIt.get(); + appState.resetLocation(); + setState(() { + sortByLocation = false; + isLoading = true; + }); + _refreshHospitalListAfterApi(); + } + } @override Widget build(BuildContext context) { habibWalletVM = Provider.of(context, listen: false); + if (displayList.isEmpty && habibWalletVM.advancePaymentHospitals.isNotEmpty) { + displayList = List.from(habibWalletVM.advancePaymentHospitals); + } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Text( - // LocaleKeys.selectHospital.tr(), - // style: TextStyle( - // fontSize: 21, - // fontWeight: FontWeight.w600, - // color: AppColors.blackColor, - // ), - // ), Text( LocaleKeys.selectHospitalForAdvancePayment.tr(context: context), style: TextStyle( fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.greyTextColor, ), ), SizedBox(height: 16.h), - // TextInputWidget( - // labelText: LocaleKeys.search.tr(), - // hintText: "Search Hospital".tr(), - // controller: searchText, - // onChange: (value) { - // // appointmentsViewModel.filterHospitalListByString(value, regionalViewModel.selectedRegionId , regionalViewModel.selectedFacilityType == - // // FacilitySelection.HMG.name); - // }, - // isEnable: true, - // prefix: null, - // autoFocus: false, - // isBorderAllowed: false, - // keyboardType: TextInputType.text, - // isAllowLeadingIcon: true, - // selectionType: SelectionTypeEnum.search, - // padding: EdgeInsets.symmetric( - // vertical: ResponsiveExtension(10).h, - // horizontal: ResponsiveExtension(15).h, - // ), - // ), - // SizedBox(height: 24.h), - // TypeSelectionWidget( - // hmcCount: "0", - // hmgCount: "0", - // ), - // SizedBox(height: 21.h), - SizedBox( + Padding( + padding: EdgeInsets.symmetric(horizontal: 4.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.location, iconColor: AppColors.greyTextColor, width: 18.h, height: 18.h), + SizedBox(width: 8.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.sortByLocation.tr(context: context).toText14(isBold: true), + LocaleKeys.sortByNearestLocation.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true), + ], + ), + ], + ), + Switch( + value: sortByLocation, + onChanged: _handleSortByLocationToggle, + activeThumbColor: AppColors.successColor, + activeTrackColor: AppColors.successColor.withValues(alpha: 0.15), + ), + ], + ), + ), + SizedBox(height: 8.h), + isLoading + ? SizedBox( + height: MediaQuery.sizeOf(context).height * .4, + child: Center(child: Utils.getLoadingWidget()), + ) + : SizedBox( height: MediaQuery.sizeOf(context).height * .4, child: ListView.separated( itemBuilder: (_, index) { return HospitalListItemAdvancePayment( - hospitalModel: habibWalletVM.advancePaymentHospitals[index], - isLocationEnabled: false, + hospitalModel: displayList[index], + isLocationEnabled: sortByLocation, ).onPress(() { - habibWalletVM.setSelectedHospital(habibWalletVM.advancePaymentHospitals[index]); + habibWalletVM.setSelectedHospital(displayList[index]); Navigator.of(context).pop(); }); }, separatorBuilder: (_, __) => SizedBox( height: 16.h, ), - itemCount: habibWalletVM.advancePaymentHospitals.length), + itemCount: displayList.length), ) ], ); diff --git a/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart b/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart index 8cccf031..3d8a60ee 100644 --- a/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart +++ b/lib/presentation/health_calculators_and_converts/health_calculator_detailed_page.dart @@ -72,7 +72,7 @@ class _HealthCalculatorDetailedPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - LocaleKeys.generalHealth.tr().toText16(weight: FontWeight.w600), - LocaleKeys.relatedToBMICalories.tr().toText12(fontWeight: FontWeight.w500, color: Color(0xFF8F9AA3)) + LocaleKeys.generalHealth.tr().toText16(isBold: true), + LocaleKeys.relatedToBMICalories.tr().toText12(isBold: true, color: Color(0xFF8F9AA3)) ], ), ), SizedBox( width: 12.w, ), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor)), ], ).paddingAll(16.w)) .onPress(() { @@ -80,13 +83,15 @@ class _HealthCalculatorsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - LocaleKeys.womensHealth.tr().toText16(weight: FontWeight.w600), - LocaleKeys.relatedToPeriodsOvulation.tr().toText12(fontWeight: FontWeight.w500, color: Color(0xFF8F9AA3)) + LocaleKeys.womensHealth.tr().toText16(isBold: true), + LocaleKeys.relatedToPeriodsOvulation.tr().toText12(isBold: true, color: Color(0xFF8F9AA3)) ], ), ), SizedBox(width: 12.w), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor), + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, width: 24.w, height: 24.h, fit: BoxFit.contain, iconColor: AppColors.textColor)), ], ).paddingAll(16.w)) .onPress(() { @@ -113,9 +118,9 @@ class _HealthCalculatorsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - LocaleKeys.bloodSugar.tr().toText16(weight: FontWeight.w600), + LocaleKeys.bloodSugar.tr().toText16(isBold: true), LocaleKeys.trackYourGlucoseLevels.tr().toText12( - fontWeight: FontWeight.w500, + isBold: true, color: Color(0xFF8F9AA3), ) ], @@ -147,9 +152,9 @@ class _HealthCalculatorsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - LocaleKeys.bloodCholesterol.tr().toText16(weight: FontWeight.w600), + LocaleKeys.bloodCholesterol.tr().toText16(isBold: true), LocaleKeys.monitorCholesterolLevels.tr() - .toText12(fontWeight: FontWeight.w500, color: Color(0xFF8F9AA3)) + .toText12(isBold: true, color: Color(0xFF8F9AA3)) ], ), ), @@ -177,9 +182,9 @@ class _HealthCalculatorsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - LocaleKeys.triglyceridesFatBlood.tr().toText16(weight: FontWeight.w600), + LocaleKeys.triglyceridesFatBlood.tr().toText16(isBold: true), LocaleKeys.understandTriglyceridesImpact.tr() - .toText12(fontWeight: FontWeight.w500, color: Color(0xFF8F9AA3)) + .toText12(isBold: true, color: Color(0xFF8F9AA3)) ], ), ), diff --git a/lib/presentation/health_calculators_and_converts/widgets/bf.dart b/lib/presentation/health_calculators_and_converts/widgets/bf.dart index 481bfcbf..d71566a2 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/bf.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/bf.dart @@ -94,8 +94,8 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.selectGender.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedGender.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.selectGender.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedGender.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -140,7 +140,7 @@ class _BodyFatWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -173,7 +173,7 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Height".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -208,8 +208,8 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -249,7 +249,7 @@ class _BodyFatWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -285,7 +285,7 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Neck".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Neck".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -325,8 +325,8 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.unit.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedNeckUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedNeckUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -366,7 +366,7 @@ class _BodyFatWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -402,7 +402,7 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Waist".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Waist".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -442,8 +442,8 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedWaistUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedWaistUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -483,7 +483,7 @@ class _BodyFatWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -519,7 +519,7 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Hip".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Hip".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -559,8 +559,8 @@ class _BodyFatWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.unit.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedHipUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedHipUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -600,7 +600,7 @@ class _BodyFatWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -631,7 +631,7 @@ class _BodyFatWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Estimate the total body fat based on the size.".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + child: "Estimate the total body fat based on the size.".toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart index e3bc7ff7..53bdf1bf 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart @@ -121,7 +121,7 @@ class _BloodCholesterolWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Convert blood cholesterol values between mg/dL and mmol/L. ".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + child: "Convert blood cholesterol values between mg/dL and mmol/L. ".toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), @@ -144,7 +144,7 @@ class _BloodCholesterolWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ label.toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.inputLabelTextColor, ), SizedBox( diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart index cf7cc61b..f2110646 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart @@ -130,7 +130,7 @@ class _BloodSugarWidgetState extends State { SizedBox(width: 12.w), Expanded( child: "Convert blood glucose values between mg/dL and mmol/L. (1 mmol/L ≈ 18.0182 mg/dL)".toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.inputLabelTextColor, ), ), @@ -155,7 +155,7 @@ class _BloodSugarWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ label.toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.inputLabelTextColor, ), SizedBox( diff --git a/lib/presentation/health_calculators_and_converts/widgets/bmi.dart b/lib/presentation/health_calculators_and_converts/widgets/bmi.dart index a5ea06d1..9e9d1bed 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/bmi.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/bmi.dart @@ -75,7 +75,7 @@ class _BMIWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Height".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -110,8 +110,8 @@ class _BMIWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toUpperCase().toText12(fontWeight: FontWeight.w600, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedHeightUnit.toUpperCase().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -152,7 +152,7 @@ class _BMIWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -187,7 +187,7 @@ class _BMIWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Weight".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -227,8 +227,8 @@ class _BMIWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toUpperCase().toText12(fontWeight: FontWeight.w600, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedWeightUnit.toUpperCase().toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -268,7 +268,7 @@ class _BMIWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -300,7 +300,7 @@ class _BMIWidgetState extends State { SizedBox(width: 12.w), Expanded( child: "Calculate the BMI value and weight status to identify the healthy weight. NOt appropriate for children and women who are pregnant or breastfeeding." - .toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + .toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/bmr.dart b/lib/presentation/health_calculators_and_converts/widgets/bmr.dart index d1cb2c3a..21a5df61 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/bmr.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/bmr.dart @@ -89,8 +89,8 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.selectGender.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedGender.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.selectGender.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedGender.toCamelCase.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -137,7 +137,7 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -170,7 +170,7 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Age (11-120) yrs".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Age (11-120) yrs".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -207,7 +207,7 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Height".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -242,8 +242,8 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.unit.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -284,7 +284,7 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -320,7 +320,7 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Weight".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -360,8 +360,8 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedWeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -402,7 +402,7 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -439,8 +439,8 @@ class _BMRWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.activityLevel.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedActivityLevel.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.activityLevel.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedActivityLevel.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -485,7 +485,7 @@ class _BMRWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -514,7 +514,7 @@ class _BMRWidgetState extends State { SizedBox(width: 12.w), Expanded( child: "Calculates daily calorie intake based on server factors, like height, weight, age, gender and daily physical activity." - .toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + .toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/calories.dart b/lib/presentation/health_calculators_and_converts/widgets/calories.dart index 1609c056..8e584e96 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/calories.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/calories.dart @@ -90,8 +90,8 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Select Gender".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedGender.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Select Gender".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedGender.toCamelCase.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -137,7 +137,7 @@ class _CaloriesWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -170,7 +170,7 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Age (11-120) yrs".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Age (11-120) yrs".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -207,7 +207,7 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Height".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -242,8 +242,8 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -283,7 +283,7 @@ class _CaloriesWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -319,7 +319,7 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Weight".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -359,8 +359,8 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedWeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -401,7 +401,7 @@ class _CaloriesWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -438,8 +438,8 @@ class _CaloriesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.activityLevel.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedActivityLevel.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.activityLevel.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedActivityLevel.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -484,7 +484,7 @@ class _CaloriesWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -513,7 +513,7 @@ class _CaloriesWidgetState extends State { SizedBox(width: 12.w), Expanded( child: "Calculates daily calorie intake based on server factors, like height, weight, age, gender and daily physical activity." - .toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + .toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/crabs.dart b/lib/presentation/health_calculators_and_converts/widgets/crabs.dart index c241e688..e3e7c1e3 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/crabs.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/crabs.dart @@ -70,7 +70,7 @@ class _CrabsWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.caloriesPerDay.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + LocaleKeys.caloriesPerDay.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -113,8 +113,8 @@ class _CrabsWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.dietType.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedDietType.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.dietType.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedDietType.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -159,7 +159,7 @@ class _CrabsWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -188,7 +188,7 @@ class _CrabsWidgetState extends State { SizedBox(width: 12.w), Expanded( child: "Calculate carbohydrate based protein and fat ration in calories and grams according to a pre set ratio." - .toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + .toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/health_card.dart b/lib/presentation/health_calculators_and_converts/widgets/health_card.dart index d54d0378..21482e7c 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/health_card.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/health_card.dart @@ -40,7 +40,7 @@ class HealthCard extends StatelessWidget { children: [ Flexible( child: labelText.toText12( - fontWeight: FontWeight.w600, + isBold: true, maxLine: 2, )), ], diff --git a/lib/presentation/health_calculators_and_converts/widgets/ibw.dart b/lib/presentation/health_calculators_and_converts/widgets/ibw.dart index a783d2c8..070e373e 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/ibw.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/ibw.dart @@ -81,7 +81,7 @@ class _IdealBodyWeightWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Height".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Height".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -116,8 +116,8 @@ class _IdealBodyWeightWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.unit.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedHeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedHeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -157,7 +157,7 @@ class _IdealBodyWeightWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -193,7 +193,7 @@ class _IdealBodyWeightWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Weight".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + "Weight".toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -233,8 +233,8 @@ class _IdealBodyWeightWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.unit.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedWeightUnit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.unit.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedWeightUnit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), @@ -273,7 +273,7 @@ class _IdealBodyWeightWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toUpperCase().toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toUpperCase().toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -310,8 +310,8 @@ class _IdealBodyWeightWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.bodyFrameSize.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - selectedBodyFrameSize.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + LocaleKeys.bodyFrameSize.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), + selectedBodyFrameSize.toText12(isBold: true, color: AppColors.textColor), ], ), ], @@ -355,7 +355,7 @@ class _IdealBodyWeightWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - unit.toCamelCase.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + unit.toCamelCase.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, ], ).onPress(() { setState(() { @@ -383,7 +383,7 @@ class _IdealBodyWeightWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Calculates the ideal body weight based on height, weight and body size.".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + child: "Calculates the ideal body weight based on height, weight and body size.".toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/ovulation.dart b/lib/presentation/health_calculators_and_converts/widgets/ovulation.dart index 556880d4..3cfd72f8 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/ovulation.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/ovulation.dart @@ -97,7 +97,7 @@ class _OvulationWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.averageCycleLength.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + LocaleKeys.averageCycleLength.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -134,7 +134,7 @@ class _OvulationWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - LocaleKeys.averageLutealPhase.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + LocaleKeys.averageLutealPhase.tr(context: context).toText12(isBold: true, color: AppColors.inputLabelTextColor), Container( height: 20.w, alignment: Alignment.centerLeft, @@ -165,7 +165,7 @@ class _OvulationWidgetState extends State { Utils.buildSvgWithAssets(icon: AppAssets.globe, width: 18.w, height: 18.w), SizedBox(width: 12.w), Expanded( - child: "Calculate ovulation and fertile window based on last period, cycle length and luteal phase.".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + child: "Calculate ovulation and fertile window based on last period, cycle length and luteal phase.".toText12(isBold: true, color: AppColors.inputLabelTextColor), ), ], ).paddingSymmetrical(0.w, 16.w), diff --git a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart index 1c8c99ed..b61eaaed 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart @@ -136,7 +136,7 @@ class _TriglyceridesWidgetState extends State { child: "Convert triglyceride values between mg/dL and mmol/L." .toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.inputLabelTextColor, ), ), @@ -160,7 +160,7 @@ class _TriglyceridesWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ label.toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.inputLabelTextColor, ), SizedBox( diff --git a/lib/presentation/health_trackers/add_health_tracker_entry_page.dart b/lib/presentation/health_trackers/add_health_tracker_entry_page.dart index 8320972e..89217aca 100644 --- a/lib/presentation/health_trackers/add_health_tracker_entry_page.dart +++ b/lib/presentation/health_trackers/add_health_tracker_entry_page.dart @@ -189,7 +189,7 @@ class _AddHealthTrackerEntryPageState extends State { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), (useUpperCase ? value.toUpperCase() : value.toCamelCase) - .toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1) + .toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1) .expanded, ], ).onPress(onTap), @@ -303,7 +303,7 @@ class _AddHealthTrackerEntryPageState extends State { ), style: TextStyle( fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ); @@ -343,7 +343,7 @@ class _AddHealthTrackerEntryPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - label.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + label.toText12(isBold: true, color: AppColors.inputLabelTextColor), if (inputField != null) Container( height: 20.w, @@ -351,7 +351,7 @@ class _AddHealthTrackerEntryPageState extends State { child: inputField, ) else if (value != null && value.isNotEmpty) - value.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + value.toCamelCase.toText12(isBold: true, color: AppColors.textColor), ], ), ), @@ -368,8 +368,8 @@ class _AddHealthTrackerEntryPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - unit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + unit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), diff --git a/lib/presentation/health_trackers/health_tracker_detail_page.dart b/lib/presentation/health_trackers/health_tracker_detail_page.dart index b5f41dae..737a150f 100644 --- a/lib/presentation/health_trackers/health_tracker_detail_page.dart +++ b/lib/presentation/health_trackers/health_tracker_detail_page.dart @@ -137,7 +137,7 @@ class _HealthTrackerDetailPageState extends State { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), (useUpperCase ? value.toUpperCase() : value.toCamelCase) - .toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1) + .toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1) .expanded, ], ).onPress(onTap), @@ -254,7 +254,7 @@ class _HealthTrackerDetailPageState extends State { ], ), SizedBox(height: 4.h), - subTitle.toText16(weight: FontWeight.w600, color: AppColors.textColor), + subTitle.toText16(isBold: true, color: AppColors.textColor), ], ), Transform.rotate( @@ -303,7 +303,7 @@ class _HealthTrackerDetailPageState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - viewModel.selectedDurationFilter.toText12(fontWeight: FontWeight.w500), + viewModel.selectedDurationFilter.toText12(isBold: true), SizedBox(width: 4.w), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 16.h), ], @@ -587,7 +587,7 @@ class _HealthTrackerDetailPageState extends State { ), SizedBox(height: 16.h), "You do not have any data available.".toText14( - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.textColor, isCenter: true, ), @@ -702,7 +702,7 @@ class _HealthTrackerDetailPageState extends State { final positions = [0.0, interval, interval * 2, interval * 3, maxY]; for (var position in positions) { if ((value - position).abs() < 1) { - return '${value.toInt()}'.toText10(weight: FontWeight.w600); + return '${value.toInt()}'.toText10(isBold: true); } } return SizedBox.shrink(); @@ -716,19 +716,19 @@ class _HealthTrackerDetailPageState extends State { if (selectedDuration == 'Week' && index < 7) { return Padding( padding: EdgeInsets.only(top: 10.h), - child: data[index].label.toText10(weight: FontWeight.w600, color: AppColors.labelTextColor), + child: data[index].label.toText10(isBold: true, color: AppColors.labelTextColor), ); } if (selectedDuration == 'Month' && index < 6) { return Padding( padding: EdgeInsets.only(top: 10.h), - child: data[index].label.toText10(weight: FontWeight.w600, color: AppColors.labelTextColor), + child: data[index].label.toText10(isBold: true, color: AppColors.labelTextColor), ); } if (selectedDuration == 'Year' && index < 12) { return Padding( padding: EdgeInsets.only(top: 10.h), - child: data[index].label.toText8(fontWeight: FontWeight.w600, color: AppColors.labelTextColor), + child: data[index].label.toText8(isBold: true, color: AppColors.labelTextColor), ); } return SizedBox.shrink(); @@ -757,7 +757,7 @@ class _HealthTrackerDetailPageState extends State { ), ), SizedBox(width: 6.w), - label.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + label.toText12(isBold: true, color: AppColors.textColor), ], ); } diff --git a/lib/presentation/health_trackers/health_trackers_page.dart b/lib/presentation/health_trackers/health_trackers_page.dart index 66630f28..e974773c 100644 --- a/lib/presentation/health_trackers/health_trackers_page.dart +++ b/lib/presentation/health_trackers/health_trackers_page.dart @@ -2,6 +2,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; @@ -47,21 +49,24 @@ Widget buildHealthTrackerCard({ crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - title.toText16(weight: FontWeight.w600), + title.toText16(isBold: true), description.toText12( - fontWeight: FontWeight.w500, + isBold: true, color: Color(0xFF8F9AA3), ), ], ), ), SizedBox(width: 12.w), - Utils.buildSvgWithAssets( - icon: AppAssets.arrowRight, - width: 24.w, - height: 24.h, - fit: BoxFit.contain, - iconColor: AppColors.textColor, + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrowRight, + width: 24.w, + height: 24.h, + fit: BoxFit.contain, + iconColor: AppColors.textColor, + ), ), ], ).paddingAll(16.w), diff --git a/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart b/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart index 39465b0f..e12f16c2 100644 --- a/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart +++ b/lib/presentation/health_trackers/widgets/tracker_last_value_card.dart @@ -225,7 +225,7 @@ class TrackerLastValueCard extends StatelessWidget { children: [ "--".toText32(isBold: true, color: AppColors.greyTextColor), SizedBox(width: 6.w), - unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h), + unit.toText12(color: AppColors.greyTextColor, isBold: true).paddingOnly(top: 8.h), ], ), SizedBox(height: 8.h), @@ -253,7 +253,7 @@ class TrackerLastValueCard extends StatelessWidget { children: [ displayValue.toText32(isBold: true, color: statusColor), SizedBox(width: 6.w), - unit.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500).paddingOnly(top: 8.h), + unit.toText12(color: AppColors.greyTextColor, isBold: true).paddingOnly(top: 8.h), ], ), SizedBox(height: 8.h), diff --git a/lib/presentation/hmg_services/services_page.dart b/lib/presentation/hmg_services/services_page.dart index 8310e6db..680bf1c7 100644 --- a/lib/presentation/hmg_services/services_page.dart +++ b/lib/presentation/hmg_services/services_page.dart @@ -12,6 +12,7 @@ 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/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/blood_donation/blood_donation_view_model.dart'; +import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/hmg_services_component_model.dart'; @@ -20,6 +21,7 @@ import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_mode import 'package:hmg_patient_app_new/features/water_monitor/water_monitor_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/blood_donation/blood_donation_page.dart'; +import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/emergency_services_page.dart'; import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; @@ -80,7 +82,11 @@ class ServicesPage extends StatelessWidget { AppAssets.appointment_calendar_icon, bgColor: AppColors.bookAppointment, true, - route: AppRoutes.bookAppointmentPage, + route: null, + onTap: () { + getIt.get().onTabChanged(0); + Navigator.of(getIt().navigatorKey.currentContext!).push(CustomPageRoute(page: BookAppointmentPage())); + } ), HmgServicesComponentModel(5, LocaleKeys.completeCheckup.tr(), "", AppAssets.comprehensiveCheckup, bgColor: AppColors.bgGreenColor, true, route: null, onTap: () async { if (getIt.get().isAuthenticated) { @@ -90,8 +96,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.comprehensiveMedicalCheckup.tr(), - serviceHeader: "Complete Health Checkup: Advanced diagnostics, Detailed Health insights".needTranslation, - serviceDescription: "Get detailed insights into your health with our advanced diagnostics. Understand your body better for a healthier future.".needTranslation, + serviceHeader: LocaleKeys.cmcServiceHeader.tr(), + serviceDescription: LocaleKeys.cmcServiceDescription.tr(), serviceImage: AppAssets.cmcService), ), ); @@ -150,8 +156,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.eReferralServices.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, + serviceHeader: LocaleKeys.eReferralServiceHeader.tr(), + serviceDescription: LocaleKeys.eReferralServiceDescription.tr(), serviceImage: AppAssets.eReferralService), ), ); @@ -177,8 +183,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.bloodDonation.tr(), - serviceHeader: "Give Blood, Save Lives. Your donation makes a difference.".needTranslation, - serviceDescription: "Donate blood, empower lives. Your contribution creates hope. Join our blood drive and be a lifeline for those in need. Every drop counts!".needTranslation, + serviceHeader: LocaleKeys.bloodDonationServiceHeader.tr(), + serviceDescription: LocaleKeys.bloodDonationServiceDescription.tr(), serviceImage: AppAssets.bloodDonationService), ), ); @@ -270,9 +276,9 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.healthTrackers.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, - serviceImage: AppAssets.eReferralService), + serviceHeader: LocaleKeys.healthTrackersServiceHeader.tr(), + serviceDescription: LocaleKeys.healthTrackersServiceDescription.tr(), + serviceImage: AppAssets.healthTrackersService), ), ); } @@ -311,8 +317,8 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.waterConsumption.tr(), - serviceHeader: "Stay Hydrated, Stay Healthy. Track your daily water intake with ease.".needTranslation, - serviceDescription: "Quench your thirst, track your health. Effortlessly monitor your daily water intake with our user-friendly app, ensuring optimal hydration and well-being.".needTranslation, + serviceHeader: LocaleKeys.waterConsumptionServiceHeader.tr(), + serviceDescription: LocaleKeys.waterConsumptionServiceDescription.tr(), serviceImage: AppAssets.waterConsumptionService), ), ); @@ -353,9 +359,9 @@ class ServicesPage extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.smartWatches.tr(), - serviceHeader: "HMG Hospital E-Referral: Streamlined patient referrals".needTranslation, - serviceDescription: "Facilitate seamless patient transfers to HMG with our secure e-referral system. Ensure continuity of care for every patient.".needTranslation, - serviceImage: AppAssets.eReferralService), + serviceHeader: LocaleKeys.smartWatchServiceHeader.tr(), + serviceDescription: LocaleKeys.smartWatchServiceDescription.tr(), + serviceImage: AppAssets.smartWatchService), ), ); } @@ -385,7 +391,7 @@ class ServicesPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16.h), - LocaleKeys.medicalAndCareServices.tr().toText18(weight: FontWeight.w600).paddingSymmetrical(24.w, 0), + LocaleKeys.medicalAndCareServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), SizedBox(height: 16.h), GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( @@ -402,7 +408,7 @@ class ServicesPage extends StatelessWidget { }, ).paddingSymmetrical(24.w, 0), SizedBox(height: 24.h), - LocaleKeys.hmgServices.tr().toText18(weight: FontWeight.w600).paddingSymmetrical(24.w, 0), + LocaleKeys.hmgServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), SizedBox(height: 16.h), SizedBox( height: 350.h, @@ -434,8 +440,9 @@ class ServicesPage extends StatelessWidget { ), SizedBox(height: 24.h), getIt.get().isAuthenticated ? Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.personalServices.tr().toText18(weight: FontWeight.w600).paddingSymmetrical(24.w, 0), + LocaleKeys.personalServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), SizedBox(height: 16.h), Row( children: [ @@ -457,17 +464,17 @@ class ServicesPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h, applyThemeColor: false), - LocaleKeys.habibWallet.tr().toText14(weight: FontWeight.w600, maxlines: 2).expanded, + LocaleKeys.habibWallet.tr().toText14(isBold: true, maxlines: 2).expanded, Utils.buildSvgWithAssets(icon: getIt.get().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward), ], ), Spacer(), getIt.get().isAuthenticated ? Consumer(builder: (context, habibWalletVM, child) { - return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false) + return Utils.getPaymentAmountWithSymbol2(num.parse(NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount)), isExpanded: false) .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); }) - : LocaleKeys.loginToViewWalletBalance.tr().toText12(fontWeight: FontWeight.w500, maxLine: 2), + : LocaleKeys.loginToViewWalletBalance.tr().toText12(isBold: true, maxLine: 2), Spacer(), getIt.get().isAuthenticated ? CustomButton( @@ -478,7 +485,7 @@ class ServicesPage extends StatelessWidget { textColor: AppColors.infoColor, text: LocaleKeys.recharge.tr(), borderWidth: 0.w, - fontWeight: FontWeight.w500, + isBold: true, borderColor: Colors.transparent, backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08), padding: EdgeInsets.all(8.w), @@ -517,7 +524,7 @@ class ServicesPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Utils.buildSvgWithAssets(icon: AppAssets.services_medical_file_icon, width: 40.w, height: 40.h, applyThemeColor: false), - LocaleKeys.medicalFile.tr().toText16(weight: FontWeight.w600, maxlines: 2).expanded, + LocaleKeys.medicalFile.tr().toText16(isBold: true, maxlines: 2).expanded, Utils.buildSvgWithAssets(icon: getIt.get().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward), ], ), @@ -553,7 +560,7 @@ class ServicesPage extends StatelessWidget { ), ], ) - : LocaleKeys.loginToViewMedicalFile.tr().toText12(fontWeight: FontWeight.w500, maxLine: 2), + : LocaleKeys.loginToViewMedicalFile.tr().toText12(isBold: true, maxLine: 2), Spacer(), getIt.get().isAuthenticated ? CustomButton( @@ -564,7 +571,7 @@ class ServicesPage extends StatelessWidget { textColor: AppColors.primaryRedColor, text: LocaleKeys.addMember.tr(), borderWidth: 0.w, - fontWeight: FontWeight.w500, + isBold: true, borderColor: Colors.transparent, backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.08), padding: EdgeInsets.all(8.w), @@ -600,7 +607,7 @@ class ServicesPage extends StatelessWidget { ], ) : SizedBox(), SizedBox(height: 24.h), - LocaleKeys.healthTools.tr().toText18(weight: FontWeight.w600).paddingSymmetrical(24.w, 0), + LocaleKeys.healthTools.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), SizedBox(height: 16.h), GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( @@ -623,7 +630,7 @@ class ServicesPage extends StatelessWidget { }, ).paddingSymmetrical(24.w, 0), SizedBox(height: 24.h), - LocaleKeys.supportServices.tr().toText18(weight: FontWeight.w600).paddingSymmetrical(24.w, 0), + LocaleKeys.supportServices.tr().toText18(isBold: true).paddingSymmetrical(24.w, 0), SizedBox(height: 16.h), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -648,7 +655,7 @@ class ServicesPage extends StatelessWidget { fit: BoxFit.contain, ), SizedBox(width: 8.w), - LocaleKeys.virtualTour.tr().toText14(weight: FontWeight.w500) + LocaleKeys.virtualTour.tr().toText14(isBold: true) ], ), ), @@ -677,7 +684,7 @@ class ServicesPage extends StatelessWidget { fit: BoxFit.contain, ), SizedBox(width: 8.w), - LocaleKeys.carParking.tr().toText14(weight: FontWeight.w500) + LocaleKeys.carParking.tr().toText14(isBold: true) ], ).onPress(() { Navigator.push( @@ -716,7 +723,7 @@ class ServicesPage extends StatelessWidget { fit: BoxFit.contain, ), SizedBox(width: 8.w), - LocaleKeys.latestNews.tr().toText14(weight: FontWeight.w500) + LocaleKeys.latestNews.tr().toText14(isBold: true) ], ), ), @@ -745,7 +752,7 @@ class ServicesPage extends StatelessWidget { fit: BoxFit.contain, ), SizedBox(width: 8.w), - Expanded(child: LocaleKeys.hmgContact.tr().toText14(weight: FontWeight.w500)) + Expanded(child: LocaleKeys.hmgContact.tr().toText14(isBold: true)) ], ), ), @@ -779,7 +786,7 @@ class ServicesPage extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.privacy_terms, width: 32.w, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.blackColor), SizedBox(width: 8.w), - Expanded(child: LocaleKeys.termsConditoins.tr().toText14(weight: FontWeight.w500)) + Expanded(child: LocaleKeys.termsConditoins.tr().toText14(isBold: true)) ], ), ), @@ -803,7 +810,7 @@ class ServicesPage extends StatelessWidget { children: [ Utils.buildSvgWithAssets(icon: AppAssets.privacy_terms, width: 32.w, height: 32.h, fit: BoxFit.contain, iconColor: AppColors.blackColor), SizedBox(width: 8.w), - Expanded(child: LocaleKeys.privacyPolicy.tr().toText14(weight: FontWeight.w500)) + Expanded(child: LocaleKeys.privacyPolicy.tr().toText14(isBold: true)) ], ), ), diff --git a/lib/presentation/hmg_services/services_view.dart b/lib/presentation/hmg_services/services_view.dart index dd70c413..7192ce23 100644 --- a/lib/presentation/hmg_services/services_view.dart +++ b/lib/presentation/hmg_services/services_view.dart @@ -54,7 +54,7 @@ class ServiceGridViewItem extends StatelessWidget { ), SizedBox(height: 4.h), hmgServiceComponentModel.title.toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColor, maxLine: 2, ), diff --git a/lib/presentation/home/app_update_page.dart b/lib/presentation/home/app_update_page.dart index 18382838..8750fcb7 100644 --- a/lib/presentation/home/app_update_page.dart +++ b/lib/presentation/home/app_update_page.dart @@ -41,7 +41,7 @@ class AppUpdatePage extends StatelessWidget { ), ), SizedBox(height: 50.h), - appUpdateMessage.toText16(weight: FontWeight.w500), + appUpdateMessage.toText16(isBold: true), ], ), ), diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart index 4606c800..33b12855 100644 --- a/lib/presentation/home/data/landing_page_data.dart +++ b/lib/presentation/home/data/landing_page_data.dart @@ -1,6 +1,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/home/data/service_card_data.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -123,8 +125,8 @@ class LandingPageData { ServiceCardData( serviceName: "my_doctors", icon: AppAssets.my_doctors_icon, - title: "My", - subtitle: "Doctors", + title: getIt.get().isArabic() ? LocaleKeys.myDoctor : "My", + subtitle: getIt.get().isArabic() ? "" : "Doctors", backgroundColor: AppColors.whiteColor, iconColor: AppColors.blackColor, textColor: AppColors.blackColor, diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 3109443f..93d616da 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -55,6 +55,7 @@ import 'package:hmg_patient_app_new/presentation/notifications/notifications_lis import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart'; import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointment_doctor.dart'; import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.dart'; +import 'package:hmg_patient_app_new/presentation/todo_section/todo_page.dart'; import 'package:hmg_patient_app_new/presentation/todo_section/widgets/ancillary_orders_list.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart'; @@ -65,6 +66,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/countdown_timer.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/routes/spring_page_route_builder.dart'; import 'package:provider/provider.dart'; @@ -72,6 +74,8 @@ import 'package:smooth_corner/smooth_corner.dart'; import '../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart'; +import 'dart:ui' as ui; + class LandingPage extends StatefulWidget { const LandingPage({super.key}); @@ -117,7 +121,12 @@ class _LandingPageState extends State { showQuickLogin(context); }); } - scheduleMicrotask(() { + scheduleMicrotask(() async { + if (!appState.isAuthenticated) { + // LoaderBottomSheet.showLoader(); + await authVM.handleNewImeiRegistration(isNavigate: false); + } + if (appState.isAuthenticated) { habibWalletVM.initHabibWalletProvider(); habibWalletVM.getPatientBalanceAmount(); @@ -133,22 +142,22 @@ class _LandingPageState extends State { // Commented as per new requirement to remove rating popup from the app - // if(!appState.isRatedVisible) { - // appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) { - // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { - // appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!, - // onSuccess: ((response) { - // appointmentRatingViewModel.setClinicOrDoctor(false); - // appointmentRatingViewModel.setTitle("Rate Doctor".needTranslation); - // appointmentRatingViewModel.setSubTitle("How was your last visit with doctor?".needTranslation); - // openLastRating(); - // appState.setRatedVisible(true); - // }), - // ); - // } - // }, - // ); - // } + if(!appState.isRatedVisible) { + appointmentRatingViewModel.getLastRatingAppointment(onSuccess: (response) { + if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) { + appointmentRatingViewModel.getAppointmentDetails(appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!, appointmentRatingViewModel.appointmentRatedList.last.projectID!, + onSuccess: ((response) { + appointmentRatingViewModel.setClinicOrDoctor(false); + appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context)); + appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context)); + openLastRating(); + appState.setRatedVisible(true); + }), + ); + } + }, + ); + } } }); super.initState(); @@ -171,563 +180,649 @@ class _LandingPageState extends State { body: Consumer(builder: (context, insuranceVM, child) { return Stack( children: [ - SingleChildScrollView( - padding: EdgeInsets.only( - top: (!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) - ? (MediaQuery.paddingOf(context).top + 70.h) - : kToolbarHeight + 0.h, - bottom: 24), - child: Column( - spacing: 16.h, - children: [ - Row( - spacing: 8.h, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - appState.isAuthenticated - ? WelcomeWidget( - onTap: () { - // DialogService dialogService = getIt.get(); - // dialogService.showFamilyBottomSheetWithoutH( - // label: LocaleKeys.familyTitle.tr(context: context), - // message: "", - // isShowManageButton: true, - // onSwitchPress: (FamilyFileResponseModelLists profile) { - // getIt.get().switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); - // }, - // profiles: getIt.get().patientFamilyFiles); + RefreshIndicator( + color: AppColors.primaryRedColor, + onRefresh: () async { + if (appState.isAuthenticated) { + // Refresh Appointments Data + myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); + myAppointmentsViewModel.initAppointmentsViewModel(); + myAppointmentsViewModel.getPatientAppointments(true, false); - Navigator.of(context).push( - CustomPageRoute( - page: FamilyMedicalScreen(), - ), - ); - }, - name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), - imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, - ).expanded - : CustomButton( - text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () async { - await authVM.onLoginPressed(); + // Refresh Appointments Data + habibWalletVM.initHabibWalletProvider(); + habibWalletVM.getPatientBalanceAmount(); - // Navigator.pushReplacementNamed( - // // context, - // context, - // AppRoutes.zoomCallPage, - // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1), - // arguments: CallArguments("test123", "123", "Patient", "40", "0", true, 1), - // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)), - // ); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 14.f, - fontWeight: FontWeight.w500, - borderRadius: 12.r, - padding: EdgeInsets.fromLTRB(12.h, 0, 12.h, 0), - height: 40.h, - ), - Consumer(builder: (context, todoSectionVM, child) { - return Row( - mainAxisSize: MainAxisSize.min, - spacing: 18.h, - children: [ - Stack(children: [ - if (appState.isAuthenticated) - Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 24.h, width: 24.h).onPress(() async { - if (appState.isAuthenticated) { - notificationsViewModel.setNotificationStatusID(2); - notificationsViewModel.getAllNotifications(); - Navigator.of(context).push( - CustomPageRoute( - page: NotificationsListPage(), - // page: LoginScreen(), - ), - ); - } else { - await authVM.onLoginPressed(); - } - }), - (appState.isAuthenticated && (int.parse(todoSectionVM.notificationsCount ?? "0") > 0)) - ? Positioned( - right: 0, - top: 0, - child: Container( - width: 8.w, - height: 8.h, - padding: EdgeInsets.all(4), - decoration: BoxDecoration( - color: AppColors.primaryRedColor, - borderRadius: BorderRadius.circular(20.r), - ), - child: Text( - "", - style: TextStyle( - color: Colors.white, - fontSize: 8.f, - ), - textAlign: TextAlign.center, + // Refresh Ancillary Orders + todoSectionViewModel.initializeTodoSectionViewModel(); + + // Refresh Immediate LiveCare Data + immediateLiveCareViewModel.initImmediateLiveCare(); + immediateLiveCareViewModel.getPatientLiveCareHistory(); + } + }, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.only( + top: (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) + ? (MediaQuery.paddingOf(context).top + 70.h) + : kToolbarHeight + 0.h, + bottom: 24), + child: Column( + spacing: 16.h, + children: [ + Row( + spacing: 8.h, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + appState.isAuthenticated + ? WelcomeWidget( + onTap: () { + // DialogService dialogService = getIt.get(); + // dialogService.showFamilyBottomSheetWithoutH( + // label: LocaleKeys.familyTitle.tr(context: context), + // message: "", + // isShowManageButton: true, + // onSwitchPress: (FamilyFileResponseModelLists profile) { + // getIt.get().switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); + // }, + // profiles: getIt.get().patientFamilyFiles); + + Navigator.of(context).push( + CustomPageRoute( + direction: AxisDirection.down, + page: FamilyMedicalScreen(), + ), + ); + }, + name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), + // imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, + imageUrl: appState + .getAuthenticatedUser() + ?.gender == 1 + ? ((appState + .getAuthenticatedUser() + ?.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.maleImg) + : ((appState + .getAuthenticatedUser() + ?.age ?? 0) < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg), + ).expanded + : CustomButton( + text: LocaleKeys.loginOrRegister.tr(context: context), + onPressed: () async { + await authVM.onLoginPressed(); + // Navigator.pushReplacementNamed( + // // context, + // context, + // AppRoutes.zoomCallPage, + // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1), + // arguments: CallArguments("test123", "123", "Patient", "40", "0", true, 1), + // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)), + // ); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(12.h, 0, 12.h, 0), + height: 40.h, + ), + Consumer(builder: (context, todoSectionVM, child) { + return Row( + mainAxisSize: MainAxisSize.min, + spacing: 18.h, + children: [ + Stack(clipBehavior: Clip.none, children: [ + if (appState.isAuthenticated) + Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 24.h, width: 24.h).onPress(() async { + if (appState.isAuthenticated) { + notificationsViewModel.setNotificationStatusID(2); + notificationsViewModel.getAllNotifications(); + Navigator.of(context).push( + CustomPageRoute( + page: NotificationsListPage(), + // page: LoginScreen(), ), + ); + } else { + await authVM.onLoginPressed(); + } + }), + (appState.isAuthenticated && (int.parse(todoSectionVM.notificationsCount ?? "0") > 0)) + ? Positioned( + right: appState.isArabic() ? 8.w : -8.w, + top: -8.h, + // left: 4.h, + // bottom: 4.h, + child: Container( + width: 18.w, + height: 18.h, + padding: EdgeInsets.all(2), + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + borderRadius: BorderRadius.circular(20.r), + ), + child: Text( + todoSectionVM.notificationsCount.toString(), + style: TextStyle( + color: Colors.white, + fontFamily: "Poppins", + fontSize: 10.f, + fontWeight: FontWeight.w600, ), - ) - : SizedBox.shrink(), - ]), - Utils.buildSvgWithAssets(icon: AppAssets.location, height: 24.h, width: 24.w).onPress(() { - // openIndoorNavigationBottomSheet(context); - showCommonBottomSheetWithoutHeight( - context, - title: LocaleKeys.contactUs.tr(), - child: ContactUs(), - callBackFunc: () {}, - isFullScreen: false, - ); - }), - // Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 24.h, width: 24.h).onPress(() { - // showCommonBottomSheetWithoutHeight( - // context, - // title: LocaleKeys.contactUs.tr(), - // child: ContactUs(), - // callBackFunc: () {}, - // isFullScreen: false, - // ); - // }), - !appState.isAuthenticated - ? Utils.buildSvgWithAssets(icon: appState.isArabic() ? AppAssets.enLangIcon : AppAssets.arLangIcon, height: 24.h, width: 24.h).onPress(() { - context.setLocale(appState.isArabic() ? Locale('en', 'US') : Locale('ar', 'SA')); - }) - : SizedBox.shrink() - ], - ); - }), - ], - ).paddingSymmetrical(24.h, 0.h), - !appState.isAuthenticated - ? Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.r, - hasShadow: false, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Row( + textAlign: TextAlign.center, + ), + ), + ) + : SizedBox.shrink(), + ]), + Utils.buildSvgWithAssets(icon: AppAssets.location, height: 24.h, width: 24.w).onPress(() { + // openIndoorNavigationBottomSheet(context); + showCommonBottomSheetWithoutHeight( + context, + title: LocaleKeys.contactUs.tr(), + child: ContactUs(), + callBackFunc: () {}, + isFullScreen: false, + ); + }), + // Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 24.h, width: 24.h).onPress(() { + // showCommonBottomSheetWithoutHeight( + // context, + // title: LocaleKeys.contactUs.tr(), + // child: ContactUs(), + // callBackFunc: () {}, + // isFullScreen: false, + // ); + // }), + !appState.isAuthenticated + ? Utils.buildSvgWithAssets(icon: appState.isArabic() ? AppAssets.enLangIcon : AppAssets.arLangIcon, height: 24.h, width: 24.h).onPress(() { + context.setLocale(appState.isArabic() ? Locale('en', 'US') : Locale('ar', 'SA')); + }) + : SizedBox.shrink() + ], + ); + }), + ], + ).paddingSymmetrical(24.h, 0.h), + !appState.isAuthenticated + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Row( + children: [ + Utils.buildSvgWithAssets( + width: 50.w, + height: 60.h, + icon: AppAssets.symptomCheckerIcon, + fit: BoxFit.contain, + ), + SizedBox(width: 12.w), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.buildSvgWithAssets( - width: 50.w, - height: 60.h, - icon: AppAssets.symptomCheckerIcon, - fit: BoxFit.contain, + LocaleKeys.howAreYouFeelingToday.tr(context: context).toText14(isBold: true), + LocaleKeys.checkYourSymptomsWithScale.tr(context: context).toText12(isBold: true), + SizedBox(height: 14.h), + CustomButton( + text: LocaleKeys.checkYourSymptoms.tr(context: context), + onPressed: () async { + context.navigateWithName(AppRoutes.userInfoSelection); + }, + padding: EdgeInsetsGeometry.zero, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: Colors.white, + fontSize: 14.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + height: 40.h, ), - SizedBox(width: 12.w), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.howAreYouFeelingToday.tr(context: context).toText14(weight: FontWeight.w600), - LocaleKeys.checkYourSymptomsWithScale.tr(context: context).toText12(fontWeight: FontWeight.w500), - SizedBox(height: 14.h), - CustomButton( - text: LocaleKeys.checkYourSymptoms.tr(context: context), - onPressed: () async { - context.navigateWithName(AppRoutes.userInfoSelection); - }, - padding: EdgeInsetsGeometry.zero, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: Colors.white, - fontSize: 14.f, - fontWeight: FontWeight.w600, - borderRadius: 12.r, - height: 40.h, - ), - ], - ).expanded ], - ), - ), - ).paddingSymmetrical(24.w, 0.h) - : SizedBox.shrink(), - appState.isAuthenticated - ? Column( + ).expanded + ], + ), + ), + ).paddingSymmetrical(24.w, 0.h) + : SizedBox.shrink(), + appState.isAuthenticated + ? Column( + children: [ + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox(height: 12.h), + LocaleKeys.appointmentsAndVisits.tr(context: context).toText16(isBold: true), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.appointmentsAndVisits.tr(context: context).toText16(weight: FontWeight.w600), - Row( - children: [ - LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), - ], - ), + LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage())); - }), - Consumer3( - builder: (context, myAppointmentsVM, immediateLiveCareVM, todoSectionVM, 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, 16.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.first, - myAppointmentsViewModel: myAppointmentsViewModel, - bookAppointmentsViewModel: bookAppointmentsViewModel, - isLoading: false, - isFromHomePage: true, - ), - ).paddingSymmetrical(24.h, 0.h) - : isTablet - ? SizedBox( - height: isFoldable ? 290.h : 255.h, - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: 3, - shrinkWrap: true, - padding: EdgeInsets.only(left: 16.h, right: 16.h), - itemBuilder: (context, index) { - return SizedBox( - height: 255.h, - width: 250.w, - child: getIndexSwiperCard(index), - ); - // return AnimationConfiguration.staggeredList( - // position: index, - // duration: const Duration(milliseconds: 1000), - // child: SlideAnimation( - // horizontalOffset: 100.0, - // child: FadeInAnimation( - // child: SizedBox( - // height: 255.h, - // width: 250.w, - // child: getIndexSwiperCard(index), - // ), - // ), - // ), - // ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox( - width: 10.w, - ), - ), - ) - : SizedBox( - height: 255.h + 20 + 30, // itemHeight + shadow padding (10 top + 10 bottom) + pagination dots space - child: Swiper( - itemCount: myAppointmentsVM.isMyAppointmentsLoading - ? 3 - : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 - ? myAppointmentsVM.patientAppointmentsHistoryList.length - : 3, - layout: SwiperLayout.STACK, - loop: false, - itemWidth: MediaQuery.of(context).size.width - 48.h, - indicatorLayout: PageIndicatorLayout.COLOR, - axisDirection: getIt.get().isArabic() ? AxisDirection.left : AxisDirection.right, - controller: _controller, - itemHeight: 255.h + 20, - // extra space for shadow - pagination: SwiperPagination( - alignment: Alignment.bottomCenter, - margin: EdgeInsets.only(top: 220.h + 20 + 8 + 24), - builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + myAppointmentsViewModel.onTabChange(0); + myAppointmentsViewModel.updateListWRTTab(0); + Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage())); + }), + Consumer3( + builder: (context, myAppointmentsVM, immediateLiveCareVM, todoSectionVM, 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, 16.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.first, + myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, + isLoading: false, + isFromHomePage: true, + ), + ).paddingSymmetrical(24.h, 0.h) + : isTablet + ? SizedBox( + height: isFoldable ? 290.h : 255.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: 3, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return SizedBox( + height: 255.h, + width: 250.w, + child: getIndexSwiperCard(index), + ); + // return AnimationConfiguration.staggeredList( + // position: index, + // duration: const Duration(milliseconds: 1000), + // child: SlideAnimation( + // horizontalOffset: 100.0, + // child: FadeInAnimation( + // child: SizedBox( + // height: 255.h, + // width: 250.w, + // child: getIndexSwiperCard(index), + // ), + // ), + // ), + // ); + }, + separatorBuilder: (BuildContext cxt, int index) => + SizedBox( + width: 10.w, + ), + ), + ) + : SizedBox( + height: 255.h + 20 + 30, // itemHeight + shadow padding (10 top + 10 bottom) + pagination dots space + child: Swiper( + itemCount: myAppointmentsVM.isMyAppointmentsLoading + ? 3 + : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 + ? myAppointmentsVM.patientAppointmentsHistoryList.length + : 3, + layout: SwiperLayout.STACK, + loop: false, + itemWidth: MediaQuery.of(context).size.width - 48.h, + indicatorLayout: PageIndicatorLayout.COLOR, + axisDirection: appState.isArabic() ? AxisDirection.right : AxisDirection.left, + controller: _controller, + itemHeight: 255.h + 20, + // extra space for shadow + pagination: SwiperPagination( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only(top: 220.h + 20 + 8 + 24), + builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), + ), + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: getIndexSwiperCard(index), + ); + }, ), - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: getIndexSwiperCard(index), - ); + ) + : Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), + SizedBox(height: 12.h), + LocaleKeys.noUpcomingAppointmentPleaseBook.tr(context: context).toText12(isCenter: true), + SizedBox(height: 12.h), + CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + getIt.get().onTabChanged(0); + Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage())); }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14.f, + fontWeight: FontWeight.w600, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + height: 40.h, ), - ) - : Container( - width: double.infinity, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), - SizedBox(height: 12.h), - LocaleKeys.noUpcomingAppointmentPleaseBook.tr(context: context).toText12(isCenter: true), - SizedBox(height: 12.h), - CustomButton( - text: LocaleKeys.bookAppo.tr(context: context), - onPressed: () { - getIt.get().onTabChanged(0); - 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: 40.h, - ), - ], - ), - ), - ).paddingSymmetrical(24.h, 16.h); - }, - ), - - // Consumer for ER Online Check-In pending request - // Consumer( - // builder: (context, emergencyServicesVM, child) { - // return emergencyServicesVM.patientHasAdvanceERBalance - // ? Column( - // children: [ - // SizedBox(height: 16.h), - // Container( - // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - // color: AppColors.whiteColor, - // borderRadius: 20.r, - // hasShadow: false, - // side: BorderSide(color: AppColors.primaryRedColor, width: 3.h), - // ), - // width: double.infinity, - // child: Padding( - // padding: EdgeInsets.all(16.h), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // // Row( - // // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // // children: [ - // // AppCustomChipWidget( - // // labelText: LocaleKeys.erOnlineCheckInRequest.tr(context: context), - // // backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.10), - // // textColor: AppColors.primaryRedColor, - // // ), - // // Utils.buildSvgWithAssets(icon: AppAssets.appointment_checkin_icon, width: 24.h, height: 24.h, iconColor: AppColors.primaryRedColor), - // // ], - // // ), - // SizedBox(height: 8.h), - // Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // LocaleKeys.youHaveEROnlineCheckInRequest.tr(context: context).toText12(isBold: true), - // Transform.flip( - // flipX: getIt.get().isArabic(), - // child: 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: ErOnlineCheckinHome())); - // // context.read().navigateToEROnlineCheckIn(); - // }), - // SizedBox(height: 12.h), - // ], - // ) - // : SizedBox(height: 0.h); - // }, - // ), - SizedBox(height: 16.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.quickLinks.tr(context: context).toText16(weight: FontWeight.w600), - Row( - children: [ - LocaleKeys.viewMedicalFile.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), - ], - ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage())); - }), - SizedBox(height: 16.h), - Container( - // height: 121.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), - child: Column( - children: [ - SizedBox( - height: 92.h + 32.h - 4.h, - child: RawScrollbar( - controller: _horizontalScrollController, - thumbVisibility: true, - radius: Radius.circular(10.0), - thumbColor: AppColors.primaryRedColor, - trackVisibility: true, - trackColor: Color(0xffD9D9D9), - trackBorderColor: Colors.transparent, - trackRadius: Radius.circular(10.0), - padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery.sizeOf(context).width / 2.5 - 10, right: MediaQuery.sizeOf(context).width / 2.5 - 10), - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getLoggedInServiceCardsList.length, - shrinkWrap: true, - controller: _horizontalScrollController, - padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.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, 16.h); + }, + ), + + // Consumer for ER Online Check-In pending request + // Consumer( + // builder: (context, emergencyServicesVM, child) { + // return emergencyServicesVM.patientHasAdvanceERBalance + // ? Column( + // children: [ + // SizedBox(height: 16.h), + // Container( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + // color: AppColors.whiteColor, + // borderRadius: 20.r, + // hasShadow: false, + // side: BorderSide(color: AppColors.primaryRedColor, width: 3.h), + // ), + // width: double.infinity, + // child: Padding( + // padding: EdgeInsets.all(16.h), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // // Row( + // // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // // children: [ + // // AppCustomChipWidget( + // // labelText: LocaleKeys.erOnlineCheckInRequest.tr(context: context), + // // backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.10), + // // textColor: AppColors.primaryRedColor, + // // ), + // // Utils.buildSvgWithAssets(icon: AppAssets.appointment_checkin_icon, width: 24.h, height: 24.h, iconColor: AppColors.primaryRedColor), + // // ], + // // ), + // SizedBox(height: 8.h), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // LocaleKeys.youHaveEROnlineCheckInRequest.tr(context: context).toText12(isBold: true), + // Transform.flip( + // flipX: getIt.get().isArabic(), + // child: 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: ErOnlineCheckinHome())); + // // context.read().navigateToEROnlineCheckIn(); + // }), + // SizedBox(height: 12.h), + // ], + // ) + // : SizedBox(height: 0.h); + // }, + // ), + SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.quickLinks.tr(context: context).toText16(isBold: true), + Row( + children: [ + LocaleKeys.viewMedicalFile.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage())); + }), + SizedBox(height: 16.h), + Consumer(builder: (BuildContext context, TodoSectionViewModel todoSectionVM, Widget? child) { + return Container( + // height: 121.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), + child: Column( + children: [ + todoSectionVM.patientAncillaryOrdersList.isNotEmpty + ? Container( + height: 50.h, + decoration: ShapeDecoration( + color: AppColors.eReferralCardColor.withAlpha(50), + shape: SmoothRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), + smoothness: 1, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.pendingAncillaryOrders.tr(context: context).toText14(color: AppColors.eReferralCardColor, isBold: true).paddingSymmetrical(24.h, 0.h), + CustomButton( + text: LocaleKeys.view.tr(context: context), + onPressed: () { + todoSectionVM.setIsAncillaryOrdersNeedReloading(true); + Navigator.of(context).push( + CustomPageRoute( + page: ToDoPage(), ), ); }, - separatorBuilder: (BuildContext cxt, int index) => 10.width, - ).paddingSymmetrical(16.h, 0.h), - ), + backgroundColor: AppColors.eReferralCardColor, + borderColor: AppColors.eReferralCardColor, + textColor: AppColors.whiteColor, + fontSize: 10.f, + fontWeight: FontWeight.w600, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(15, 0, 15, 0), + height: 30.h, + ).paddingSymmetrical(24.h, 0.h), + ], ), - SizedBox(height: 16.h), - ], - ), - ).paddingSymmetrical(24.h, 0.h), - ], - ) - : Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), - child: Column( - children: [ - SizedBox( - height: 92.h + 32.h - 4.h, - child: RawScrollbar( - controller: _horizontalScrollController, - thumbVisibility: true, - radius: Radius.circular(10.0), - thumbColor: AppColors.primaryRedColor, - trackVisibility: true, - trackColor: Color(0xffD9D9D9), - trackBorderColor: Colors.transparent, - trackRadius: Radius.circular(10.0), - padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery.sizeOf(context).width / 2.5 - 10, right: MediaQuery.sizeOf(context).width / 2.5 - 10), - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, - shrinkWrap: true, + ) + : SizedBox.shrink(), + SizedBox( + height: 92.h + 32.h - 4.h, + child: RawScrollbar( controller: _horizontalScrollController, - padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.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, + thumbVisibility: true, + radius: Radius.circular(10.0), + thumbColor: AppColors.primaryRedColor, + trackVisibility: true, + trackColor: Color(0xffD9D9D9), + trackBorderColor: Colors.transparent, + trackRadius: Radius.circular(10.0), + padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery + .sizeOf(context) + .width / 2.5 - 10, right: MediaQuery + .sizeOf(context) + .width / 2.5 - 10), + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getLoggedInServiceCardsList.length, + shrinkWrap: true, + controller: _horizontalScrollController, + padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.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, + ), ), ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, - ).paddingSymmetrical(16.h, 0.h), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 10.width, + ).paddingSymmetrical(16.h, 0.h), + ), ), - ), - SizedBox(height: 16.h), - ], - ), - ).paddingSymmetrical(24.h, 0.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - LocaleKeys.services2.tr(context: context).toText18(weight: FontWeight.w600), - Row( + SizedBox(height: 16.h), + ], + ), + ).paddingSymmetrical(24.h, 0.h); + }), + ], + ) + : Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), + child: Column( children: [ - LocaleKeys.viewAllServices.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), + SizedBox( + height: 92.h + 32.h - 4.h, + child: RawScrollbar( + controller: _horizontalScrollController, + thumbVisibility: true, + radius: Radius.circular(10.0), + thumbColor: AppColors.primaryRedColor, + trackVisibility: true, + trackColor: Color(0xffD9D9D9), + trackBorderColor: Colors.transparent, + trackRadius: Radius.circular(10.0), + padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery + .sizeOf(context) + .width / 2.5 - 10, right: MediaQuery + .sizeOf(context) + .width / 2.5 - 10), + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, + shrinkWrap: true, + controller: _horizontalScrollController, + padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.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(16.h, 0.h), + ), + ), + SizedBox(height: 16.h), ], - ).onPress(() { - Navigator.of(context).push(CustomPageRoute(page: ServicesPage())); - }), - ], - ).paddingSymmetrical(24.w, 0.h), - SizedBox( - height: 431.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: FadedLargeServiceCard( - serviceCardData: LandingPageData.getServiceCardsList[index], - image: LandingPageData.getServiceCardsList[index].icon, - title: LandingPageData.getServiceCardsList[index].title, - subtitle: LandingPageData.getServiceCardsList[index].subtitle, - icon: LandingPageData.getServiceCardsList[index].largeCardIcon, + ), + ).paddingSymmetrical(24.h, 0.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.services2.tr(context: context).toText18(isBold: true), + Row( + children: [ + LocaleKeys.viewAllServices.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), + ], + ).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: ServicesPage())); + }), + ], + ).paddingSymmetrical(24.w, 0.h), + SizedBox( + height: 431.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: FadedLargeServiceCard( + serviceCardData: LandingPageData.getServiceCardsList[index], + 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: 16.w), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w), + ), ), - ), - appState.isAuthenticated ? HabibWalletCard() : SizedBox(), - ], + appState.isAuthenticated ? HabibWalletCard() : SizedBox(), + ], + ), ), ), - (!insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) + (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown) ? Container( height: MediaQuery.paddingOf(context).top + 50.h, decoration: ShapeDecoration( @@ -744,7 +839,7 @@ class _LandingPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(0.h, 0.h), + LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true).paddingSymmetrical(0.h, 0.h), Row( children: [ CustomButton( @@ -834,11 +929,11 @@ class _LandingPageState extends State { ); } - if (todoSectionViewModel.patientAncillaryOrdersList.isNotEmpty) { - return _buildAncillaryOrderCardWrapper( - todoSectionViewModel.patientAncillaryOrdersList.first, - ); - } + // if (todoSectionViewModel.patientAncillaryOrdersList.isNotEmpty) { + // return _buildAncillaryOrderCardWrapper( + // todoSectionViewModel.patientAncillaryOrdersList.first, + // ); + // } return _buildAppointmentCardWrapper( myAppointmentsViewModel.patientAppointmentsHistoryList[1], @@ -851,9 +946,9 @@ class _LandingPageState extends State { final hasAncillaryOrders = todoSectionViewModel.patientAncillaryOrdersList.isNotEmpty; if (hasQueueOrLiveCare && hasAncillaryOrders) { - return _buildAncillaryOrderCardWrapper( - todoSectionViewModel.patientAncillaryOrdersList.first, - ); + // return _buildAncillaryOrderCardWrapper( + // todoSectionViewModel.patientAncillaryOrdersList.first, + // ); } return _buildAppointmentCardWrapper( @@ -886,7 +981,7 @@ class _LandingPageState extends State { _buildQueueGreeting(), SizedBox(height: 2.h), LocaleKeys.thankYouForPatience.tr().toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight, ), SizedBox(height: 8.h), @@ -996,22 +1091,44 @@ class _LandingPageState extends State { color: AppColors.whiteColor, borderRadius: 20.r, hasShadow: true, - side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), + // side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), ), child: Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.immediateLiveCareRequest.tr(context: context).toText16(isBold: true), - SizedBox(height: 10.h), - _buildLiveCareHeader(liveCareData), - SizedBox(height: 10.h), - _buildLiveCareGreeting(), - SizedBox(height: 8.h), - _buildLiveCareQueueInfo(liveCareData.patCount), - SizedBox(height: 8.h), - _buildLiveCareWaitingTime(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.immediateLiveCareRequest.tr(context: context).toText16(isBold: true), + SizedBox(height: 10.h), + _buildLiveCareHeader(liveCareData), + SizedBox(height: 10.h), + SizedBox(height: 8.h), + _buildLiveCareQueueInfo(liveCareData.patCount), + SizedBox(height: 25.h), + ], + ), + CustomButton( + text: LocaleKeys.viewDetails.tr(context: context), + onPressed: () { + Navigator.of(context).push( + CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()), + ); + }, + backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), + textColor: AppColors.alertColor, + borderColor: AppColors.infoBannerBgColor, + fontSize: 12.f, + fontWeight: FontWeight.w600, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ), + // _buildLiveCareWaitingTime(), ], ), ), @@ -1066,10 +1183,7 @@ class _LandingPageState extends State { // LiveCare Card: Queue position info Widget _buildLiveCareQueueInfo(int? patCount) { - return LocaleKeys.yourTurnIsAfterPatients.tr( - namedArgs: {'count': (patCount ?? 0).toString()}, - context: context, - ).toText14(isBold: true); + return LocaleKeys.yourTurnIsAfterPatients.tr(namedArgs: {'count': (patCount ?? 0).toString()}, context: context).toText14(isBold: true); } // LiveCare Card: Waiting time counter @@ -1180,7 +1294,7 @@ class _LandingPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - displayTitle.toText20(weight: FontWeight.w600), + displayTitle.toText20(isBold: true), (context.select((vm) => vm.subTitle) ?? '').toText12(), ], ); diff --git a/lib/presentation/home/navigation_screen.dart b/lib/presentation/home/navigation_screen.dart index 5cfc8ff7..d1ad847d 100644 --- a/lib/presentation/home/navigation_screen.dart +++ b/lib/presentation/home/navigation_screen.dart @@ -4,6 +4,7 @@ import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart'; import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart'; import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; @@ -45,6 +46,7 @@ class _LandingNavigationState extends State { setState(() => _currentIndex = index); if (_currentIndex == 2) { getIt.get().onTabChanged(0); + getIt.get().getPatientFavouriteDoctors(); context.navigateWithName(AppRoutes.bookAppointmentPage); return; } diff --git a/lib/presentation/home/service_info_page.dart b/lib/presentation/home/service_info_page.dart index 5d07593d..75df9437 100644 --- a/lib/presentation/home/service_info_page.dart +++ b/lib/presentation/home/service_info_page.dart @@ -23,50 +23,67 @@ class ServiceInfoPage extends StatelessWidget { @override Widget build(BuildContext context) { - return CollapsingListView( - title: "", - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: Column( children: [ - AppCustomChipWidget( - richText: serviceName.toText14(color: AppColors.infoColor, weight: FontWeight.w500), - backgroundColor: AppColors.infoColor.withAlpha(50), - textColor: AppColors.infoColor, - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 24.h), - serviceHeader.toText28(isBold: true, height: 1.4).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 24.h), - serviceDescription.toText14(weight: FontWeight.w500, color: AppColors.greyTextColor).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 24.h), - CustomButton( - text: LocaleKeys.login.tr(context: context), - onPressed: () { - getIt().onLoginPressed(); - }, - backgroundColor: AppColors.secondaryLightRedColor, - borderColor: AppColors.secondaryLightRedColor, - textColor: AppColors.primaryRedColor, - fontSize: 16.f, - fontWeight: FontWeight.w500, - borderRadius: 12.r, - padding: EdgeInsets.symmetric(horizontal: 10.w), - height: 40.h, - icon: AppAssets.login1, - iconColor: AppColors.primaryRedColor, - iconSize: 18.h, - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 36.h), - ClipRRect( - borderRadius: BorderRadius.circular(24.r), - child: Transform.flip( - flipX: getIt.get().isArabic(), - child: Image.asset( - serviceImage, - fit: BoxFit.fitHeight, - width: 520.w, + Expanded( + child: CollapsingListView( + title: "", + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(24.r), + child: Transform.flip( + flipX: getIt.get().isArabic(), + child: Image.asset( + serviceImage, + fit: BoxFit.fitHeight, + // height: 480.h, + // width: 520.w, + ), + ), + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 32.h), + AppCustomChipWidget( + richText: serviceName.toText14(color: AppColors.infoColor, isBold: true), + backgroundColor: AppColors.infoColor.withAlpha(50), + textColor: AppColors.infoColor, + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.h), + serviceHeader.toText28(isBold: true, height: 1.4).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.h), + serviceDescription.toText14(isBold: true, color: AppColors.greyTextColor).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 24.h), + ], ), ), ), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: CustomButton( + text: LocaleKeys.login.tr(context: context), + onPressed: () { + getIt().onLoginPressed(); + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 16.f, + isBold: true, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 56.h, + icon: AppAssets.login1, + iconColor: AppColors.whiteColor, + iconSize: 18.h, + ).paddingSymmetrical(24.h, 24.h), + ) ], ), ); diff --git a/lib/presentation/home/widgets/habib_wallet_card.dart b/lib/presentation/home/widgets/habib_wallet_card.dart index 19c03284..2e1292b2 100644 --- a/lib/presentation/home/widgets/habib_wallet_card.dart +++ b/lib/presentation/home/widgets/habib_wallet_card.dart @@ -24,10 +24,10 @@ class HabibWalletCard extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.myBalance.tr(context: context).toText16(weight: FontWeight.w600), + LocaleKeys.myBalance.tr(context: context).toText16(isBold: true), // Row( // children: [ - // LocaleKeys.viewAllServices.tr(context: context).toText12(color: AppColors.primaryRedColor, weight: FontWeight.w500), + // LocaleKeys.viewAllServices.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), // SizedBox(width: 2.h), // Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h), // ], @@ -55,7 +55,7 @@ class HabibWalletCard extends StatelessWidget { // Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // children: [ - LocaleKeys.habibWallet.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.habibWallet.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), // Container( // height: 40.h, // width: 40.h, @@ -76,66 +76,80 @@ class HabibWalletCard extends StatelessWidget { // ], // ), SizedBox(height: 4.h), - Consumer(builder: (context, habibWalletVM, child) { - return Row( - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.saudi_riyal_icon, - iconColor: AppColors.inputLabelTextColor, - width: 24.h, - height: 24.h, - fit: BoxFit.contain, - ), - SizedBox(width: 8.h), - habibWalletVM.habibWalletAmount - .toString() - .toText32(isBold: true, isEnglishOnly: true) - .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h), - ], - ); - }), - Padding( - padding: EdgeInsets.symmetric(horizontal: 50.h), - child: Row( - children: [ - LocaleKeys.viewDetails.tr(context: context).toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], - ).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: HabibWalletPage(), - ), - ); - }), - ), - SizedBox(height: 16.h), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Consumer(builder: (context, habibWalletVM, child) { + return Row( + children: [ + Utils.buildSvgWithAssets( + icon: AppAssets.saudi_riyal_icon, + iconColor: AppColors.inputLabelTextColor, + width: 24.h, + height: 24.h, + fit: BoxFit.contain, + ), + SizedBox(width: 8.h), + NumberFormat.decimalPattern() + .format(habibWalletVM.habibWalletAmount) + .toString() + .toText32(isBold: true, isEnglishOnly: true) + .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h), + ], + ); + }), + Padding( + padding: EdgeInsets.symmetric(horizontal: 35.h), + child: Row( + children: [ + LocaleKeys.viewDetails.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: HabibWalletPage(), + ), + ); + }), + ), + ], + ), SizedBox( - width: 200.h, + // width: 50.h, child: Utils.getPaymentMethods(), ), - CustomButton( - height: 40.h, - icon: AppAssets.recharge_icon, - iconSize: 24.h, - backgroundColor: AppColors.infoColor, - textColor: Colors.white, - text: LocaleKeys.recharge.tr(context: context), - borderWidth: 0.w, - fontWeight: FontWeight.w500, - borderColor: Colors.transparent, - padding: EdgeInsets.fromLTRB(4, 0, 12, 0), - fontSize: 14.f, - onPressed: () { - Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage())); - }, - ), ], ), + SizedBox(height: 16.h), + CustomButton( + height: 40.h, + icon: AppAssets.recharge_icon, + iconColor: AppColors.infoColor, + iconSize: 24.h, + backgroundColor: AppColors.infoColor.withAlpha(15), + textColor: AppColors.infoColor, + text: LocaleKeys.recharge.tr(context: context), + borderWidth: 0.w, + fontWeight: FontWeight.w600, + borderColor: AppColors.infoColor.withAlpha(15), + padding: EdgeInsets.fromLTRB(4, 0, 12, 0), + fontSize: 14.f, + onPressed: () { + Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage())); + }, + ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // SizedBox( + // width: 200.h, + // child: Utils.getPaymentMethods(), + // ), + // ], + // ), ], ), ), diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index f0145afa..fd6676f9 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -8,6 +8,7 @@ import 'package:hmg_patient_app_new/core/utils/size_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/book_appointments/book_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; import 'package:hmg_patient_app_new/presentation/home/data/service_card_data.dart'; @@ -95,7 +96,7 @@ class LargeServiceCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ serviceCardData.title.tr(context: context).toText14(isBold: true, color: AppColors.textColor), - serviceCardData.subtitle.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight, maxLine: 2), + serviceCardData.subtitle.tr(context: context).toText12(isBold: true, color: AppColors.textColorLight, maxLine: 2), ], ), ), @@ -111,7 +112,7 @@ class LargeServiceCard extends StatelessWidget { borderColor: serviceCardData.isBold ? AppColors.successLightColor.withValues(alpha: 0.01) : AppColors.bgRedLightColor, textColor: serviceCardData.isBold ? AppColors.successColor : AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10.r, height: 40.h, ).paddingSymmetrical(16.w, 0.h), @@ -128,6 +129,7 @@ class LargeServiceCard extends StatelessWidget { switch (serviceCardData.serviceName) { case "livecare": { + getIt.get().getPatientFavouriteDoctors(); getIt.get().onTabChanged(1); Navigator.of(getIt.get().navigatorKey.currentContext!).push( CustomPageRoute( @@ -148,8 +150,8 @@ class LargeServiceCard extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.homeHealthCare.tr(), - serviceHeader: "Your Health, Elevated. Premium care, delivered to your doorstep.", - serviceDescription: "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.", + serviceHeader: LocaleKeys.homeHealthCareServiceHeader.tr(), + serviceDescription: LocaleKeys.homeHealthCareServiceDescription.tr(), serviceImage: AppAssets.homeHealthCareService), ), ); @@ -245,11 +247,11 @@ class FadedLargeServiceCard extends StatelessWidget { ), ), SizedBox(width: 12.w), - serviceCardData.title.tr(context: context).toText18(weight: FontWeight.w500, color: AppColors.textColor).expanded, + serviceCardData.title.tr(context: context).toText18(isBold: true, color: AppColors.textColor).expanded, ], ), SizedBox(height: 10.h), - serviceCardData.subtitle.tr(context: context).toText14(weight: FontWeight.w500, color: AppColors.blackBgColor, letterSpacing: 0, maxlines: 2), + serviceCardData.subtitle.tr(context: context).toText14(isBold: true, color: AppColors.blackBgColor, letterSpacing: 0, maxlines: 2), SizedBox(height: 12.h), CustomButton( text: serviceCardData.isBold ? LocaleKeys.visitPharmacyOnline.tr(context: context) : LocaleKeys.bookNow.tr(context: context), @@ -261,7 +263,7 @@ class FadedLargeServiceCard extends StatelessWidget { borderColor: serviceCardData.isBold ? AppColors.successLightColor.withValues(alpha: 0.01) : AppColors.blackBgColor, textColor: serviceCardData.isBold ? AppColors.successColor : AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10.r, height: 40.h, ), @@ -295,8 +297,8 @@ class FadedLargeServiceCard extends StatelessWidget { CustomPageRoute( page: ServiceInfoPage( serviceName: LocaleKeys.homeHealthCare.tr(), - serviceHeader: "Your Health, Elevated. Premium care, delivered to your doorstep.".needTranslation, - serviceDescription: "We bring quality healthcare to your doorstep. Experienced nurses providing compassionate care in the comfort of your home.".needTranslation, + serviceHeader: LocaleKeys.homeHealthCareServiceHeader.tr(), + serviceDescription: LocaleKeys.homeHealthCareServiceDescription.tr(), serviceImage: AppAssets.homeHealthCareService), ), ); diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart index 0c2da8d6..ff834204 100644 --- a/lib/presentation/home/widgets/small_service_card.dart +++ b/lib/presentation/home/widgets/small_service_card.dart @@ -68,11 +68,11 @@ class SmallServiceCard extends StatelessWidget { Utils.buildSvgWithAssets(icon: icon, iconColor: resolvedIconColor, width: 32.h, height: 32.h), SizedBox(height: 6.h), if (isFoldable || isTablet) ...[ - title.tr(context: context).toText10(color: resolvedTextColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxlines: 1), - subtitle.tr(context: context).toText10(color: resolvedTextColor, weight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxlines: 1), + title.tr(context: context).toText10(color: resolvedTextColor, isBold: true, isCenter: true, letterSpacing: -0.2, maxlines: 1), + subtitle.tr(context: context).toText10(color: resolvedTextColor, isBold: true, isCenter: true, letterSpacing: -0.2, maxlines: 1), ] else ...[ - title.tr(context: context).toText12(color: resolvedTextColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxLine: 1), - subtitle.tr(context: context).toText12(color: resolvedTextColor, fontWeight: FontWeight.w500, isCenter: true, letterSpacing: -0.2, maxLine: 1), + title.tr(context: context).toText12(color: resolvedTextColor, isBold: true, isCenter: true, letterSpacing: -0.2, maxLine: 1), + subtitle.tr(context: context).toText12(color: resolvedTextColor, isBold: true, isCenter: true, letterSpacing: -0.2, maxLine: 1), ] ], ), diff --git a/lib/presentation/home/widgets/welcome_widget.dart b/lib/presentation/home/widgets/welcome_widget.dart index beff2ae4..170eb4e3 100644 --- a/lib/presentation/home/widgets/welcome_widget.dart +++ b/lib/presentation/home/widgets/welcome_widget.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.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'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -42,7 +43,12 @@ class WelcomeWidget extends StatelessWidget { children: [ Icon(Icons.menu, color: AppColors.textColor).onPress(() { getIt.get().getProfileSettings(); - Navigator.of(context).push(springPageRoute(ProfileSettings())); + Navigator.of(context).push( + CustomPageRoute( + direction: getIt.get().isArabic() ? AxisDirection.right : AxisDirection.left, + page: ProfileSettings(), + ), + ); }), Image.asset(imageUrl, width: 40, height: 40), Column( @@ -50,15 +56,25 @@ class WelcomeWidget extends StatelessWidget { spacing: 4.h, mainAxisSize: MainAxisSize.min, children: [ - LocaleKeys.welcome.tr(context: context).toText14(color: AppColors.greyTextColor, height: 1, weight: FontWeight.w500), + LocaleKeys.welcome.tr(context: context).toText14(color: AppColors.greyTextColor, height: 1, isBold: true), Row( spacing: 4.h, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Flexible(child: name.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1, isEnglishOnly: true)), + Flexible(child: name.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1, isEnglishOnly: true)), // Icon(Icons.keyboard_arrow_down, size: 20, color: AppColors.greyTextColor), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w) + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrowRight, + iconColor: AppColors.blackColor, + width: 22.w, + height: 22.h, + fit: BoxFit.contain, + ) + ), + // Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w) ], ), ], diff --git a/lib/presentation/home_health_care/hhc_order_detail_page.dart b/lib/presentation/home_health_care/hhc_order_detail_page.dart index 7017441c..ff2f9af7 100644 --- a/lib/presentation/home_health_care/hhc_order_detail_page.dart +++ b/lib/presentation/home_health_care/hhc_order_detail_page.dart @@ -102,7 +102,7 @@ class _HhcOrderDetailPageState extends State { child: (isLoading ? "Processing" : order.statusText ?? '') .toText12( color: statusColor, - fontWeight: FontWeight.w600, + isBold: true, ) .toShimmer2(isShow: isLoading), ), @@ -110,7 +110,7 @@ class _HhcOrderDetailPageState extends State { (isLoading ? "Jan 15, 2024" : _formatDate(order.created)) .toText12( color: AppColors.textColorLight, - fontWeight: FontWeight.w500, + isBold: true, ) .toShimmer2(isShow: isLoading), ], @@ -124,7 +124,7 @@ class _HhcOrderDetailPageState extends State { if (!isLoading) ...[ LocaleKeys.requestID.tr(context: context).toText14( color: AppColors.textColorLight, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(width: 4.w), ], @@ -161,7 +161,7 @@ class _HhcOrderDetailPageState extends State { // borderColor: AppColors.primaryRedColor, // textColor: AppColors.whiteColor, // fontSize: 14.f, - // fontWeight: FontWeight.w600, + // isBold: true, // borderRadius: 10.r, // height: 44.h, // ).toShimmer2(isShow: isLoading), diff --git a/lib/presentation/home_health_care/hhc_procedures_page.dart b/lib/presentation/home_health_care/hhc_procedures_page.dart index 88766ea0..2ece0598 100644 --- a/lib/presentation/home_health_care/hhc_procedures_page.dart +++ b/lib/presentation/home_health_care/hhc_procedures_page.dart @@ -97,7 +97,7 @@ class _HhcProceduresPageState extends State { children: [ Row( children: [ - LocaleKeys.requestID.tr(context: context).toText14(color: AppColors.textColorLight, weight: FontWeight.w500), + LocaleKeys.requestID.tr(context: context).toText14(color: AppColors.textColorLight, isBold: true), SizedBox(width: 4.w), "${order.iD ?? '-'}".toText16(isBold: true), ], @@ -106,7 +106,7 @@ class _HhcProceduresPageState extends State { if (order.created != null) DateFormat('MMM dd, yyyy').format(DateTime.parse(order.created!)).toText12( color: AppColors.textColorLight, - fontWeight: FontWeight.w500, + isBold: true, ), ], ), @@ -178,7 +178,7 @@ class _HhcProceduresPageState extends State { Expanded( child: serviceName.toText14( color: AppColors.blackColor, - weight: FontWeight.w500, + weight: FontWeight.w600, maxlines: 3, ), ), @@ -215,7 +215,7 @@ class _HhcProceduresPageState extends State { Expanded( child: LocaleKeys.pendingOrderWait.tr(context: context).toText12( color: AppColors.infoBannerTextColor, - fontWeight: FontWeight.w500, + isBold: true, ), ), ], @@ -233,7 +233,7 @@ class _HhcProceduresPageState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w600, + isBold: true, borderRadius: 10.r, height: 44.h, ), @@ -321,7 +321,7 @@ class _HhcProceduresPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ serviceName.toText16( - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.blackColor, maxlines: 2, ), @@ -357,13 +357,12 @@ class _HhcProceduresPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.selectedServices.tr(context: context).toText12( - color: AppColors.textColorLight, - fontWeight: FontWeight.w600, - ), - "${hmgServicesViewModel.selectedHhcServices.length} service(s) selected".toText14( - isBold: true, - weight: FontWeight.bold, + LocaleKeys.selectedServices.tr(context: context).toText12(color: AppColors.textColorLight, isBold: true), + Row( + children: [ + ("${hmgServicesViewModel.selectedHhcServices.length} ").toText14(isBold: true, weight: FontWeight.bold, isEnglishOnly: true), + LocaleKeys.servicesSelected.tr(context: context).toText14(isBold: true, weight: FontWeight.bold), + ], ), ], ), @@ -508,7 +507,7 @@ class _HhcProceduresPageState extends State { context, noDataText: LocaleKeys.youHaveNoPendingRequests.tr(context: context), ), - ) : LocaleKeys.homeHealthCareText.tr(context: context).toText18(weight: FontWeight.w500).paddingSymmetrical(24.h, 24.h), + ) : LocaleKeys.homeHealthCareText.tr(context: context).toText18(isBold: true).paddingSymmetrical(24.h, 24.h), ], ); } diff --git a/lib/presentation/home_health_care/hhc_selection_review_page.dart b/lib/presentation/home_health_care/hhc_selection_review_page.dart index 9a9342d7..27245bee 100644 --- a/lib/presentation/home_health_care/hhc_selection_review_page.dart +++ b/lib/presentation/home_health_care/hhc_selection_review_page.dart @@ -172,7 +172,7 @@ class _HhcSelectionReviewPageState extends State { children: [ LocaleKeys.hereIsYourRequestNumber.tr(context: context).toText14( color: AppColors.textColorLight, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(width: 4.w), ("$requestId").toText16(isBold: true), diff --git a/lib/presentation/insurance/insurance_approval_details_page.dart b/lib/presentation/insurance/insurance_approval_details_page.dart index 7a69e088..6589bc4e 100644 --- a/lib/presentation/insurance/insurance_approval_details_page.dart +++ b/lib/presentation/insurance/insurance_approval_details_page.dart @@ -16,6 +16,8 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; + class InsuranceApprovalDetailsPage extends StatelessWidget { InsuranceApprovalDetailsPage({super.key, required this.insuranceApprovalResponseModel}); @@ -36,7 +38,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, - hasShadow: true, + hasShadow: false, ), child: Padding( padding: EdgeInsets.all(14.h), @@ -84,21 +86,42 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ (insuranceApprovalResponseModel.doctorName!).toText16(isBold: true), + SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 3.h, runSpacing: 4.h, children: [ AppCustomChipWidget(labelText: insuranceApprovalResponseModel.clinicName!), - AppCustomChipWidget(labelText: "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}"), - AppCustomChipWidget(labelText: "${LocaleKeys.unusedCount.tr(context: context)} ${insuranceApprovalResponseModel.unUsedCount}"), + AppCustomChipWidget(labelText: "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}", isEnglishOnly: true), + AppCustomChipWidget(labelText: "${LocaleKeys.unusedCount.tr(context: context)} ${insuranceApprovalResponseModel.unUsedCount}", isEnglishOnly: true), AppCustomChipWidget(labelText: "${LocaleKeys.companyName.tr(context: context)} ${insuranceApprovalResponseModel.companyName}"), AppCustomChipWidget( - labelText: - "${LocaleKeys.receiptOn.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.receiptOn), false)}"), + richText: Row( + mainAxisSize: MainAxisSize.min, + children: [ + "${LocaleKeys.receiptOn.tr(context: context)} ".toText10(), + Directionality( + textDirection: ui.TextDirection.ltr, + child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.receiptOn), false).toText10(isEnglishOnly: true)), + ], + ), + isEnglishOnly: true, + ), + AppCustomChipWidget( - labelText: - "${LocaleKeys.expiryOn.tr(context: context)} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.expiryDate), false)}"), + richText: Row( + mainAxisSize: MainAxisSize.min, + children: [ + "${LocaleKeys.expiryOn.tr(context: context)} ".toText10(), + Directionality( + textDirection: ui.TextDirection.ltr, + child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.expiryDate), false).toText10(isEnglishOnly: true)), + ], + ), + isEnglishOnly: true, + ), + ], ), ], @@ -115,14 +138,14 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, - hasShadow: true, + hasShadow: false, ), child: Padding( padding: EdgeInsets.all(16.h), child: insuranceApprovalResponseModel.apporvalDetails != null ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Approval Details".toText16(isBold: true), + LocaleKeys.approvalDetails.toText16(isBold: true), ListView.separated( padding: EdgeInsets.only(top: 16.h), shrinkWrap: true, @@ -138,7 +161,6 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -148,7 +170,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { LocaleKeys.procedureName.tr(context: context).toText14(isBold: true), Expanded( child: (insuranceApprovalResponseModel.apporvalDetails![index]!.procedureName ?? "") - .toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, textAlignment: TextAlign.end, maxLine: 2), + .toText12(isBold: true, color: AppColors.greyTextColor, textAlignment: TextAlign.end, maxLine: 2), ), ], ), @@ -157,7 +179,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "${LocaleKeys.status.tr(context: context)}: ".toText14(isBold: true), - insuranceApprovalResponseModel.apporvalDetails![index]!.status!.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + insuranceApprovalResponseModel.apporvalDetails![index]!.status!.toText12(isBold: true, color: AppColors.greyTextColor), ], ), SizedBox(height: 8.h), @@ -165,7 +187,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "${LocaleKeys.usageStatus.tr(context: context)}: ".toText14(isBold: true), - (insuranceApprovalResponseModel.apporvalDetails![index]!.isInvoicedDesc ?? "").toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + (insuranceApprovalResponseModel.apporvalDetails![index]!.isInvoicedDesc ?? "").toText12(isBold: true, color: AppColors.greyTextColor), ], ), ], diff --git a/lib/presentation/insurance/insurance_home_page.dart b/lib/presentation/insurance/insurance_home_page.dart index 35e3acb8..985de895 100644 --- a/lib/presentation/insurance/insurance_home_page.dart +++ b/lib/presentation/insurance/insurance_home_page.dart @@ -74,6 +74,7 @@ class _InsuranceHomePageState extends State { return Column( children: [ PatientInsuranceCard( + isShowButtons: index == 0, insuranceCardDetailsModel: insuranceVM.patientInsuranceList[index], isInsuranceExpired: DateTime.now().isAfter(DateUtil.convertStringToDate(insuranceVM.patientInsuranceList.first.cardValidTo))), SizedBox( @@ -104,7 +105,7 @@ class _InsuranceHomePageState extends State { borderColor: AppColors.bgGreenColor.withOpacity(0.0), textColor: AppColors.bgGreenColor, fontSize: 14, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, diff --git a/lib/presentation/insurance/widgets/insurance_approval_card.dart b/lib/presentation/insurance/widgets/insurance_approval_card.dart index 8f5f7cfb..b50e199f 100644 --- a/lib/presentation/insurance/widgets/insurance_approval_card.dart +++ b/lib/presentation/insurance/widgets/insurance_approval_card.dart @@ -15,6 +15,8 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'dart:ui' as ui; + class InsuranceApprovalCard extends StatelessWidget { InsuranceApprovalCard({super.key, required this.insuranceApprovalResponseModel, required this.isLoading, required this.appState}); @@ -88,6 +90,7 @@ class InsuranceApprovalCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ (isLoading ? "John Smith" : insuranceApprovalResponseModel.doctorName!).toText16(isBold: true).toShimmer2(isShow: isLoading), + SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 3.h, @@ -98,14 +101,17 @@ class InsuranceApprovalCard extends StatelessWidget { // textColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor : AppColors.primaryRedColor, // ).toShimmer2(isShow: isLoading), AppCustomChipWidget(labelText: isLoading ? "Cardiology" : insuranceApprovalResponseModel.clinicName!).toShimmer2(isShow: isLoading), - AppCustomChipWidget( - icon: AppAssets.doctor_calendar_icon, - labelText: isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.submitOn), false)) - .toShimmer2(isShow: isLoading), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(insuranceApprovalResponseModel.submitOn), false), isEnglishOnly: true,) + .toShimmer2(isShow: isLoading), + ), isLoading ? SizedBox.shrink() : AppCustomChipWidget( - labelText: isLoading ? LocaleKeys.approvalNo.tr(context: context) : "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}") + labelText: isLoading ? LocaleKeys.approvalNo.tr(context: context) : "${LocaleKeys.approvalNo.tr(context: context)} ${insuranceApprovalResponseModel.approvalNo}", isEnglishOnly: true,) .toShimmer2(isShow: isLoading), ], ), @@ -130,7 +136,7 @@ class InsuranceApprovalCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: isTablet || isFoldable ? 46.h : 40.h, diff --git a/lib/presentation/insurance/widgets/insurance_history.dart b/lib/presentation/insurance/widgets/insurance_history.dart index 7219a34e..0f5bde1e 100644 --- a/lib/presentation/insurance/widgets/insurance_history.dart +++ b/lib/presentation/insurance/widgets/insurance_history.dart @@ -73,7 +73,7 @@ class InsuranceHistory extends StatelessWidget { borderColor: AppColors.primaryRedColor.withOpacity(0.0), textColor: AppColors.primaryRedColor, fontSize: 10, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 8, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 30.h, @@ -81,7 +81,7 @@ class InsuranceHistory extends StatelessWidget { ], ), SizedBox(height: 8.h), - // "Haroon Amjad".toText16(weight: FontWeight.w600), + // "Haroon Amjad".toText16(isBold: true), SizedBox(height: 8.h), Row( children: [ diff --git a/lib/presentation/insurance/widgets/insurance_update_details_card.dart b/lib/presentation/insurance/widgets/insurance_update_details_card.dart index 770f4da2..f7fd4402 100644 --- a/lib/presentation/insurance/widgets/insurance_update_details_card.dart +++ b/lib/presentation/insurance/widgets/insurance_update_details_card.dart @@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; +import 'package:hmg_patient_app_new/services/dialog_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; @@ -57,8 +58,8 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - insuranceViewModel.patientInsuranceUpdateResponseModel!.memberName!.toText16(weight: FontWeight.w600), - "Policy: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.policyNumber}".toText12(isBold: true, color: AppColors.lightGrayColor), + insuranceViewModel.patientInsuranceUpdateResponseModel!.memberName!.toText16(isBold: true), + LocaleKeys.policyNumberInsurancePage.tr(namedArgs: {'number': insuranceViewModel.patientInsuranceUpdateResponseModel!.policyNumber.toString()}).toText12(isBold: true, color: AppColors.lightGrayColor, isEnglishOnly: true), SizedBox(height: 8.h), Row( children: [ @@ -86,9 +87,10 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { icon: AppAssets.doctor_calendar_icon, labelText: "${LocaleKeys.expiryOn.tr(context: context)} ${DateUtil.formatDateToDate(DateTime.parse(insuranceViewModel.patientInsuranceUpdateResponseModel!.effectiveTo!), false)}", + isEnglishOnly: true, ), AppCustomChipWidget( - labelText: "Member ID: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.memberID!}", + labelText: "Member ID: ${insuranceViewModel.patientInsuranceUpdateResponseModel!.memberID!}", isEnglishOnly: true, ), ], ), @@ -121,16 +123,27 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { insuranceCardImage: "", onSuccess: (val) { LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - title: LocaleKeys.success.tr(context: context), - context, - child: Utils.getSuccessWidget(loadingText: LocaleKeys.insuranceRequestSubmittedSuccessfully.tr(context: context)), - callBackFunc: () { - Navigator.pop(context); - }, - isFullScreen: false, - isCloseButtonVisible: false, - ); + getIt.get().showCommonBottomSheetWithoutH( + message: LocaleKeys.insuranceRequestSubmittedSuccessfully.tr(context: context), + label: LocaleKeys.notice.tr(), + onOkPressed: () { + Navigator.pop(context); + }, + okLabel: "confirm", + cancelLabel: LocaleKeys.acknowledged.tr(context: context), + isConfirmButton: true, + ); + // showCommonBottomSheetWithoutHeight( + // title: LocaleKeys.success.tr(context: context), + // context, + // child: Utils.getSuccessWidget(loadingText: LocaleKeys.insuranceRequestSubmittedSuccessfully.tr(context: context)), + // callBackFunc: () { + // Navigator.pop(context); + // }, + // isFullScreen: false, + // isCloseButtonVisible: false, + // isAutoDismiss: true + // ); // Future.delayed(Duration(milliseconds: 2000)).then((value) async { // Navigator.pop(context); // }); @@ -155,7 +168,7 @@ class PatientInsuranceCardUpdateCard extends StatelessWidget { borderColor: AppColors.successColor.withOpacity(0.01), textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), ).paddingSymmetrical(24.w, 0.h), diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart index ab70eb6f..3734bd95 100644 --- a/lib/presentation/insurance/widgets/patient_insurance_card.dart +++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart @@ -20,10 +20,11 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; class PatientInsuranceCard extends StatelessWidget { - PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired}); + PatientInsuranceCard({super.key, required this.insuranceCardDetailsModel, required this.isInsuranceExpired, this.isShowButtons = true}); PatientInsuranceDetailsResponseModel insuranceCardDetailsModel; bool isInsuranceExpired = false; + bool isShowButtons = true; late InsuranceViewModel insuranceViewModel; late AppState appState; @@ -53,14 +54,15 @@ class PatientInsuranceCard extends StatelessWidget { SizedBox( width: MediaQuery.of(context).size.width * 0.4, child: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText18(isBold: true, textOverflow: TextOverflow.clip, isEnglishOnly: true)), - Row( - children: [ - "${LocaleKeys.policyNumber.tr(context: context)}${insuranceCardDetailsModel.insurancePolicyNo}".toText12(isBold: true, color: AppColors.lightGrayColor), - ], - ), + LocaleKeys.policyNumberInsurancePage.tr(namedArgs: {'number': insuranceCardDetailsModel.insurancePolicyNo.toString()}).toText12(isBold: true, color: AppColors.lightGrayColor, isEnglishOnly: true), + // Row( + // children: [ + // "${LocaleKeys.policyNumber.tr(context: context)}${insuranceCardDetailsModel.insurancePolicyNo}".toText12(isBold: true, color: AppColors.lightGrayColor), + // ], + // ), ], ), - AppCustomChipWidget( + isShowButtons ? AppCustomChipWidget( icon: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!) ? AppAssets.cancel_circle_icon : insuranceViewModel.isInsuranceActive @@ -120,7 +122,7 @@ class PatientInsuranceCard extends StatelessWidget { ? AppColors.successColor.withOpacity(0.1) : AppColors.warningColorYellow.withOpacity(0.1), labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceViewModel.isInsuranceActive ? 6.w : 0.w), - ).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading), + ).toShimmer2(isShow: insuranceViewModel.isInsuranceLoading) : SizedBox.shrink(), // AppCustomChipWidget( // icon: isInsuranceExpired ? AppAssets.cancel_circle_icon : AppAssets.insurance_active_icon, // labelText: isInsuranceExpired ? LocaleKeys.insuranceExpired.tr(context: context) : LocaleKeys.insuranceActive.tr(context: context), @@ -133,10 +135,10 @@ class PatientInsuranceCard extends StatelessWidget { ], ), SizedBox(height: 12.h), - insuranceCardDetailsModel.groupName!.toText12(isBold: true), + insuranceCardDetailsModel.groupName!.toText12(isBold: true, isEnglishOnly: true), Row( children: [ - (insuranceCardDetailsModel.companyName!.length > 40 ? "${insuranceCardDetailsModel.companyName!.substring(0, 40)}..." : insuranceCardDetailsModel.companyName!).toText12(isBold: true), + (insuranceCardDetailsModel.companyName!.length > 40 ? "${insuranceCardDetailsModel.companyName!.substring(0, 40)}..." : insuranceCardDetailsModel.companyName!).toText12(isBold: true, isEnglishOnly: true), SizedBox( width: 6.h, ), @@ -147,7 +149,7 @@ class PatientInsuranceCard extends StatelessWidget { borderRadius: 50.r, ), child: ((insuranceCardDetailsModel.subCategoryDesc ?? "").length > 10 ? (insuranceCardDetailsModel.subCategoryDesc ?? "").substring(0, 10) : (insuranceCardDetailsModel.subCategoryDesc ?? "")) - .toText8(isBold: true, color: AppColors.whiteColor), + .toText8(isBold: true, color: AppColors.whiteColor, isEnglishOnly: true), ), ], ), @@ -168,7 +170,7 @@ class PatientInsuranceCard extends StatelessWidget { ), SizedBox(height: 10.h), isInsuranceExpired - ? CustomButton( + ? isShowButtons ? CustomButton( icon: AppAssets.update_insurance_card_icon, iconColor: AppColors.warningColorYellow, iconSize: 15.h, @@ -190,11 +192,11 @@ class PatientInsuranceCard extends StatelessWidget { borderColor: AppColors.warningColorYellow.withOpacity(0.0), textColor: AppColors.warningColorYellow, fontSize: 14, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, - ) + ) : SizedBox.shrink() : Container(), ], ), diff --git a/lib/presentation/lab/alphabeticScroll.dart b/lib/presentation/lab/alphabeticScroll.dart index 95d9da45..933f399c 100644 --- a/lib/presentation/lab/alphabeticScroll.dart +++ b/lib/presentation/lab/alphabeticScroll.dart @@ -45,41 +45,55 @@ class _AlphabetScrollPageState extends State { void initState() { super.initState(); scheduleMicrotask((){ - for(var char in widget.alpahbetsAvailable){ - data[char] = widget.details.where((element)=>element.description?.toLowerCase().startsWith(char.toLowerCase()) == true).toList(); - - } - setState((){}); - + _rebuildData(); }); - itemPositionsListener.itemPositions.addListener((){ + itemPositionsListener.itemPositions.addListener(_onPositionChanged); + } + + @override + void didUpdateWidget(covariant AlphabeticScroll oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.details != widget.details || oldWidget.alpahbetsAvailable != widget.alpahbetsAvailable) { + _rebuildData(); + } + } - final positions = itemPositionsListener.itemPositions.value; + void _rebuildData() { + data.clear(); + for (var char in widget.alpahbetsAvailable) { + data[char] = widget.details + .where((element) => element.description?.toLowerCase().startsWith(char.toLowerCase()) == true) + .toList(); + } + if (_activeIndex >= widget.alpahbetsAvailable.length) { + _activeIndex = 0; + } + setState(() {}); + } - if (positions.isEmpty) return; + void _onPositionChanged() { + final positions = itemPositionsListener.itemPositions.value; - // Get FIRST visible item (top-most) - final firstVisible = positions - .where((p) => p.itemTrailingEdge > 0) // visible - .reduce((min, p) => - p.itemLeadingEdge < min.itemLeadingEdge ? p : min); + if (positions.isEmpty) return; - if(_activeIndex == firstVisible.index) return ; - setState(() { - _activeIndex = firstVisible.index; - }); + // Get FIRST visible item (top-most) + final firstVisible = positions + .where((p) => p.itemTrailingEdge > 0) // visible + .reduce((min, p) => + p.itemLeadingEdge < min.itemLeadingEdge ? p : min); - print("Active index = $_activeIndex"); + if(_activeIndex == firstVisible.index) return ; + setState(() { + _activeIndex = firstVisible.index; }); + + print("Active index = $_activeIndex"); } @override void dispose() { - itemPositionsListener.itemPositions.removeListener((){ - - }); + itemPositionsListener.itemPositions.removeListener(_onPositionChanged); super.dispose(); - } void _scrollToLetter(String letter) async { @@ -145,7 +159,6 @@ class _AlphabetScrollPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children:List.generate(widget.alpahbetsAvailable.length, (i) { final isActive = (i == _activeIndex); - return GestureDetector( onTap: () { setState(() => _activeIndex = i); @@ -161,7 +174,7 @@ class _AlphabetScrollPageState extends State { child: Opacity( opacity: isActive ? 1.0 : 0.5, child: widget.alpahbetsAvailable[i].toUpperCase().toText14( - color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor + color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor, isEnglishOnly: true ), ), ); diff --git a/lib/presentation/lab/lab_order_by_test.dart b/lib/presentation/lab/lab_order_by_test.dart index ac6f0f3b..7740a596 100644 --- a/lib/presentation/lab/lab_order_by_test.dart +++ b/lib/presentation/lab/lab_order_by_test.dart @@ -11,6 +11,7 @@ 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/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'dart:ui' as ui; class LabOrderByTest extends StatelessWidget { final VoidCallback onTap; @@ -36,15 +37,21 @@ class LabOrderByTest extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - '${tests!.description}'.toText16(isBold: true), + '${tests!.description}'.toText16(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), - (appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(fontWeight: FontWeight.w500), + (appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(isBold: true), SizedBox(height: 8.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ AppCustomChipWidget( - richText: '${"${LocaleKeys.lastTested.tr(context: context)}:"} ${DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false)}'.toText12(fontWeight: FontWeight.w500), + richText: Row( + children: [ + "${LocaleKeys.lastTested.tr(context: context)}: ".toText12(isBold: true), + Directionality( + textDirection: ui.TextDirection.ltr,child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false).toText12(isBold: true, isEnglishOnly: true)) + ], + ), backgroundColor: AppColors.greyLightColor, textColor: AppColors.textColor, ), @@ -67,7 +74,7 @@ class LabOrderByTest extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart index 900477e4..c635246e 100644 --- a/lib/presentation/lab/lab_orders_page.dart +++ b/lib/presentation/lab/lab_orders_page.dart @@ -1 +1 @@ -import 'dart:async'; import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; 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/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/appbar/collapsing_toolbar.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.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:provider/provider.dart'; import 'alphabeticScroll.dart'; import 'dart:ui' as ui; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // fontWeight: FontWeight.w500, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // fontWeight: FontWeight.w500, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.patientLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.patientLabOrders[index]; final isExpanded = expandedIndex == index; return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, margin: EdgeInsets.symmetric(vertical: 8.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: InkWell( onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded(child: (group.doctorName ?? group.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}".toText14(weight: FontWeight.w500), SizedBox(height: 4.h), order.testDetails![index].testDescriptionEn!.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), // Row( // mainAxisSize: MainAxisSize.min, // children: [ // Image.network( // order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", // width: 24.w, // height: 24.h, // fit: BoxFit.cover, // ).circle(100), // SizedBox(width: 8.h), // Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), // ], // ), // SizedBox(height: 8.h), // Wrap( // direction: Axis.horizontal, // spacing: 4.h, // runSpacing: 4.h, // children: [ // AppCustomChipWidget( // labelText: ("${LocaleKeys.orderNo.tr(context: context)}: ${order.orderNo!}"), isEnglishOnly: true, // ), // Directionality( // textDirection: ui.TextDirection.ltr, // child: AppCustomChipWidget( // labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // isEnglishOnly: true, // )), // AppCustomChipWidget( // labelText: labViewModel.isSortByClinic ? (order.projectName ?? "") : (order.clinicDescription ?? ""), // ), // ], // ), // // Row( // // children: [ // // CustomButton( // // text: ("Order No: ".needTranslation + order.orderNo!), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // SizedBox(width: 8.h), // // CustomButton( // // text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // // SizedBox(height: 8.h), // // Row( // // children: [ // // CustomButton( // // text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // fontWeight: FontWeight.w500, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // SizedBox(height: 12.h), // Row( // children: [ // Expanded(flex: 2, child: SizedBox()), // // Expanded( // // flex: 1, // // child: Container( // // height: 40.h, // // width: 40.w, // // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( // // color: AppColors.textColor, // // borderRadius: 12, // // ), // // child: Padding( // // padding: EdgeInsets.all(12.h), // // child: Transform.flip( // // flipX: _appState.isArabic(), // // child: Utils.buildSvgWithAssets( // // icon: AppAssets.forward_arrow_icon_small, // // iconColor: AppColors.whiteColor, // // fit: BoxFit.contain, // // ), // // ), // // ), // // ).onPress(() { // // model.currentlySelectedPatientOrder = order; // // labProvider.getPatientLabResultByHospital(order); // // labProvider.getPatientSpecialResult(order); // // Navigator.of(context).push( // // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // // ); // // }), // // ) // // Expanded( // flex: 2, // child: CustomButton( // icon: AppAssets.view_report_icon, // iconColor: AppColors.primaryRedColor, // iconSize: 16.h, // text: LocaleKeys.viewResults.tr(context: context), // onPressed: () { // labViewModel.currentlySelectedPatientOrder = order; // labProvider.getPatientLabResultByHospital(order); // labProvider.getPatientSpecialResult(order); // Navigator.of(context).push( // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // ); // }, // 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, // ), // ) // ], // ), // SizedBox(height: 12.h), // Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), // SizedBox(height: 12.h), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, fontWeight: FontWeight.w500, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.indexedCharacterForUniqueTest, details: labViewModel.uniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file +import 'dart:async'; import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; 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/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart'; import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/appbar/collapsing_toolbar.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.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:provider/provider.dart'; import 'alphabeticScroll.dart'; import 'dart:ui' as ui; class LabOrdersPage extends StatefulWidget { const LabOrdersPage({super.key}); @override State createState() => _LabOrdersPageState(); } class _LabOrdersPageState extends State { late LabViewModel labProvider; late DateRangeSelectorRangeViewModel rangeViewModel; late AppState _appState; List?> labSuggestions = []; int? expandedIndex; String? selectedFilterText = ''; int activeIndex = 0; @override void initState() { scheduleMicrotask(() { labProvider.initLabProvider(); }); super.initState(); } @override Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); rangeViewModel = Provider.of(context); _appState = getIt(); return CollapsingListView( title: LocaleKeys.labResults.tr(context: context), search: () async { if (labProvider.isLabOrdersLoading) { return; } else { String? value = await Navigator.of(context).push( CustomPageRoute( page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions), fullScreenDialog: true, direction: AxisDirection.down, ), ); if (value != null) { selectedFilterText = value; labProvider.filterLabReports(value); } } }, child: Consumer( builder: (context, labViewModel, child) { return SingleChildScrollView( physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.all(24.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: CustomTabBar( activeTextColor: Color(0xffED1C2B), activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), tabs: [ CustomTabBarModel(null, LocaleKeys.byVisit.tr()), CustomTabBarModel(null, LocaleKeys.byTest.tr()), // CustomTabBarModel(null, "Completed".needTranslation), ], onTabChange: (index) { activeIndex = index; setState(() {}); }, ), ), ], ), if (activeIndex == 0) Padding( padding: EdgeInsets.symmetric(vertical: 10.h), child: Row( children: [ // CustomButton( // text: LocaleKeys.byClinic.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(true); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor, // borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12.f, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // SizedBox(width: 8.h), // CustomButton( // text: LocaleKeys.byHospital.tr(context: context), // onPressed: () { // labViewModel.setIsSortByClinic(false); // }, // backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor, // borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), ], ), ), SizedBox(height: 8.h), selectedFilterText!.isNotEmpty ? Column( children: [ AppCustomChipWidget( labelText: selectedFilterText!, backgroundColor: AppColors.alertColor, textColor: AppColors.whiteColor, deleteIcon: AppAssets.close_bottom_sheet_icon, deleteIconColor: AppColors.whiteColor, deleteIconHasColor: true, isEnglishOnly: true, onDeleteTap: () { selectedFilterText = ""; labProvider.filterLabReports(""); }, ), SizedBox(height: 8.h), ], ) : SizedBox(), activeIndex == 0 ? // By Visit - show grouped view when available labViewModel.isLabOrdersLoading ? ListView.builder( shrinkWrap: true, physics: AlwaysScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: 5, itemBuilder: (context, index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, ), ) : (labViewModel.patientLabOrders.isNotEmpty ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemCount: labViewModel.filteredLabOrders.length, itemBuilder: (context, index) { final group = labViewModel.filteredLabOrders[index]; final isExpanded = expandedIndex == index; return AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 500), child: SlideAnimation( verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, margin: EdgeInsets.symmetric(vertical: 8.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true), child: InkWell( onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ AppCustomChipWidget( richText: Row( children: [ "${group.testDetails!.length} ".toText10(color: AppColors.successColor, isEnglishOnly: true), LocaleKeys.tests.tr(context: context).toText10(color: AppColors.successColor), ], ), labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}", backgroundColor: AppColors.successColor.withOpacity(0.1), textColor: AppColors.successColor, ), AppCustomChipWidget( labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}", backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ) ], ), Icon(isExpanded ? Icons.expand_less : Icons.expand_more), ], ), SizedBox(height: 8.h), Row( mainAxisSize: MainAxisSize.min, children: [ Image.network( group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", width: 24.w, height: 24.h, fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), Expanded(child: (group.doctorName ?? group.doctorNameEnglish ?? "").toString().toText14(isBold: true)), ], ), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, spacing: 4.h, runSpacing: 4.h, children: [ Directionality( textDirection: ui.TextDirection.ltr, child: AppCustomChipWidget( labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(group.orderDate ?? ""), false), isEnglishOnly: true, )), AppCustomChipWidget( labelText: (group.projectName ?? ""), ), AppCustomChipWidget( labelText: (group.clinicDescription ?? ""), ), ], ), ], ), ), AnimatedSwitcher( duration: Duration(milliseconds: 500), switchInCurve: Curves.easeIn, switchOutCurve: Curves.easeOut, transitionBuilder: (Widget child, Animation animation) { return FadeTransition( opacity: animation, child: SizeTransition( sizeFactor: animation, axisAlignment: 0.0, child: child, ), ); }, child: isExpanded ? Container( key: ValueKey(index), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h), child: Column( children: [ ListView.separated( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), padding: EdgeInsets.zero, itemBuilder: (cxt, index) { PatientLabOrdersResponseModel order = group; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ "• ${order.testDetails![index].description!}".toText14(isBold: true, isEnglishOnly: true), SizedBox(height: 4.h), order.testDetails![index].testDescriptionEn!.toText12(isBold: true, color: AppColors.textColorLight, isEnglishOnly: true), // Row( // mainAxisSize: MainAxisSize.min, // children: [ // Image.network( // order.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", // width: 24.w, // height: 24.h, // fit: BoxFit.cover, // ).circle(100), // SizedBox(width: 8.h), // Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(isBold: true)), // ], // ), // SizedBox(height: 8.h), // Wrap( // direction: Axis.horizontal, // spacing: 4.h, // runSpacing: 4.h, // children: [ // AppCustomChipWidget( // labelText: ("${LocaleKeys.orderNo.tr(context: context)}: ${order.orderNo!}"), isEnglishOnly: true, // ), // Directionality( // textDirection: ui.TextDirection.ltr, // child: AppCustomChipWidget( // labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // isEnglishOnly: true, // )), // AppCustomChipWidget( // labelText: labViewModel.isSortByClinic ? (order.projectName ?? "") : (order.clinicDescription ?? ""), // ), // ], // ), // // Row( // // children: [ // // CustomButton( // // text: ("Order No: ".needTranslation + order.orderNo!), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // isBold: true, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // SizedBox(width: 8.h), // // CustomButton( // // text: DateUtil.formatDateToDate(DateUtil.convertStringToDate(order.orderDate ?? ""), false), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // isBold: true, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // // SizedBox(height: 8.h), // // Row( // // children: [ // // CustomButton( // // text: model.isSortByClinic ? (order.clinicDescription ?? "") : (order.projectName ?? ""), // // onPressed: () {}, // // backgroundColor: AppColors.greyColor, // // borderColor: AppColors.greyColor, // // textColor: AppColors.blackColor, // // fontSize: 10, // // isBold: true, // // borderRadius: 8, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 24.h, // // ), // // ], // // ), // SizedBox(height: 12.h), // Row( // children: [ // Expanded(flex: 2, child: SizedBox()), // // Expanded( // // flex: 1, // // child: Container( // // height: 40.h, // // width: 40.w, // // decoration: RoundedRectangleBorder().toSmoothCornerDecoration( // // color: AppColors.textColor, // // borderRadius: 12, // // ), // // child: Padding( // // padding: EdgeInsets.all(12.h), // // child: Transform.flip( // // flipX: _appState.isArabic(), // // child: Utils.buildSvgWithAssets( // // icon: AppAssets.forward_arrow_icon_small, // // iconColor: AppColors.whiteColor, // // fit: BoxFit.contain, // // ), // // ), // // ), // // ).onPress(() { // // model.currentlySelectedPatientOrder = order; // // labProvider.getPatientLabResultByHospital(order); // // labProvider.getPatientSpecialResult(order); // // Navigator.of(context).push( // // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // // ); // // }), // // ) // // Expanded( // flex: 2, // child: CustomButton( // icon: AppAssets.view_report_icon, // iconColor: AppColors.primaryRedColor, // iconSize: 16.h, // text: LocaleKeys.viewResults.tr(context: context), // onPressed: () { // labViewModel.currentlySelectedPatientOrder = order; // labProvider.getPatientLabResultByHospital(order); // labProvider.getPatientSpecialResult(order); // Navigator.of(context).push( // CustomPageRoute(page: LabResultByClinic(labOrder: order)), // ); // }, // backgroundColor: AppColors.secondaryLightRedColor, // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 14, // isBold: true, // borderRadius: 12, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, // ), // ) // ], // ), // SizedBox(height: 12.h), // Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), // SizedBox(height: 12.h), ], ).paddingOnly(top: 16, bottom: 16); }, separatorBuilder: (cxt, index) => Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h), itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length), SizedBox(height: 16.h), CustomButton( text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})", onPressed: () { labProvider.currentlySelectedPatientOrder = group; labProvider.getPatientLabResultByHospital(group); labProvider.getPatientSpecialResult(group); Navigator.of(context).push( CustomPageRoute( page: LabResultByClinic(labOrder: group), ), ); }, backgroundColor: AppColors.infoColor.withAlpha(20), borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, iconSize: 14.h, icon: AppAssets.view_report_icon, iconColor: AppColors.infoColor, ), SizedBox(height: 16.h), ], ), ) : SizedBox.shrink(), ), ], ), ), ), ), )); }, ) : Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context))) : // By Test or other tabs keep existing behavior (labViewModel.isLabOrdersLoading) ? Column( children: List.generate( 5, (index) => LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, )), ) : AlphabeticScroll( alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest, details: labViewModel.filteredUniqueTestsList, labViewModel: labViewModel, rangeViewModel: rangeViewModel, appState: _appState, ) ], )); }, ), ); } Color getLabOrderStatusColor(num status) { switch (status) { case 44: return AppColors.warningColorYellow; case 45: return AppColors.warningColorYellow; case 16: return AppColors.successColor; case 17: return AppColors.successColor; default: return AppColors.greyColor; } } String getLabOrderStatusText(num status) { switch (status) { case 44: return LocaleKeys.resultsPending.tr(context: context); case 45: return LocaleKeys.resultsPending.tr(context: context); case 16: return LocaleKeys.resultsAvailable.tr(context: context); case 17: return LocaleKeys.resultsAvailable.tr(context: context); default: return ""; } } getLabSuggestions(LabViewModel model) { if (model.patientLabOrders.isEmpty) { return []; } return model.patientLabOrders.map((m) => m.testDetails).toList(); } } \ No newline at end of file diff --git a/lib/presentation/lab/lab_result_item_view.dart b/lib/presentation/lab/lab_result_item_view.dart index db80918b..554bd8f2 100644 --- a/lib/presentation/lab/lab_result_item_view.dart +++ b/lib/presentation/lab/lab_result_item_view.dart @@ -118,7 +118,7 @@ class LabResultItemView extends StatelessWidget { // ...labOrder!.testDetails!.map((detail) { // return Padding( // padding: EdgeInsets.only(bottom: 8.h), - // child: '● ${detail.description}'.toText14(weight: FontWeight.w500), + // child: '● ${detail.description}'.toText14(isBold: true), // ); // }).toList(), // SizedBox(height: 16.h), diff --git a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart index 07b9fb65..7bb0cf32 100644 --- a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart +++ b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart @@ -8,6 +8,8 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart'; import 'package:provider/provider.dart' show Selector, Provider, ReadContext; +import '../../../widgets/date_range_selector/viewmodel/date_range_view_model.dart'; + class LabResultList extends StatelessWidget { late LabViewModel model; @@ -27,7 +29,8 @@ class LabResultList extends StatelessWidget { shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) { var labItem = list[index]; return LabOrderResultItem(onTap: () { - model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.testShortDescription!, labItem.uOM ?? ""); + context.read().flush(); + model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.testShortDescription ?? "", labItem.uOM ?? ""); }, tests: labItem, index: index, diff --git a/lib/presentation/lab/lab_result_via_clinic/lab_ai_analysis_detailed_page.dart b/lib/presentation/lab/lab_result_via_clinic/lab_ai_analysis_detailed_page.dart index 2a4fd66d..8d25ef37 100644 --- a/lib/presentation/lab/lab_result_via_clinic/lab_ai_analysis_detailed_page.dart +++ b/lib/presentation/lab/lab_result_via_clinic/lab_ai_analysis_detailed_page.dart @@ -30,6 +30,7 @@ class _LabAiAnalysisDetailedPageState extends State { Widget build(BuildContext context) { labProvider = Provider.of(context, listen: false); return CollapsingListView( + isClose: true, title: LocaleKeys.aiAnalysis.tr(context: context), child: Consumer( builder: (context, model, child) { @@ -122,7 +123,7 @@ class _LabAiAnalysisDetailedPageState extends State { Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration(color: _getPriorityColor(rec.priority), borderRadius: BorderRadius.circular(4.r)), - child: rec.priority!.toText12(fontWeight: FontWeight.w600, color: _getPriorityTextColor(rec.priority)), + child: rec.priority!.toText12(isBold: true, color: _getPriorityTextColor(rec.priority)), ), SizedBox(height: 8.h), ], @@ -172,7 +173,7 @@ class _LabAiAnalysisDetailedPageState extends State { decoration: BoxDecoration(color: Color(0xFFFBCB6E).withAlpha(38), borderRadius: BorderRadius.circular(8)), child: Padding( padding: const EdgeInsets.all(8.0), - child: (risk.risk ?? "").toText10(weight: FontWeight.w500, color: AppColors.blackColor), + child: (risk.risk ?? "").toText10(isBold: true, color: AppColors.blackColor), ), ), ), @@ -415,7 +416,7 @@ class _LabAiAnalysisDetailedPageState extends State { Widget _buildCombinedLabCard(AiGeneratedAttribute attr) { return Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: false), padding: EdgeInsets.all(16.h), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -434,7 +435,7 @@ class _LabAiAnalysisDetailedPageState extends State { children: [ Utils.buildSvgWithAssets(icon: AppAssets.informationIcon, width: 14.w, height: 14.h), SizedBox(width: 4.w), - LocaleKeys.contextDependent.tr().toText12(fontWeight: FontWeight.w600, color: AppColors.textColor), + LocaleKeys.contextDependent.tr().toText12(isBold: true, color: AppColors.textColor), ], ), ), @@ -449,7 +450,7 @@ class _LabAiAnalysisDetailedPageState extends State { decoration: BoxDecoration(color: _getResultFlagColor(attr.calculatedResultFlag), borderRadius: BorderRadius.circular(4.r)), child: Row( children: [ - (attr.calculatedResultFlag ?? "").toText12(fontWeight: FontWeight.w600, color: _getResultTextColor(attr.calculatedResultFlag)), + (attr.calculatedResultFlag ?? "").toText12(isBold: true, color: _getResultTextColor(attr.calculatedResultFlag)), ], ), ), @@ -470,7 +471,7 @@ class _LabAiAnalysisDetailedPageState extends State { // children: [ // Consumer( // builder: (context, labViewModel, child) { - // return labViewModel.getSeverityText(attr.calculatedResultFlag ?? "").tr(context: context).toText10(weight: FontWeight.w500, color: AppColors.greyTextColor); + // return labViewModel.getSeverityText(attr.calculatedResultFlag ?? "").tr(context: context).toText10(isBold: true, color: AppColors.greyTextColor); // }, // ), // Consumer( @@ -502,13 +503,17 @@ class _LabAiAnalysisDetailedPageState extends State { Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration(color: _getResultFlagColor(attr.calculatedResultFlag), borderRadius: BorderRadius.circular(4.r)), - child: ("${LocaleKeys.calculatedValue.tr()}: ${attr.resultValue!}").toText12(fontWeight: FontWeight.w600, color: _getResultTextColor(attr.calculatedResultFlag)), + child: ("${LocaleKeys.calculatedValue.tr()}: ${attr.resultValue!}").toText12( + isBold: true, + color: _getResultTextColor(attr.calculatedResultFlag), + isEnglishOnly: true + ), ), SizedBox(width: 8.h), Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration(color: AppColors.greyLightColor, borderRadius: BorderRadius.circular(4.r)), - child: ("${LocaleKeys.unit.tr()}: ${attr.uom!}").toText12(fontWeight: FontWeight.w600, color: AppColors.textColor), + child: ("${LocaleKeys.unit.tr()}: ${attr.uom!}").toText12(isBold: true, color: AppColors.textColor), ), ], ), @@ -519,7 +524,7 @@ class _LabAiAnalysisDetailedPageState extends State { Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration(color: AppColors.greyLightColor, borderRadius: BorderRadius.circular(4.r)), - child: ("${LocaleKeys.referenceRange.tr(context: context)}: ${attr.referanceRange!.trim()}").toText12(fontWeight: FontWeight.w600, color: AppColors.textColor), + child: ("${LocaleKeys.referenceRange.tr(context: context)}: ${attr.referanceRange!.trim()}").toText12(isBold: true, color: AppColors.textColor), ), if (attr.calculationFormula != null && attr.calculationFormula!.isNotEmpty) ...[ SizedBox(height: 8.h), @@ -533,8 +538,8 @@ class _LabAiAnalysisDetailedPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ - LocaleKeys.calculationFormula.tr().toText16(weight: FontWeight.w600), - (attr.calculationFormula ?? "").toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + LocaleKeys.calculationFormula.tr().toText16(isBold: true), + (attr.calculationFormula ?? "").toText12(isBold: true, color: AppColors.textColorLight, isEnglishOnly: true), ], ), ), diff --git a/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart b/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart index 1a97d48d..84e69e53 100644 --- a/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart +++ b/lib/presentation/lab/lab_result_via_clinic/lab_order_result_item.dart @@ -40,12 +40,12 @@ class LabOrderResultItem extends StatelessWidget { // ...labOrder!.testDetails!.map((detail) { Padding( padding: EdgeInsets.only(bottom: 8.h), - child: '${tests!.description}'.toText14(weight: FontWeight.w500), + child: '${tests!.description}'.toText14(isBold: true, isEnglishOnly: true), ), - // (tests!.packageShortDescription ?? "").toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + // (tests!.packageShortDescription ?? "").toText12(isBold: true, color: AppColors.textColorLight), - ((tests!.testShortDescription != null && tests!.testShortDescription!.isNotEmpty) ? tests!.testShortDescription : tests!.packageShortDescription)! - .toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + (((tests!.testShortDescription != null && tests!.testShortDescription!.isNotEmpty) ? tests!.testShortDescription : tests!.packageShortDescription) ?? "") + .toText12(isBold: true, color: AppColors.textColorLight), SizedBox(height: 12.h), Directionality( textDirection: ui.TextDirection.ltr, @@ -82,7 +82,7 @@ class LabOrderResultItem extends StatelessWidget { "(${LocaleKeys.referenceRange.tr(context: context)}: ${tests?.referanceRange})", style: TextStyle( fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', color: AppColors.greyTextColor, ), @@ -107,27 +107,30 @@ class LabOrderResultItem extends StatelessWidget { spacing: 6.h, crossAxisAlignment: CrossAxisAlignment.start, children: [ - severityText.tr().toText10(weight: FontWeight.w500, color: AppColors.greyTextColor), + severityText.tr().toText10(isBold: true, color: AppColors.greyTextColor), Utils.buildSvgWithAssets(icon: AppAssets.lab_result_indicator, width: 21, height: 23, iconColor: iconColor), ], ), ), - CustomButton( - icon: AppAssets.view_report_icon, - iconColor: AppColors.infoColor, - iconSize: 16.h, - text: LocaleKeys.viewReport.tr(context: context), - onPressed: () { - onTap(); - }, - backgroundColor: AppColors.infoColor.withAlpha(20), - borderColor: AppColors.infoColor.withAlpha(0), - textColor: AppColors.infoColor, - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40.h, + Visibility( + visible: tests?.checkIfGraphShouldBeDisplayed() == true, + child: CustomButton( + icon: AppAssets.view_report_icon, + iconColor: AppColors.infoColor, + iconSize: 16.h, + text: LocaleKeys.viewReport.tr(context: context), + onPressed: () { + onTap(); + }, + backgroundColor: AppColors.infoColor.withAlpha(20), + borderColor: AppColors.infoColor.withAlpha(0), + textColor: AppColors.infoColor, + fontSize: 14, + fontWeight: FontWeight.w600, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40.h, + ), ), ], ), diff --git a/lib/presentation/lab/lab_result_via_clinic/lab_order_specialResult.dart b/lib/presentation/lab/lab_result_via_clinic/lab_order_specialResult.dart index 5f71a7ea..55bf8ef1 100644 --- a/lib/presentation/lab/lab_result_via_clinic/lab_order_specialResult.dart +++ b/lib/presentation/lab/lab_result_via_clinic/lab_order_specialResult.dart @@ -62,14 +62,14 @@ class LabOrderSpecialResult extends StatelessWidget { // ...labOrder!.testDetails!.map((detail) { LocaleKeys.specialResult .tr() - .toText14(weight: FontWeight.w500) + .toText14(isBold: true) .toShimmer2(isShow: isLoading), data.isEmpty ? LocaleKeys.noSpecialResult .tr() .toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight) .toShimmer2(isShow: isLoading) : HtmlWidget(data).toShimmer2(isShow: isLoading) @@ -89,7 +89,7 @@ class LabOrderSpecialResult extends StatelessWidget { "Special Results", style: TextStyle( fontSize: 18.fSize, - fontWeight: FontWeight.w600, + isBold: true, color: AppColors.blackColor), ), Container( @@ -111,7 +111,7 @@ class LabOrderSpecialResult extends StatelessWidget { ? Text("No result available".needTranslation, style: TextStyle( fontSize: 12.fSize, - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight)) .toShimmer2(isShow: isLoading) : HtmlWidget(data) diff --git a/lib/presentation/lab/lab_results/lab_result_details.dart b/lib/presentation/lab/lab_results/lab_result_details.dart index ded1e693..bb4b2d8b 100644 --- a/lib/presentation/lab/lab_results/lab_result_details.dart +++ b/lib/presentation/lab/lab_results/lab_result_details.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.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'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/route_extensions.dart'; @@ -19,23 +20,62 @@ import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calen import 'package:hmg_patient_app_new/presentation/lab/lab_results/lab_result_list_item.dart'; import 'package:hmg_patient_app_new/theme/colors.dart' show AppColors; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart'; import 'package:provider/provider.dart' show Consumer, Provider, ReadContext, Selector; +import '../../../core/common_models/data_points.dart'; import '../../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; +import 'dart:ui' as ui; -class LabResultDetails extends StatelessWidget { +class LabResultDetails extends StatefulWidget { final LabResult recentLabResult; final String? testDescription; const LabResultDetails({super.key, required this.recentLabResult, required this.testDescription}); + @override + State createState() => _LabResultDetailsState(); +} + +class _LabResultDetailsState extends State { + final GlobalKey _aiAnalysisKey = GlobalKey(); + LabOrderResponseByAi? _previousAiData; + + LabResult get recentLabResult => widget.recentLabResult; + + String? get testDescription => widget.testDescription; + @override Widget build(BuildContext context) { LabViewModel labViewModel = Provider.of(context, listen: false); final appState = getIt.get(); return Scaffold( body: Consumer(builder: (context, labVM, child) { + // Detect changes in labOrderResponseByAi + WidgetsBinding.instance.addPostFrameCallback((_) { + if (labVM.labOrderResponseByAi != null && _previousAiData == null) { + // AI data just became available — scroll to it + final ctx = _aiAnalysisKey.currentContext; + if (ctx != null) { + Scrollable.ensureVisible( + ctx, + duration: const Duration(milliseconds: 400), + curve: Curves.easeOut, + ); + } + } else if (labVM.labOrderResponseByAi == null && _previousAiData != null) { + // AI data just became null — scroll to top + final scrollable = Scrollable.maybeOf(context); + scrollable?.position.animateTo( + 0, + duration: const Duration(milliseconds: 400), + curve: Curves.easeOut, + ); + } + _previousAiData = labVM.labOrderResponseByAi; + }); + return Column( children: [ Expanded( @@ -62,12 +102,51 @@ class LabResultDetails extends StatelessWidget { children: [ LabNameAndStatus(context), getLabDescription(context), - LabGraph(context), + Selector?>( + selector: (_, model) => model.mainLabResults, + builder: (_, data, __) => Visibility( + visible: data?.isNotEmpty == true, + child: Column( + spacing: 16.h, + children: [ + LabGraph(context), + Container( + padding: EdgeInsets.symmetric(horizontal: 24.h, vertical: 16.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: true, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.history.tr(context: context).toText16(isBold: true, color: AppColors.textColor), + (labVM.isLabResultsHistoryShowMore ? "Show Less" : "Show More") + .toText14(isBold: true, color: AppColors.primaryRedColor, isUnderLine: true) + .onPress(() { + labVM.setIsLabResultsHistoryShowMore(); + }), + ], + ), + SizedBox( + height: 24.h, + ), + labHistoryList(labVM), + ], + ), + ), + ], + ), + ), + ), Selector( selector: (_, model) => model.labOrderResponseByAi, builder: (_, aiData, __) { if (aiData != null) { - return AiAnalysisWidget(data: aiData).paddingOnly(bottom: 16.h); + return AiAnalysisWidget(key: _aiAnalysisKey, data: aiData).paddingOnly(bottom: 16.h); } return const SizedBox.shrink(); }, @@ -144,7 +223,7 @@ class LabResultDetails extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (recentLabResult.testCode ?? "").toText28(isBold: true, color: AppColors.textColor, letterSpacing: -2), + (recentLabResult.testCode ?? "").toText28(isBold: true, color: AppColors.textColor, letterSpacing: -2, isEnglishOnly: true), Visibility( visible: recentLabResult.checkIfGraphShouldBeDisplayed() == true, child: Column( @@ -156,7 +235,7 @@ class LabResultDetails extends StatelessWidget { recentLabResult.calculatedResultFlag ?? "", ) .tr(context: context) - .toText10(weight: FontWeight.w500, color: AppColors.greyTextColor), + .toText10(isBold: true, color: AppColors.greyTextColor), Utils.buildSvgWithAssets( icon: AppAssets.lab_result_indicator, width: 21, @@ -170,7 +249,7 @@ class LabResultDetails extends StatelessWidget { ], ), SizedBox(height: 4.h), - ("${LocaleKeys.resultOf.tr(context: context)} ${recentLabResult.verifiedOn ?? ""}").toText11(weight: FontWeight.w500, color: AppColors.greyTextColor), + ("${LocaleKeys.resultOf.tr(context: context)} ${recentLabResult.verifiedOn ?? ""}").toText11(isBold: true, color: AppColors.greyTextColor, isEnglishOnly: true), ], ), Row( @@ -181,12 +260,16 @@ class LabResultDetails extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: [ Flexible( - child: ("${recentLabResult.resultValue} ${recentLabResult.uOM}" ?? "").toText28( - isBold: true, - color: context.read().getColor( - recentLabResult.calculatedResultFlag ?? "", - ), - letterSpacing: -2), + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: ("${recentLabResult.resultValue} ${recentLabResult.uOM}" ?? "").toText28( + isBold: true, + color: context.read().getColor( + recentLabResult.calculatedResultFlag ?? "", + ), + letterSpacing: -2, + isEnglishOnly: true), + ), ), SizedBox( width: 4.h, @@ -202,7 +285,7 @@ class LabResultDetails extends StatelessWidget { "${LocaleKeys.referenceRange.tr(context: context)}: \n${recentLabResult.referanceRange!.trim()}", style: TextStyle( fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', color: AppColors.greyTextColor, ), @@ -232,24 +315,25 @@ class LabResultDetails extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), - style: TextStyle( - fontSize: 16.f, - fontFamily: 'Poppins', - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), + (labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context)).toText16(isBold: true, color: AppColors.textColor), + // Text( + // labmodel.isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), + // style: TextStyle( + // fontSize: 16.f, + // fontFamily: 'Poppins', + // isBold: true, + // color: AppColors.textColor, + // ), + // ), Row( spacing: 16.h, children: [ //todo handle when the graph icon is being displayed - Utils.buildSvgWithAssets(icon: labmodel.isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, width: 24.h, height: 24.h).onPress(() { - if (labmodel.shouldShowGraph) { - labmodel.alterGraphVisibility(); - } - }), + // Utils.buildSvgWithAssets(icon: labmodel.isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, width: 24.h, height: 24.h).onPress(() { + // if (labmodel.shouldShowGraph) { + // labmodel.alterGraphVisibility(); + // } + // }), Utils.buildSvgWithAssets(icon: AppAssets.ic_date_filter, width: 24, height: 24).onPress(() { showCommonBottomSheetWithoutHeight( title: LocaleKeys.setTheDateRange.tr(context: context), @@ -298,7 +382,7 @@ class LabResultDetails extends StatelessWidget { } Widget historyBody(LabViewModel labmodel) { - if (labmodel.isGraphVisible && labmodel.shouldShowGraph) { + if (labmodel.shouldShowGraph) { var graphColor = labmodel.getColor(recentLabResult.calculatedResultFlag ?? "N"); return CustomGraph( dataPoints: labmodel.filteredGraphValues, @@ -310,9 +394,11 @@ class LabResultDetails extends StatelessWidget { maxY: (labmodel.maxY), minY: labmodel.minY, maxX: labmodel.filteredGraphValues.length.toDouble() - .75, - horizontalInterval: .1, + horizontalInterval: .01, + isRTL: getIt.get().isArabic(), getDrawingHorizontalLine: (value) { - value = double.parse(value.toStringAsFixed(1)); + value = double.parse(value.toStringAsFixed(2)); + // print("the value is $value"); if (value == labmodel.highRefrenceValue || value == labmodel.lowRefenceValue) { return FlLine( color: AppColors.bgGreenColor.withOpacity(0.6), @@ -327,7 +413,7 @@ class LabResultDetails extends StatelessWidget { ; }, leftLabelFormatter: (value) { - value = double.parse(value.toStringAsFixed(1)); + value = double.parse(value.toStringAsFixed(2)); return leftLabels(value.toStringAsFixed(2)); // if (value == labmodel.highRefrenceValue) { // return leftLabels(LocaleKeys.high.tr()); @@ -340,7 +426,7 @@ class LabResultDetails extends StatelessWidget { return SizedBox.shrink(); // } }, - graphColor: AppColors.graphGridColor, + graphColor: AppColors.textColor.withAlpha(90), graphShadowColor: AppColors.graphGridColor.withOpacity(.5), graphGridColor: graphColor.withOpacity(.4), bottomLabelFormatter: (value, data) { @@ -360,9 +446,8 @@ class LabResultDetails extends StatelessWidget { minX: (labmodel.filteredGraphValues.length == 1) ? null : -.2, scrollDirection: Axis.horizontal, height: 180.h); - } else { - return labHistoryList(labmodel); } + return SizedBox.shrink(); } List _buildRangeShades( @@ -392,17 +477,18 @@ class LabResultDetails extends StatelessWidget { Widget labHistoryList(LabViewModel labmodel) { return SizedBox( - height: labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues.length * 64 : 180.h, + height: labmodel.mainLabResults.length < 3 ? labmodel.mainLabResults.length * 64 : (!labmodel.isLabResultsHistoryShowMore ? 180.h : 220.h), child: ListView.separated( padding: EdgeInsets.zero, - itemCount: labmodel.filteredGraphValues.length, + // itemCount: labmodel.isLabResultsHistoryShowMore ? (labmodel.filteredGraphValues.length < 3 ? labmodel.filteredGraphValues : 3) : labmodel.filteredGraphValues.length, + itemCount: !labmodel.isLabResultsHistoryShowMore ? (labmodel.mainLabResults.length < 3 ? labmodel.mainLabResults.length : 3) : labmodel.mainLabResults.length, itemBuilder: (context, index) { - var data = labmodel.filteredGraphValues.reversed.toList()[index]; + var data = labmodel.mainLabResults.toList()[index]; return LabHistoryItem( - dayNameAndDate: labmodel.getFormattedDate(data.time), - result: data.actualValue, - assetUrl: labmodel.getAssetUrlWRTResult(data.referenceValue), - shouldRotateIcon: labmodel.getRotationWRTResult(data.referenceValue), + dayNameAndDate: labmodel.getFormattedDate(DateUtil.convertStringToDate(data.verifiedOnDateTime)), + result: data.resultValue??"", + assetUrl: labmodel.getAssetUrlWRTResult(data.calculatedResultFlag??""), + shouldRotateIcon: labmodel.getRotationWRTResult(data.calculatedResultFlag??""), ); }, separatorBuilder: (_, __) => Divider( @@ -429,6 +515,7 @@ class LabResultDetails extends StatelessWidget { Widget getLabDescription(BuildContext context) { return Container( + width: MediaQuery.sizeOf(context).width, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, @@ -439,8 +526,8 @@ class LabResultDetails extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, children: [ - LocaleKeys.whatIsThisResult.tr(context: context).toText16(weight: FontWeight.w600, color: AppColors.textColor), - testDescription?.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight) ?? SizedBox.shrink() + LocaleKeys.whatIsThisResult.tr(context: context).toText16(isBold: true, color: AppColors.textColor), + testDescription?.toText12(isBold: true, color: AppColors.textColorLight) ?? SizedBox.shrink() ], )); } diff --git a/lib/presentation/lab/lab_results/lab_result_list_item.dart b/lib/presentation/lab/lab_results/lab_result_list_item.dart index f2dc90e4..908d129d 100644 --- a/lib/presentation/lab/lab_results/lab_result_list_item.dart +++ b/lib/presentation/lab/lab_results/lab_result_list_item.dart @@ -24,7 +24,7 @@ class LabHistoryItem extends StatelessWidget{ dayNameAndDate, style: TextStyle( fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', color: AppColors.labelTextColor ), @@ -33,7 +33,7 @@ class LabHistoryItem extends StatelessWidget{ result, style: TextStyle( fontSize: 18.f, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', color: AppColors.textColor ), diff --git a/lib/presentation/lab/search_lab_report.dart b/lib/presentation/lab/search_lab_report.dart index d366f38f..ac559f42 100644 --- a/lib/presentation/lab/search_lab_report.dart +++ b/lib/presentation/lab/search_lab_report.dart @@ -78,8 +78,9 @@ class _SearchLabResultsContentState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ TextInputWidget( - labelText: "Search lab results", - hintText: "Type test name", + fontFamily: "Poppins", + labelText: LocaleKeys.searchLabResults.tr(context: context), + hintText: "", controller: searchEditingController, isEnable: true, prefix: null, @@ -93,7 +94,7 @@ class _SearchLabResultsContentState extends State { ), SizedBox(height: ResponsiveExtension(20).h), if (filteredSuggestions.isNotEmpty) ...[ - "Suggestions".toText16(isBold: true), + LocaleKeys.suggestions.tr(context: context).toText16(isBold: true, isEnglishOnly: true), ], ], ), @@ -146,7 +147,8 @@ class SuggestionChip extends StatelessWidget { child: label.toText12( // color: isSelected ? AppColors.textColor : Colors.black87, color: AppColors.textColor, - fontWeight: FontWeight.w500, + isBold: true, + isEnglishOnly: true ), ), ); diff --git a/lib/presentation/medical_file/eye_measurement_details_page.dart b/lib/presentation/medical_file/eye_measurement_details_page.dart index 08226095..68f261cd 100644 --- a/lib/presentation/medical_file/eye_measurement_details_page.dart +++ b/lib/presentation/medical_file/eye_measurement_details_page.dart @@ -172,7 +172,7 @@ class EyeMeasurementDetailsPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded(flex: 2, child: title.toText11(weight: FontWeight.w500)), + Expanded(flex: 2, child: title.toText11(isBold: true)), Expanded( flex: 2, child: Row( diff --git a/lib/presentation/medical_file/eye_measurements_appointments_page.dart b/lib/presentation/medical_file/eye_measurements_appointments_page.dart index d8438ba4..df30a5a3 100644 --- a/lib/presentation/medical_file/eye_measurements_appointments_page.dart +++ b/lib/presentation/medical_file/eye_measurements_appointments_page.dart @@ -25,7 +25,7 @@ class EyeMeasurementsAppointmentsPage extends StatelessWidget { return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( - title: "Eye Measurements", + title: LocaleKeys.eyeMeasurements.tr(context: context), child: SingleChildScrollView( child: Consumer(builder: (context, myAppointmentsVM, child) { return Column( diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 3e440723..d04f2ac8 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -199,11 +199,12 @@ class _MedicalFilePageState extends State { ], ), SizedBox(width: 4.h), - Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w) + Transform.flip(flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.arrowRight, height: 22.h, width: 22.w)) ], ).onPress(() { Navigator.of(context).push( CustomPageRoute( + direction: AxisDirection.down, page: FamilyMedicalScreen(), ), ); @@ -241,7 +242,12 @@ class _MedicalFilePageState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, width: 56.w, height: 56.h), + Image.asset( + (appState.getAuthenticatedUser()?.gender == 1 + ? ((appState.getAuthenticatedUser()?.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.maleImg) + : ((appState.getAuthenticatedUser()?.age ?? 0) < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg)), + width: 56.w, + height: 56.h), SizedBox(width: 8.w), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -249,7 +255,7 @@ class _MedicalFilePageState extends State { SizedBox( width: MediaQuery.of(context).size.width * 0.6, child: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" - .toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 2), + .toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 2), ), SizedBox(height: 4.h), Wrap( @@ -374,10 +380,10 @@ class _MedicalFilePageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.vitalSigns.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.vitalSigns.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), Row( children: [ - LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), SizedBox(width: 2.h), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], @@ -496,24 +502,20 @@ class _MedicalFilePageState extends State { itemPadding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 14.h), items: [ ExpandableListItem( - title: LocaleKeys.medicalServices.tr(context: context).toText18(weight: FontWeight.w600), + title: LocaleKeys.medicalServices.tr(context: context).toText18(isBold: true), expandedBackgroundColor: Colors.transparent, children: [ SizedBox(height: 10.h), getSelectedTabData(0), ], ), - ExpandableListItem( - title: LocaleKeys.medicalReports.tr(context: context).toText18( - weight: FontWeight.w600, - ), - expandedBackgroundColor: Colors.transparent, + ExpandableListItem(title: LocaleKeys.medicalReports.tr(context: context).toText18(isBold: true), expandedBackgroundColor: Colors.transparent, children: [ SizedBox(height: 10.h), getSelectedTabData(2), ]), ExpandableListItem( - title: LocaleKeys.insuranceAndPayments.tr(context: context).toText18(weight: FontWeight.w600), + title: LocaleKeys.insuranceAndPayments.tr(context: context).toText18(isBold: true), expandedBackgroundColor: Colors.transparent, children: [ SizedBox(height: 10.h), @@ -521,7 +523,7 @@ class _MedicalFilePageState extends State { ], ), ExpandableListItem( - title: LocaleKeys.healthTrackers.tr(context: context).toText18(weight: FontWeight.w600), + title: LocaleKeys.healthTrackers.tr(context: context).toText18(isBold: true), expandedBackgroundColor: Colors.transparent, children: [ SizedBox(height: 10.h), @@ -546,7 +548,7 @@ class _MedicalFilePageState extends State { doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL, doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle, name: patientAppointmentHistoryResponseModel.doctorNameObj, - nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", + nationalityFlagURL: "", speciality: [], clinicName: patientAppointmentHistoryResponseModel.clinicName, projectName: patientAppointmentHistoryResponseModel.projectName, @@ -588,10 +590,10 @@ class _MedicalFilePageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.appointmentsAndVisits.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.appointmentsAndVisits.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), Row( children: [ - LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), SizedBox(width: 2.h), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], @@ -641,7 +643,7 @@ class _MedicalFilePageState extends State { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: isFoldable || isTablet ? 50.h : 40.h, @@ -714,7 +716,7 @@ class _MedicalFilePageState extends State { ); }), SizedBox(height: 10.h), - LocaleKeys.labAndRadiology.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.labAndRadiology.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), SizedBox(height: 16.h), Row( children: [ @@ -756,10 +758,10 @@ class _MedicalFilePageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.activeMedicationsAndPrescriptions.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.activeMedicationsAndPrescriptions.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), Row( children: [ - LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), SizedBox(width: 2.w), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], @@ -871,7 +873,7 @@ class _MedicalFilePageState extends State { // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 12.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.r, // height: 40.h, // icon: AppAssets.requests, @@ -892,7 +894,7 @@ class _MedicalFilePageState extends State { // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 12.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.h, // height: 40.h, // icon: AppAssets.all_medications_icon, @@ -926,10 +928,10 @@ class _MedicalFilePageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - LocaleKeys.myDoctor.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.myDoctor.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), Row( children: [ - LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true), SizedBox(width: 2.w), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], @@ -956,7 +958,7 @@ class _MedicalFilePageState extends State { fit: BoxFit.cover, ).circle(100).toShimmer2(isShow: true, radius: 50.r), SizedBox(height: 8.h), - ("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true), + ("Dr. John Smith Smith Smith").toString().toText12(isBold: true, isCenter: true, maxLine: 2).toShimmer2(isShow: true), ], ) : myAppointmentsVM.patientMyDoctorsList.isEmpty @@ -1002,9 +1004,7 @@ class _MedicalFilePageState extends State { SizedBox(height: 8.h), Expanded( child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName) - .toString() - .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2) - .toShimmer2(isShow: false), + .toString().toText12(isBold: true, isCenter: true, maxLine: 2).toShimmer2(isShow: false), ), ], ), @@ -1019,7 +1019,7 @@ class _MedicalFilePageState extends State { LoaderBottomSheet.hideLoader(); Navigator.of(context).push( CustomPageRoute( - page: DoctorProfilePage(), + page: DoctorProfilePage(isDoctorAllowedToBook: !(myAppointmentsVM.patientMyDoctorsList[index].isLiveCareClinic ?? false)), ), ); }, onError: (err) { @@ -1041,7 +1041,7 @@ class _MedicalFilePageState extends State { ).paddingSymmetrical(0.w, 0); }), SizedBox(height: 24.h), - LocaleKeys.others.tr(context: context).toText16(weight: FontWeight.w500, letterSpacing: -0.2), + LocaleKeys.others.tr(context: context).toText16(isBold: true, letterSpacing: -0.2), SizedBox(height: 16.h), GridView( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( @@ -1151,7 +1151,7 @@ class _MedicalFilePageState extends State { borderColor: AppColors.bgGreenColor.withOpacity(0.0), textColor: AppColors.bgGreenColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: isFoldable ? 50.h : 40.h, @@ -1348,7 +1348,7 @@ class _MedicalFilePageState extends State { children: [ // Row( // children: [ - // LocaleKeys.healthTrackers.tr(context: context).toText16(weight: FontWeight.w500, color: AppColors.textColor), + // LocaleKeys.healthTrackers.tr(context: context).toText16(isBold: true, color: AppColors.textColor), // ], // ), // SizedBox(height: 16.h), @@ -1392,7 +1392,7 @@ class _MedicalFilePageState extends State { SizedBox(height: 16.h), // Row( // children: [ - // LocaleKeys.others.tr(context: context).toText16(weight: FontWeight.w500, color: AppColors.textColor), + // LocaleKeys.others.tr(context: context).toText16(isBold: true, color: AppColors.textColor), // ], // ), SizedBox(height: 16.h), @@ -1566,7 +1566,7 @@ class _MedicalFilePageState extends State { label: LocaleKeys.weight.tr(context: context), value: vitalSign.weightKg?.toString() ?? '--', unit: 'kg', - status: vitalSign.weightKg != null ? "Normal" : null, + status: vitalSign.weightKg != null ? _getWeightStatus(vitalSign.bodyMassIndex) : null, onTap: onTap, ), ), @@ -1593,6 +1593,11 @@ class _MedicalFilePageState extends State { return VitalSignUiModel.bmiStatus(bmi); } + String? _getWeightStatus(dynamic bmi) { + if (bmi == null) return 'Normal'; + return VitalSignUiModel.bmiStatus(bmi); + } + String? _getBloodPressureStatus({dynamic systolic, dynamic diastolic}) { return VitalSignUiModel.bloodPressureStatus(systolic: systolic, diastolic: diastolic); } @@ -1673,7 +1678,7 @@ class _MedicalFilePageState extends State { SizedBox(width: 3.w), unit.toText12( color: AppColors.textColor, - fontWeight: FontWeight.w500, + isBold: true, ), ], ], diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart index 4518eca5..dd7942d5 100644 --- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -24,6 +24,8 @@ import 'package:provider/provider.dart'; import 'widgets/patient_sick_leave_card.dart'; +import 'dart:ui' as ui; + class PatientSickleavesListPage extends StatefulWidget { const PatientSickleavesListPage({super.key}); @@ -70,7 +72,7 @@ class _PatientSickleavesListPageState extends State { // borderColor: model.isSickLeavesSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: model.isSickLeavesSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -85,7 +87,7 @@ class _PatientSickleavesListPageState extends State { // borderColor: model.isSickLeavesSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: model.isSickLeavesSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -144,7 +146,7 @@ class _PatientSickleavesListPageState extends State { fit: BoxFit.fill, ).circle(100), SizedBox(width: 8.h), - Expanded(child: model.patientSickLeaveList[index].doctorName!.toText14(weight: FontWeight.w500)), + Expanded(child: model.patientSickLeaveList[index].doctorName!.toText14(isBold: true)), ], ), SizedBox(height: 8.h), @@ -153,14 +155,26 @@ class _PatientSickleavesListPageState extends State { spacing: 6.h, runSpacing: 6.h, children: [ - AppCustomChipWidget( - labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientSickLeaveList[index].appointmentDate), false), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(model.patientSickLeaveList[index].appointmentDate), false), + isEnglishOnly: true, + ), ), AppCustomChipWidget( labelText: model.isSickLeavesSortByClinic ? model.patientSickLeaveList[index].projectName! : model.patientSickLeaveList[index].clinicName!, ), AppCustomChipWidget( - labelText: "${model.patientSickLeaveList[index].sickLeaveDays} Days", + richText: Row( + mainAxisSize: MainAxisSize.min, + children: [ + "${model.patientSickLeaveList[index].sickLeaveDays} ".toText10(isEnglishOnly: true), + LocaleKeys.days.tr(context: context).toText10() + ], + ), + // labelText: "${model.patientSickLeaveList[index].sickLeaveDays} ${LocaleKeys.days.tr(context: context)}", + isEnglishOnly: true, ), ], ), @@ -189,7 +203,7 @@ class _PatientSickleavesListPageState extends State { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -222,7 +236,7 @@ class _PatientSickleavesListPageState extends State { // fit: BoxFit.fill, // ).circle(100), // SizedBox(width: 8.h), - // Expanded(child: sickLeave.doctorName!.toText14(weight: FontWeight.w500)), + // Expanded(child: sickLeave.doctorName!.toText14(isBold: true)), // ], // ), // SizedBox(height: 8.h), @@ -267,7 +281,7 @@ class _PatientSickleavesListPageState extends State { // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 14, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -312,7 +326,7 @@ class _PatientSickleavesListPageState extends State { // borderColor: AppColors.greyColor, // textColor: AppColors.blackColor, // fontSize: 10, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 8, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 30.h, @@ -360,7 +374,7 @@ class _PatientSickleavesListPageState extends State { // // fit: BoxFit.fill, // // ).circle(100), // // SizedBox(width: 8.h), - // // Expanded(child: sickLeave.doctorName!.toText14(weight: FontWeight.w500)), + // // Expanded(child: sickLeave.doctorName!.toText14(isBold: true)), // // ], // // ), // // SizedBox(height: 8.h), @@ -405,7 +419,7 @@ class _PatientSickleavesListPageState extends State { // // borderColor: AppColors.secondaryLightRedColor, // // textColor: AppColors.primaryRedColor, // // fontSize: 14, - // // fontWeight: FontWeight.w500, + // // isBold: true, // // borderRadius: 12, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 40.h, diff --git a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart index 4c1a72b7..51e0cc15 100644 --- a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart @@ -44,7 +44,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { richText: Directionality( textDirection: ui.TextDirection.ltr, child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false) - .toText12(color: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, fontWeight: FontWeight.w500, isEnglishOnly: true) + .toText12(color: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, isBold: true, isEnglishOnly: true) .paddingSymmetrical(8.w, 0), ), icon: AppointmentType.isArrived(patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon, @@ -76,7 +76,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { children: [ (patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText14(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? "")).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading), (patientAppointmentHistoryResponseModel.clinicName ?? "") - .toText12(maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor) + .toText12(maxLine: 1, isBold: true, color: AppColors.greyTextColor) .toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading), ], ), @@ -108,7 +108,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { borderColor: AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction), fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -173,7 +173,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, @@ -190,7 +190,7 @@ class MedicalFileAppointmentCard extends StatelessWidget { borderColor: AppColors.greyColor, textColor: AppColors.blackColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, diff --git a/lib/presentation/medical_file/widgets/medical_file_card.dart b/lib/presentation/medical_file/widgets/medical_file_card.dart index c2551d30..84f977ce 100644 --- a/lib/presentation/medical_file/widgets/medical_file_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_card.dart @@ -11,6 +11,7 @@ class MedicalFileCard extends StatelessWidget { final String svgIcon; final double? iconSize; final bool isLargeText; + final bool isLocked; const MedicalFileCard({ super.key, @@ -20,6 +21,7 @@ class MedicalFileCard extends StatelessWidget { this.svgIcon = "", this.iconSize, this.isLargeText = false, + this.isLocked = false, }); @override @@ -37,9 +39,17 @@ class MedicalFileCard extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - Utils.buildSvgWithAssets(icon: svgIcon, width: iconS, height: iconS, fit: BoxFit.contain, applyThemeColor: false), - SizedBox(height: 8.h), - isLargeText ? label.toText13(color: textColor, isBold: true, maxLine: 2) : label.toText11(color: textColor, isBold: true, maxLine: 2), + Opacity( + opacity: isLocked ? 0.4 : 1.0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Utils.buildSvgWithAssets(icon: svgIcon, width: iconS, height: iconS, fit: BoxFit.contain, applyThemeColor: false), + SizedBox(height: 8.h), + isLargeText ? label.toText13(color: textColor, isBold: true, maxLine: 2) : label.toText11(color: textColor, isBold: true, maxLine: 2), + ], + ), + ), ], ), ); diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart index 18bd2e69..a8edeae3 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -21,6 +21,8 @@ import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; + class PatientSickLeaveCard extends StatelessWidget { PatientSickLeaveCard({super.key, required this.patientSickLeavesResponseModel, this.isLoading = false, this.isSickLeaveListPage = false}); @@ -72,11 +74,15 @@ class PatientSickLeaveCard extends StatelessWidget { spacing: 3.h, runSpacing: 4.h, children: [ - AppCustomChipWidget( - icon: AppAssets.doctor_calendar_icon, - labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientSickLeavesResponseModel.appointmentDate), false), - labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h), - ).toShimmer2(isShow: isLoading), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientSickLeavesResponseModel.appointmentDate), false), + labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h), + isEnglishOnly: true, + ).toShimmer2(isShow: isLoading), + ), AppCustomChipWidget(labelText: isLoading ? "Pending Activation" : patientSickLeavesResponseModel.clinicName!).toShimmer2(isShow: isLoading), ], ), @@ -118,7 +124,7 @@ class PatientSickLeaveCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart index fd3ba7f9..b8fe4369 100644 --- a/lib/presentation/medical_report/medical_reports_page.dart +++ b/lib/presentation/medical_report/medical_reports_page.dart @@ -63,7 +63,7 @@ class _MedicalReportsPageState extends State { // borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -78,7 +78,7 @@ class _MedicalReportsPageState extends State { // borderColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: medicalFileVM.isMedicalReportsSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -100,7 +100,7 @@ class _MedicalReportsPageState extends State { borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: medicalFileVM.selectedMedicalReportsTabIndex == 0 ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -118,7 +118,7 @@ class _MedicalReportsPageState extends State { borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: medicalFileVM.selectedMedicalReportsTabIndex == 1 ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -136,7 +136,7 @@ class _MedicalReportsPageState extends State { borderColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), textColor: medicalFileVM.selectedMedicalReportsTabIndex == 2 ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -200,7 +200,7 @@ class _MedicalReportsPageState extends State { borderColor: AppColors.greyColor, textColor: AppColors.blackColor, fontSize: 10, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 8, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 30.h, @@ -248,7 +248,7 @@ class _MedicalReportsPageState extends State { fit: BoxFit.fill, ).circle(100), SizedBox(width: 8.h), - Expanded(child: report.doctorName!.toText14(weight: FontWeight.w500)), + Expanded(child: report.doctorName!.toText14(isBold: true)), ], ), SizedBox(height: 8.h), @@ -295,7 +295,7 @@ class _MedicalReportsPageState extends State { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -372,7 +372,7 @@ class _MedicalReportsPageState extends State { borderColor: AppColors.primaryRedColor, textColor: Colors.white, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, height: 46.h, icon: AppAssets.requests, @@ -419,6 +419,7 @@ class _MedicalReportsPageState extends State { callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, + isAutoDismiss: true ); } } diff --git a/lib/presentation/medical_report/widgets/patient_medical_report_card.dart b/lib/presentation/medical_report/widgets/patient_medical_report_card.dart index 282a2d60..49cb0e3a 100644 --- a/lib/presentation/medical_report/widgets/patient_medical_report_card.dart +++ b/lib/presentation/medical_report/widgets/patient_medical_report_card.dart @@ -93,7 +93,7 @@ class PatientMedicalReportCard extends StatelessWidget { borderColor: AppColors.secondaryLightRedColor, textColor: AppColors.primaryRedColor, fontSize: 14, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.h, height: 40.h, icon: AppAssets.download_1, @@ -112,7 +112,7 @@ class PatientMedicalReportCard extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.h, height: 40.h, icon: AppAssets.download_1, diff --git a/lib/presentation/monthly_report/monthly_report.dart b/lib/presentation/monthly_report/monthly_report.dart index 998d7f48..1bbaf178 100644 --- a/lib/presentation/monthly_report/monthly_report.dart +++ b/lib/presentation/monthly_report/monthly_report.dart @@ -79,10 +79,10 @@ class MonthlyReport extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), + LocaleKeys.email.tr(context: context).toText12(color: AppColors.greyTextColor, isBold: true), SizedBox( width: MediaQuery.of(context).size.width * 0.7, - child: "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, weight: FontWeight.w500, maxlines: 2), + child: "${appState.getAuthenticatedUser()!.emailAddress}".toText16(color: AppColors.textColor, isBold: true, maxlines: 2), ), ], ), @@ -98,7 +98,7 @@ class MonthlyReport extends StatelessWidget { Utils.buildSvgWithAssets(icon: AppAssets.prescription_remarks_icon, width: 18.w, height: 18.h), SizedBox(width: 9.h), Expanded( - child: LocaleKeys.monthlyHealthSummaryReportDisclaimer.tr(context: context).toText10(weight: FontWeight.w500, color: AppColors.greyTextColorLight), + child: LocaleKeys.monthlyHealthSummaryReportDisclaimer.tr(context: context).toText10(isBold: true, color: AppColors.greyTextColorLight), ), ], ), @@ -172,7 +172,7 @@ class MonthlyReport extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, height: 46.h, iconColor: AppColors.whiteColor, diff --git a/lib/presentation/my_family/my_family.dart b/lib/presentation/my_family/my_family.dart index dca75747..d25154c7 100644 --- a/lib/presentation/my_family/my_family.dart +++ b/lib/presentation/my_family/my_family.dart @@ -43,6 +43,7 @@ class _FamilyMedicalScreenState extends State { AppState appState = getIt.get(); return CollapsingListView( + isClose: true, title: LocaleKeys.familyTitle.tr(context: context), bottomChild: appState.getAuthenticatedUser()!.isParentUser! ? Container( @@ -66,7 +67,7 @@ class _FamilyMedicalScreenState extends State { icon: AppAssets.add_icon, fontSize: 16.f, borderRadius: 12.r, - fontWeight: FontWeight.w500)) + isBold: true)) .paddingOnly(bottom: 20.h) : SizedBox(), child: Column( diff --git a/lib/presentation/my_family/widget/family_cards.dart b/lib/presentation/my_family/widget/family_cards.dart index a621168f..39344f30 100644 --- a/lib/presentation/my_family/widget/family_cards.dart +++ b/lib/presentation/my_family/widget/family_cards.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -79,8 +80,7 @@ class _FamilyCardsState extends State { children: [ Utils.buildSvgWithAssets(icon: AppAssets.alertSquare), SizedBox(width: 8.h), - LocaleKeys.whoCanViewMyMedicalFile.tr(context: context).toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500) - .onPress(() { + LocaleKeys.whoCanViewMyMedicalFile.tr(context: context).toText14(color: AppColors.textColor, isUnderLine: true, isBold: true).onPress(() { dialogService.showFamilyBottomSheetWithoutHWithChild( label: LocaleKeys.manageFiles.tr(context: context), message: "", @@ -136,18 +136,13 @@ 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), + Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [ + (profile.patientName ?? "").toText14(isBold: true, isCenter: false, maxlines: 1), (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), - ]), + isCenter: false, maxLine: 1, isBold: true, color: AppColors.greyTextColor), + ]), SizedBox(height: 8.h), CustomChipWidget( height: 30.h, @@ -181,7 +176,12 @@ class _FamilyCardsState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, width: 56.w, height: 56.h), + Image.asset( + (appState.getAuthenticatedUser()?.gender == 1 + ? ((appState.getAuthenticatedUser()?.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.maleImg) + : ((appState.getAuthenticatedUser()?.age ?? 0) < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg)), + width: 56.w, + height: 56.h), SizedBox(width: 8.w), Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -189,7 +189,7 @@ class _FamilyCardsState extends State { SizedBox( width: MediaQuery.of(context).size.width * 0.6, child: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}" - .toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 2), + .toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 2), ), SizedBox(height: 4.h), Wrap( @@ -311,14 +311,19 @@ class _FamilyCardsState extends State { crossAxisCount: 2, crossAxisSpacing: 10.w, mainAxisSpacing: 10.h, - childAspectRatio: widget.isShowDetails ? 0.56.h : 0.74.h, + // childAspectRatio: widget.isShowDetails ? 0.56.h : 0.74.h, + // childAspectRatio: 0.7.h, ), padding: EdgeInsets.only(bottom: 20.h), itemBuilder: (context, index) { final profile = widget.profileViewList![index]; final isActive = (profile.responseId == appState.getAuthenticatedUser()?.patientId); final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; - final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); + print(jsonEncode(appState.getUserSwitchPayloads)); + final isSuperUser = appState.getUserSwitchPayloads; + // TODO: canSwitch functionality is currently disabled - set to true to allow all switches + // Original logic: final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); + final canSwitch = true; // Disabled - allows all users to switch return Container( padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), @@ -339,22 +344,22 @@ class _FamilyCardsState extends State { height: 70.h, ), SizedBox(height: 8.h), - (profile.patientName ?? "Unknown").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), - SizedBox(height: 8.h), - CustomChipWidget( - chipType: ChipTypeEnum.alert, - backgroundColor: AppColors.lightGrayBGColor, - chipText: "Relation: ${profile.relationship ?? " N/A"}", - iconAsset: AppAssets.heart, - isShowBorder: false, - borderRadius: 8.h, - textColor: AppColors.textColor), + (profile.patientName ?? "Unknown").toText14(isBold: true, isCenter: true, maxlines: 1), + // SizedBox(height: 8.h), + // CustomChipWidget( + // chipType: ChipTypeEnum.alert, + // backgroundColor: AppColors.lightGrayBGColor, + // chipText: "${LocaleKeys.relationship.tr(context: context)}: ${profile.relationship ?? " N/A"}", + // iconAsset: AppAssets.heart, + // isShowBorder: false, + // borderRadius: 8.h, + // textColor: AppColors.textColor), widget.isShowDetails ? SizedBox(height: 4.h) : SizedBox(), widget.isShowDetails ? CustomChipWidget( chipType: ChipTypeEnum.alert, backgroundColor: AppColors.lightGrayBGColor, - chipText: "Age: ${profile.age ?? "N/A"} Years", + chipText: "${LocaleKeys.age.tr(context: context)}: ${profile.age ?? "N/A"} ${LocaleKeys.years.tr(context: context)}", isShowBorder: false, borderRadius: 8.h, textColor: AppColors.textColor, @@ -468,15 +473,15 @@ class _FamilyCardsState extends State { Wrap( alignment: WrapAlignment.start, children: [ - (profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), - SizedBox(width: 2.w,), - (getStatusTextByRequest( - FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)) - .toText14( + (profile.patientName ?? "").toText14(isBold: true, isCenter: true, maxlines: 1), + SizedBox( + width: 2.w, + ), + (getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)).toText14( isBold: false, isCenter: true, maxlines: 1, - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.greyTextColor, ), ], diff --git a/lib/presentation/my_invoices/my_invoices_details_page.dart b/lib/presentation/my_invoices/my_invoices_details_page.dart index 0410d311..62e5a336 100644 --- a/lib/presentation/my_invoices/my_invoices_details_page.dart +++ b/lib/presentation/my_invoices/my_invoices_details_page.dart @@ -18,8 +18,11 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:open_filex/open_filex.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; + class MyInvoicesDetailsPage extends StatefulWidget { GetInvoiceDetailsResponseModel getInvoiceDetailsResponseModel; GetInvoicesListResponseModel getInvoicesListResponseModel; @@ -43,7 +46,42 @@ class _MyInvoicesDetailsPageState extends State { Expanded( child: CollapsingListView( title: LocaleKeys.invoiceDetails.tr(context: context), - sendEmail: () async { + downloadInvoice: Utils.isVidaPlusProject(widget.getInvoiceDetailsResponseModel.projectID ?? 0) ? null : () { + LoaderBottomSheet.showLoader(loadingText: LocaleKeys.loadingText.tr(context: context)); + myInvoicesViewModel.downloadInvoicePDF( + setupId: widget.getInvoicesListResponseModel.setupId!, + invoiceNo: widget.getInvoicesListResponseModel.invoiceNo!, + projectID: widget.getInvoicesListResponseModel.projectID!, + onError: (err) { + LoaderBottomSheet.hideLoader(); + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: err), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + }, + onSuccess: (value) async { + LoaderBottomSheet.hideLoader(); + if (myInvoicesViewModel.downloadInvoicePDFBase64!.isNotEmpty) { + String path = await Utils.createFileFromString(myInvoicesViewModel.downloadInvoicePDFBase64!, "pdf"); + try { + OpenFilex.open(path); + } catch (ex) { + showCommonBottomSheetWithoutHeight( + context, + child: Utils.getErrorWidget(loadingText: "Cannot open file"), + callBackFunc: () {}, + isFullScreen: false, + isCloseButtonVisible: true, + ); + } + } + }, + ); + }, + sendEmail: Utils.isVidaPlusProject(widget.getInvoiceDetailsResponseModel.projectID ?? 0) ? () async { LoaderBottomSheet.showLoader(loadingText: LocaleKeys.sendingEmailPleaseWait.tr(context: context)); await myInvoicesViewModel.sendInvoiceEmail( appointmentNo: widget.getInvoiceDetailsResponseModel.appointmentNo!, @@ -56,6 +94,7 @@ class _MyInvoicesDetailsPageState extends State { callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, + isAutoDismiss: true ); }, onError: (err) { @@ -68,7 +107,7 @@ class _MyInvoicesDetailsPageState extends State { isCloseButtonVisible: true, ); }); - }, + } : null, child: SingleChildScrollView( child: Padding( padding: EdgeInsets.all(24.h), @@ -146,7 +185,7 @@ class _MyInvoicesDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (getIt().isArabic() ? widget.getInvoiceDetailsResponseModel.doctorNameN! : widget.getInvoiceDetailsResponseModel.doctorName!).toText16(isBold: true), + (getIt().isArabic() ? widget.getInvoiceDetailsResponseModel.doctorNameN ?? LocaleKeys.doctor.tr(context: context) : widget.getInvoiceDetailsResponseModel.doctorName!).toText16(isBold: true), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, @@ -154,23 +193,28 @@ class _MyInvoicesDetailsPageState extends State { runSpacing: 6.h, children: [ AppCustomChipWidget( - labelText: "${LocaleKeys.invoiceNo}: ${widget.getInvoiceDetailsResponseModel.invoiceNo!}", + labelText: "${LocaleKeys.invoiceNo.tr(context: context)}: ${widget.getInvoiceDetailsResponseModel.invoiceNo!}", labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), + isEnglishOnly: true, ), AppCustomChipWidget( - labelText: (widget.getInvoiceDetailsResponseModel.clinicDescription!.length > 15 + labelText: ((getIt().isArabic() ? widget.getInvoiceDetailsResponseModel.clinicDescriptionN : widget.getInvoiceDetailsResponseModel.clinicDescription ?? "")!.length > 15 ? '${widget.getInvoiceDetailsResponseModel.clinicDescription!.substring(0, 12)}...' - : widget.getInvoiceDetailsResponseModel.clinicDescription!), + : getIt().isArabic() ? widget.getInvoiceDetailsResponseModel.clinicDescriptionN : widget.getInvoiceDetailsResponseModel.clinicDescription ?? ""), labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w), ), AppCustomChipWidget( labelText: widget.getInvoiceDetailsResponseModel.projectName!, labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), ), - AppCustomChipWidget( - labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), - icon: AppAssets.doctor_calendar_icon, - labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.getInvoiceDetailsResponseModel.appointmentDate), false), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.getInvoiceDetailsResponseModel.appointmentDate), false), + isEnglishOnly: true, + ), ), ], ), @@ -195,12 +239,12 @@ class _MyInvoicesDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Insurance Details".toText16(isBold: true), + LocaleKeys.insurance.tr(context: context).toText16(isBold: true), SizedBox(height: 16.h), - widget.getInvoiceDetailsResponseModel.groupName!.toText14(isBold: true), + (getIt().isArabic() ? widget.getInvoiceDetailsResponseModel.groupNameN : widget.getInvoiceDetailsResponseModel.groupName)!.toText14(isBold: true), Row( children: [ - Expanded(child: widget.getInvoiceDetailsResponseModel.companyName!.toText14(isBold: true)), + Expanded(child: (widget.getInvoiceDetailsResponseModel.companyName ?? "").toText14(isBold: true)), ], ), SizedBox(height: 12.h), @@ -209,6 +253,7 @@ class _MyInvoicesDetailsPageState extends State { AppCustomChipWidget( labelText: "Insurance ID: ${widget.getInvoiceDetailsResponseModel.insuranceID ?? "-"}", labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), + isEnglishOnly: true, ), ], ), @@ -235,12 +280,12 @@ class _MyInvoicesDetailsPageState extends State { // Header row Row( children: [ - Expanded(flex: 3, child: LocaleKeys.name.tr().toText12(color: AppColors.textColor, fontWeight: FontWeight.w600)), - Expanded(flex: 1, child: LocaleKeys.quantity.tr().toText12(color: AppColors.textColor, fontWeight: FontWeight.w600, isCenter: true)), + Expanded(flex: 3, child: LocaleKeys.name.tr().toText12(color: AppColors.textColor, isBold: true)), + Expanded(flex: 1, child: LocaleKeys.quantity.tr().toText12(color: AppColors.textColor, isBold: true, isCenter: true)), Expanded( - flex: 2, child: LocaleKeys.price.tr().toText12(color: AppColors.textColor, fontWeight: FontWeight.w600, isCenter: true)), + flex: 2, child: LocaleKeys.price.tr().toText12(color: AppColors.textColor, isBold: true, isCenter: true)), Expanded( - flex: 2, child: LocaleKeys.total.tr().toText12(color: AppColors.textColor, fontWeight: FontWeight.w600, isCenter: true)), + flex: 2, child: LocaleKeys.total.tr().toText12(color: AppColors.textColor, isBold: true, isCenter: true)), ], ), SizedBox(height: 16.h), @@ -252,10 +297,10 @@ class _MyInvoicesDetailsPageState extends State { padding: EdgeInsets.symmetric(vertical: 6.h), child: Row( children: [ - Expanded(flex: 3, child: (consultation.procedureName ?? '-').toText12(fontWeight: FontWeight.w500)), - Expanded(flex: 1, child: '${consultation.quantity ?? '-'}'.toText12(fontWeight: FontWeight.w500, isCenter: true)), - Expanded(flex: 2, child: '${consultation.price ?? '-'}'.toText12(fontWeight: FontWeight.w500, isCenter: true)), - Expanded(flex: 2, child: '${consultation.total ?? '-'}'.toText12(fontWeight: FontWeight.w500, isCenter: true)), + Expanded(flex: 3, child: (consultation.procedureName ?? '-').toText12(isBold: true)), + Expanded(flex: 1, child: '${consultation.quantity ?? '-'}'.toText12(isBold: true, isCenter: true, isEnglishOnly: true)), + Expanded(flex: 2, child: (NumberFormat.decimalPattern().format(consultation.price) ?? '-').toText12(isBold: true, isCenter: true, isEnglishOnly: true)), + Expanded(flex: 2, child: (NumberFormat.decimalPattern().format(consultation.total) ?? '-').toText12(isBold: true, isCenter: true, isEnglishOnly: true)), ], ), ), @@ -289,7 +334,8 @@ class _MyInvoicesDetailsPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.amountBeforeTax.tr(context: context).toText14(isBold: true), - Utils.getPaymentAmountWithSymbol(widget.getInvoiceDetailsResponseModel.listConsultation!.first.totalShare.toString().toText16(isBold: true), AppColors.blackColor, 13, + Utils.getPaymentAmountWithSymbol( + NumberFormat.decimalPattern().format(widget.getInvoiceDetailsResponseModel.listConsultation!.first.totalShare).toString().toText16(isBold: true, isEnglishOnly: true), AppColors.blackColor, 13, isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), @@ -298,7 +344,12 @@ class _MyInvoicesDetailsPageState extends State { children: [ LocaleKeys.vat15.tr(context: context).toText14(isBold: true, color: AppColors.greyTextColor), Utils.getPaymentAmountWithSymbol( - widget.getInvoiceDetailsResponseModel.listConsultation!.first.totalVATAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, + NumberFormat.decimalPattern() + .format(widget.getInvoiceDetailsResponseModel.listConsultation!.first.totalVATAmount!) + .toString() + .toText14(isBold: true, color: AppColors.greyTextColor, isEnglishOnly: true), + AppColors.greyTextColor, + 13, isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), @@ -307,7 +358,11 @@ class _MyInvoicesDetailsPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ LocaleKeys.discount.tr(context: context).toText14(isBold: true), - Utils.getPaymentAmountWithSymbol(widget.getInvoiceDetailsResponseModel.listConsultation!.first.discountAmount!.toString().toText14(isBold: true, color: AppColors.primaryRedColor), + Utils.getPaymentAmountWithSymbol( + NumberFormat.decimalPattern() + .format(widget.getInvoiceDetailsResponseModel.listConsultation!.first.discountAmount!) + .toString() + .toText14(isBold: true, color: AppColors.primaryRedColor, isEnglishOnly: true), AppColors.primaryRedColor, 13, isSaudiCurrency: true), ], @@ -317,7 +372,9 @@ class _MyInvoicesDetailsPageState extends State { children: [ LocaleKeys.paid.tr(context: context).toText14(isBold: true), Utils.getPaymentAmountWithSymbol( - widget.getInvoiceDetailsResponseModel.listConsultation!.first.grandTotal!.toString().toText14(isBold: true, color: AppColors.textColor), AppColors.textColor, 13, + NumberFormat.decimalPattern().format(widget.getInvoiceDetailsResponseModel.listConsultation!.first.grandTotal!).toString().toText14(isBold: true, color: AppColors.textColor, isEnglishOnly: true), + AppColors.textColor, + 13, isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), diff --git a/lib/presentation/my_invoices/my_invoices_list.dart b/lib/presentation/my_invoices/my_invoices_list.dart index 054d2fce..bb2c4f8e 100644 --- a/lib/presentation/my_invoices/my_invoices_list.dart +++ b/lib/presentation/my_invoices/my_invoices_list.dart @@ -134,7 +134,7 @@ class _MyInvoicesListState extends State { borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -150,7 +150,7 @@ class _MyInvoicesListState extends State { borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -166,7 +166,7 @@ class _MyInvoicesListState extends State { borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -182,7 +182,7 @@ class _MyInvoicesListState extends State { borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), textColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.blackColor, fontSize: 12, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, diff --git a/lib/presentation/my_invoices/widgets/invoice_list_card.dart b/lib/presentation/my_invoices/widgets/invoice_list_card.dart index e5ae7de8..8aa4e174 100644 --- a/lib/presentation/my_invoices/widgets/invoice_list_card.dart +++ b/lib/presentation/my_invoices/widgets/invoice_list_card.dart @@ -14,6 +14,8 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'dart:ui' as ui; + class InvoiceListCard extends StatelessWidget { final GetInvoicesListResponseModel getInvoicesListResponseModel; Function? onTap; @@ -94,7 +96,7 @@ class InvoiceListCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - (getIt().isArabic() ? getInvoicesListResponseModel.doctorNameN! : getInvoicesListResponseModel.doctorName!).toText16(isBold: true), + (getIt().isArabic() ? getInvoicesListResponseModel.doctorNameN! : getInvoicesListResponseModel.doctorName ?? "HMG Doctor").toText16(isBold: true), SizedBox(height: 8.h), Wrap( direction: Axis.horizontal, @@ -102,8 +104,9 @@ class InvoiceListCard extends StatelessWidget { runSpacing: 6.h, children: [ AppCustomChipWidget( - labelText: "${LocaleKeys.invoiceNo}: ${getInvoicesListResponseModel.invoiceNo!}", + labelText: "${LocaleKeys.invoiceNo.tr(context: context)}: ${getInvoicesListResponseModel.invoiceNo!}", labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), + isEnglishOnly: true, ), AppCustomChipWidget( labelText: @@ -114,10 +117,14 @@ class InvoiceListCard extends StatelessWidget { labelText: getInvoicesListResponseModel.projectName!, labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w), ), - AppCustomChipWidget( - labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), - icon: AppAssets.doctor_calendar_icon, - labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(getInvoicesListResponseModel.appointmentDate), false), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: 6.w), + icon: AppAssets.doctor_calendar_icon, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(getInvoicesListResponseModel.appointmentDate), false), + isEnglishOnly: true, + ), ), ], ), @@ -138,7 +145,7 @@ class InvoiceListCard extends StatelessWidget { borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01), textColor: AppColors.primaryRedColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, diff --git a/lib/presentation/notifications/notification_details_page.dart b/lib/presentation/notifications/notification_details_page.dart index 1607e8f5..abe8b521 100644 --- a/lib/presentation/notifications/notification_details_page.dart +++ b/lib/presentation/notifications/notification_details_page.dart @@ -73,7 +73,7 @@ class NotificationDetailsPage extends StatelessWidget { Icon(Icons.access_time, size: 12.w, color: AppColors.textColor), SizedBox(width: 4.w), _formatTime(notification.isSentOn).toText10( - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.textColor, ), ], @@ -93,7 +93,7 @@ class NotificationDetailsPage extends StatelessWidget { Icon(Icons.calendar_today, size: 12.w, color: AppColors.textColor), SizedBox(width: 4.w), _formatDate(notification.isSentOn).toText10( - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.textColor, ), ], diff --git a/lib/presentation/notifications/notifications_list_page.dart b/lib/presentation/notifications/notifications_list_page.dart index 6c344b2e..e9a9447e 100644 --- a/lib/presentation/notifications/notifications_list_page.dart +++ b/lib/presentation/notifications/notifications_list_page.dart @@ -2,6 +2,8 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.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'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -9,6 +11,7 @@ import 'package:hmg_patient_app_new/extensions/int_extensions.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/notifications/notifications_view_model.dart'; +import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart'; import 'package:hmg_patient_app_new/presentation/notifications/notification_details_page.dart'; @@ -16,179 +19,231 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:provider/provider.dart'; import 'package:intl/intl.dart'; +import 'dart:ui' as ui; -class NotificationsListPage extends StatelessWidget { +class NotificationsListPage extends StatefulWidget { const NotificationsListPage({super.key}); + @override + State createState() => _NotificationsListPageState(); +} + +class _NotificationsListPageState extends State { + final GlobalKey _bottomKey = GlobalKey(); + + void _scrollToBottom() { + WidgetsBinding.instance.addPostFrameCallback((_) { + final ctx = _bottomKey.currentContext; + if (ctx != null) { + Scrollable.ensureVisible( + ctx, + duration: const Duration(milliseconds: 400), + curve: Curves.easeOut, + ); + } + }); + } + @override Widget build(BuildContext context) { return CollapsingListView( title: LocaleKeys.notification.tr(context: context), - child: SingleChildScrollView( - child: Consumer(builder: (context, notificationsVM, child) { - return Container( - margin: EdgeInsets.symmetric(vertical: 24.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: false, - ), - child: ListView.builder( - itemCount: notificationsVM.isNotificationsLoading ? 4 : notificationsVM.notificationsList.length, - physics: NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: EdgeInsetsGeometry.zero, - itemBuilder: (context, index) { - return notificationsVM.isNotificationsLoading - ? LabResultItemView( + child: Consumer(builder: (context, notificationsVM, child) { + return Column( + children: [ + Container( + margin: EdgeInsets.symmetric(vertical: 24.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.h, + hasShadow: false, + ), + child: ListView.builder( + itemCount: notificationsVM.isNotificationsLoading + ? 4 + : notificationsVM.notificationsList.length + (notificationsVM.hasMoreNotifications ? 1 : 0), + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsetsGeometry.zero, + itemBuilder: (context, index) { + if (notificationsVM.isNotificationsLoading) { + return LabResultItemView( onTap: () {}, labOrder: null, index: index, isLoading: true, - ) - : AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 500), - child: SlideAnimation( - verticalOffset: 100.0, - child: FadeInAnimation( - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => NotificationDetailsPage( - notification: notificationsVM.notificationsList[index], - ), - ), - ); - }, - child: AnimatedContainer( - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 16.h), - // Message row with red dot for unread - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: notificationsVM.notificationsList[index].message!.toText16( - isBold: (notificationsVM.notificationsList[index].isRead == false), - weight: (notificationsVM.notificationsList[index].isRead == false) - ? FontWeight.w600 - : FontWeight.w400, - ), + ); + } + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 500), + child: SlideAnimation( + verticalOffset: 100.0, + child: FadeInAnimation( + child: GestureDetector( + onTap: () { + notificationsVM.markAsRead(notificationID: notificationsVM.notificationsList[index].id ?? 0); + getIt.get().getPatientDashboard(); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => NotificationDetailsPage( + notification: notificationsVM.notificationsList[index], ), - SizedBox(width: 8.w), - // Red dot for unread notifications ONLY - if (notificationsVM.notificationsList[index].isRead == false) - Container( - width: 8.w, - height: 8.w, - decoration: BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - ), - ], - ), - SizedBox(height: 12.h), - // First row: Time and Date chips with arrow - Row( + ), + ); + }, + child: AnimatedContainer( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Time chip with clock icon - Container( - padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), - decoration: BoxDecoration( - color: AppColors.greyColor, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.access_time, size: 12.w, color: AppColors.textColor), - SizedBox(width: 4.w), - _formatTime(notificationsVM.notificationsList[index].isSentOn).toText10( - weight: FontWeight.w500, - color: AppColors.textColor, + SizedBox(height: 16.h), + // Message row with red dot for unread + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: notificationsVM.notificationsList[index].message!.toText16( + isBold: (notificationsVM.notificationsList[index].isRead == false), + weight: (notificationsVM.notificationsList[index].isRead == false) + ? FontWeight.w600 + : FontWeight.w400, ), - ], - ), - ), - SizedBox(width: 8.w), - // Date chip with calendar icon - Container( - padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), - decoration: BoxDecoration( - color: AppColors.greyColor, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.calendar_today, size: 12.w, color: AppColors.textColor), - SizedBox(width: 4.w), - _formatDate(notificationsVM.notificationsList[index].isSentOn).toText10( - weight: FontWeight.w500, - color: AppColors.textColor, + ), + SizedBox(width: 8.w), + // Red dot for unread notifications ONLY + if (notificationsVM.notificationsList[index].isRead == false) + Container( + width: 8.w, + height: 8.w, + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), ), - ], - ), - ), - Spacer(), - // Arrow icon - Utils.buildSvgWithAssets( - icon: AppAssets.arrow_forward, - width: 16.w, - height: 16.h, - iconColor: AppColors.greyTextColor, + ], ), - ], - ), - // Second row: Contains Image chip (if MessageType is "image") - if (notificationsVM.notificationsList[index].messageType != null && - notificationsVM.notificationsList[index].messageType!.toLowerCase() == "image") - Padding( - padding: EdgeInsets.only(top: 8.h), - child: Row( + SizedBox(height: 12.h), + // First row: Time and Date chips with arrow + Row( children: [ + // Time chip with clock icon Container( padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), decoration: BoxDecoration( color: AppColors.greyColor, borderRadius: BorderRadius.circular(8), ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.image_outlined, size: 12.w, color: AppColors.textColor), - SizedBox(width: 4.w), - 'Contains Image'.toText10( - weight: FontWeight.w500, - color: AppColors.textColor, - ), - ], + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.access_time, size: 12.w, color: AppColors.textColor), + SizedBox(width: 4.w), + _formatTime(notificationsVM.notificationsList[index].isSentOn).toText10( + weight: FontWeight.w600, + color: AppColors.textColor, + isEnglishOnly: true + ), + ], + ), + ), + ), + SizedBox(width: 8.w), + // Date chip with calendar icon + Container( + padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), + decoration: BoxDecoration( + color: AppColors.greyColor, + borderRadius: BorderRadius.circular(8), + ), + child: Directionality( + textDirection: ui.TextDirection.ltr, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.calendar_today, size: 12.w, color: AppColors.textColor), + SizedBox(width: 4.w), + _formatDate(notificationsVM.notificationsList[index].isSentOn).toText10( + weight: FontWeight.w600, + color: AppColors.textColor, + isEnglishOnly: true + ), + ], + ), + ), + ), + Spacer(), + // Arrow icon + Transform.flip( + flipX: getIt.get().isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_forward, + width: 16.w, + height: 16.h, + iconColor: AppColors.greyTextColor, ), ), ], ), - ), - SizedBox(height: 16.h), - 1.divider, - ], + // Second row: Contains Image chip (if MessageType is "image") + if (notificationsVM.notificationsList[index].messageType != null && + notificationsVM.notificationsList[index].messageType!.toLowerCase() == "image") + Padding( + padding: EdgeInsets.only(top: 8.h), + child: Row( + children: [ + Container( + padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), + decoration: BoxDecoration( + color: AppColors.greyColor, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.image_outlined, size: 12.w, color: AppColors.textColor), + SizedBox(width: 4.w), + LocaleKeys.containsImage.toText10( + weight: FontWeight.w600, + color: AppColors.textColor, + ), + ], + ), + ), + ], + ), + ), + SizedBox(height: 16.h), + 1.divider, + ], + ), + ), ), ), ), - ), - ), - ); - }).paddingSymmetrical(16.w, 0.h), - ).paddingSymmetrical(24.w, 0.h); + ); + }).paddingSymmetrical(16.w, 0.h), + ).paddingSymmetrical(24.w, 0.h), + SizedBox(height: 16.h), + // SizedBox( + // key: _bottomKey, + // child: "Show more notifications".toText16( + // color: AppColors.primaryRedColor, + // isBold: true, + // isUnderLine: true + // ), + // ).onPress(() async { + // await notificationsVM.loadMoreNotifications(); + // _scrollToBottom(); + // }), + SizedBox(height: 24.h), + ], + ); }), - ), ); } diff --git a/lib/presentation/onboarding/onboarding_screen.dart b/lib/presentation/onboarding/onboarding_screen.dart index 4855c0cf..0af63b40 100644 --- a/lib/presentation/onboarding/onboarding_screen.dart +++ b/lib/presentation/onboarding/onboarding_screen.dart @@ -143,7 +143,7 @@ class _OnboardingScreenState extends State { }) : CustomButton( text: LocaleKeys.getStarted.tr(context: context), - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontSize: 16.f, height: 56.h, textOverflow: TextOverflow.ellipsis, @@ -178,7 +178,7 @@ class _OnboardingScreenState extends State { ), Text( body, - style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), + style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w600, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), ), ], ).paddingOnly(left: 24.w, right: 24.w); diff --git a/lib/presentation/parking/paking_page.dart b/lib/presentation/parking/paking_page.dart index db451b27..736ef2a0 100644 --- a/lib/presentation/parking/paking_page.dart +++ b/lib/presentation/parking/paking_page.dart @@ -1,7 +1,10 @@ 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_export.dart'; +import 'package:hmg_patient_app_new/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.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/generated/locale_keys.g.dart'; @@ -101,8 +104,20 @@ class _ParkingPageState extends State { hasShadow: true, ), child: Padding( - padding: EdgeInsets.all(16.h), child: LocaleKeys.parkingDescription.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColor)), + padding: EdgeInsets.all(16.h), child: LocaleKeys.parkingDescription.tr(context: context).toText12(isBold: true, color: AppColors.textColor)), ).paddingOnly(top: 16, bottom: 16), + ClipRRect( + borderRadius: BorderRadius.circular(24.r), + child: Transform.flip( + flipX: getIt.get().isArabic(), + child: Image.asset( + AppAssets.carParkingService, + fit: BoxFit.fitHeight, + height: 480.h, + width: 520.w, + ), + ), + ), ], ), ), diff --git a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart index d6a616ee..3a287a44 100644 --- a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart +++ b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart @@ -66,7 +66,7 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget { Expanded( child: Padding( padding: EdgeInsets.all(8.h), - child: prescriptionsViewModel.prescriptionDetailsList[index].itemDescription!.trim().toText14(weight: FontWeight.w500), + child: prescriptionsViewModel.prescriptionDetailsList[index].itemDescription!.trim().toText14(isBold: true), ), ), ], @@ -135,6 +135,7 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget { isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, + isAutoDismiss: true ); }, onError: (err) { @@ -156,7 +157,7 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget { borderColor: AppColors.successColor.withOpacity(0.01), textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart b/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart index 7760fd2d..31c6ae95 100644 --- a/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart +++ b/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart @@ -70,7 +70,7 @@ class PrescriptionDeliveryOrdersListPage extends StatelessWidget { spacing: 8.h, children: [ RequestStatus(status: model.prescriptionsOrderList[index].statusId ?? 0), - "Req ID: ${model.prescriptionsOrderList[index].iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600), + "Req ID: ${model.prescriptionsOrderList[index].iD}".toText16(color: AppColors.textColor, isBold: true), Row( spacing: 4.w, children: [ diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index 0ac9ce40..b3d37664 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -65,7 +65,7 @@ class _PrescriptionDetailPageState extends State { Expanded( child: CollapsingListView( title: LocaleKeys.prescriptions.tr(context: context), - instructions: widget.prescriptionsResponseModel.isInOutPatient! + instructions: widget.prescriptionsResponseModel.isInOutPatient ?? false ? () async { LoaderBottomSheet.showLoader(loadingText: LocaleKeys.fetchingPrescriptionPDFPleaseWait.tr(context: context)); await prescriptionsViewModel.getPrescriptionInstructionsPDF(widget.prescriptionsResponseModel, onSuccess: (val) { @@ -112,8 +112,9 @@ class _PrescriptionDetailPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ AppCustomChipWidget( - labelText: - "${getIt.get().isArabic() ? widget.prescriptionsResponseModel.isInOutPatientDescriptionN : widget.prescriptionsResponseModel.isInOutPatientDescription}", + labelText: getIt.get().isArabic() + ? (widget.prescriptionsResponseModel.isInOutPatientDescriptionN ?? "مراجع للعيادات الخارجية") + : (widget.prescriptionsResponseModel.isInOutPatientDescription ?? "OutPatient"), backgroundColor: AppColors.warningColorYellow.withOpacity(0.1), textColor: AppColors.warningColorYellow, ), @@ -154,6 +155,7 @@ class _PrescriptionDetailPageState extends State { icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: LocaleKeys.ratingValue.tr(namedArgs: {'rating': widget.prescriptionsResponseModel.decimalDoctorRate.toString()}, context: context), + isEnglishOnly: true, ), AppCustomChipWidget( labelText: widget.prescriptionsResponseModel.name ?? "", @@ -196,7 +198,7 @@ class _PrescriptionDetailPageState extends State { borderColor: AppColors.successColor.withValues(alpha: 0.01), textColor: AppColors.successColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -256,7 +258,7 @@ class _PrescriptionDetailPageState extends State { borderColor: AppColors.successColor.withOpacity(0.01), textColor: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? Colors.white : AppColors.textColor.withOpacity(0.35), fontSize: 16, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 50.h, diff --git a/lib/presentation/prescriptions/prescription_item_view.dart b/lib/presentation/prescriptions/prescription_item_view.dart index 13ddebf1..83c2f7f1 100644 --- a/lib/presentation/prescriptions/prescription_item_view.dart +++ b/lib/presentation/prescriptions/prescription_item_view.dart @@ -68,7 +68,7 @@ class PrescriptionItemView extends StatelessWidget { labelText: "${LocaleKeys.dailyDoses.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].doseDailyQuantity}", ).toShimmer2(isShow: isLoading), AppCustomChipWidget( - labelText: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}", + labelText: "${LocaleKeys.days.tr(context: context)}: ${isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].days}", isEnglishOnly: true, ).toShimmer2(isShow: isLoading), ], ).paddingSymmetrical(16.h, 0.h), @@ -98,7 +98,7 @@ class PrescriptionItemView extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), - "Notify me before the consumption time".toText10(color: AppColors.textColorLight, isEnglishOnly: true), + LocaleKeys.notifyPrescription.tr(context: context).toText10(color: AppColors.textColorLight), ], ).toShimmer2(isShow: isLoading).expanded, Switch( @@ -183,7 +183,7 @@ class PrescriptionItemView extends StatelessWidget { // borderColor: AppColors.primaryRedColor.withOpacity(0.0), // textColor: AppColors.primaryRedColor, // fontSize: 14.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, diff --git a/lib/presentation/prescriptions/prescription_reminder_view.dart b/lib/presentation/prescriptions/prescription_reminder_view.dart index d1dcd07a..0d4ec9f5 100644 --- a/lib/presentation/prescriptions/prescription_reminder_view.dart +++ b/lib/presentation/prescriptions/prescription_reminder_view.dart @@ -57,7 +57,7 @@ class _PrescriptionReminderViewState extends State { "${(_options[index] / 60).toInt()} ${LocaleKeys.hours.tr(context: context)}", style: TextStyle( fontSize: 16.h, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), value: index, diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index 3a54926f..b88eec30 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -80,7 +80,7 @@ class _PrescriptionsListPageState extends State { // borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withOpacity(0.2), // textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -95,7 +95,7 @@ class _PrescriptionsListPageState extends State { // borderColor: model.isSortByClinic ? AppColors.textColor.withOpacity(0.2) : AppColors.primaryRedColor, // textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -157,7 +157,7 @@ class _PrescriptionsListPageState extends State { fit: BoxFit.fill, ).circle(100), SizedBox(width: 8.h), - Expanded(child: model.patientPrescriptionOrders[index].doctorName!.toText14(weight: FontWeight.w500)), + Expanded(child: model.patientPrescriptionOrders[index].doctorName!.toText14(isBold: true)), ], ), SizedBox(height: 8.h), @@ -205,7 +205,7 @@ class _PrescriptionsListPageState extends State { borderColor: AppColors.successColor.withOpacity(0.01), textColor: model.patientPrescriptionOrders[index].isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), fontSize: model.patientPrescriptionOrders[index].isHomeMedicineDeliverySupported! ? 14.f : 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10, 0, 10, 0), height: 40.h, @@ -271,7 +271,7 @@ class _PrescriptionsListPageState extends State { // fit: BoxFit.fill, // ).circle(100), // SizedBox(width: 8.h), - // Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)), + // Expanded(child: prescription.doctorName!.toText14(isBold: true)), // ], // ), // SizedBox(height: 8.h), @@ -315,7 +315,7 @@ class _PrescriptionsListPageState extends State { // borderColor: AppColors.successColor.withOpacity(0.01), // textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), // fontSize: prescription.isHomeMedicineDeliverySupported! ? 14.f : 12.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -393,7 +393,7 @@ class _PrescriptionsListPageState extends State { // borderColor: AppColors.greyColor, // textColor: AppColors.blackColor, // fontSize: 10, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 8, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 30.h, @@ -441,7 +441,7 @@ class _PrescriptionsListPageState extends State { // // fit: BoxFit.fill, // // ).circle(100), // // SizedBox(width: 8.h), - // // Expanded(child: prescription.doctorName!.toText14(weight: FontWeight.w500)), + // // Expanded(child: prescription.doctorName!.toText14(isBold: true)), // // ], // // ), // // SizedBox(height: 8.h), @@ -485,7 +485,7 @@ class _PrescriptionsListPageState extends State { // // borderColor: AppColors.successColor.withOpacity(0.01), // // textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35), // // fontSize: prescription.isHomeMedicineDeliverySupported! ? 14.f : 12.f, - // // fontWeight: FontWeight.w500, + // // isBold: true, // // borderRadius: 12.r, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 40.h, @@ -543,7 +543,17 @@ class _PrescriptionsListPageState extends State { // ], // ), // ), - ), + ).onPress(() { + model.setPrescriptionsDetailsLoading(); + Navigator.of(context).push( + CustomPageRoute( + page: PrescriptionDetailPage( + prescriptionsResponseModel: model.patientPrescriptionOrders[index], + isFromAppointments: false, + ), + ), + ); + }), ), ), ) diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index fd88c049..127f9a3e 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -42,6 +42,8 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:in_app_review/in_app_review.dart'; +import 'dart:ui' as ui; + class ProfileSettings extends StatefulWidget { const ProfileSettings({super.key}); @@ -180,7 +182,7 @@ class ProfileSettingsState extends State { // crossAxisAlignment: CrossAxisAlignment.center, // children: [ // Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h, applyThemeColor: false), - // LocaleKeys.habibWallet.tr(context: context).toText16(weight: FontWeight.w600, maxlines: 2).expanded, + // LocaleKeys.habibWallet.tr(context: context).toText16(isBold: true, maxlines: 2).expanded, // Utils.buildSvgWithAssets(icon: getIt.get().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward), // ], // ), @@ -198,7 +200,7 @@ class ProfileSettingsState extends State { // textColor: AppColors.infoColor, // text: LocaleKeys.recharge.tr(context: context), // borderWidth: 0.w, - // fontWeight: FontWeight.w500, + // isBold: true, // borderColor: Colors.transparent, // backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08), // padding: EdgeInsets.all(8.w), @@ -214,11 +216,11 @@ class ProfileSettingsState extends State { // ), // ], // ), - LocaleKeys.quickActions.tr(context: context).toText18(weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24.w, right: 24.w), + LocaleKeys.quickActions.tr(context: context).toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24.w, right: 24.w), Container( margin: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h, bottom: 24.h), padding: EdgeInsets.only(top: 4.h, bottom: 4.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), child: Column( children: [ actionItem(AppAssets.language_change, LocaleKeys.language.tr(context: context), () { @@ -247,11 +249,11 @@ class ProfileSettingsState extends State { ], ), ), - LocaleKeys.personalInformation.tr(context: context).toText18(weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24.w, right: 24.w), + LocaleKeys.personalInformation.tr(context: context).toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24.w, right: 24.w), Container( margin: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h, bottom: 24.h), padding: EdgeInsets.only(top: 4.h, bottom: 4.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), child: Column( children: [ actionItem(AppAssets.email_transparent, LocaleKeys.updateEmailAddress.tr(context: context), () { @@ -288,20 +290,20 @@ class ProfileSettingsState extends State { ], ), ), - LocaleKeys.helpAndSupport.tr(context: context).toText18(weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24.w, right: 24.w), + LocaleKeys.helpAndSupport.tr(context: context).toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24.w, right: 24.w), Container( margin: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h), padding: EdgeInsets.only(top: 4.h, bottom: 4.h), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false), child: Column( children: [ actionItem(AppAssets.call_fill, LocaleKeys.contactUs.tr(context: context), () { launchUrl(Uri.parse("tel://" + "+966 92 006 6666")); - }, trailingLabel: "92 006 6666"), + }, trailingLabel: "92 006 6666", isEnglishOnly: true), 1.divider, actionItem(AppAssets.permission, LocaleKeys.permissionsProfile.tr(context: context), () { openAppSettings(); - }, trailingLabel: getCurrentPermissions()), + }, trailingLabel: getCurrentPermissions(), isEnglishOnly: true), actionItem(AppAssets.feedbackFill, LocaleKeys.feedback.tr(context: context), () { contactUsViewModel.setSelectedFeedbackType( FeedbackType(id: 5, nameEN: "Not classified", nameAR: 'غير محدد'), @@ -392,18 +394,18 @@ class ProfileSettingsState extends State { } Widget actionItem(String icon, String label, VoidCallback onPress, - {String trailingLabel = "", bool? switchValue, ValueChanged? onSwitchChanged, bool isExternalLink = false, bool isShowLoading = false}) { + {String trailingLabel = "", bool? switchValue, ValueChanged? onSwitchChanged, bool isExternalLink = false, bool isShowLoading = false, bool isEnglishOnly = false}) { return SizedBox( height: 56.h, child: Row( spacing: 8.w, children: [ Utils.buildSvgWithAssets(icon: icon, iconColor: AppColors.greyTextColor), - label.toText14(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + label.toText14(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, if (trailingLabel.isNotEmpty) ConstrainedBox( constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.35), - child: trailingLabel.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).toShimmer2(isShow: isShowLoading), + child: isEnglishOnly ? Directionality(textDirection: ui.TextDirection.ltr, child: trailingLabel.toText14(color: AppColors.greyTextColor, isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: isEnglishOnly).toShimmer2(isShow: isShowLoading)) : trailingLabel.toText14(color: AppColors.greyTextColor, isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: isEnglishOnly).toShimmer2(isShow: isShowLoading), ), switchValue != null ? Switch( @@ -458,7 +460,7 @@ class FamilyCardWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - "${profile.patientName}".toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: true), + "${profile.patientName}".toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: true), Wrap( direction: Axis.horizontal, spacing: 4.w, diff --git a/lib/presentation/profile_settings/widgets/family_card_widget.dart b/lib/presentation/profile_settings/widgets/family_card_widget.dart index b21c52d4..ffa6644b 100644 --- a/lib/presentation/profile_settings/widgets/family_card_widget.dart +++ b/lib/presentation/profile_settings/widgets/family_card_widget.dart @@ -54,7 +54,7 @@ class FamilyCardWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - "${profile.patientName}".toText18(isBold: true, weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1), + "${profile.patientName}".toText18(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1), AppCustomChipWidget( icon: AppAssets.file_icon, labelText: "${LocaleKeys.fileNo.tr(context: context)}: ${profile.responseId}", diff --git a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart index dcd30360..5b6fe7f9 100644 --- a/lib/presentation/profile_settings/widgets/preferred_language_widget.dart +++ b/lib/presentation/profile_settings/widgets/preferred_language_widget.dart @@ -72,7 +72,7 @@ class _PreferredLanguageWidgetState extends State { callBackFunc: () async { Navigator.of(context).pop(); profileSettingsViewModel.getProfileSettings(); - }, isFullScreen: false); + }, isFullScreen: false, isAutoDismiss: true); }, onError: (error) { LoaderBottomSheet.hideLoader(); @@ -104,7 +104,7 @@ class _PreferredLanguageWidgetState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - title.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, fontFamily: _value == "1" ? 'GESSTwo' : 'Poppins').expanded, + title.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, fontFamily: _value == "1" ? 'GESSTwo' : 'Poppins').expanded, ], ).paddingOnly(left: 16, right: 16).onPress(() { setState(() { diff --git a/lib/presentation/profile_settings/widgets/update_email_widget.dart b/lib/presentation/profile_settings/widgets/update_email_widget.dart index cb844bcb..d2633b93 100644 --- a/lib/presentation/profile_settings/widgets/update_email_widget.dart +++ b/lib/presentation/profile_settings/widgets/update_email_widget.dart @@ -56,7 +56,7 @@ class _UpdateEmailDialogState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Enter the new email address to be updated: ".toText16(textAlign: TextAlign.start, weight: FontWeight.w500), + "Enter the new email address to be updated: ".toText16(textAlign: TextAlign.start, isBold: true), SizedBox(height: 12.h), TextInputWidget( labelText: LocaleKeys.email.tr(), @@ -96,7 +96,7 @@ class _UpdateEmailDialogState extends State { callBackFunc: () async { Navigator.of(context).pop(); profileSettingsViewModel!.getProfileSettings(); - }, isFullScreen: false); + }, isFullScreen: false, isAutoDismiss: true); }, onError: (error) { LoaderBottomSheet.hideLoader(); diff --git a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart index f725bba1..e1bb59ad 100644 --- a/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart +++ b/lib/presentation/profile_settings/widgets/update_emergency_contact_widget.dart @@ -56,7 +56,7 @@ class _UpdateEmergencyContactDialogState extends State { // borderColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2), // textColor: model.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -124,7 +123,7 @@ class _RadiologyOrdersPageState extends State { // borderColor: model.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor, // textColor: model.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor, // fontSize: 12, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 10, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -147,6 +146,7 @@ class _RadiologyOrdersPageState extends State { model.filterRadiologyReports(''); }); }, + isEnglishOnly: true // chipType: ChipTypeEnum.alert, // isSelected: true, ) @@ -237,7 +237,7 @@ class _RadiologyOrdersPageState extends State { // SizedBox(height: 8.h), // Text( // displayName, - // style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600), + // style: TextStyle(fontSize: 16.h, isBold: true), // overflow: TextOverflow.ellipsis, // ), // ], @@ -279,7 +279,7 @@ class _RadiologyOrdersPageState extends State { // ).circle(100), // SizedBox(width: 8.h), // Expanded( - // child: (order.doctorName ?? '').toString().toText14(weight: FontWeight.w500), + // child: (order.doctorName ?? '').toString().toText14(isBold: true), // ), // ], // ), @@ -329,7 +329,7 @@ class _RadiologyOrdersPageState extends State { // borderColor: AppColors.secondaryLightRedColor, // textColor: AppColors.primaryRedColor, // fontSize: 14, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12, // padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -407,7 +407,7 @@ class _RadiologyOrdersPageState extends State { fit: BoxFit.cover, ).circle(100), SizedBox(width: 8.h), - Expanded(child: (group.doctorName ?? "").toString().toText14(weight: FontWeight.w500)), + Expanded(child: (group.doctorName ?? "").toString().toText14(isBold: true)), ], ), SizedBox(height: 8.h), @@ -434,8 +434,8 @@ class _RadiologyOrdersPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "• ${group.procedureName.toString().trim() ?? ""}".toText14(weight: FontWeight.w500), - // "Lorem ipsum text".toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + "• ${group.procedureName.toString().trim() ?? ""}".toText14(isBold: true, isEnglishOnly: true), + // "Lorem ipsum text".toText12(isBold: true, color: AppColors.textColorLight), SizedBox(height: 16.h), CustomButton( text: LocaleKeys.viewReport.tr(), @@ -450,7 +450,7 @@ class _RadiologyOrdersPageState extends State { borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), height: 40.h, @@ -492,10 +492,10 @@ class _RadiologyOrdersPageState extends State { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ - // "• ${order.procedureName ?? ""}".toText14(weight: FontWeight.w500), - // "Lorem ipsum text".toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + // "• ${order.procedureName ?? ""}".toText14(isBold: true), + // "Lorem ipsum text".toText12(isBold: true, color: AppColors.textColorLight), // // SizedBox(height: 4.h), - // // order.testDetails![index].testDescriptionEn!.toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + // // order.testDetails![index].testDescriptionEn!.toText12(isBold: true, color: AppColors.textColorLight), // // Row( // // mainAxisSize: MainAxisSize.min, // // children: [ @@ -506,7 +506,7 @@ class _RadiologyOrdersPageState extends State { // // fit: BoxFit.cover, // // ).circle(100), // // SizedBox(width: 8.h), - // // Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(weight: FontWeight.w500)), + // // Expanded(child: (order.doctorName ?? order.doctorNameEnglish ?? "").toString().toText14(isBold: true)), // // ], // // ), // // SizedBox(height: 8.h), @@ -538,7 +538,7 @@ class _RadiologyOrdersPageState extends State { // // // borderColor: AppColors.greyColor, // // // textColor: AppColors.blackColor, // // // fontSize: 10, - // // // fontWeight: FontWeight.w500, + // // // isBold: true, // // // borderRadius: 8, // // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // // height: 24.h, @@ -551,7 +551,7 @@ class _RadiologyOrdersPageState extends State { // // // borderColor: AppColors.greyColor, // // // textColor: AppColors.blackColor, // // // fontSize: 10, - // // // fontWeight: FontWeight.w500, + // // // isBold: true, // // // borderRadius: 8, // // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // // height: 24.h, @@ -568,7 +568,7 @@ class _RadiologyOrdersPageState extends State { // // // borderColor: AppColors.greyColor, // // // textColor: AppColors.blackColor, // // // fontSize: 10, - // // // fontWeight: FontWeight.w500, + // // // isBold: true, // // // borderRadius: 8, // // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // // height: 24.h, @@ -628,7 +628,7 @@ class _RadiologyOrdersPageState extends State { // // borderColor: AppColors.secondaryLightRedColor, // // textColor: AppColors.primaryRedColor, // // fontSize: 14, - // // fontWeight: FontWeight.w500, + // // isBold: true, // // borderRadius: 12, // // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // // height: 40.h, @@ -658,7 +658,7 @@ class _RadiologyOrdersPageState extends State { // borderColor: AppColors.infoColor.withAlpha(0), // textColor: AppColors.infoColor, // fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12.r, // padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), // height: 40.h, diff --git a/lib/presentation/radiology/radiology_result_page.dart b/lib/presentation/radiology/radiology_result_page.dart index 05f23530..bb305c32 100644 --- a/lib/presentation/radiology/radiology_result_page.dart +++ b/lib/presentation/radiology/radiology_result_page.dart @@ -133,7 +133,7 @@ class _RadiologyResultPageState extends State { // borderColor: AppColors.primaryRedColor, // textColor: Colors.white, // fontSize: 14, - // fontWeight: FontWeight.w500, + // isBold: true, // borderRadius: 12, // padding: EdgeInsets.fromLTRB(10, 0, 10, 0), // height: 40.h, @@ -158,28 +158,64 @@ class _RadiologyResultPageState extends State { borderRadius: 24.h, hasShadow: true, ), - child: widget.patientRadiologyResponseModel.dIAPACSURL != "" ? CustomButton( - text: LocaleKeys.openRad.tr(context: context), - onPressed: () async { - if (radiologyViewModel.radiologyImageURL.isNotEmpty) { - Uri uri = Uri.parse(radiologyViewModel.radiologyImageURL); - launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: ""); - } else { - Utils.showToast("Radiology image not available"); - } - }, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedColor, - textColor: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 45.h, - icon: AppAssets.imageIcon, - iconColor: Colors.white, - iconSize: 20.h, - ).paddingSymmetrical(24.h, 24.h) : SizedBox.shrink(), + child: Column( + children: [ + SizedBox(height: 24.h), + widget.patientRadiologyResponseModel.dIAPACSURL != "" + ? CustomButton( + text: LocaleKeys.openRad.tr(context: context), + onPressed: () async { + if (radiologyViewModel.radiologyImageURL.isNotEmpty) { + Uri uri = Uri.parse(radiologyViewModel.radiologyImageURL); + launchUrl(uri, mode: LaunchMode.platformDefault, webOnlyWindowName: ""); + } else { + Utils.showToast("Radiology image not available"); + } + }, + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: Colors.white, + fontSize: 16, + isBold: true, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 56.h, + icon: AppAssets.imageIcon, + iconColor: Colors.white, + iconSize: 20.h, + ).paddingSymmetrical(24.h, 0.h) + : SizedBox.shrink(), + Container( + height: 56.h, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.r), + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: [0.236, 1.0], // 53.6% and 100% + colors: [ + Color(0xFF8A38F5), // Transparent + Color(0xFFE20BBB), // Solid #F8F8F8 + ], + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(right: 4.w, left: 4.w), + child: Utils.buildSvgWithAssets(icon: AppAssets.aiOverView, width: 16.h, height: 16.h, iconColor: Colors.white), + ), + LocaleKeys.generateAiAnalysisRadResult.tr(context: context).toText16(isBold: true, color: Colors.white) + ], + ), + ).paddingSymmetrical(24.h, 12.h).onPress(() { + // Need to implement the Radiology AI analysis result generation functionality here + Utils.showToast("Radiology AI analysis result generation functionality is not implemented yet"); + }), + ], + ), ), ], ), diff --git a/lib/presentation/radiology/search_radiology.dart b/lib/presentation/radiology/search_radiology.dart index 7c14fd1d..6f78713c 100644 --- a/lib/presentation/radiology/search_radiology.dart +++ b/lib/presentation/radiology/search_radiology.dart @@ -78,8 +78,9 @@ class _SearchRadiologyContentContentState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ TextInputWidget( - labelText: "Search radiology results", - hintText: "Type test description", + fontFamily: "Poppins", + labelText: LocaleKeys.search.tr(context: context), + hintText: "", controller: searchEditingController, isEnable: true, prefix: null, @@ -93,7 +94,7 @@ class _SearchRadiologyContentContentState extends State ), SizedBox(height: ResponsiveExtension(20).h), if (filteredSuggestions.isNotEmpty) ...[ - "Suggestions".toText16(isBold: true), + LocaleKeys.suggestions.tr(context: context).toText16(isBold: true), ], ], ), @@ -146,8 +147,8 @@ class SuggestionChip extends StatelessWidget { child: label.toText12( // color: isSelected ? Colors.white : Colors.black87, color: AppColors.textColor, - fontWeight: FontWeight.w500, - ), + isBold: true, + isEnglishOnly: true), ), ); } diff --git a/lib/presentation/rate_appointment/rate_appointment_doctor.dart b/lib/presentation/rate_appointment/rate_appointment_doctor.dart index 02457347..c3e7b526 100644 --- a/lib/presentation/rate_appointment/rate_appointment_doctor.dart +++ b/lib/presentation/rate_appointment/rate_appointment_doctor.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointme import 'package:hmg_patient_app_new/presentation/rate_appointment/widget/doctor_row.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:provider/provider.dart'; class RateAppointmentDoctor extends StatefulWidget { @@ -25,7 +26,7 @@ class RateAppointmentDoctor extends StatefulWidget { class _RateAppointmentDoctorState extends State { final formKey = GlobalKey(); String note = ""; - int rating = 5; + int rating = 0; // ProjectViewModel? projectViewModel; AppointmentRatingViewModel? appointmentRatingViewModel; @@ -40,7 +41,6 @@ class _RateAppointmentDoctorState extends State { @override Widget build(BuildContext context) { - myAppointmentsViewModel = Provider.of(context, listen: false); appointmentRatingViewModel = Provider.of(context, listen: false); @@ -48,16 +48,16 @@ class _RateAppointmentDoctorState extends State { return Selector( selector: (_, vm) => vm.isRateClinic, - builder: (context, isRateClinic, child) => isRateClinic - ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,) - : SizedBox( - height: sheetHeight, + builder: (context, isRateClinic, child) => + // isRateClinic ? RateAppointmentClinic(doctorNote: note, doctorRate: rating,) + // : + SizedBox( + height: sheetHeight, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Scrollable main content Expanded( - child: Padding( padding: const EdgeInsets.only(top: 0.0, left: 0, right: 0), child: Column( @@ -157,7 +157,6 @@ class _RateAppointmentDoctorState extends State { ), ), - // Bottom action buttons pinned to bottom SafeArea( top: false, @@ -167,7 +166,7 @@ class _RateAppointmentDoctorState extends State { children: [ Expanded( child: CustomButton( - text: "Later", + text: LocaleKeys.later.tr(context: context), backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), @@ -179,12 +178,18 @@ class _RateAppointmentDoctorState extends State { SizedBox(width: 10), Expanded( child: CustomButton( - text: LocaleKeys.next.tr(context: context), - onPressed: () { + text: LocaleKeys.submit.tr(context: context), + onPressed: () async { // Set up clinic rating and show clinic rating view - appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); - appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); - appointmentRatingViewModel!.setClinicOrDoctor(true); + // appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context),); + // appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context),); + // appointmentRatingViewModel!.setClinicOrDoctor(true); + + LoaderBottomSheet.showLoader(); + await appointmentRatingViewModel!.submitDoctorRating(docRate: rating, docNote: note); + // await appointmentRatingViewModel!.submitClinicRating(clinicRate: rating, clinicNote: note); + LoaderBottomSheet.hideLoader(); + Navigator.pop(context); setState(() {}); }, diff --git a/lib/presentation/rate_appointment/widget/doctor_row.dart b/lib/presentation/rate_appointment/widget/doctor_row.dart index 725ac7c3..2a139d05 100644 --- a/lib/presentation/rate_appointment/widget/doctor_row.dart +++ b/lib/presentation/rate_appointment/widget/doctor_row.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.dart'; @@ -8,6 +7,8 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/appointment_details_resp_model.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'dart:ui' as ui; + class BuildDoctorRow extends StatelessWidget { bool isForClinic = false; AppointmentDetails? appointmentDetails; @@ -36,32 +37,27 @@ class BuildDoctorRow extends StatelessWidget { SizedBox(height: 8.h), - isForClinic ? Wrap( direction: Axis.horizontal, spacing: 3.h, runSpacing: 4.h, children: [ AppCustomChipWidget( - labelText: appointmentDetails!.clinicName.toString(), - ), AppCustomChipWidget( icon: AppAssets.ic_date_filter, labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false), - + isEnglishOnly: true, ), AppCustomChipWidget( icon: AppAssets.appointment_time_icon, labelText: appointmentDetails!.startTime.substring(0, appointmentDetails!.startTime.length - 3), - ), - ] ) : Wrap( direction: Axis.horizontal, @@ -69,26 +65,30 @@ class BuildDoctorRow extends StatelessWidget { runSpacing: 4.h, children: [ AppCustomChipWidget( - labelText: appointmentDetails!.projectName.toString(), - - ), AppCustomChipWidget( - labelText: appointmentDetails!.clinicName.toString(), - - ) - - ] - ) - ], + ), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelPadding: EdgeInsetsDirectional.only(start: -8.w, end: 6.w), + icon: AppAssets.ic_date_filter, + labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(appointmentDetails!.appointmentDate), false), + isEnglishOnly: true, + ), + ), + ], + ), + ], + ), ), - ), ], - )); + ), + ); } } \ No newline at end of file diff --git a/lib/presentation/servicesPriceList/services_price_list_page.dart b/lib/presentation/servicesPriceList/services_price_list_page.dart index 0ee81ae3..a2e4fbb6 100644 --- a/lib/presentation/servicesPriceList/services_price_list_page.dart +++ b/lib/presentation/servicesPriceList/services_price_list_page.dart @@ -57,7 +57,7 @@ class _ServicesPriceListPageState extends State { ), child: Padding( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), - child: LocaleKeys.servicePriceListDesc.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + child: LocaleKeys.servicePriceListDesc.tr(context: context).toText12(isBold: true, color: AppColors.textColorLight), ), ), SizedBox(height: 16.h), @@ -119,7 +119,7 @@ class _ServicesPriceListPageState extends State { children: [ Expanded( child: (appState.isArabic() ? service.nameAR ?? service.nameEN ?? '' : service.nameEN ?? service.nameAR ?? '').toText16( - weight: FontWeight.w500, + weight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -146,7 +146,7 @@ class _ServicesPriceListPageState extends State { ), child: Padding( padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), - child: LocaleKeys.servicePriceListRights.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + child: LocaleKeys.servicePriceListRights.tr(context: context).toText12(isBold: true, color: AppColors.textColorLight), ), ), ], diff --git a/lib/presentation/smartwatches/activity_detail.dart b/lib/presentation/smartwatches/activity_detail.dart index 78527140..2d2532b5 100644 --- a/lib/presentation/smartwatches/activity_detail.dart +++ b/lib/presentation/smartwatches/activity_detail.dart @@ -113,8 +113,8 @@ class _ActivityDetailsState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - widget.sectionName.capitalizeFirstofEach.toText32(weight: FontWeight.w600, color: AppColors.textColor), - "Average".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor) + widget.sectionName.capitalizeFirstofEach.toText32(isBold: true, color: AppColors.textColor), + "Average".toText12(isBold: true, color: AppColors.greyTextColor) ], ), Selector>( @@ -127,10 +127,10 @@ class _ActivityDetailsState extends State { textBaseline: TextBaseline.alphabetic, spacing: 4.w, children: [ - (averageAsDouble?.toStringAsFixed(2) ?? averageAsString ?? "N/A").toText24(color: AppColors.textGreenColor, fontWeight: FontWeight.w600), + (averageAsDouble?.toStringAsFixed(2) ?? averageAsString ?? "N/A").toText24(color: AppColors.textGreenColor, isBold: true), Visibility( visible: averageAsDouble != null || averageAsString != null, - child: widget.uom.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500) + child: widget.uom.toText12(color: AppColors.greyTextColor, isBold: true) ) ], ); @@ -279,7 +279,7 @@ class _ActivityDetailsState extends State { TextStyle( color: Colors.black, fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ); } diff --git a/lib/presentation/smartwatches/huawei_health_example.dart b/lib/presentation/smartwatches/huawei_health_example.dart index bf569a6a..857d26aa 100644 --- a/lib/presentation/smartwatches/huawei_health_example.dart +++ b/lib/presentation/smartwatches/huawei_health_example.dart @@ -14,7 +14,7 @@ // class _HuaweiHealthExampleState extends State { // /// Styles // static const TextStyle cardTitleTextStyle = TextStyle( -// fontWeight: FontWeight.w500, +// isBold: true, // fontSize: 18, // ); // static const EdgeInsets componentPadding = EdgeInsets.all(8.0); diff --git a/lib/presentation/smartwatches/smart_watch_activity.dart b/lib/presentation/smartwatches/smart_watch_activity.dart index 678fd715..f6c1baa6 100644 --- a/lib/presentation/smartwatches/smart_watch_activity.dart +++ b/lib/presentation/smartwatches/smart_watch_activity.dart @@ -216,14 +216,14 @@ class SmartWatchActivity extends StatelessWidget { title.toText16( weight: FontWeight.w600, color: AppColors.textColor), ], ), - description.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + description.toText12(isBold: true, color: AppColors.greyTextColor), Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, spacing: 2.h, children: [ - result.toText21(weight: FontWeight.w600, color: AppColors.textColor), - unitsOfMeasure.toText10(weight: FontWeight.w500, color:AppColors.greyTextColor ), + result.toText21(isBold: true, color: AppColors.textColor), + unitsOfMeasure.toText10(isBold: true, color:AppColors.greyTextColor ), if(resultSecondValue != null) Visibility( visible: resultSecondValue != null , @@ -233,8 +233,8 @@ class SmartWatchActivity extends StatelessWidget { spacing: 2.h, children: [ SizedBox(width: 2.w,), - resultSecondValue.toText21(weight: FontWeight.w600, color: AppColors.textColor), - unitOfSecondMeasure!.toText10(weight: FontWeight.w500, color:AppColors.greyTextColor ) + resultSecondValue.toText21(isBold: true, color: AppColors.textColor), + unitOfSecondMeasure!.toText10(isBold: true, color:AppColors.greyTextColor ) ], ), ) diff --git a/lib/presentation/smartwatches/smartwatch_home_page.dart b/lib/presentation/smartwatches/smartwatch_home_page.dart index 800ff030..b6c98ee9 100644 --- a/lib/presentation/smartwatches/smartwatch_home_page.dart +++ b/lib/presentation/smartwatches/smartwatch_home_page.dart @@ -52,7 +52,7 @@ class SmartwatchHomePage extends StatelessWidget { borderColor: AppColors.infoColor.withAlpha(0), textColor: AppColors.infoColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 50.h, icon: AppAssets.ask_doctor_icon, @@ -101,7 +101,7 @@ class SmartwatchHomePage extends StatelessWidget { borderColor: AppColors.primaryRedColor.withAlpha(0), textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, ).paddingSymmetrical(16.w, 12.h), @@ -133,7 +133,7 @@ class SmartwatchHomePage extends StatelessWidget { borderColor: AppColors.primaryRedColor.withAlpha(0), textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, ).paddingSymmetrical(16.w, 12.h), @@ -167,7 +167,7 @@ class SmartwatchHomePage extends StatelessWidget { borderColor: AppColors.primaryRedColor.withAlpha(0), textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, ).paddingSymmetrical(16.w, 12.h), @@ -202,7 +202,7 @@ class SmartwatchHomePage extends StatelessWidget { borderColor: AppColors.primaryRedColor.withAlpha(0), textColor: AppColors.primaryRedColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 40.h, ).paddingSymmetrical(16.w, 12.h), diff --git a/lib/presentation/smartwatches/smartwatch_instructions_page.dart b/lib/presentation/smartwatches/smartwatch_instructions_page.dart index fa5f3135..6a2aae29 100644 --- a/lib/presentation/smartwatches/smartwatch_instructions_page.dart +++ b/lib/presentation/smartwatches/smartwatch_instructions_page.dart @@ -42,7 +42,7 @@ class SmartwatchInstructionsPage extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 16.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.r, height: 50.h, ).paddingSymmetrical(24.w, 30.h), @@ -93,8 +93,8 @@ class SmartwatchInstructionsPage extends StatelessWidget { ), - title.toText16(weight: FontWeight.w600, color: AppColors.textColor), - description.toText12(fontWeight: FontWeight.w500, color: descriptionTextColor) + title.toText16(isBold: true, color: AppColors.textColor), + description.toText12(isBold: true, color: descriptionTextColor) ], ); } diff --git a/lib/presentation/smartwatches/widgets/supported_watches_list.dart b/lib/presentation/smartwatches/widgets/supported_watches_list.dart index 839af802..f28d57c5 100644 --- a/lib/presentation/smartwatches/widgets/supported_watches_list.dart +++ b/lib/presentation/smartwatches/widgets/supported_watches_list.dart @@ -25,11 +25,11 @@ class SupportedWatchesList extends StatelessWidget { ], ), SizedBox(height: 4.h), - "• Apple Watch Ultra".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Apple Watch 11".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Apple Watch 10".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Apple Watch 9".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Apple Watch 8".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + "• Apple Watch Ultra".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Apple Watch 11".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Apple Watch 10".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Apple Watch 9".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Apple Watch 8".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), SizedBox(height: 16.h), Row( children: [ @@ -37,12 +37,12 @@ class SupportedWatchesList extends StatelessWidget { ], ), SizedBox(height: 4.h), - "• Samsung Watch Ultra".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Galaxy Watch 8".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Galaxy Watch 7".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Galaxy Watch 6".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Galaxy Watch FE".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Galaxy Watch Fit".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + "• Samsung Watch Ultra".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Galaxy Watch 8".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Galaxy Watch 7".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Galaxy Watch 6".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Galaxy Watch FE".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Galaxy Watch Fit".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), SizedBox(height: 16.h), Row( children: [ @@ -50,10 +50,10 @@ class SupportedWatchesList extends StatelessWidget { ], ), SizedBox(height: 4.h), - "• Huawei Watch Ultimate Series".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Huawei Watch Series".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Huawei Watch Fit Series".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Huawei Watch Bands".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + "• Huawei Watch Ultimate Series".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Huawei Watch Series".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Huawei Watch Fit Series".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Huawei Watch Bands".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), SizedBox(height: 16.h), Row( children: [ @@ -61,8 +61,8 @@ class SupportedWatchesList extends StatelessWidget { ], ), SizedBox(height: 4.h), - "• Whoop 1".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), - "• Whoop 2".needTranslation.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + "• Whoop 1".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), + "• Whoop 2".needTranslation.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), diff --git a/lib/presentation/symptoms_checker/possible_conditions_screen.dart b/lib/presentation/symptoms_checker/possible_conditions_screen.dart index 81980c66..fa5d77d0 100644 --- a/lib/presentation/symptoms_checker/possible_conditions_screen.dart +++ b/lib/presentation/symptoms_checker/possible_conditions_screen.dart @@ -37,6 +37,8 @@ class PossibleConditionsPage extends StatelessWidget { late BookAppointmentsViewModel bookAppointmentsViewModel; late AppointmentViaRegionViewmodel regionalViewModel; + late AppState appState; + Widget _buildLoadingShimmer() { return ListView.separated( shrinkWrap: true, @@ -170,9 +172,9 @@ class PossibleConditionsPage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - title.toText14(weight: FontWeight.w600, color: AppColors.textColor), + title.toText14(isBold: true, color: AppColors.textColor), SizedBox(height: 4.h), - description.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + description.toText12(isBold: true, color: AppColors.greyTextColor), ], ), ), @@ -223,6 +225,7 @@ class PossibleConditionsPage extends StatelessWidget { symptomsCheckerViewModel = context.read(); bookAppointmentsViewModel = context.read(); regionalViewModel = context.read(); + appState = getIt.get(); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: CollapsingListView( @@ -248,7 +251,7 @@ class PossibleConditionsPage extends StatelessWidget { // Get selected symptoms names for display final symptoms = symptomsCheckerViewModel .getAllSelectedSymptoms() - .map((s) => s.commonName ?? s.name ?? '') + .map((s) => (appState.isArabic() ? s.commonNameAr ?? s.nameAr ?? '' : s.commonName ?? s.name ?? '')) .where((name) => name.isNotEmpty) .take(3) .toList(); @@ -274,7 +277,7 @@ class PossibleConditionsPage extends StatelessWidget { getTitle(AppointmentViaRegionViewmodel data, BuildContext context) { if (data.selectedRegionId == null) { - return LocaleKeys.selectRegion.tr(context: context).toText20(weight: FontWeight.w600); + return LocaleKeys.selectRegion.tr(context: context).toText20(isBold: true); } else { return Transform.flip( flipX: data.isArabic ? true : false, @@ -376,7 +379,6 @@ class PossibleConditionsPage extends StatelessWidget { }, ); } else { - final appState = getIt.get(); appState.resetLocation(); regionVM.setSortByLocation(false); _refreshHospitalListAfterApi(regionVM); diff --git a/lib/presentation/symptoms_checker/risk_factors_screen.dart b/lib/presentation/symptoms_checker/risk_factors_screen.dart index 462cffbd..969a884d 100644 --- a/lib/presentation/symptoms_checker/risk_factors_screen.dart +++ b/lib/presentation/symptoms_checker/risk_factors_screen.dart @@ -84,7 +84,7 @@ class _RiskFactorsScreenState extends State { Expanded( child: optionText.toText14( color: riskFactorId == "not_applicable" ? AppColors.errorColor : AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, )), ], ), @@ -123,7 +123,7 @@ class _RiskFactorsScreenState extends State { style: TextStyle( height: 1.3, fontSize: 13.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.greyInfoTextColor, fontFamily: isArabic ? 'GESSTwo' : 'Poppins' ), @@ -135,7 +135,7 @@ class _RiskFactorsScreenState extends State { text: LocaleKeys.readMore.tr(context: context), style: TextStyle( color: AppColors.primaryRedColor, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), recognizer: TapGestureRecognizer() ..onTap = () { diff --git a/lib/presentation/symptoms_checker/suggestions_screen.dart b/lib/presentation/symptoms_checker/suggestions_screen.dart index cbfc15a1..8fc58fd7 100644 --- a/lib/presentation/symptoms_checker/suggestions_screen.dart +++ b/lib/presentation/symptoms_checker/suggestions_screen.dart @@ -86,7 +86,7 @@ class _SuggestionsScreenState extends State { Expanded( child: optionText.toText14( color: suggestionId == "not_applicable" ? AppColors.errorColor : AppColors.textColor, - weight: FontWeight.w500, + weight: FontWeight.w600, )), ], ), diff --git a/lib/presentation/symptoms_checker/triage_screen.dart b/lib/presentation/symptoms_checker/triage_screen.dart index b4ceff4c..da35a805 100644 --- a/lib/presentation/symptoms_checker/triage_screen.dart +++ b/lib/presentation/symptoms_checker/triage_screen.dart @@ -123,7 +123,7 @@ class _TriagePageState extends State { SizedBox(height: 8.h), LocaleKeys.emergencyTriage.tr(context: context).toText28(color: AppColors.whiteColor, isBold: true), SizedBox(height: 8.h), - LocaleKeys.emergencyEvidenceDetected.tr(context: context).toText14(color: AppColors.whiteColor, weight: FontWeight.w500), + LocaleKeys.emergencyEvidenceDetected.tr(context: context).toText14(color: AppColors.whiteColor, isBold: true), SizedBox(height: 24.h), CustomButton( text: LocaleKeys.confirm.tr(context: context), @@ -333,7 +333,7 @@ class _TriagePageState extends State { if (viewModel.currentTriageQuestion == null) { return Center( - child: LocaleKeys.noQuestionAvailable.tr(context: context).toText16(weight: FontWeight.w500), + child: LocaleKeys.noQuestionAvailable.tr(context: context).toText16(isBold: true), ); } @@ -373,7 +373,7 @@ class _TriagePageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Main question text - (question.text ?? "").toText16(weight: FontWeight.w600, color: AppColors.textColor), + (question.text ?? "").toText16(isBold: true, color: AppColors.textColor), SizedBox(height: 24.h), // Show all items with dividers @@ -386,7 +386,7 @@ class _TriagePageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Item name (sub-question) - (item.name ?? "").toText14(weight: FontWeight.w600, color: AppColors.textColor), + (item.name ?? "").toText14(isBold: true, color: AppColors.textColor), SizedBox(height: 8.h), // Choices for this item ...List.generate(choices.length, (choiceIndex) { @@ -430,7 +430,7 @@ class _TriagePageState extends State { child: selected ? Icon(Icons.check, size: 16.f, color: AppColors.whiteColor) : null, ), SizedBox(width: 12.w), - Expanded(child: optionText.toText13(weight: FontWeight.w500)), + Expanded(child: optionText.toText13(isBold: true)), ], ), ), @@ -474,7 +474,7 @@ class _TriagePageState extends State { text: suggestedCondition, style: TextStyle( color: AppColors.textColor, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w600, fontSize: 14.f, fontFamily: isArabic ? 'GESSTwo' : 'Poppins'), ), ], @@ -491,7 +491,7 @@ class _TriagePageState extends State { text: TextSpan( text: "${probability.toStringAsFixed(1)}% ", style: TextStyle( - fontFamily: isArabic ? 'GESSTwo' : 'Poppins', + fontFamily: 'Poppins', color: AppColors.primaryRedColor, fontWeight: FontWeight.w600, fontSize: 14.f, @@ -501,7 +501,7 @@ class _TriagePageState extends State { text: LocaleKeys.symptomsCheckerFindingScore.tr(context: context), style: TextStyle( color: AppColors.textColor, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontSize: 13.f, fontFamily: isArabic ? 'GESSTwo' : 'Poppins', ), diff --git a/lib/presentation/symptoms_checker/user_info_selection.dart b/lib/presentation/symptoms_checker/user_info_selection.dart index b245c014..bd19f31b 100644 --- a/lib/presentation/symptoms_checker/user_info_selection.dart +++ b/lib/presentation/symptoms_checker/user_info_selection.dart @@ -98,9 +98,9 @@ class _UserInfoSelectionPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - title.toText14(weight: FontWeight.w500), + title.toText14(isBold: true), subTitle - .toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500, isEnglishOnly: true) + .toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true) .toShimmer2(isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading), ], ), @@ -175,9 +175,13 @@ class _UserInfoSelectionPageState extends State { int? displayAge = viewModel.selectedAge ?? userAgeFromDOB; String ageText = displayAge != null ? "$displayAge ${LocaleKeys.years.tr(context: context)}" : LocaleKeys.notSet.tr(context: context); - String heightText = viewModel.selectedHeight != null ? "${viewModel.selectedHeight!.round()} ${viewModel.isHeightCm ? 'cm' : 'ft'}" : LocaleKeys.notSet.tr(context: context); + String heightText = (viewModel.selectedHeight != null && viewModel.selectedHeight != 0) + ? "${viewModel.selectedHeight!.round()} ${viewModel.isHeightCm ? 'cm' : 'ft'}" + : LocaleKeys.notSet.tr(context: context); - String weightText = viewModel.selectedWeight != null ? "${viewModel.selectedWeight!.round()} ${viewModel.isWeightKg ? 'kg' : 'lbs'}" : LocaleKeys.notSet.tr(context: context); + String weightText = (viewModel.selectedWeight != null && viewModel.selectedWeight != 0) + ? "${viewModel.selectedWeight!.round()} ${viewModel.isWeightKg ? 'kg' : 'lbs'}" + : LocaleKeys.notSet.tr(context: context); return Column( children: [ diff --git a/lib/presentation/symptoms_checker/user_info_selection/pages/age_selection_page.dart b/lib/presentation/symptoms_checker/user_info_selection/pages/age_selection_page.dart index 37802a27..63cff2e4 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/pages/age_selection_page.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/pages/age_selection_page.dart @@ -27,7 +27,7 @@ class AgeSelectionPage extends StatelessWidget { builder: (BuildContext context, symptomsViewModel, Widget? child) { return Column( children: [ - LocaleKeys.dateOfBirthSymptoms.tr(context: context).toText18(weight: FontWeight.w600, color: AppColors.textColor).paddingAll(24.w), + LocaleKeys.dateOfBirthSymptoms.tr(context: context).toText18(isBold: true, color: AppColors.textColor).paddingAll(24.w), SizedBox(height: 30.h), ThreeColumnDatePicker( enableHaptic: true, diff --git a/lib/presentation/symptoms_checker/user_info_selection/pages/gender_selection_page.dart b/lib/presentation/symptoms_checker/user_info_selection/pages/gender_selection_page.dart index 07f44f16..401f8529 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/pages/gender_selection_page.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/pages/gender_selection_page.dart @@ -37,7 +37,7 @@ class GenderSelectionPage extends StatelessWidget { Utils.buildImgWithAssets(icon: iconPng, height: 80.h, width: 80.h, fit: BoxFit.contain), SizedBox(height: 8.h), label.toText16( - weight: FontWeight.w500, + weight: FontWeight.w600, ) ], ), @@ -74,7 +74,7 @@ class GenderSelectionPage extends StatelessWidget { builder: (BuildContext context, symptomsViewModel, Widget? child) { return Column( children: [ - LocaleKeys.genderSymptoms.tr(context: context).toText18(weight: FontWeight.w600, color: AppColors.textColor), + LocaleKeys.genderSymptoms.tr(context: context).toText18(isBold: true, color: AppColors.textColor), SizedBox(height: 70.h), Row( children: [ diff --git a/lib/presentation/symptoms_checker/user_info_selection/pages/height_selection_page.dart b/lib/presentation/symptoms_checker/user_info_selection/pages/height_selection_page.dart index ce8087d4..28de7894 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/pages/height_selection_page.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/pages/height_selection_page.dart @@ -49,14 +49,13 @@ class HeightSelectionPage extends StatelessWidget { }, child: Container( alignment: Alignment.center, - color: Colors.transparent, + color: Colors.white, child: Text( 'CM', style: TextStyle( fontWeight: FontWeight.w700, fontSize: 14.f, - color: viewModel.isHeightCm ? AppColors.textColor : AppColors.textColor.withValues(alpha: 0.6), - ), + color: viewModel.isHeightCm ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6), fontFamily: "Poppins"), ), ), ), @@ -71,14 +70,13 @@ class HeightSelectionPage extends StatelessWidget { }, child: Container( alignment: Alignment.center, - color: Colors.transparent, + color: Colors.white, child: Text( 'FT', style: TextStyle( fontWeight: FontWeight.w700, fontSize: 14.f, - color: !viewModel.isHeightCm ? AppColors.textColor : AppColors.textColor.withValues(alpha: 0.6), - ), + color: !viewModel.isHeightCm ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6), fontFamily: "Poppins"), ), ), ), @@ -133,8 +131,7 @@ class HeightSelectionPage extends StatelessWidget { style: TextStyle( fontSize: 90.f, color: AppColors.textColor, - height: 1, - ), + height: 1, fontFamily: "Poppins"), ), TextSpan( text: viewModel.isHeightCm ? 'cm' : 'ft', diff --git a/lib/presentation/symptoms_checker/user_info_selection/pages/weight_selection_page.dart b/lib/presentation/symptoms_checker/user_info_selection/pages/weight_selection_page.dart index 6e68eef9..fefc9abe 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/pages/weight_selection_page.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/pages/weight_selection_page.dart @@ -57,13 +57,14 @@ class WeightSelectionPage extends StatelessWidget { }, child: Container( alignment: Alignment.center, - color: Colors.transparent, + color: Colors.white, child: Text( 'KG', style: TextStyle( fontWeight: FontWeight.w700, fontSize: 14.f, - color: isKg ? AppColors.textColor : AppColors.textColor.withValues(alpha: 0.6), + color: isKg ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6), + fontFamily: "Poppins" ), ), ), @@ -86,7 +87,8 @@ class WeightSelectionPage extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.w700, fontSize: 14.f, - color: !isKg ? AppColors.textColor : AppColors.textColor.withValues(alpha: 0.6), + color: !isKg ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.6), + fontFamily: "Poppins" ), ), ), @@ -133,12 +135,12 @@ class WeightSelectionPage extends StatelessWidget { children: [ Text( viewModel.selectedWeight!.round().toString(), - style: TextStyle(fontSize: 100.f, color: AppColors.textColor, height: 1), + style: TextStyle(fontSize: 100.f, color: AppColors.textColor, height: 1, fontFamily: "Poppins"), ), SizedBox(width: 8.w), Text( isKg ? 'kg' : 'lbs', - style: TextStyle(fontWeight: FontWeight.w700, fontSize: 24.f), + style: TextStyle(fontWeight: FontWeight.w700, fontSize: 24.f, fontFamily: "Poppins"), ).paddingOnly(bottom: 10.h), ], ), diff --git a/lib/presentation/symptoms_checker/user_info_selection/widgets/custom_date_picker.dart b/lib/presentation/symptoms_checker/user_info_selection/widgets/custom_date_picker.dart index 98051e04..c0b76be7 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/widgets/custom_date_picker.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/widgets/custom_date_picker.dart @@ -146,9 +146,10 @@ class _ThreeColumnDatePickerState extends State { textAlign: TextAlign.center, style: TextStyle( fontSize: selected ? 22.f : 20.f, - fontWeight: selected ? FontWeight.w600 : FontWeight.w500, + fontWeight: selected ? FontWeight.w600 : FontWeight.w600, color: selected ? AppColors.textColor : AppColors.greyTextColor.withValues(alpha: 0.9), height: 1.0, + fontFamily: "Poppins", letterSpacing: selected ? -0.02 * 30 : -0.02 * 18, ), ); diff --git a/lib/presentation/symptoms_checker/user_info_selection/widgets/height_scale.dart b/lib/presentation/symptoms_checker/user_info_selection/widgets/height_scale.dart index e09380eb..6695bbd4 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/widgets/height_scale.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/widgets/height_scale.dart @@ -133,8 +133,9 @@ class _HeightScaleState extends State { style: TextStyle( fontSize: 11.f, color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, height: 1, + fontFamily: "Poppins" ), textAlign: TextAlign.right, ), diff --git a/lib/presentation/symptoms_checker/user_info_selection/widgets/weight_scale.dart b/lib/presentation/symptoms_checker/user_info_selection/widgets/weight_scale.dart index 8526a526..9ab2d73c 100644 --- a/lib/presentation/symptoms_checker/user_info_selection/widgets/weight_scale.dart +++ b/lib/presentation/symptoms_checker/user_info_selection/widgets/weight_scale.dart @@ -152,8 +152,9 @@ class _WeightScaleState extends State { style: TextStyle( fontSize: 11.f, color: AppColors.greyTextColor, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, height: 1, + fontFamily: "Poppins" ), textAlign: TextAlign.center, maxLines: 1, diff --git a/lib/presentation/symptoms_checker/widgets/condition_card.dart b/lib/presentation/symptoms_checker/widgets/condition_card.dart index d753b780..d231f52f 100644 --- a/lib/presentation/symptoms_checker/widgets/condition_card.dart +++ b/lib/presentation/symptoms_checker/widgets/condition_card.dart @@ -168,7 +168,7 @@ class ConditionCard extends StatelessWidget { crossAxisAlignment: WrapCrossAlignment.center, children: [ for (int i = 0; i < symptoms.length; i++) ...[ - "● ${symptoms[i]}".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, isEnglishOnly: true), + "● ${symptoms[i]}".toText12(isBold: true, color: AppColors.greyTextColor, isEnglishOnly: true), if (i != symptoms.length - 1) Padding( padding: EdgeInsets.symmetric(horizontal: 2.w), @@ -212,7 +212,7 @@ class ConditionCard extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: title.toText18(weight: FontWeight.w600, color: AppColors.textColor), + child: title.toText18(isBold: true, color: AppColors.textColor), ), AppCustomChipWidget( labelText: tagText, @@ -241,12 +241,12 @@ class ConditionCard extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.commonSymptom.tr(context: context).toText14(weight: FontWeight.w600, color: AppColors.textColor), + LocaleKeys.commonSymptom.tr(context: context).toText14(isBold: true, color: AppColors.textColor), _buildSymptomsRow(), SizedBox(height: 16.h), LocaleKeys.description.tr(context: context).toText14(weight: FontWeight.bold, color: AppColors.textColor), SizedBox(height: 2.h), - description.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor), + description.toText12(isBold: true, color: AppColors.greyTextColor), if (severityEnum != PossibleConditionsSeverityEnum.monitorOnly) CustomButton( text: LocaleKeys.bookAppointment.tr(context: context), @@ -259,7 +259,7 @@ class ConditionCard extends StatelessWidget { borderColor: Colors.transparent, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, padding: EdgeInsets.symmetric(horizontal: 10.w), icon: AppAssets.add_icon, height: 48.h, diff --git a/lib/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart b/lib/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart index 6c2625b3..2852d930 100644 --- a/lib/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart +++ b/lib/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart @@ -28,7 +28,7 @@ class OrganTooltipWidget extends StatelessWidget { style: TextStyle( color: AppColors.textColor, fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), ), diff --git a/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart b/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart index b66ef037..13688e0d 100644 --- a/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart +++ b/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart @@ -57,7 +57,7 @@ class _SelectedOrgansSectionState extends State { LocaleKeys.selectedOrgans.tr(context: context), style: TextStyle( fontSize: 16.f, - fontWeight: FontWeight.w600, + isBold: true, color: AppColors.textColor, ), ), @@ -78,7 +78,7 @@ class _SelectedOrgansSectionState extends State { style: TextStyle( color: AppColors.whiteColor, fontSize: 12.f, - fontWeight: FontWeight.w600, + isBold: true, ), ), ), diff --git a/lib/presentation/tele_consultation/zoom/call_screen.dart b/lib/presentation/tele_consultation/zoom/call_screen.dart index 59be70e0..21836a8a 100644 --- a/lib/presentation/tele_consultation/zoom/call_screen.dart +++ b/lib/presentation/tele_consultation/zoom/call_screen.dart @@ -897,7 +897,7 @@ class _CallScreenState extends State { // style: GoogleFonts.lato( // textStyle: const TextStyle( // fontSize: 18, - // fontWeight: FontWeight.w600, + // isBold: true, // color: Colors.black, // ), // ), @@ -1388,7 +1388,7 @@ class _CallScreenState extends State { 'Change Name', style: const TextStyle( fontSize: 18, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), ), @@ -1598,7 +1598,7 @@ class _CallScreenState extends State { // style: GoogleFonts.lato( // textStyle: const TextStyle( // fontSize: 18, - // fontWeight: FontWeight.w600, + // isBold: true, // ), // ), // ), @@ -1678,7 +1678,7 @@ class _CallScreenState extends State { // style: GoogleFonts.lato( // textStyle: const TextStyle( // fontSize: 14, - // fontWeight: FontWeight.w600, + // isBold: true, // color: Colors.white, // ), // ), @@ -1692,7 +1692,7 @@ class _CallScreenState extends State { // style: GoogleFonts.lato( // textStyle: const TextStyle( // fontSize: 14, - // fontWeight: FontWeight.w600, + // isBold: true, // color: Colors.white, // ), // ), diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart index 9d75c769..a8de2708 100644 --- a/lib/presentation/todo_section/ancillary_order_payment_page.dart +++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart @@ -35,7 +35,7 @@ class AncillaryOrderPaymentPage extends StatefulWidget { final int orderNo; final int projectID; final List selectedProcedures; - final double totalAmount; + final num totalAmount; const AncillaryOrderPaymentPage({ super.key, @@ -491,7 +491,7 @@ class _AncillaryOrderPaymentPageState extends State { child: Utils.getSuccessWidget(loadingText: "${LocaleKeys.paymentCompletedSuccessfully.tr(context: context)}, ${LocaleKeys.hereIsYourInvoiceNumber.tr(context: context)}$invoiceNo"), isCloseButtonVisible: true, isDismissible: true, - isFullScreen: false, callBackFunc: () { + isFullScreen: false, isAutoDismiss: true, callBackFunc: () { // Pop until we reach the LandingPage todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true); Navigator.pushAndRemoveUntil( diff --git a/lib/presentation/todo_section/ancillary_procedures_details_page.dart b/lib/presentation/todo_section/ancillary_procedures_details_page.dart index ef1aba72..4d2034ba 100644 --- a/lib/presentation/todo_section/ancillary_procedures_details_page.dart +++ b/lib/presentation/todo_section/ancillary_procedures_details_page.dart @@ -24,6 +24,8 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; + class AncillaryOrderDetailsList extends StatefulWidget { final int appointmentNoVida; final int orderNo; @@ -114,12 +116,13 @@ class _AncillaryOrderDetailsListState extends State { } } - double _getTotalAmount() { - double total = 0.0; + num _getTotalAmount() { + num total = 0.0; for (var proc in selectedProcedures) { total += (proc.patientShareWithTax ?? 0); } return total; + // return 1234.50; } @override @@ -253,8 +256,9 @@ class _AncillaryOrderDetailsListState extends State { children: [ AppCustomChipWidget( // icon: AppAssets.file_icon, - labelText: "MRN: ${patientMRN ?? 'N/A'}", + labelText: "${LocaleKeys.fileno.tr(context: context)}: ${patientMRN ?? 'N/A'}", iconSize: 12.w, + isEnglishOnly: true, ), // National ID @@ -263,20 +267,23 @@ class _AncillaryOrderDetailsListState extends State { // icon: AppAssets.card_user, labelText: "ID: $nationalID", iconSize: 12.w, + isEnglishOnly: true, ), // Appointment Number if (orderData.appointmentNo != null) AppCustomChipWidget( // icon: AppAssets.calendar, - labelText: "Appt #: ${orderData.appointmentNo}", + labelText: "${LocaleKeys.appointment.tr(context: context)}: ${orderData.appointmentNo}", iconSize: 12.w, + isEnglishOnly: true, ), // Order Number if (orderData.ancillaryOrderProcDetailsList?.firstOrNull?.orderNo != null) AppCustomChipWidget( labelText: "Order #: ${orderData.ancillaryOrderProcDetailsList!.first.orderNo}", + isEnglishOnly: true, ), // Blood Group @@ -297,12 +304,14 @@ class _AncillaryOrderDetailsListState extends State { backgroundColor: AppColors.successColor.withValues(alpha: 0.15), iconSize: 12.w, labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 8.w), + isEnglishOnly: true, ), // Policy Number if (orderData.insurancePolicyNo != null && orderData.insurancePolicyNo!.isNotEmpty) AppCustomChipWidget( - labelText: "Policy: ${orderData.insurancePolicyNo}", + labelText: "${LocaleKeys.policyNumber.tr(context: context)}: ${orderData.insurancePolicyNo}", + isEnglishOnly: true, ), AppCustomChipWidget( @@ -317,8 +326,12 @@ class _AncillaryOrderDetailsListState extends State { labelText: "Clinic: ${orderData.clinicName!}", ), if (orderData.clinicName != null && orderData.clinicName!.isNotEmpty) - AppCustomChipWidget( - labelText: "Date: ${DateFormat('MMM dd, yyyy').format(orderData.appointmentDate!)}", + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + labelText: "${LocaleKeys.date.tr(context: context)}: ${DateFormat('MMM dd yyyy').format(orderData.appointmentDate!)}", + isEnglishOnly: true, + ), ), ], ), @@ -375,7 +388,7 @@ class _AncillaryOrderDetailsListState extends State { children: [ LocaleKeys.procedures.tr(context: context).toText12( color: AppColors.textColorLight, - fontWeight: FontWeight.w600, + isBold: true, ), "$selectedCount of $totalProcedures selected".toText14( isBold: true, @@ -388,7 +401,7 @@ class _AncillaryOrderDetailsListState extends State { children: [ LocaleKeys.totalAmount.tr(context: context).toText12( color: AppColors.textColorLight, - fontWeight: FontWeight.w600, + isBold: true, ), Row( children: [ @@ -426,12 +439,12 @@ class _AncillaryOrderDetailsListState extends State { children: [ Expanded( flex: 2, - child: "$label:".toText12(color: AppColors.textColorLight, fontWeight: FontWeight.w600), + child: "$label:".toText12(color: AppColors.textColorLight, isBold: true), ), SizedBox(width: 8.w), Expanded( flex: 3, - child: value.toText12(color: AppColors.textColor, fontWeight: FontWeight.w600), + child: value.toText12(color: AppColors.textColor, isBold: true), ), ], ), @@ -557,7 +570,7 @@ class _AncillaryOrderDetailsListState extends State { Row( children: [ Utils.getPaymentAmountWithSymbol( - (procedure.patientShare ?? 0).toStringAsFixed(2).toText13(weight: FontWeight.w600), + (procedure.patientShare ?? 0).toStringAsFixed(2).toText14(isBold: true, isEnglishOnly: true), AppColors.textColorLight, 13, isSaudiCurrency: true, @@ -576,7 +589,7 @@ class _AncillaryOrderDetailsListState extends State { Row( children: [ Utils.getPaymentAmountWithSymbol( - (procedure.patientTaxAmount ?? 0).toStringAsFixed(2).toText13(weight: FontWeight.w600), + (procedure.patientTaxAmount ?? 0).toStringAsFixed(2).toText12(isBold: true, isEnglishOnly: true), AppColors.textColorLight, 13, isSaudiCurrency: true, @@ -595,7 +608,7 @@ class _AncillaryOrderDetailsListState extends State { Row( children: [ Utils.getPaymentAmountWithSymbol( - (procedure.patientShareWithTax ?? 0).toStringAsFixed(2).toText13(weight: FontWeight.w600), + (procedure.patientShareWithTax ?? 0).toStringAsFixed(2).toText12(isBold: true, isEnglishOnly: true), AppColors.textColorLight, 13, isSaudiCurrency: true, @@ -632,7 +645,7 @@ class _AncillaryOrderDetailsListState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), + Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)), ], ), SizedBox(height: 18.h), @@ -646,7 +659,7 @@ class _AncillaryOrderDetailsListState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Utils.getPaymentAmountWithSymbol(_getTotalAmount().toStringAsFixed(2).toText24(isBold: true), AppColors.blackColor, 17, isSaudiCurrency: true), + Utils.getPaymentAmountWithSymbol(NumberFormat.decimalPattern().format(_getTotalAmount()).toText24(isBold: true, isEnglishOnly: true), AppColors.blackColor, 17, isSaudiCurrency: true), ], ), ], diff --git a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart index 6a41252e..627642ef 100644 --- a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart +++ b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart @@ -15,6 +15,8 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'dart:ui' as ui; + class AncillaryOrdersList extends StatelessWidget { final List orders; final Function(AncillaryOrderItem order)? onCheckIn; @@ -173,20 +175,26 @@ class AncillaryOrderCard extends StatelessWidget { if (order.orderNo != null || isLoading) AppCustomChipWidget( // icon: AppAssets.calendar, - labelText: "${LocaleKeys.orderNumber.tr(context: context)}${order.orderNo ?? '-'}", - ).toShimmer2(isShow: isLoading), + labelText: "${LocaleKeys.orderNumber.tr(context: context)}${order.orderNo ?? '-'}", + isEnglishOnly: true) + .toShimmer2(isShow: isLoading), // Appointment Date if (order.appointmentDate != null || isLoading) - AppCustomChipWidget( - icon: AppAssets.appointment_calendar_icon, - labelText: isLoading ? "Date: Jan 20, 2024" : DateFormat('MMM dd, yyyy').format(order.appointmentDate!), - ).toShimmer2(isShow: isLoading), + Directionality( + textDirection: ui.TextDirection.ltr, + child: AppCustomChipWidget( + icon: AppAssets.appointment_calendar_icon, + labelText: isLoading ? "Date: Jan 20, 2024" : DateFormat('MMM dd, yyyy').format(order.appointmentDate!), + isEnglishOnly: true, + ).toShimmer2(isShow: isLoading), + ), // Appointment Number if (order.appointmentNo != null || isLoading) AppCustomChipWidget( - labelText: isLoading ? "Appt# : 98765" : "Appt #: ${order.appointmentNo}", + labelText: isLoading ? "Appt# : 98765" : "${LocaleKeys.appointment.tr(context: context)}: ${order.appointmentNo}", + isEnglishOnly: true, ).toShimmer2(isShow: isLoading), // Invoice Number @@ -228,7 +236,7 @@ class AncillaryOrderCard extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), height: 40.h, @@ -252,7 +260,7 @@ class AncillaryOrderCard extends StatelessWidget { borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), fontSize: (isFoldable || isTablet) ? 12.f : 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), height: 40.h, diff --git a/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart b/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart index 2d99aa0f..54c92789 100644 --- a/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart +++ b/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart @@ -121,7 +121,7 @@ class AncillaryOrderCard extends StatelessWidget { children: [ LocaleKeys.orderNumber.tr(context: context).toText14( color: AppColors.textColorLight, - weight: FontWeight.w500, + weight: FontWeight.w600, ), SizedBox(width: 4.w), (isLoading ? "12345" : "${order.orderNo ?? '-'}").toText16(isBold: true).toShimmer2(isShow: isLoading), @@ -158,7 +158,7 @@ class AncillaryOrderCard extends StatelessWidget { (isLoading ? "Cardiology Clinic" : order.clinicName!) .toString() .toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, maxLine: 2, ) @@ -232,7 +232,7 @@ class AncillaryOrderCard extends StatelessWidget { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), height: 40.h, @@ -256,7 +256,7 @@ class AncillaryOrderCard extends StatelessWidget { borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 10.r, padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), height: 40.h, diff --git a/lib/presentation/vital_sign/vital_sign_details_page.dart b/lib/presentation/vital_sign/vital_sign_details_page.dart index ca6e09a5..ff1daaf9 100644 --- a/lib/presentation/vital_sign/vital_sign_details_page.dart +++ b/lib/presentation/vital_sign/vital_sign_details_page.dart @@ -16,6 +16,8 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/graph/custom_graph.dart'; import 'package:provider/provider.dart'; +import 'dart:ui' as ui; + /// Which vital sign is being shown in the details screen. enum VitalSignMetric { bmi, @@ -89,6 +91,21 @@ class _VitalSignDetailsPageState extends State { ), _whatIsThisResultCard(context), _historyCard(context, history: history), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 16.r, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.history.tr(context: context).toText16(isBold: true, color: AppColors.textColor), + SizedBox(height: 16.h), + _buildHistoryList(context, history), + ], + ).paddingSymmetrical(16.h, 16.h), + ), ], ).paddingAll(24.h), ); @@ -124,15 +141,13 @@ class _VitalSignDetailsPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ title.toText28(isBold: true, color: AppColors.textColor, letterSpacing: -1), - - ], ), SizedBox(height: 8.h), (latestDate != null ? LocaleKeys.resultOf.tr(namedArgs: {'date': latestDate.toString().split(' ').first}) : LocaleKeys.resultOfNoDate.tr(context: context)) - .toText11(weight: FontWeight.w500, color: AppColors.greyTextColor), + .toText11(isBold: true, color: AppColors.greyTextColor, isEnglishOnly: true), ], ), Row( @@ -156,7 +171,7 @@ class _VitalSignDetailsPageState extends State { Column( spacing: 6.h, children: [ - status.toText10(weight: FontWeight.w500, color: AppColors.greyTextColor), + status.toText10(isBold: true, color: AppColors.greyTextColor), Utils.buildSvgWithAssets( icon: AppAssets.lab_result_indicator, width: 21, @@ -175,7 +190,7 @@ class _VitalSignDetailsPageState extends State { _referenceText(context), style: TextStyle( fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', color: AppColors.greyTextColor, ), @@ -221,8 +236,8 @@ class _VitalSignDetailsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, spacing: 8.h, children: [ - LocaleKeys.whatIsThisResult.tr(context: context).toText16(weight: FontWeight.w600, color: AppColors.textColor), - _descriptionText(context).toText12(fontWeight: FontWeight.w500, color: AppColors.textColorLight), + LocaleKeys.whatIsThisResult.tr(context: context).toText16(isBold: true, color: AppColors.textColor), + _descriptionText(context).toText12(isBold: true, color: AppColors.textColorLight), ], ), ); @@ -264,35 +279,27 @@ class _VitalSignDetailsPageState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - _isGraphVisible ? LocaleKeys.historyFlowchart.tr(context: context) : LocaleKeys.history.tr(context: context), - style: TextStyle( - fontSize: 16, - fontFamily: 'Poppins', - fontWeight: FontWeight.w600, - color: AppColors.textColor, - ), - ), + LocaleKeys.historyFlowchart.tr(context: context).toText16(isBold: true, color: AppColors.textColor), Row( mainAxisSize: MainAxisSize.min, children: [ - Container( - width: 24.h, - height: 24.h, - alignment: Alignment.center, - child: InkWell( - onTap: () { - setState(() { - _isGraphVisible = !_isGraphVisible; - }); - }, - child: Utils.buildSvgWithAssets( - icon: _isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, - width: 24.h, - height: 24.h, - ), - ), - ), + // Container( + // width: 24.h, + // height: 24.h, + // alignment: Alignment.center, + // child: InkWell( + // onTap: () { + // setState(() { + // _isGraphVisible = !_isGraphVisible; + // }); + // }, + // child: Utils.buildSvgWithAssets( + // icon: _isGraphVisible ? AppAssets.ic_list : AppAssets.ic_graph, + // width: 24.h, + // height: 24.h, + // ), + // ), + // ), // SizedBox(width: 16.h), // Container( // width: 24.h, @@ -423,10 +430,14 @@ class _VitalSignDetailsPageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - dp.displayTime.toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500), - valueText.toText12( - color: AppColors.textColor, - fontWeight: FontWeight.w600, + dp.displayTime.toText12(color: AppColors.greyTextColor, isBold: true, isEnglishOnly: true), + Directionality( + textDirection: ui.TextDirection.ltr, + child: valueText.toText12( + color: AppColors.textColor, + isBold: true, + isEnglishOnly: true + ), ), ], ), @@ -663,7 +674,8 @@ class _VitalSignDetailsPageState extends State { case VitalSignMetric.height: return null; case VitalSignMetric.weight: - return latest.weightKg != null ? 'Normal' : null; + // return latest.weightKg != null ? 'Normal' : null; + return VitalSignUiModel.bmiStatus(latest.bodyMassIndex); case VitalSignMetric.temperature: return null; case VitalSignMetric.heartRate: @@ -722,9 +734,9 @@ class _VitalSignDetailsPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.whatShouldIDoNext.tr(context: context).toText16(weight: FontWeight.w600), + LocaleKeys.whatShouldIDoNext.tr(context: context).toText16(isBold: true), SizedBox(height: 8.h), - _nextStepsText(context).toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500, maxLine: 10), + _nextStepsText(context).toText12(color: AppColors.greyTextColor, isBold: true, maxLine: 10), ], ), ); @@ -748,7 +760,7 @@ class _VitalSignDetailsPageState extends State { top: 8.0, right: isLast ? 16.h : 0, ), - child: label.toText8(fontWeight: FontWeight.w500), + child: label.toText10(isBold: true, isEnglishOnly: true), ); } diff --git a/lib/presentation/vital_sign/vital_sign_page.dart b/lib/presentation/vital_sign/vital_sign_page.dart index 82c64686..2e6e98ed 100644 --- a/lib/presentation/vital_sign/vital_sign_page.dart +++ b/lib/presentation/vital_sign/vital_sign_page.dart @@ -138,11 +138,13 @@ class _VitalSignPageState extends State { label: LocaleKeys.weight.tr(context: context), value: latestVitalSign?.weightKg?.toString() ?? '--', unit: 'kg', - status: (latestVitalSign?.weightKg != null) ? 'Normal' : null, + status: (latestVitalSign?.weightKg != null) + ? VitalSignUiModel.bmiStatus(latestVitalSign?.bodyMassIndex) + : null, onTap: () => _openDetails( VitalSignDetailsArgs( metric: VitalSignMetric.weight, - title: LocaleKeys.height.tr(context: context), + title: LocaleKeys.weight.tr(context: context), icon: AppAssets.weightVital, unit: 'kg', ), @@ -389,7 +391,7 @@ class _VitalSignPageState extends State { SizedBox(width: 3.w), unit.toText12( color: AppColors.textColor, - fontWeight: FontWeight.w500, + isBold: true, ), ], ], diff --git a/lib/presentation/water_monitor/water_consumption_page.dart b/lib/presentation/water_monitor/water_consumption_page.dart index 7e6d1d5e..27d60d5b 100644 --- a/lib/presentation/water_monitor/water_consumption_page.dart +++ b/lib/presentation/water_monitor/water_consumption_page.dart @@ -75,8 +75,8 @@ class _WaterConsumptionPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - title.toText14(weight: FontWeight.w500, color: AppColors.labelTextColor), - subTitle.toText18(weight: FontWeight.w600), + title.toText14(isBold: true, color: AppColors.labelTextColor), + subTitle.toText18(isBold: true), ], ), Utils.buildSvgWithAssets(icon: AppAssets.greenTickIcon, applyThemeColor: false) @@ -115,7 +115,7 @@ class _WaterConsumptionPageState extends State { ), child: Row( children: [ - viewModel.selectedDurationFilter.toText12(fontWeight: FontWeight.w500), + viewModel.selectedDurationFilter.toText12(isBold: true), Utils.buildSvgWithAssets(icon: AppAssets.arrow_down, height: 16.h), ], ), @@ -502,16 +502,16 @@ class _WaterConsumptionPageState extends State { for (var position in positions) { if ((value - position).abs() < 1) { - return '${value.toInt()}ml'.toText10(weight: FontWeight.w600); + return '${value.toInt()}ml'.toText10(isBold: true); } } } else { // Show percentage labels - if (value == 0) return '0%'.toText10(weight: FontWeight.w600); - if (value == 25) return '25%'.toText10(weight: FontWeight.w600); - if (value == 50) return '50%'.toText10(weight: FontWeight.w600); - if (value == 75) return '75%'.toText10(weight: FontWeight.w600); - if (value == 100) return '100%'.toText10(weight: FontWeight.w600); + if (value == 0) return '0%'.toText10(isBold: true); + if (value == 25) return '25%'.toText10(isBold: true); + if (value == 50) return '50%'.toText10(isBold: true); + if (value == 75) return '75%'.toText10(isBold: true); + if (value == 100) return '100%'.toText10(isBold: true); } return SizedBox.shrink(); }, @@ -533,7 +533,7 @@ class _WaterConsumptionPageState extends State { return Padding( padding: EdgeInsets.only(top: 10.h), child: data[index].label.toText8( - fontWeight: FontWeight.w600, + isBold: true, color: AppColors.labelTextColor, ), ); @@ -554,7 +554,7 @@ class _WaterConsumptionPageState extends State { if (selectedDuration == 'Monthly' && index < 7) { return Padding( padding: EdgeInsets.only(top: 10.h), - child: data[index].label.toText10(weight: FontWeight.w600, color: AppColors.labelTextColor), + child: data[index].label.toText10(isBold: true, color: AppColors.labelTextColor), ); } @@ -588,7 +588,7 @@ class _WaterConsumptionPageState extends State { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), (useUpperCase ? value.toUpperCase() : value.toCamelCase) - .toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1) + .toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1) .expanded, ], ).onPress(onTap), diff --git a/lib/presentation/water_monitor/water_monitor_settings_page.dart b/lib/presentation/water_monitor/water_monitor_settings_page.dart index 81157c8a..5c47bc38 100644 --- a/lib/presentation/water_monitor/water_monitor_settings_page.dart +++ b/lib/presentation/water_monitor/water_monitor_settings_page.dart @@ -54,7 +54,7 @@ class _WaterMonitorSettingsPageState extends State { materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), (useUpperCase ? value.toUpperCase() : value.toCamelCase) - .toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1) + .toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1) .expanded, ], ).onPress(onTap), @@ -167,7 +167,7 @@ class _WaterMonitorSettingsPageState extends State { ), style: TextStyle( fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ); @@ -206,7 +206,7 @@ class _WaterMonitorSettingsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - label.toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), + label.toText12(isBold: true, color: AppColors.inputLabelTextColor), if (inputField != null) Container( height: 20.w, @@ -214,7 +214,7 @@ class _WaterMonitorSettingsPageState extends State { child: inputField, ) else if (value != null) - value.toCamelCase.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + value.toCamelCase.toText12(isBold: true, color: AppColors.textColor), ], ), ), @@ -231,8 +231,8 @@ class _WaterMonitorSettingsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Unit".toText12(fontWeight: FontWeight.w500, color: AppColors.inputLabelTextColor), - unit.toText12(fontWeight: FontWeight.w500, color: AppColors.textColor), + "Unit".toText12(isBold: true, color: AppColors.inputLabelTextColor), + unit.toText12(isBold: true, color: AppColors.textColor), ], ), SizedBox(width: 12.w), diff --git a/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart b/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart index 4ee6170e..40018b61 100644 --- a/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart +++ b/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart @@ -22,8 +22,8 @@ void showSwitchCupBottomSheet(BuildContext context) { titleWidget: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Switch Cups".toText20(weight: FontWeight.w600), - "Select your preferred cup size".toText16(weight: FontWeight.w500, color: AppColors.greyTextColor), + "Switch Cups".toText20(isBold: true), + "Select your preferred cup size".toText16(isBold: true, color: AppColors.greyTextColor), ], ), child: SwitchCupBottomSheet(), @@ -125,7 +125,7 @@ class SwitchCupBottomSheet extends StatelessWidget { ], ), SizedBox(height: 2.h), - '${cup.capacityMl}ml'.toText10(weight: FontWeight.w500), + '${cup.capacityMl}ml'.toText10(isBold: true), ], ), ); @@ -145,7 +145,7 @@ class SwitchCupBottomSheet extends StatelessWidget { child: Center(child: Utils.buildSvgWithAssets(icon: AppAssets.cupAdd, height: 30.h, width: 42.w)), ), SizedBox(height: 4.h), - LocaleKeys.add.tr(context: context).toText10(weight: FontWeight.w500), + LocaleKeys.add.tr(context: context).toText10(isBold: true), ], ), ); @@ -159,7 +159,7 @@ void showCustomizeCupBottomSheet(BuildContext context, {WaterCupModel? cupToEdit titleWidget: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.customizeDrinkCup.tr(context: context).toText20(weight: FontWeight.w600), + LocaleKeys.customizeDrinkCup.tr(context: context).toText20(isBold: true), ], ), child: CustomizeCupBottomSheet(cupToEdit: cupToEdit), @@ -252,7 +252,7 @@ class _CustomizeCupBottomSheetState extends State { 'ml', style: TextStyle( fontSize: 16.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.greyTextColor, height: 1.0, ), diff --git a/lib/presentation/water_monitor/widgets/hydration_tips_widget.dart b/lib/presentation/water_monitor/widgets/hydration_tips_widget.dart index 7c49550c..af714bd7 100644 --- a/lib/presentation/water_monitor/widgets/hydration_tips_widget.dart +++ b/lib/presentation/water_monitor/widgets/hydration_tips_widget.dart @@ -37,22 +37,22 @@ class HydrationTipsWidget extends StatelessWidget { ), SizedBox(height: 8.h), " • ${LocaleKeys.drinkBeforeYouFeelThirsty.tr(context: context)}".toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight, ), SizedBox(height: 4.h), " • ${LocaleKeys.keepRefillableBottleNextToYou.tr(context: context)}".toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight, ), SizedBox(height: 4.h), " • ${LocaleKeys.trackYourDailyIntakeToStayMotivated.tr(context: context)}".toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight, ), SizedBox(height: 4.h), " • ${LocaleKeys.chooseSparklingWaterInsteadOfSoda.tr(context: context)}".toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.textColorLight, ), SizedBox(height: 8.h), diff --git a/lib/presentation/water_monitor/widgets/water_action_buttons_widget.dart b/lib/presentation/water_monitor/widgets/water_action_buttons_widget.dart index 115dd212..42ffc6f5 100644 --- a/lib/presentation/water_monitor/widgets/water_action_buttons_widget.dart +++ b/lib/presentation/water_monitor/widgets/water_action_buttons_widget.dart @@ -54,7 +54,7 @@ class WaterActionButtonsWidget extends StatelessWidget { hasShadow: true, ), child: (cupAmount > 0 ? "+ $cupAmount ml" : "+ 0ml").toText12( - fontWeight: FontWeight.w600, + isBold: true, color: AppColors.whiteColor, ), ), diff --git a/lib/presentation/water_monitor/widgets/water_intake_summary_widget.dart b/lib/presentation/water_monitor/widgets/water_intake_summary_widget.dart index ec796abc..4426375a 100644 --- a/lib/presentation/water_monitor/widgets/water_intake_summary_widget.dart +++ b/lib/presentation/water_monitor/widgets/water_intake_summary_widget.dart @@ -53,13 +53,13 @@ class WaterIntakeSummaryWidget extends StatelessWidget { if (!vm.nextDrinkTime.toLowerCase().contains('goal achieved')) // Show "Tomorrow" if nextDrinkTime contains "tomorrow", otherwise "Next Drink Time" (vm.nextDrinkTime.toLowerCase().contains('tomorrow') ? "Tomorrow" : "Next Drink Time") - .toText18(weight: FontWeight.w600, color: AppColors.textColor), + .toText18(isBold: true, color: AppColors.textColor), // Extract only time if "tomorrow" is present, otherwise show as is (vm.nextDrinkTime.toLowerCase().contains('tomorrow') ? vm.nextDrinkTime.replaceAll(RegExp(r'tomorrow', caseSensitive: false), '').trim() : vm.nextDrinkTime) - .toText32(weight: FontWeight.w600, color: AppColors.blueColor), + .toText32(isBold: true, color: AppColors.blueColor), SizedBox(height: 12.h), Row( @@ -97,9 +97,9 @@ class WaterIntakeSummaryWidget extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "$title: ".toText16(weight: FontWeight.w500, color: AppColors.textColor), + "$title: ".toText16(isBold: true, color: AppColors.textColor), subTitle.toText12( - fontWeight: FontWeight.w600, + isBold: true, color: subTitleColor ?? AppColors.greyTextColor, ), ], diff --git a/lib/services/dialog_service.dart b/lib/services/dialog_service.dart index d2ba2723..ac7acf9e 100644 --- a/lib/services/dialog_service.dart +++ b/lib/services/dialog_service.dart @@ -23,7 +23,7 @@ abstract class DialogService { Future showExceptionBottomSheet({required String message, required Function() onOkPressed, Function()? onCancelPressed}); - Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}); + Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}); Future showSuccessBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed}); @@ -119,7 +119,7 @@ class DialogServiceImp implements DialogService { } @override - Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}) async { + Future showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) async { final context = navigationService.navigatorKey.currentContext; if (context == null) return; showCommonBottomSheetWithoutHeight( @@ -132,6 +132,7 @@ class DialogServiceImp implements DialogService { cancelLabel: cancelLabel, onOkPressed: onOkPressed, onCancelPressed: onCancelPressed, + isConfirmButton: isConfirmButton ), callBackFunc: () {}, ); @@ -239,7 +240,7 @@ class DialogServiceImp implements DialogService { } } -Widget exceptionBottomSheetWidget({required BuildContext context, required String message, String? okLabel, String? cancelLabel, required Function() onOkPressed, Function()? onCancelPressed}) { +Widget exceptionBottomSheetWidget({required BuildContext context, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) { return Column( children: [ (message).toText16(isBold: false, color: AppColors.textColor), @@ -267,7 +268,7 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin text: okLabel ?? LocaleKeys.confirm.tr(context: context), onPressed: onOkPressed, backgroundColor: AppColors.bgGreenColor, - borderColor: AppColors.bgGreenColor, + borderColor: AppColors.successColor, textColor: Colors.white, icon: AppAssets.confirm, ), @@ -284,8 +285,9 @@ Widget exceptionBottomSheetWidget({required BuildContext context, required Strin context.pop(); } : onCancelPressed, - backgroundColor: AppColors.primaryRedColor, - borderColor: AppColors.primaryRedBorderColor, + // backgroundColor: AppColors.primaryRedColor, + backgroundColor: isConfirmButton ? AppColors.successColor : AppColors.primaryRedColor, + borderColor: isConfirmButton ? AppColors.successColor : AppColors.primaryRedColor, textColor: Colors.white, icon: AppAssets.confirm, ), diff --git a/lib/services/livecare_permission_service.dart b/lib/services/livecare_permission_service.dart index a9a5d294..b8d71ff1 100644 --- a/lib/services/livecare_permission_service.dart +++ b/lib/services/livecare_permission_service.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; @@ -29,6 +30,7 @@ class LiveCarePermissionService { Permission.camera, Permission.microphone, Permission.notification, + if (Platform.isAndroid) Permission.systemAlertWindow, ]; try { @@ -87,6 +89,7 @@ class LiveCarePermissionService { if (permission == Permission.camera) return 'Camera'; if (permission == Permission.microphone) return 'Microphone'; if (permission == Permission.notification) return 'Notifications'; + if (permission == Permission.systemAlertWindow) return 'Display over other apps'; return permission.toString(); } } diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart index 51d2cbc2..0f057efe 100644 --- a/lib/theme/app_theme.dart +++ b/lib/theme/app_theme.dart @@ -4,7 +4,13 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; class AppTheme { static ThemeData getTheme(isArabic) => ThemeData( - fontFamily: isArabic ? 'GESSTwo' : 'Poppins', + fontFamily: isArabic ? 'CairoArabic' : 'Poppins', + // fontFamily: 'CairoArabic', + textTheme: const TextTheme( + displayLarge: TextStyle(fontFamily: 'CairoArabic'), + bodyLarge: TextStyle(fontFamily: 'CairoArabic'), + // Add others as needed + ), primarySwatch: Colors.red, visualDensity: VisualDensity.adaptivePlatformDensity, brightness: Brightness.light, @@ -35,7 +41,13 @@ class AppTheme { ); static ThemeData getDarkTheme(isArabic) => ThemeData( - fontFamily: isArabic ? 'GESSTwo' : 'Poppins', + fontFamily: isArabic ? 'CairoArabic' : 'Poppins', + // fontFamily: 'CairoArabic', + textTheme: const TextTheme( + displayLarge: TextStyle(fontFamily: 'CairoArabic'), + bodyLarge: TextStyle(fontFamily: 'CairoArabic'), + // Add others as needed + ), primarySwatch: Colors.red, visualDensity: VisualDensity.adaptivePlatformDensity, brightness: Brightness.dark, diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index c8cbf308..e9cd83eb 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -8,7 +8,7 @@ class AppColors { static const transparent = Colors.transparent; // ── Scaffold / Background ───────────────────────────────────────────────── - static Color get scaffoldBgColor => isDarkMode ? dark.scaffoldBgColor : const Color(0xFFF0F0F0); + static Color get scaffoldBgColor => isDarkMode ? dark.scaffoldBgColor : const Color(0xFFF8F8F8); static Color get bottomSheetBgColor => isDarkMode ? dark.bottomSheetBgColor : const Color(0xFFF8F8FA); static Color get lightGreyEFColor => isDarkMode ? dark.lightGreyEFColor : const Color(0xffeaeaff); static Color get greyF7Color => isDarkMode ? dark.greyF7Color : const Color(0xffF7F7F7); @@ -41,6 +41,17 @@ class AppColors { static Color get ratingColorYellow => isDarkMode ? dark.ratingColorYellow : const Color(0xFFFFAF15); static Color get spacerLineColor => isDarkMode ? dark.spacerLineColor : const Color(0x2E30391A); + // ── Doctor Rating Colors ────────────────────────────────────────────────── + static const Color ratingStarIconColor = Color(0xFFFFA726); // Orange-amber for star icons + static const Color ratingFiveStarColor = Color(0xFF18C273); // Green for 5 stars + static const Color ratingFourStarColor = Color(0xFF8BC34A); // Light green for 4 stars + static const Color ratingThreeStarColor = Color(0xFFC0A000); // Dark yellow/olive for 3 stars + static const Color ratingTwoStarColor = Color(0xFFD84315); // Dark orange-red for 2 stars + static const Color ratingOneStarColor = Color(0xFFD32F2F); // Red for 1 star + static const Color ratingBadgeFiveStarColor = Color(0xFF5BBD8E); // Green badge for ≥4.0 + static const Color ratingPercentageTextColor = Color(0xFF4A4A4A); // Dark grey for percentage text + + // ── Semantic / Status Colors ────────────────────────────────────────────── static Color get errorColor => isDarkMode ? dark.errorColor : const Color(0xFFED1C2B); static Color get alertColor => isDarkMode ? dark.alertColor : const Color(0xFFD48D05); diff --git a/lib/widgets/app_language_change.dart b/lib/widgets/app_language_change.dart index 1200c53b..a6769b3e 100644 --- a/lib/widgets/app_language_change.dart +++ b/lib/widgets/app_language_change.dart @@ -57,9 +57,9 @@ class _AppLanguageChangeState extends State { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: Column( children: [ - languageItem("English", "en"), - 1.divider, languageItem("العربية", "ar"), + 1.divider, + languageItem("English", "en"), ], ), ), @@ -103,7 +103,7 @@ class _AppLanguageChangeState extends State { }, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ), - title.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, fontFamily: _value == "ar" ? 'GESSTwo' : 'Poppins').expanded, + title.toText16(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, fontFamily: _value == "ar" ? 'GESSTwo' : 'Poppins').expanded, ], ).paddingOnly(left: 16, right: 16).onPress(() { setState(() { diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart index 4c754b65..d641cb77 100644 --- a/lib/widgets/appbar/collapsing_list_view.dart +++ b/lib/widgets/appbar/collapsing_list_view.dart @@ -28,6 +28,7 @@ class CollapsingListView extends StatelessWidget { VoidCallback? downloadReport; VoidCallback? viewImage; VoidCallback? location; + VoidCallback? downloadInvoice; Widget? bottomChild; Widget? trailing; bool isClose; @@ -53,6 +54,7 @@ class CollapsingListView extends StatelessWidget { this.downloadReport, this.viewImage, this.location, + this.downloadInvoice, this.isLeading = true, this.trailing, this.leadingCallback, @@ -98,6 +100,7 @@ class CollapsingListView extends StatelessWidget { downloadReport: downloadReport, viewImage: viewImage, location: location, + downloadInvoice: downloadInvoice, bottomChild: bottomChild, trailing: trailing, aiOverview: aiOverview, @@ -178,7 +181,7 @@ class CollapsingListView extends StatelessWidget { // fontSize: (12 - (2 * (1 - t))).f, // fontWeight: FontWeight.lerp( // FontWeight.w300, -// FontWeight.w500, +// FontWeight.w600, // t, // )!, // ), @@ -212,6 +215,7 @@ class ScrollAnimatedTitle extends StatefulWidget implements PreferredSizeWidget VoidCallback? downloadReport; VoidCallback? viewImage; VoidCallback? location; + VoidCallback? downloadInvoice; Widget? bottomChild; Widget? trailing; @@ -232,6 +236,7 @@ class ScrollAnimatedTitle extends StatefulWidget implements PreferredSizeWidget this.downloadReport, this.viewImage, this.location, + this.downloadInvoice, this.bottomChild, this.trailing, }); @@ -313,6 +318,7 @@ class _ScrollAnimatedTitleState extends State { if (widget.downloadReport != null) actionButton(context, t, title: LocaleKeys.downloadReport.tr(context: context), icon: AppAssets.download).onPress(widget.downloadReport!), if (widget.viewImage != null) actionButton(context, t, title: LocaleKeys.viewRadiologyImage.tr(context: context), icon: AppAssets.download).onPress(widget.viewImage!), if (widget.location != null) actionButton(context, t, title: LocaleKeys.sortByLocation.tr(context: context), icon: AppAssets.location).onPress(widget.location!), + if (widget.downloadInvoice != null) actionButton(context, t, title: LocaleKeys.downloadInvoice.tr(context: context), icon: AppAssets.download).onPress(widget.downloadInvoice!), if (widget.trailing != null) widget.trailing!, ] ], @@ -347,7 +353,7 @@ class _ScrollAnimatedTitleState extends State { fontSize: (14 - (2 * (1 - t))).f, fontWeight: FontWeight.lerp( FontWeight.w400, - FontWeight.w500, + FontWeight.w600, t, )!, ), @@ -382,8 +388,8 @@ class _ScrollAnimatedTitleState extends State { letterSpacing: -0.4, fontSize: (12 - (2 * (1 - t))).f, fontWeight: FontWeight.lerp( - FontWeight.w400, FontWeight.w500, + FontWeight.w700, t, )!, ), diff --git a/lib/widgets/appbar/collapsing_toolbar.dart b/lib/widgets/appbar/collapsing_toolbar.dart index 9b1d4854..f7b3edbb 100644 --- a/lib/widgets/appbar/collapsing_toolbar.dart +++ b/lib/widgets/appbar/collapsing_toolbar.dart @@ -194,7 +194,7 @@ class _CollapsingToolbarState extends State { fontSize: (14 - (2 * (1 - t))).f, fontWeight: FontWeight.lerp( FontWeight.w300, - FontWeight.w500, + FontWeight.w600, t, )!, ), diff --git a/lib/widgets/attachment_options.dart b/lib/widgets/attachment_options.dart index be443f81..30a0da0c 100644 --- a/lib/widgets/attachment_options.dart +++ b/lib/widgets/attachment_options.dart @@ -1,5 +1,12 @@ +import 'package:easy_localization/easy_localization.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'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +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/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -12,28 +19,84 @@ class AttachmentOptions extends StatelessWidget { AttachmentOptions({Key? key, required this.onCameraTap, required this.onGalleryTap, required this.onFilesTap, this.showFilesOption = true}) : super(key: key); + late AppState? appState; + @override Widget build(BuildContext context) { + appState = getIt.get(); return SizedBox( width: double.infinity, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Upload Attachment".toSectionHeading(), - "Select from gallery or open camera".toText11(weight: FontWeight.w500), - GridView( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 105 / 105, crossAxisSpacing: 9, mainAxisSpacing: 9), - physics: const NeverScrollableScrollPhysics(), - padding: const EdgeInsets.only(top: 21, bottom: 14), - shrinkWrap: true, + // "Upload Attachment".toSectionHeading(), + // "Select from gallery or open camera".toText11(isBold: true), + checkInOptionCard( + AppAssets.ask_doctor_icon, + LocaleKeys.openCamera.tr(context: context), + "", + ).onPress(() { + onCameraTap(); + }), + SizedBox(height: 16.h), + checkInOptionCard( + AppAssets.ask_doctor_icon, + LocaleKeys.openGallery.tr(context: context), + "", + ).onPress(() { + onGalleryTap(); + }), + SizedBox(height: 16.h), + checkInOptionCard( + AppAssets.ask_doctor_icon, + LocaleKeys.openFiles.tr(context: context), + "", + ).onPress(() { + onFilesTap(); + }), + ], + ).paddingOnly(), + ); + } + + Widget checkInOptionCard(String icon, String title, String subTitle) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.r, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Utils.buildSvgWithAssets(icon: icon, width: 24.h, height: 24.h, fit: BoxFit.fill, iconColor: AppColors.textColor), + // SizedBox(height: 16.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - itemView("open_camera.svg", "Open\nCamera", onCameraTap), - itemView("gallery.svg", "Upload from\nGallery", onGalleryTap), - if (showFilesOption) itemView("files.svg", "Upload from\nFiles", onFilesTap), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + title.toText16(isBold: true, color: AppColors.textColor), + // subTitle.toText12(isBold: true, color: AppColors.greyTextColor), + ], + ), + ), + Transform.flip( + flipX: appState!.isArabic(), + child: Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, + width: 18.h, + height: 13.h, + fit: BoxFit.contain, + ), + ), ], - ) + ), ], - ).paddingOnly(left: 21, right: 21, bottom: 21), + ).paddingAll(16.h), ); } diff --git a/lib/widgets/bottom_navigation/bottom_navigation.dart b/lib/widgets/bottom_navigation/bottom_navigation.dart index 9d390388..2518375a 100644 --- a/lib/widgets/bottom_navigation/bottom_navigation.dart +++ b/lib/widgets/bottom_navigation/bottom_navigation.dart @@ -80,7 +80,7 @@ class BottomNavigation extends StatelessWidget { ), // const SizedBox(height: 10), SizedBox(height: item.isSpecial ? 0 : 8.h), - item.label.toText11(weight: FontWeight.w500), + item.label.toText11(isBold: true), // SizedBox(height: item.isSpecial ? 5 : 0) ], ), diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index de3b598a..ae1d3643 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -26,6 +26,7 @@ class CustomButton extends StatelessWidget { final TextOverflow? textOverflow; final BorderSide? borderSide; final bool applyThemeColor; + final bool isBold; const CustomButton({ super.key, @@ -39,8 +40,9 @@ class CustomButton extends StatelessWidget { this.padding = const EdgeInsets.fromLTRB(8, 10, 8, 10), this.fontSize, this.fontFamily, - this.fontWeight = FontWeight.w500, + this.fontWeight = FontWeight.w700, this.isDisabled = false, + this.isBold = false, this.icon, this.iconColor = Colors.white, this.height, @@ -90,7 +92,7 @@ class CustomButton extends StatelessWidget { fontSize: fontS, color: isDisabled ? AppColors.greyTextColor : textColor, letterSpacing: 0, - fontWeight: fontWeight, + fontWeight: isBold ? FontWeight.w700 : fontWeight, ), ), ], diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index b2ae4288..58ce21a0 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -89,7 +89,7 @@ class AppCustomChipWidget extends StatelessWidget { applyThemeColor: applyThemeColor, ) : SizedBox.shrink(), - label: richText ?? (labelText?? "").toText10(weight: FontWeight.w500, letterSpacing: 0, color: resolvedTextColor, isEnglishOnly: isEnglishOnly), + label: richText ?? (labelText?? "").toText10(isBold: true, letterSpacing: 0, color: resolvedTextColor, isEnglishOnly: isEnglishOnly), padding: padding, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, labelPadding: labelPadding ?? EdgeInsetsDirectional.only(end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w), @@ -119,7 +119,7 @@ class AppCustomChipWidget extends StatelessWidget { ) : Chip( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - label: richText ?? (labelText?? "").toText10(weight: FontWeight.w500, letterSpacing: 0, color: resolvedTextColor, isCenter: true, isEnglishOnly: isEnglishOnly), + label: richText ?? (labelText?? "").toText10(isBold: true, letterSpacing: 0, color: resolvedTextColor, isCenter: true, isEnglishOnly: isEnglishOnly), padding: EdgeInsets.zero, backgroundColor: resolvedBackgroundColor, shape: shape ?? diff --git a/lib/widgets/chip/custom_chip_widget.dart b/lib/widgets/chip/custom_chip_widget.dart index b60db577..afa00efa 100644 --- a/lib/widgets/chip/custom_chip_widget.dart +++ b/lib/widgets/chip/custom_chip_widget.dart @@ -71,7 +71,7 @@ class CustomChipWidget extends StatelessWidget { isBold: true, color: isSelected ? Colors.white : textColor ?? chipType.color, maxlines: 1, - weight: FontWeight.w500, + weight: FontWeight.w600, letterSpacing: -0.5), ], ), diff --git a/lib/widgets/chip/custom_selectable_chip.dart b/lib/widgets/chip/custom_selectable_chip.dart index 06ccb95f..cfefbbd2 100644 --- a/lib/widgets/chip/custom_selectable_chip.dart +++ b/lib/widgets/chip/custom_selectable_chip.dart @@ -44,7 +44,7 @@ class CustomSelectableChip extends StatelessWidget { label, style: TextStyle( fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: selected ? activeTextColor : resolvedInactiveTextColor, letterSpacing: -0.02 * 18, height: 1.0, diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index bc52c0cc..85d50b48 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -116,6 +116,7 @@ void showCommonBottomSheet(BuildContext context, Function(String?)? callBackFunc, String? title, required double height, + bool isAutoDismiss = false, bool isCloseButtonVisible = true, bool isFullScreen = true, bool isDismissible = true, @@ -131,6 +132,14 @@ void showCommonBottomSheet(BuildContext context, isDismissible: isDismissible, backgroundColor: isSuccessDialog ? AppColors.whiteColor : AppColors.scaffoldBgColor, builder: (BuildContext context) { + if (isAutoDismiss) { + Future.delayed(Duration(seconds: 2), () { + Navigator.of(context).pop(); + if (callBackFunc != null) { + callBackFunc(null); + } + }); + } return Container( height: height, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.scaffoldBgColor, borderRadius: 24.h), @@ -211,9 +220,11 @@ void showCommonBottomSheetWithoutHeight( required Widget child, VoidCallback? callBackFunc, String title = "", + bool isConfirmButton = false, bool isCloseButtonVisible = true, bool isFullScreen = true, bool isDismissible = true, + bool isAutoDismiss = false, Widget? titleWidget, bool useSafeArea = false, bool hasBottomPadding = true, @@ -237,6 +248,14 @@ void showCommonBottomSheetWithoutHeight( backgroundColor: resolvedBgColor, useSafeArea: useSafeArea, builder: (BuildContext context) { + if (isAutoDismiss) { + Future.delayed(Duration(seconds: 2), () { + Navigator.of(context).pop(); + if (callBackFunc != null) { + callBackFunc(); + } + }); + } return SafeArea( top: false, left: false, @@ -273,7 +292,7 @@ void showCommonBottomSheetWithoutHeight( children: [ titleWidget ?? Expanded( - child: title.toText20(weight: FontWeight.w600), + child: title.toText20(isBold: true), ), if (isCloseButtonVisible) ...[ Utils.buildSvgWithAssets( @@ -344,7 +363,7 @@ void showCommonBottomSheetWithoutHeight( // mainAxisAlignment: MainAxisAlignment.spaceBetween, // crossAxisAlignment: CrossAxisAlignment.center, // children: [ -// titleWidget ?? Expanded(child: title.toText20(weight: FontWeight.w600)), +// titleWidget ?? Expanded(child: title.toText20(isBold: true)), // Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { // Navigator.of(context).pop(); // }), diff --git a/lib/widgets/custom_tab_bar.dart b/lib/widgets/custom_tab_bar.dart index b93519ca..c2d5d537 100644 --- a/lib/widgets/custom_tab_bar.dart +++ b/lib/widgets/custom_tab_bar.dart @@ -48,6 +48,14 @@ class CustomTabBarState extends State { super.initState(); } + @override + void didUpdateWidget(covariant CustomTabBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.initialIndex != widget.initialIndex) { + selectedIndex = widget.initialIndex; + } + } + @override void dispose() { super.dispose(); @@ -119,10 +127,7 @@ class CustomTabBarState extends State { width: 18.w, iconColor: isSelected ? activeTextColor : inActiveTextColor, ), - tabBar.title.toText13( - weight: isSelected ? FontWeight.w600 : FontWeight.w500, - color: isSelected ? activeTextColor : inActiveTextColor, - letterSpacing: isSelected ? -0.3 : -0.1), + tabBar.title.toText13(color: isSelected ? activeTextColor : inActiveTextColor, letterSpacing: isSelected ? -0.3 : -0.1, isBold: true), ], )).onPress(() { setState(() { diff --git a/lib/widgets/date_range_selector/date_range_calender.dart b/lib/widgets/date_range_selector/date_range_calender.dart index d6e2eadf..fd37d0cb 100644 --- a/lib/widgets/date_range_selector/date_range_calender.dart +++ b/lib/widgets/date_range_selector/date_range_calender.dart @@ -75,7 +75,7 @@ class _DateRangeSelectorState extends State { style: TextStyle( color: AppColors.calenderTextColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, letterSpacing: -.2 ), ).paddingSymmetrical(24.h,0.h), @@ -109,7 +109,7 @@ class _DateRangeSelectorState extends State { viewHeaderStyle: DateRangePickerViewHeaderStyle( backgroundColor: AppColors.whiteColor, textStyle: TextStyle( - fontSize: 14.f, + fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.46, color: AppColors.textColor, @@ -121,17 +121,25 @@ class _DateRangeSelectorState extends State { selectionShape: DateRangePickerSelectionShape.rectangle, selectionRadius: 12.h, selectionColor: AppColors.transparent, + selectionTextStyle: TextStyle( + fontFamily: "Poppins", + ), startRangeSelectionColor: AppColors.primaryRedColor, endRangeSelectionColor: AppColors.primaryRedColor, rangeSelectionColor: AppColors.primaryRedColor.withOpacity(0.1), + rangeTextStyle: TextStyle( + fontFamily: "Poppins", + ), todayHighlightColor: Colors.transparent, monthCellStyle: DateRangePickerMonthCellStyle( textStyle: TextStyle( + fontFamily: "Poppins", fontSize: 12.f, color: AppColors.textColor, ), todayTextStyle: TextStyle( + fontFamily: "Poppins", color: AppColors.textColor, fontWeight: FontWeight.bold, ), @@ -248,7 +256,7 @@ class _DateRangeSelectorState extends State { style: TextStyle( color: AppColors.inputLabelTextColor, fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ), Text( @@ -256,7 +264,7 @@ class _DateRangeSelectorState extends State { style: TextStyle( color: AppColors.textColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ) ], @@ -318,6 +326,7 @@ class _DateRangeSelectorState extends State { width: 1, ), borderRadius: BorderRadius.circular(10)), + isEnglishOnly: true, ).onPress((){ _calendarController.selectedRange = null; model.currentlySelectedRange = Range.LAST_6MONTH; @@ -325,7 +334,13 @@ class _DateRangeSelectorState extends State { }), AppCustomChipWidget( - labelText: "Year ${model.getCurrentYear}", + richText: Row( + children: [ + "${LocaleKeys.year.tr(context: context)} ".toText10(), + model.getCurrentYear.toString().toText10(isEnglishOnly: true), + ], + ), + labelText: "${LocaleKeys.year.tr(context: context)} ${model.getCurrentYear}", backgroundColor: model.currentlySelectedRange == Range.THIS_YEAR ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.whiteColor, @@ -337,6 +352,7 @@ class _DateRangeSelectorState extends State { width: 1, ), borderRadius: BorderRadius.circular(10)), + isEnglishOnly: true, ).onPress((){ _calendarController.selectedRange = null; model.currentlySelectedRange = Range.THIS_YEAR; diff --git a/lib/widgets/datepicker_widget.dart b/lib/widgets/datepicker_widget.dart index bac05512..bbbd2a0a 100644 --- a/lib/widgets/datepicker_widget.dart +++ b/lib/widgets/datepicker_widget.dart @@ -55,7 +55,7 @@ class DatePickerWidget extends StatelessWidget { labelText, style: TextStyle( fontSize: 12.f, - fontWeight: FontWeight.w500, + isBold: true, color: Color(0xff898A8D), letterSpacing: -0.2, height: 18 / 12, @@ -102,7 +102,7 @@ class DatePickerWidget extends StatelessWidget { style: TextStyle( fontSize: 14.f, height: 21 / 14, - fontWeight: FontWeight.w500, + isBold: true, color: (selectedValue != null && selectedValue!.isNotEmpty) ? const Color(0xff2E3039) : const Color(0xffB0B0B0), letterSpacing: -0.2, ), diff --git a/lib/widgets/dialogs/confirm_dialog.dart b/lib/widgets/dialogs/confirm_dialog.dart index 5f7782fa..10889b5d 100644 --- a/lib/widgets/dialogs/confirm_dialog.dart +++ b/lib/widgets/dialogs/confirm_dialog.dart @@ -35,7 +35,7 @@ class ConfirmDialog extends StatelessWidget { Expanded( child: Text( title ?? LocaleKeys.confirm.tr(), - style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: AppColors.blackColor, height: 35 / 24, letterSpacing: -0.96), + style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700, color: AppColors.blackColor, height: 35 / 24, letterSpacing: -0.96), ).paddingOnly(top: 16), ), IconButton( diff --git a/lib/widgets/dropdown/country_dropdown_widget.dart b/lib/widgets/dropdown/country_dropdown_widget.dart index c0efbc98..ce1a660f 100644 --- a/lib/widgets/dropdown/country_dropdown_widget.dart +++ b/lib/widgets/dropdown/country_dropdown_widget.dart @@ -93,7 +93,7 @@ class CustomCountryDropdownState extends State { children: [ Text( LocaleKeys.phoneNumber.tr(), - style: TextStyle(fontSize: 12.f, height: 1.5, fontWeight: FontWeight.w500, letterSpacing: -1), + style: TextStyle(fontSize: 12.f, height: 1.5, fontWeight: FontWeight.w600, letterSpacing: -1), ), Row( mainAxisAlignment: MainAxisAlignment.start, @@ -138,7 +138,7 @@ class CustomCountryDropdownState extends State { ? selectedCountry!.nameArabic : selectedCountry!.displayName : LocaleKeys.selectCountry.tr(), - style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2), + style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2), ), ], ); @@ -201,7 +201,7 @@ class CustomCountryDropdownState extends State { if (!widget.isFromBottomSheet) SizedBox(width: 12.h), if (!widget.isFromBottomSheet) Text(appState.getLanguageCode() == "ar" ? country.nameArabic : country.displayName, - style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)), + style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2)), ], ), )), @@ -266,7 +266,7 @@ class CustomCountryDropdownState extends State { // if (!widget.isFromBottomSheet) SizedBox(width: 12.h), // if (!widget.isFromBottomSheet) // Text(appState.getLanguageCode() == "ar" ? country.nameArabic : country.displayName, - // style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)), + // style: TextStyle(fontSize: 14.fSize, height: 21 / 14, isBold: true, letterSpacing: -0.2)), // ], // ), // )), diff --git a/lib/widgets/dropdown/dropdown_widget.dart b/lib/widgets/dropdown/dropdown_widget.dart index a78b8fa5..7f76d8a6 100644 --- a/lib/widgets/dropdown/dropdown_widget.dart +++ b/lib/widgets/dropdown/dropdown_widget.dart @@ -101,7 +101,7 @@ class DropdownWidget extends StatelessWidget { labelText, style: TextStyle( fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: labelColor ?? Color(0xff898A8D), letterSpacing: -0.2, height: 18 / 12, @@ -132,7 +132,7 @@ class DropdownWidget extends StatelessWidget { style: TextStyle( fontSize: 14.f, height: 21 / 14, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, letterSpacing: -0.2, ), ), @@ -163,7 +163,7 @@ class DropdownWidget extends StatelessWidget { style: TextStyle( fontSize: 14.f, height: 21 / 14, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, // color: (selectedValue != null && selectedValue!.isNotEmpty) ? const Color(0xff2E3039) : const Color(0xffB0B0B0), color: AppColors.textColor, letterSpacing: -0.2, diff --git a/lib/widgets/family_files/family_file_add_widget.dart b/lib/widgets/family_files/family_file_add_widget.dart index c4f43248..ff19fa28 100644 --- a/lib/widgets/family_files/family_file_add_widget.dart +++ b/lib/widgets/family_files/family_file_add_widget.dart @@ -36,7 +36,7 @@ class _FamilyFileAddWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - widget.message.toText16(color: AppColors.textColor, weight: FontWeight.w500), + widget.message.toText16(color: AppColors.textColor, isBold: true), SizedBox(height: 20.h), Container( decoration: BoxDecoration(color: AppColors.whiteColor, borderRadius: BorderRadius.circular(24)), @@ -108,7 +108,7 @@ class _FamilyFileAddWidgetState extends State { }, icon: AppAssets.add_icon, height: 56.h, - fontWeight: FontWeight.w600), + isBold: true), SizedBox(height: 20.h), ], ); diff --git a/lib/widgets/graph/CustomBarGraph.dart b/lib/widgets/graph/CustomBarGraph.dart index 2f50f20d..2b3026d1 100644 --- a/lib/widgets/graph/CustomBarGraph.dart +++ b/lib/widgets/graph/CustomBarGraph.dart @@ -85,7 +85,7 @@ class CustomBarChart extends StatelessWidget { this.showBottomTitleDates = true, this.isFullScreeGraph = false, this.secondaryBarColor, - this.bottomLabelFontWeight = FontWeight.w500, + this.bottomLabelFontWeight = FontWeight.w600, this.bottomLabelSize, this.leftLabelInterval, this.leftLabelReservedSize, @@ -139,7 +139,7 @@ class CustomBarChart extends StatelessWidget { TextStyle( color: Colors.black, fontSize: 12.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, ), ); }, diff --git a/lib/widgets/graph/custom_graph.dart b/lib/widgets/graph/custom_graph.dart index 3b0629f0..a5c9f897 100644 --- a/lib/widgets/graph/custom_graph.dart +++ b/lib/widgets/graph/custom_graph.dart @@ -31,6 +31,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; /// - [showBottomTitleDates]: Whether to show bottom axis labels. /// - [isFullScreeGraph]: Whether the graph is fullscreen. /// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting. +/// - [isRTL]: Whether the graph should render in right-to-left direction. /// /// Example usage: /// ```dart @@ -70,6 +71,7 @@ class CustomGraph extends StatelessWidget { final bool showLinePoints; final double? cutOffY; final RangeAnnotations? rangeAnnotations; + final bool isRTL; ///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart final Widget Function(double) leftLabelFormatter; @@ -98,7 +100,7 @@ class CustomGraph extends StatelessWidget { this.graphShadowColor, this.graphGridColor, this.bottomLabelColor, - this.bottomLabelFontWeight = FontWeight.w500, + this.bottomLabelFontWeight = FontWeight.w600, this.bottomLabelSize, this.leftLabelInterval, this.leftLabelReservedSize, @@ -113,7 +115,8 @@ class CustomGraph extends StatelessWidget { this.showShadow = false, this.showLinePoints = false, this.cutOffY = 0, - this.rangeAnnotations}); + this.rangeAnnotations, + this.isRTL = false}); @override Widget build(BuildContext context) { @@ -122,6 +125,12 @@ class CustomGraph extends StatelessWidget { final resolvedGraphShadowColor = graphShadowColor ?? AppColors.graphGridColor; final resolvedGraphGridColor = graphGridColor ?? AppColors.graphGridColor; + // For RTL, reverse the data points so the graph reads right-to-left + final displayDataPoints = isRTL ? dataPoints.reversed.toList() : dataPoints; + final displaySecondaryDataPoints = (isRTL && secondaryDataPoints != null) + ? secondaryDataPoints!.reversed.toList() + : secondaryDataPoints; + return Material( color: AppColors.whiteColor, child: SizedBox( @@ -176,14 +185,14 @@ class CustomGraph extends StatelessWidget { // Show tooltip for each touched line return touchedSpots.map((spot) { // Determine which dataset this spot belongs to - final isSecondary = secondaryDataPoints != null && spot.barIndex == 1; + final isSecondary = displaySecondaryDataPoints != null && spot.barIndex == 1; final dataPoint = isSecondary - ? secondaryDataPoints![spot.x.toInt()] - : dataPoints[spot.x.toInt()]; + ? displaySecondaryDataPoints[spot.x.toInt()] + : displayDataPoints[spot.x.toInt()]; return LineTooltipItem( '${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""} - ${dataPoint.displayTime}', - TextStyle(color: Colors.black, fontSize: 12.f, fontWeight: FontWeight.w500), + TextStyle(color: Colors.black, fontSize: 12.f, fontWeight: FontWeight.w600, fontFamily: "Poppins"), ); }).toList(); }, @@ -192,9 +201,19 @@ class CustomGraph extends StatelessWidget { titlesData: FlTitlesData( leftTitles: AxisTitles( sideTitles: SideTitles( - showTitles: true, + showTitles: !isRTL, + reservedSize: leftLabelReservedSize ?? 80, + interval: leftLabelInterval ?? .1, + getTitlesWidget: (value, _) { + return leftLabelFormatter(value); + }, + ), + ), + rightTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: isRTL, reservedSize: leftLabelReservedSize ?? 80, - interval: leftLabelInterval ?? .1, // Let fl_chart handle it + interval: leftLabelInterval ?? .1, getTitlesWidget: (value, _) { return leftLabelFormatter(value); }, @@ -206,30 +225,28 @@ class CustomGraph extends StatelessWidget { showTitles: showBottomTitleDates, reservedSize: bottomLabelReservedSize ?? 20, getTitlesWidget: (value, _) { - return bottomLabelFormatter(value, dataPoints); + return bottomLabelFormatter(value, displayDataPoints); }, interval: 1, // ensures 1:1 mapping with spots ), ), topTitles: AxisTitles(), - rightTitles: AxisTitles(), ), borderData: FlBorderData( show: true, - border: const Border( + border: Border( bottom: BorderSide.none, - left: BorderSide(color: Colors.grey, width: .5), - right: BorderSide.none, + left: isRTL ? BorderSide.none : const BorderSide(color: Colors.grey, width: .5), + right: isRTL ? const BorderSide(color: Colors.grey, width: .5) : BorderSide.none, top: BorderSide.none, ), ), - lineBarsData: _buildColoredLineSegments(dataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor), + lineBarsData: _buildColoredLineSegments(displayDataPoints, showLinePoints, resolvedGraphColor, resolvedGraphShadowColor, + secondaryPoints: displaySecondaryDataPoints), gridData: FlGridData( show: showGridLines ?? true, drawVerticalLine: false, horizontalInterval: horizontalInterval, - // checkToShowHorizontalLine: (value) => - // value >= 0 && value <= 100, getDrawingHorizontalLine: getDrawingHorizontalLine ?? (value) { return FlLine( @@ -245,7 +262,8 @@ class CustomGraph extends StatelessWidget { ); } - List _buildColoredLineSegments(List dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor) { + List _buildColoredLineSegments(List dataPoints, bool showLinePoints, Color resolvedGraphColor, Color resolvedGraphShadowColor, + {List? secondaryPoints}) { final List allSpots = dataPoints.asMap().entries.map((entry) { double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; return FlSpot(entry.key.toDouble(), value); @@ -263,7 +281,17 @@ class CustomGraph extends StatelessWidget { begin: Alignment.centerLeft, end: Alignment.centerRight, ), - dotData: FlDotData(show: showLinePoints), + dotData: FlDotData( + show: true, + getDotPainter: (spot, percent, barData, idx) { + return FlDotCirclePainter( + radius: 6, + color: resolvedGraphColor, + strokeWidth: 2, + strokeColor: Colors.white, + ); + }, + ), belowBarData: BarAreaData( show: showShadow, applyCutOffY: cutOffY != null, @@ -281,8 +309,8 @@ class CustomGraph extends StatelessWidget { ]; // Add secondary line if provided (for dual-line graphs like blood pressure) - if (secondaryDataPoints != null && secondaryDataPoints!.isNotEmpty) { - final List secondarySpots = secondaryDataPoints!.asMap().entries.map((entry) { + if (secondaryPoints != null && secondaryPoints.isNotEmpty) { + final List secondarySpots = secondaryPoints.asMap().entries.map((entry) { double value = (makeGraphBasedOnActualValue) ? double.tryParse(entry.value.actualValue) ?? 0.0 : entry.value.value; return FlSpot(entry.key.toDouble(), value); }).toList(); @@ -299,7 +327,17 @@ class CustomGraph extends StatelessWidget { begin: Alignment.centerLeft, end: Alignment.centerRight, ), - dotData: FlDotData(show: showLinePoints), + dotData: FlDotData( + show: true, + getDotPainter: (spot, percent, barData, idx) { + return FlDotCirclePainter( + radius: 6, + color: secondaryGraphColor ?? AppColors.blueColor, + strokeWidth: 2, + strokeColor: Colors.white, + ); + }, + ), belowBarData: BarAreaData(show: false), ), ); diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index eb3021f5..c6d35617 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -1,21 +1,24 @@ import 'dart:convert'; import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.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/attachment_options.dart'; import 'package:hmg_patient_app_new/widgets/bottom_sheet.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:image_picker/image_picker.dart'; final ImagePicker picker = ImagePicker(); class ImageOptions { static void showImageOptionsNew(BuildContext context, bool showFilesOption, Function(String, File) image) { - showMyBottomSheet( + showCommonBottomSheetWithoutHeight( context, - callBackFunc: () {}, + title: LocaleKeys.feedback.tr(), child: AttachmentOptions( showFilesOption: showFilesOption, onCameraTap: () async { @@ -66,7 +69,63 @@ class ImageOptions { image(result.files.first.path.toString(), files.first); }, ), + callBackFunc: () {}, + isFullScreen: false, ); + // showMyBottomSheet( + // context, + // callBackFunc: () {}, + // child: AttachmentOptions( + // showFilesOption: showFilesOption, + // onCameraTap: () async { + // if (Platform.isAndroid) { + // cameraImageAndroid(image); + // } else { + // File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 20))?.path ?? ""); + // // XFile? media = await picker.pickMedia(); + // String? fileName = _image.path; + // var bytes = File(fileName!).readAsBytesSync(); + // String base64Encode = base64.encode(bytes); + // if (base64Encode != null) { + // image(base64Encode, _image); + // } + // } + // }, + // onGalleryTap: () async { + // if (Platform.isAndroid) { + // galleryImageAndroid(image); + // } else { + // File _image = File((await picker.pickMedia())?.path ?? ""); + // String fileName = _image.path; + // var bytes = File(fileName).readAsBytesSync(); + // String base64Encode = base64.encode(bytes); + // if (base64Encode != null) { + // image(base64Encode, _image); + // } + // } + // }, + // onFilesTap: () async { + // FilePickerResult? result = await FilePicker.platform.pickFiles( + // type: FileType.custom, + // allowedExtensions: [ + // 'jpg', + // 'jpeg ', + // 'pdf', + // 'txt', + // 'docx', + // 'doc', + // 'pptx', + // 'xlsx', + // 'png', + // 'rar', + // 'zip', + // ], + // ); + // List files = result!.paths.map((path) => File(path!)).toList(); + // image(result.files.first.path.toString(), files.first); + // }, + // ), + // ); } // static void showImageOptions(BuildContext context, Function(String, File) image) { diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index 25dd4580..5b598c40 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -110,7 +110,7 @@ class TextInputWidget extends StatelessWidget { // onTap: () => node.unfocus(), // child: Container( // padding: const EdgeInsets.all(12.0), - // child: "Done".toText16(weight: FontWeight.w500, color: AppColors.infoColor), + // child: "Done".toText16(isBold: true, color: AppColors.infoColor), // ), // ); // } @@ -159,8 +159,8 @@ class TextInputWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildLabelText(labelColor).paddingOnly( - right: (appState.getLanguageCode() == "ar" ? 10 : 0), - left: (appState.getLanguageCode() == "en" ? 10 : 0), + right: (appState.isArabic() ? 10 : 10), + left: (!appState.isArabic() ? 10 : 10), ), Row( children: [ @@ -230,7 +230,8 @@ class TextInputWidget extends StatelessWidget { language: appState.getLanguageCode()!, initialDate: DateTime.now(), showCalendarToggle: isHideSwitcher == true ? false : true, - fontFamily: appState.getLanguageCode() == "ar" ? "GESSTwo" : "Poppins", + // fontFamily: appState.getLanguageCode() == "ar" ? "GESSTwo" : "Poppins", + fontFamily: "Poppins", okWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.confirm, width: 24.h, height: 24.h)), cancelWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)), onCalendarTypeChanged: (bool value) { @@ -289,15 +290,7 @@ class TextInputWidget extends StatelessWidget { } Widget _buildLabelText(Color? labelColor) { - return Text( - labelText, - style: TextStyle( - fontSize: 12.f, - fontWeight: FontWeight.w500, - color: labelColor ?? AppColors.inputLabelTextColor, - letterSpacing: -0, - ), - ); + return labelText.toText12(isBold: true, color: labelColor ?? AppColors.inputLabelTextColor); } Widget _buildTextField(BuildContext context) { @@ -311,7 +304,7 @@ class TextInputWidget extends StatelessWidget { hintLocales: const [Locale('en', 'US')], enabled: isEnable, scrollPadding: EdgeInsets.zero, - keyboardType: isMultiline ? TextInputType.multiline : keyboardType, + keyboardType: isMultiline ? TextInputType.multiline : (isWalletAmountInput! ? const TextInputType.numberWithOptions(decimal: true) : keyboardType), controller: controller, readOnly: isReadOnly, textAlignVertical: TextAlignVertical.top, @@ -322,6 +315,12 @@ class TextInputWidget extends StatelessWidget { autofocus: autoFocus, textInputAction: TextInputAction.done, cursorHeight: isWalletAmountInput! ? 40.h : 20.h, + maxLength: isWalletAmountInput! ? 7 : 100, + inputFormatters: isWalletAmountInput! + ? [ + _ThousandSeparatorInputFormatter(), + ] + : null, onTapOutside: (event) { FocusManager.instance.primaryFocus?.unfocus(); }, @@ -331,7 +330,7 @@ class TextInputWidget extends StatelessWidget { style: TextStyle( fontSize: fontS, height: isMultiline ? 1.2 : (isWalletAmountInput! ? 1 / 4 : 0), - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -1, // fontFamily: keyboardType == TextInputType.number ? getIt.get().isArabic() ? 'GESSTwo' : 'Poppins' : 'Poppins', @@ -339,11 +338,12 @@ class TextInputWidget extends StatelessWidget { locale: const Locale('en', 'US'), // Force English locale for text style ), decoration: InputDecoration( + counterText: "", isDense: true, hintText: hintText, - hintStyle: TextStyle(fontSize: 14.f, height: 21 / 16, fontWeight: FontWeight.w500, color: hintColor != null ? AppColors.textColor : Color(0xff898A8D), letterSpacing: -0.75), + hintStyle: TextStyle(fontSize: 14.f, height: 21 / 16, fontWeight: FontWeight.w600, color: hintColor != null ? AppColors.textColor : Color(0xff898A8D), letterSpacing: -0.75), prefixIconConstraints: BoxConstraints(minWidth: 30.h), - prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, weight: FontWeight.w500), + prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, isBold: true), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, @@ -378,7 +378,7 @@ class TextInputWidget extends StatelessWidget { // style: TextStyle( // fontSize: fontS, // height: isMultiline ? 1.2 : (isWalletAmountInput! ? 1 / 4 : 0), - // fontWeight: FontWeight.w500, + // isBold: true, // color: AppColors.textColor, // letterSpacing: -1, // ), @@ -388,11 +388,11 @@ class TextInputWidget extends StatelessWidget { // hintStyle: TextStyle( // fontSize: 14.f, // height: 21 / 16, - // fontWeight: FontWeight.w500, + // isBold: true, // color: hintColor != null ? AppColors.textColor : Color(0xff898A8D), // letterSpacing: -0.75), // prefixIconConstraints: BoxConstraints(minWidth: 30.h), - // prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, weight: FontWeight.w500), + // prefixIcon: prefix == null ? null : "+${prefix!}".toText14(letterSpacing: -1, color: AppColors.textColor, isBold: true), // contentPadding: EdgeInsets.zero, // border: InputBorder.none, // focusedBorder: InputBorder.none, @@ -412,3 +412,63 @@ class TextInputWidget extends StatelessWidget { ); } } + +class _ThousandSeparatorInputFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + // Remove all commas to get the raw number + String newText = newValue.text.replaceAll(',', ''); + + // Allow only digits and one decimal point + if (newText.isNotEmpty && !RegExp(r'^\d*\.?\d{0,2}$').hasMatch(newText)) { + return oldValue; + } + + // Split into integer and decimal parts + String integerPart; + String decimalPart = ''; + + if (newText.contains('.')) { + final parts = newText.split('.'); + integerPart = parts[0]; + decimalPart = '.${parts[1]}'; + } else { + integerPart = newText; + } + + // Add thousand separators to the integer part + if (integerPart.isNotEmpty) { + final buffer = StringBuffer(); + int count = 0; + for (int i = integerPart.length - 1; i >= 0; i--) { + buffer.write(integerPart[i]); + count++; + if (count == 3 && i > 0) { + buffer.write(','); + count = 0; + } + } + integerPart = buffer.toString().split('').reversed.join(); + } + + final formatted = '$integerPart$decimalPart'; + + // Calculate new cursor position + int cursorOffset = newValue.selection.baseOffset; + // Count commas before cursor in the new formatted string + int commasInNew = ','.allMatches(formatted.substring(0, cursorOffset.clamp(0, formatted.length))).length; + // Count commas before cursor in the old value + int commasInOld = ','.allMatches(oldValue.text.substring(0, oldValue.selection.baseOffset.clamp(0, oldValue.text.length))).length; + int newCursorPos = cursorOffset + (commasInNew - commasInOld); + newCursorPos = newCursorPos.clamp(0, formatted.length); + + return TextEditingValue( + text: formatted, + selection: TextSelection.collapsed(offset: newCursorPos), + ); + } +} + diff --git a/lib/widgets/language_switcher.dart b/lib/widgets/language_switcher.dart index 033eaaff..3ee10df3 100644 --- a/lib/widgets/language_switcher.dart +++ b/lib/widgets/language_switcher.dart @@ -62,12 +62,12 @@ class _LanguageSelectorState extends State { // currentLangData['name']?.toUpperCase() ?? 'EN', widget.currentLanguage == 'ar' ? "ENGLISH" : "العربية", style: context.dynamicTextStyle( - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, fontSize: 14.f, color: AppColors.primaryRedColor, letterSpacing: 0.1, isLanguageSwitcher: true, - fontFamily: widget.currentLanguage == 'ar' ? "Poppins" : "GESSTwo"), + fontFamily: widget.currentLanguage == 'ar' ? "Poppins" : "CairoArabic"), ), ], ), diff --git a/lib/widgets/loader/bottomsheet_loader.dart b/lib/widgets/loader/bottomsheet_loader.dart index bff293d0..306d7ce4 100644 --- a/lib/widgets/loader/bottomsheet_loader.dart +++ b/lib/widgets/loader/bottomsheet_loader.dart @@ -5,6 +5,8 @@ import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/route_extensions.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/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; @@ -13,7 +15,7 @@ class LoaderBottomSheet { static final NavigationService _navService = GetIt.I(); static bool _isVisible = false; - static void showLoader({String? loadingText}) { + static void showLoader({String? loadingText, bool showCloseButton = false, Function? onCloseTap}) { if (_isVisible) return; _isVisible = true; @@ -32,7 +34,31 @@ class LoaderBottomSheet { color: AppColors.whiteColor, borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), - child: Center( + child: (showCloseButton) ? Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "".toText20(), + if (showCloseButton) + IconButton( + icon: Icon(Icons.close, color: AppColors.textColor), + onPressed: () { + context.pop(); + if(onCloseTap != null) { + onCloseTap(); + } + }, + ) + else + SizedBox(width: 48), // Placeholder for alignment + ], + ), + Center( + child: Utils.getLoadingWidget(loadingText: loadingText), + ).paddingSymmetrical(24.w, 0), + ], + ) : Center( child: Utils.getLoadingWidget(loadingText: loadingText), ).paddingSymmetrical(24.w, 0), ); diff --git a/lib/widgets/map/HMSMap.dart b/lib/widgets/map/HMSMap.dart index 96b2facd..50da5f3a 100644 --- a/lib/widgets/map/HMSMap.dart +++ b/lib/widgets/map/HMSMap.dart @@ -1,63 +1,263 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; -import 'package:huawei_map/huawei_map.dart' ; +import 'package:huawei_map/huawei_map.dart'; -class HMSMap extends StatefulWidget{ +class HMSMap extends StatefulWidget { final CameraPosition currentLocation; final Function(CameraPosition) onCameraMoved; final VoidCallback onCameraIdle; - final MapType mapType; + final MapType initialMapType; final bool compassEnabled; final bool myLocationEnabled; final bool showCenterMarker; final Completer? inputController; - HMSMap({super.key, required this.currentLocation ,required this.onCameraMoved,required this.onCameraIdle, - this.mapType = MapType.normal,this.compassEnabled = false,this.showCenterMarker = false, - this.myLocationEnabled = true, this.inputController}); + const HMSMap({ + super.key, + required this.currentLocation, + required this.onCameraMoved, + required this.onCameraIdle, + this.initialMapType = MapType.normal, + this.compassEnabled = false, + this.showCenterMarker = false, + this.myLocationEnabled = true, + this.inputController, + }); @override State createState() => _HMSMapState(); } class _HMSMapState extends State { - Completer? controller; + Completer? controller; + late MapType _selectedMapType; + bool _showMapTypeSelector = false; + + final List<_MapTypeOption> _mapTypeOptions = [ + _MapTypeOption(type: MapType.normal, label: LocaleKeys.normalMapType.tr(), icon: Icons.map_outlined), + _MapTypeOption(type: MapType.terrain, label: LocaleKeys.terrainMapType.tr(), icon: Icons.terrain_outlined), + ]; @override void initState() { + super.initState(); HuaweiMapInitializer.initializeMap(); controller = widget.inputController ?? Completer(); - super.initState(); + _selectedMapType = widget.initialMapType; + } + + void _onMapTypeSelected(MapType type) { + setState(() { + _selectedMapType = type; + _showMapTypeSelector = false; + }); } - // @override + + void _toggleSelector() => + setState(() => _showMapTypeSelector = !_showMapTypeSelector); + @override - Widget build(BuildContext context) => - Stack( - children: [ - HuaweiMap( - mapType: widget.mapType, - zoomControlsEnabled: false, - myLocationEnabled: widget.myLocationEnabled, - myLocationButtonEnabled: false, - compassEnabled: widget.compassEnabled, - onCameraIdle:()=> widget.onCameraIdle(), - initialCameraPosition: widget.currentLocation, - onCameraMove: (value) => widget.onCameraMoved(value), - onMapCreated: (HuaweiMapController controller) { - this.controller?.complete(controller); - }, + Widget build(BuildContext context) { + return Stack( + children: [ + // ── Huawei Map ──────────────────────────────────────────────── + HuaweiMap( + mapType: _selectedMapType, + zoomControlsEnabled: false, + myLocationEnabled: widget.myLocationEnabled, + myLocationButtonEnabled: false, + compassEnabled: widget.compassEnabled, + onCameraIdle: widget.onCameraIdle, + initialCameraPosition: widget.currentLocation, + onCameraMove: widget.onCameraMoved, + onMapCreated: (HuaweiMapController ctrl) { + controller?.complete(ctrl); + }, + ), + + // ── Centre pin marker ───────────────────────────────────────── + if (widget.showCenterMarker) + Align( + alignment: Alignment.center, + child: Icon( + Icons.location_pin, + size: 36.h, + color: AppColors.primaryRedColor, + ).paddingOnly(bottom: 24.h), + ), + + // ── Map-type toggle + panel ─────────────────────────────────── + PositionedDirectional( + top: 48.h, + end: 16.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + _MapTypeToggleButton( + isActive: _showMapTypeSelector, + onTap: _toggleSelector, + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 220), + transitionBuilder: (child, animation) => FadeTransition( + opacity: animation, + child: SizeTransition( + sizeFactor: animation, + axisAlignment: -1, + child: child, + ), + ), + child: _showMapTypeSelector + ? _MapTypeSelectorPanel( + options: _mapTypeOptions, + selected: _selectedMapType, + onSelected: _onMapTypeSelected, + ) + : const SizedBox.shrink(), ), - Visibility( - visible: widget.showCenterMarker, - child: Align( - alignment: Alignment.center, - child: Icon(Icons.location_pin, size: 36.h, color: AppColors.primaryRedColor).paddingOnly(bottom: 24.h), + ], + ), + ), + ], + ); + } +} + +// ── Toggle button ───────────────────────────────────────────────────────────── + +class _MapTypeToggleButton extends StatelessWidget { + final bool isActive; + final VoidCallback onTap; + + const _MapTypeToggleButton({required this.isActive, required this.onTap}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 42.w, + height: 42.h, + decoration: BoxDecoration( + color: isActive ? AppColors.primaryRedColor : AppColors.whiteColor, + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + Icons.layers, + size: 22.h, + color: isActive ? AppColors.whiteColor : AppColors.blackBgColor, + ), + ), + ); + } +} + +// ── Selector panel ──────────────────────────────────────────────────────────── + +class _MapTypeSelectorPanel extends StatelessWidget { + final List<_MapTypeOption> options; + final MapType selected; + final ValueChanged onSelected; + + const _MapTypeSelectorPanel({ + required this.options, + required this.selected, + required this.onSelected, + }); + + @override + Widget build(BuildContext context) { + return Container( + key: const ValueKey('map_type_panel'), + margin: EdgeInsets.only(top: 8.h), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 6.w), + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(12.r), + + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: options + .map((opt) => _MapTypeItem( + option: opt, + isSelected: selected == opt.type, + onTap: () => onSelected(opt.type), + )) + .toList(), + ), + ); + } +} + +// ── Single row item ─────────────────────────────────────────────────────────── + +class _MapTypeItem extends StatelessWidget { + final _MapTypeOption option; + final bool isSelected; + final VoidCallback onTap; + + const _MapTypeItem({ + required this.option, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 180), + margin: EdgeInsets.symmetric(vertical: 3.h), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 8.h), + decoration: BoxDecoration( + color: isSelected + ? AppColors.primaryRedColor.withOpacity(0.1) + : Colors.transparent, + borderRadius: BorderRadius.circular(8.r), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + option.icon, + size: 18.h, + color: isSelected ? AppColors.primaryRedColor : AppColors.blackBgColor, ), - ) - ], - ); + SizedBox(width: 8.w), + Text( + option.label, + style: TextStyle( + fontSize: 14.f, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, + color: isSelected ? AppColors.primaryRedColor :AppColors.blackBgColor, + ), + ), + ], + ), + ), + ); + } +} + +// ── Data class ──────────────────────────────────────────────────────────────── + +class _MapTypeOption { + final MapType type; + final String label; + final IconData icon; + + const _MapTypeOption({ + required this.type, + required this.label, + required this.icon, + }); } \ No newline at end of file diff --git a/lib/widgets/map/gms_map.dart b/lib/widgets/map/gms_map.dart index f4c3ad54..1d06fb10 100644 --- a/lib/widgets/map/gms_map.dart +++ b/lib/widgets/map/gms_map.dart @@ -1,54 +1,417 @@ +// import 'dart:async'; +// +// import 'package:flutter/material.dart'; +// import 'package:google_maps_flutter/google_maps_flutter.dart'; +// import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +// import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +// import 'package:hmg_patient_app_new/theme/colors.dart'; +// +// class GMSMap extends StatelessWidget{ +// Completer? controller; +// final CameraPosition currentLocation; +// final Function(CameraPosition) onCameraMoved; +// final VoidCallback onCameraIdle; +// final MapType mapType; +// final bool compassEnabled; +// final bool myLocationEnabled; +// final bool showCenterMarker; +// +// +// GMSMap({super.key, required this.currentLocation ,required this.onCameraMoved,required this.onCameraIdle, +// this.mapType = MapType.normal,this.compassEnabled = false,this.showCenterMarker = false, +// this.myLocationEnabled = true,Completer? inputController}){ +// controller = inputController ?? Completer(); +// } +// +// @override +// Widget build(BuildContext context) { +// return Stack( +// children: [ +// GoogleMap( +// mapType: mapType, +// zoomControlsEnabled: true, +// myLocationEnabled: myLocationEnabled, +// myLocationButtonEnabled: false, +// compassEnabled: compassEnabled, +// initialCameraPosition: currentLocation, +// onCameraMove: (value) => onCameraMoved(value), +// onCameraIdle: ()=>onCameraIdle(), +// // padding: EdgeInsets.only(bottom: 300.h), +// onMapCreated: (GoogleMapController controller) { +// this.controller?.complete(controller); +// }, +// ), +// Visibility( +// visible: showCenterMarker, +// child: Align( +// alignment: Alignment.center, +// child: Icon(Icons.location_pin, size: 36.h, color: AppColors.primaryRedColor).paddingOnly(bottom: 24.h), +// ), +// ) +// ], +// ); +// } +// } + import 'dart:async'; +import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.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'; +import 'package:hmg_patient_app_new/core/location_util.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; -class GMSMap extends StatelessWidget{ - Completer? controller; +class GMSMap extends StatefulWidget { final CameraPosition currentLocation; final Function(CameraPosition) onCameraMoved; final VoidCallback onCameraIdle; - final MapType mapType; + final MapType initialMapType; final bool compassEnabled; final bool myLocationEnabled; final bool showCenterMarker; + final Completer? inputController; + final double bottomPaddingHeight; + + const GMSMap({ + super.key, + required this.currentLocation, + required this.onCameraMoved, + required this.onCameraIdle, + this.initialMapType = MapType.normal, + this.compassEnabled = false, + this.showCenterMarker = false, + this.myLocationEnabled = true, + this.inputController, + required this.bottomPaddingHeight, + }); + + @override + State createState() => _GMSMapState(); +} + +class _GMSMapState extends State { + late Completer _controller; + GoogleMapController? _activeController; + late MapType _selectedMapType; + bool _showMapTypeSelector = false; + num bottomPaddingHeight = 0; + + /// All supported map types with display labels and icons + final List<_MapTypeOption> _mapTypeOptions = [ + _MapTypeOption( + type: MapType.normal, + label: LocaleKeys.normalMapType.tr(), + icon: Icons.map_outlined, + ), + _MapTypeOption( + type: MapType.satellite, + label: LocaleKeys.satelliteMapType.tr(), + icon: Icons.satellite_alt_outlined, + ), + _MapTypeOption( + type: MapType.terrain, + label: LocaleKeys.terrainMapType.tr(), + icon: Icons.terrain_outlined, + ), + _MapTypeOption( + type: MapType.hybrid, + label: LocaleKeys.hybridMapType.tr(), + icon: Icons.layers_outlined, + ), + ]; + + @override + void initState() { + super.initState(); + _controller = widget.inputController ?? Completer(); + _selectedMapType = widget.initialMapType; + } + + void _onMapTypeSelected(MapType type) { + setState(() { + _selectedMapType = type; + _showMapTypeSelector = false; + }); + } + + void _toggleMapTypeSelector() { + setState(() => _showMapTypeSelector = !_showMapTypeSelector); + } + Future _animateToMyLocation() async { + final appState = getIt.get(); + final locationUtils = getIt.get(); - GMSMap({super.key, required this.currentLocation ,required this.onCameraMoved,required this.onCameraIdle, - this.mapType = MapType.normal,this.compassEnabled = false,this.showCenterMarker = false, - this.myLocationEnabled = true,Completer? inputController}){ - controller = inputController ?? Completer(); + void _moveToLocation() { + if (appState.userLat != 0.0 && appState.userLong != 0.0 && _activeController != null) { + _activeController!.animateCamera( + CameraUpdate.newCameraPosition( + CameraPosition( + target: LatLng(appState.userLat, appState.userLong), + zoom: 16, + ), + ), + ); + } + } + + if (appState.userLat != 0.0 && appState.userLong != 0.0) { + _moveToLocation(); + } else { + locationUtils.getLocation( + isShowConfirmDialog: true, + onSuccess: (latLng) { + _moveToLocation(); + }, + onFailure: () {}, + onLocationDeniedForever: () {}, + ); + } } @override Widget build(BuildContext context) { - return Stack( - children: [ - GoogleMap( - mapType: mapType, - zoomControlsEnabled: true, - myLocationEnabled: myLocationEnabled, + return Stack( + children: [ + // ── Google Map ────────────────────────────────────────────────── + GoogleMap( + mapType: _selectedMapType, + zoomControlsEnabled: false, + // myLocationEnabled: widget.myLocationEnabled, + myLocationEnabled: true, myLocationButtonEnabled: false, - compassEnabled: compassEnabled, - initialCameraPosition: currentLocation, - onCameraMove: (value) => onCameraMoved(value), - onCameraIdle: ()=>onCameraIdle(), - // padding: EdgeInsets.only(bottom: 300.h), + // padding: EdgeInsets.only(bottom: double.parse(widget.bottomPaddingHeight.toString())), + // padding: EdgeInsets.only(top: Platform.isAndroid ? double.parse(widget.bottomPaddingHeight.toString()) : 0, bottom: Platform.isIOS ? double.parse(widget.bottomPaddingHeight.toString()) : 0), + compassEnabled: widget.compassEnabled, + initialCameraPosition: widget.currentLocation, + onCameraMove: widget.onCameraMoved, + onCameraIdle: widget.onCameraIdle, onMapCreated: (GoogleMapController controller) { - this.controller?.complete(controller); + _activeController = controller; + if (!_controller.isCompleted) { + _controller.complete(controller); + } }, - ), - Visibility( - visible: showCenterMarker, - child: Align( - alignment: Alignment.center, - child: Icon(Icons.location_pin, size: 36.h, color: AppColors.primaryRedColor).paddingOnly(bottom: 24.h), + ), + + // ── Centre pin marker ─────────────────────────────────────────── + if (widget.showCenterMarker) + Align( + alignment: Alignment.center, + child: Icon( + Icons.location_pin, + size: 36.h, + color: AppColors.primaryRedColor, + ).paddingOnly(bottom: Platform.isAndroid ? 30.h : double.parse((widget.bottomPaddingHeight + 25.h).toString())), ), - ) + + // ── My Location button ──────────────────────────────────────────── + PositionedDirectional( + // bottom: 300.h, + // bottom: 400.h, + bottom: widget.bottomPaddingHeight, + start: 16.w, + child: GestureDetector( + onTap: _animateToMyLocation, + child: Container( + width: 42.w, + height: 42.h, + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + Icons.my_location, + size: 22.h, + color: AppColors.blackBgColor, + ), + ), + ), + ), + + // ── Map-type toggle button ────────────────────────────────────── + PositionedDirectional( + top: 48.h, + end: 16.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // Toggle button + _MapTypeToggleButton( + isActive: _showMapTypeSelector, + onTap: _toggleMapTypeSelector, + ), + + // Expandable selector panel + AnimatedSwitcher( + duration: const Duration(milliseconds: 220), + transitionBuilder: (child, animation) => FadeTransition( + opacity: animation, + child: SizeTransition( + sizeFactor: animation, + axisAlignment: -1, + child: child, + ), + ), + child: _showMapTypeSelector + ? _MapTypeSelectorPanel( + options: _mapTypeOptions, + selected: _selectedMapType, + onSelected: _onMapTypeSelected, + ) + : const SizedBox.shrink(), + ), + ], + ), + ), ], ); } +} + +// ── Toggle button ───────────────────────────────────────────────────────────── + +class _MapTypeToggleButton extends StatelessWidget { + final bool isActive; + final VoidCallback onTap; + + const _MapTypeToggleButton({ + required this.isActive, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 42.w, + height: 42.h, + decoration: BoxDecoration( + color: isActive ? AppColors.primaryRedColor : AppColors.whiteColor, + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + Icons.layers, + size: 22.h, + color: isActive ? AppColors.whiteColor : AppColors.blackBgColor, + ), + ), + ); + } +} + +// ── Selector panel ──────────────────────────────────────────────────────────── + +class _MapTypeSelectorPanel extends StatelessWidget { + final List<_MapTypeOption> options; + final MapType selected; + final ValueChanged onSelected; + + const _MapTypeSelectorPanel({ + required this.options, + required this.selected, + required this.onSelected, + }); + + @override + Widget build(BuildContext context) { + return Container( + key: const ValueKey('map_type_panel'), + margin: EdgeInsets.only(top: 8.h), + padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 6.w), + decoration: BoxDecoration( + color: AppColors.whiteColor, + borderRadius: BorderRadius.circular(12.r), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: options + .map( + (opt) => _MapTypeItem( + option: opt, + isSelected: selected == opt.type, + onTap: () => onSelected(opt.type), + ), + ) + .toList(), + ), + ); + } +} + +// ── Single row item ─────────────────────────────────────────────────────────── + +class _MapTypeItem extends StatelessWidget { + final _MapTypeOption option; + final bool isSelected; + final VoidCallback onTap; + + const _MapTypeItem({ + required this.option, + required this.isSelected, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 180), + margin: EdgeInsets.symmetric(vertical: 3.h), + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 8.h), + decoration: BoxDecoration( + color: isSelected + ? AppColors.primaryRedColor.withOpacity(0.1) + : Colors.transparent, + borderRadius: BorderRadius.circular(8.r), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + option.icon, + size: 18.h, + color: isSelected ? AppColors.primaryRedColor : Colors.black54, + ), + SizedBox(width: 8.w), + Text( + option.label, + style: TextStyle( + fontSize: 13.f, + fontWeight: + isSelected ? FontWeight.w600 : FontWeight.w400, + color: + isSelected ? AppColors.primaryRedColor : AppColors.blackBgColor, + ), + ), + ], + ), + ), + ); + } +} + +// ── Data class ──────────────────────────────────────────────────────────────── + +class _MapTypeOption { + final MapType type; + final String label; + final IconData icon; + + const _MapTypeOption({ + required this.type, + required this.label, + required this.icon, + }); } \ No newline at end of file diff --git a/lib/widgets/map/location_map_widget.dart b/lib/widgets/map/location_map_widget.dart index e3139a93..da17b486 100644 --- a/lib/widgets/map/location_map_widget.dart +++ b/lib/widgets/map/location_map_widget.dart @@ -108,7 +108,7 @@ class LocationMapWidget extends StatelessWidget { address, style: TextStyle( fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.blackColor, letterSpacing: -0.4, ), @@ -174,7 +174,7 @@ class LocationMapWidget extends StatelessWidget { borderColor: AppColors.textColor.withValues(alpha: 0.01), textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, borderRadius: 12.r, padding: EdgeInsets.symmetric(horizontal: 10.w), height: 40.h, diff --git a/lib/widgets/map/map_utility_screen.dart b/lib/widgets/map/map_utility_screen.dart index 422809cf..a24317e6 100644 --- a/lib/widgets/map/map_utility_screen.dart +++ b/lib/widgets/map/map_utility_screen.dart @@ -48,15 +48,15 @@ class MapUtilityScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: Padding( - padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), - child: DecoratedBox( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), - child: Utils.buildSvgWithAssets(icon: AppAssets.locate_me, width: 24.h, height: 24.h).paddingAll(12.h).onPress(() { - context.read().moveToCurrentLocation(); - }), - ), - ), + // floatingActionButton: Padding( + // padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), + // child: DecoratedBox( + // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.h), + // child: Utils.buildSvgWithAssets(icon: AppAssets.locate_me, width: 24.h, height: 24.h).paddingAll(12.h).onPress(() { + // context.read().moveToCurrentLocation(); + // }), + // ), + // ), bottomSheet: FixedBottomSheet(context), body: Stack( children: [ @@ -68,6 +68,7 @@ class MapUtilityScreen extends StatelessWidget { myLocationEnabled: true, inputController: context.read().gmsController, showCenterMarker: true, + bottomPaddingHeight: 300.h, ) else HMSMap( @@ -129,7 +130,7 @@ class MapUtilityScreen extends StatelessWidget { color: AppColors.textColor, ), subTitleString.toText12( - fontWeight: FontWeight.w500, + isBold: true, color: AppColors.greyTextColor, ) ], diff --git a/lib/widgets/medication_reminder/reminder_timer_dialog.dart b/lib/widgets/medication_reminder/reminder_timer_dialog.dart index 62dbd963..221e2b70 100644 --- a/lib/widgets/medication_reminder/reminder_timer_dialog.dart +++ b/lib/widgets/medication_reminder/reminder_timer_dialog.dart @@ -48,7 +48,7 @@ // LocaleKeys.cancel.tr(), // style: TextStyle( // color: AppColors.errorColor, -// fontWeight: FontWeight.w500, +// isBold: true, // fontSize: 14 // ), // ), @@ -72,7 +72,7 @@ // label: Text( // LocaleKeys.setReminder.tr(), // style: TextStyle( -// fontWeight: FontWeight.w500, +// isBold: true, // fontSize: 14 // ), // ), diff --git a/lib/widgets/nfc/nfc_reader_sheet.dart b/lib/widgets/nfc/nfc_reader_sheet.dart index 956f758b..0d6c2866 100644 --- a/lib/widgets/nfc/nfc_reader_sheet.dart +++ b/lib/widgets/nfc/nfc_reader_sheet.dart @@ -126,7 +126,7 @@ class _NfcLayoutState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14.f, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.h, height: 40.h, icon: AppAssets.cancel, @@ -194,7 +194,7 @@ class _NfcLayoutState extends State { borderColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor, fontSize: 14, - fontWeight: FontWeight.w500, + isBold: true, borderRadius: 12.h, height: 40.h, icon: AppAssets.cancel, diff --git a/lib/widgets/radio/custom_radio_button.dart b/lib/widgets/radio/custom_radio_button.dart index a5301020..540aab46 100644 --- a/lib/widgets/radio/custom_radio_button.dart +++ b/lib/widgets/radio/custom_radio_button.dart @@ -38,7 +38,7 @@ class CustomRadioOption extends StatelessWidget { border: Border.all(color: isSelected ? AppColors.primaryRedColor : AppColors.bottomNAVBorder, width: 2.h)), ), SizedBox(width: 8.h), - text.toText16(weight: FontWeight.w500), // The provided content + text.toText16(isBold: true), // The provided content ], ), ), diff --git a/lib/widgets/radio_list_tile_widget.dart b/lib/widgets/radio_list_tile_widget.dart index 9025e190..afcebdf9 100644 --- a/lib/widgets/radio_list_tile_widget.dart +++ b/lib/widgets/radio_list_tile_widget.dart @@ -68,7 +68,7 @@ class RadioListTileWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - title.toText15(weight: FontWeight.w500, letterSpacing: -0.64), + title.toText15(isBold: true, letterSpacing: -0.64), if (subtitleWidget != null) ...[subtitleWidget!], if (subtitle != null) ...[ SizedBox(height: 4.h), diff --git a/lib/widgets/routes/custom_page_route.dart b/lib/widgets/routes/custom_page_route.dart index b89d7102..7cddf18d 100644 --- a/lib/widgets/routes/custom_page_route.dart +++ b/lib/widgets/routes/custom_page_route.dart @@ -10,7 +10,6 @@ class CustomPageRoute extends PageRouteBuilder { CustomPageRoute({required this.page, this.direction = AxisDirection.right, this.fullScreenDialog = false, super.settings}) : super( - transitionDuration: const Duration(milliseconds: 1500), reverseTransitionDuration: const Duration(milliseconds: 500), fullscreenDialog: fullScreenDialog, diff --git a/lib/widgets/time_picker_widget.dart b/lib/widgets/time_picker_widget.dart index bc996de6..fd5cb409 100644 --- a/lib/widgets/time_picker_widget.dart +++ b/lib/widgets/time_picker_widget.dart @@ -201,7 +201,7 @@ class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> { hour.toString().padLeft(2, '0'), style: TextStyle( fontSize: 24.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -216,7 +216,7 @@ class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> { ':', style: TextStyle( fontSize: 24.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -241,7 +241,7 @@ class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> { index.toString().padLeft(2, '0'), style: TextStyle( fontSize: 24.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -270,7 +270,7 @@ class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> { 'AM', style: TextStyle( fontSize: 24.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), @@ -280,7 +280,7 @@ class _TimePickerBottomSheetState extends State<_TimePickerBottomSheet> { 'PM', style: TextStyle( fontSize: 24.f, - fontWeight: FontWeight.w500, + fontWeight: FontWeight.w600, color: AppColors.textColor, ), ), diff --git a/lib/widgets/vital_sing_chart_and_detials.dart b/lib/widgets/vital_sing_chart_and_detials.dart index decca3f7..10805e58 100644 --- a/lib/widgets/vital_sing_chart_and_detials.dart +++ b/lib/widgets/vital_sing_chart_and_detials.dart @@ -77,7 +77,7 @@ // style: TextStyle( // fontSize: 16, // letterSpacing: -0.64, -// fontWeight: FontWeight.w600, +// isBold: true, // ), // ), // Table( diff --git a/lib/widgets/vital_sing_chart_blood_pressure.dart b/lib/widgets/vital_sing_chart_blood_pressure.dart index 8bff71b0..6730414b 100644 --- a/lib/widgets/vital_sing_chart_blood_pressure.dart +++ b/lib/widgets/vital_sing_chart_blood_pressure.dart @@ -72,7 +72,7 @@ // style: TextStyle( // fontSize: 16, // letterSpacing: -0.64, -// fontWeight: FontWeight.w600, +// isBold: true, // ), // ), // Table( diff --git a/package/device_calendar_plus/example/lib/main.dart b/package/device_calendar_plus/example/lib/main.dart index dbe66532..60011b2f 100644 --- a/package/device_calendar_plus/example/lib/main.dart +++ b/package/device_calendar_plus/example/lib/main.dart @@ -519,7 +519,7 @@ class _MyHomePageState extends State { style: const TextStyle( fontSize: 12, color: Colors.grey, - fontWeight: FontWeight.w500, + isBold: true, ), ), const SizedBox(height: 2), @@ -804,7 +804,7 @@ class _MyHomePageState extends State { title: Text( event.title, style: const TextStyle( - fontWeight: FontWeight.w500), + isBold: true), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/package/flutter_swiper_view/CHANGELOG-ZH.md b/package/flutter_swiper_view/CHANGELOG-ZH.md new file mode 100644 index 00000000..8591f0e2 --- /dev/null +++ b/package/flutter_swiper_view/CHANGELOG-ZH.md @@ -0,0 +1,85 @@ +## [1.1.8] - [2022/05/27] + + * 修复 example 无法运行的问题 + * 修复 SwiperPagination.rect 不显示指示器的问题 + * 支持空安全 + * 支持 Flutter 3 + +## [1.1.7] - [2022/05/27] + + * 测试发布 + + +## [1.1.1] - [2018/09/20] + * 修复自动集成测试错误 + +## [1.1.0] - [2018/09/20] + * 修复index的bug ,见 #11 + * 增加 `autoplayDisableOnInteraction` 选项, 如果设置为true,那么在用户滑动的时候停止自动播放,滑动之后重新自动播放 + + +## [1.0.7] - [2018/09/02] + * 在Swiper dispose的时候不调用Controller的dispose. + +## [1.0.6] - [2018/08/28] + * `TINDER` 和 `STACK` 这两种布局方式实现垂直滚动, #9 + +## [1.0.5] - [2018/08/09] + * viewportFraction<1.0增加fade参数 + +## [1.0.4] - [2018/07/18] + * 修复一些错别字,感谢[csharad](https://github.com/csharad) + +## [1.0.3] - [2018/07/18] + * 根据#5 ,用new来创建对象 + +## [1.0.2] - [2018/07/16] + * 修复 #4 + +## [1.0.1] - [2018/07/11] + * 支持布局方式(CUSTOM),你可以定制你自己的布局 + +## [1.0.0] - [2018/07/08] + * 增加布局方式( DEFAULT,STACK,TINDER ) + * 可以将分页指示器放在容器外面 + +## [0.0.9] - [2018/06/10] + * 增加CI + * 增加测试用例 + +## [0.0.8] - [2018/06/07] + * 新增中文文档 + * 更新依赖包:infinity_page_view 版本到 1.0.0 + +## [0.0.7] - [2018/05/24] + * The default color of pagination is ThemeData.scaffoldBackgroundColor and ThemeData.primaryColor + * The default color of control buttons is ThemeData.primaryColor and ThemeData.disabledColor + * The alignment of pagination is Alignment.bottomCenter by default when scrollDirection== Axis.horizontal, Alignment.centerRight by default when scrollDirection== Axis.vertical + * Change default value of `autoplay` to false + +## [0.0.6] - [2018/05/24] + * Fix index bug + +## [0.0.5] - [2018/05/24] + * Fix zero itemCount bug + +## [0.0.4] - [2018/05/20] + * Update README + +## [0.0.3] - [2018/05/20] + * Update README + * Support none loop mode + * Add more examples + +## [0.0.2] - [2018/05/20] + * Autoplay + * Plugins support + * Examples + +## [0.0.1] - [2018/05/20] + * Infinite loop + * Control buttons + * Pagination + * Custom control buttons + * Custom pagination + * Controler \ No newline at end of file diff --git a/package/flutter_swiper_view/CHANGELOG.md b/package/flutter_swiper_view/CHANGELOG.md new file mode 100644 index 00000000..77c5eb2f --- /dev/null +++ b/package/flutter_swiper_view/CHANGELOG.md @@ -0,0 +1,95 @@ +## [1.1.8] - [2022/05/27] + + * fix example error + * fix SwiperPagination.rect indicator does not display + * support null safety + * support Flutter 3 + +## [1.1.7] - [2022/05/27] + + * test publish to pub.dev + + +## [1.1.5] - [2019/03/10] + + * Fix findRenderObject is null + + +## [1.1.4] - [2018/10/18] + +## [1.1.2] - [2018/10/10] + * Fix #7 + * Support layout for pagination. + +## [1.1.1] - [2018/09/20] + * Fix `test` failure. + +## [1.1.0] - [2018/09/20] + * Fix index bug ,See #11 + * Enable `autoplayDisableOnInteraction` property, if set `true`,disable autoplay when user swipes. + +## [1.0.7] - [2018/09/02] + * Don't call SwiperController's dispose when `Swiper` dispose. + +## [1.0.6] - [2018/08/28] + * Implement vertical scroll direction for `TINDER` and `STACK` layout, see #9 + +## [1.0.5] - [2018/08/09] + * Add feature : support fade for `viewportFraction` + +## [1.0.4] - [2018/07/18] + * Fix some typo,thanks to [csharad](https://github.com/csharad) + +## [1.0.3] - [2018/07/18] + * Use new to create everything. See #5 + +## [1.0.2] - [2018/07/16] + * Fix #4 + +## [1.0.1] - [2018/07/11] + * Add layout (CUSTOM) so that you can create your own layout + +## [1.0.0] - [2018/07/08] + * Add layouts ( DEFAULT,STACK,TINDER ) + * Allow to put pagination outer of the swiper container. + +## [0.0.9] - [2018/06/10] + * Add ci + * Add testcase + +## [0.0.8] - [2018/06/07] + * And chinese document + * Update infinity_page_view version to 1.0.0 + +## [0.0.7] - [2018/05/24] + * The default color of pagination is ThemeData.scaffoldBackgroundColor and ThemeData.primaryColor + * The default color of control buttons is ThemeData.primaryColor and ThemeData.disabledColor + * The alignment of pagination is Alignment.bottomCenter by default when scrollDirection== Axis.horizontal, Alignment.centerRight by default when scrollDirection== Axis.vertical + * Change default value of `autoplay` to false + +## [0.0.6] - [2018/05/24] + * Fix index bug + +## [0.0.5] - [2018/05/24] + * Fix zero itemCount bug + +## [0.0.4] - [2018/05/20] + * Update README + +## [0.0.3] - [2018/05/20] + * Update README + * Support none loop mode + * Add more examples + +## [0.0.2] - [2018/05/20] + * Autoplay + * Plugins support + * Examples + +## [0.0.1] - [2018/05/20] + * Infinite loop + * Control buttons + * Pagination + * Custom control buttons + * Custom pagination + * Controler \ No newline at end of file diff --git a/package/flutter_swiper_view/LICENSE b/package/flutter_swiper_view/LICENSE new file mode 100644 index 00000000..ac160e8a --- /dev/null +++ b/package/flutter_swiper_view/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Xueliang Ren + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package/flutter_swiper_view/README-ZH.md b/package/flutter_swiper_view/README-ZH.md new file mode 100644 index 00000000..cb2ff3c0 --- /dev/null +++ b/package/flutter_swiper_view/README-ZH.md @@ -0,0 +1,409 @@ +![Logo](banner.jpg) + +

+ + Build Status + + + Coverage Status + + + PRs Welcome + + + pub package + +

+

+ + 英文说明 + +

+ + +# flutter_swiper_view + +flutter 最强大的 swiper, 多种布局方式,无限轮播,Android 和 iOS 双端适配. + + +# :sparkles::sparkles: New Features: 视差 + +我们在 Swiper 中也像 Android 一样支持了 `PageTransformer`, 只要给 Swiper 设置一下 `transformer` 属性就行, +这里返回一个被转换的组件给 Swiper. 目前仅仅支持 `DEFAULT` 布局. +感谢 @FlutterRocks ,棒棒哒 👏. + + + + +# :sparkles::sparkles: 新功能 + + +![](https://github.com/jzoom/images/raw/master/layout1.gif) + +![](https://github.com/jzoom/images/raw/master/layout2.gif) + +![](https://github.com/jzoom/images/raw/master/layout3.gif) + +[更多](#内建的布局) + + +# 截图 + +![Horizontal](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/1.gif) + +![Vertical](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/2.gif) + +![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/3.gif) + +![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/4.gif) + +![Phone](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/5.gif) + +![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) + +[更多](#代码) + +## 功能路线 + +1.x.x 功能实现: + +- [x] 无限循环轮播 +- [x] 控制按钮 +- [x] 分页指示器 +- [x] 非无限循环模式 +- [x] 单元测试 +- [x] 例子 +- [x] 滚动方向 +- [x] 可定制控制按钮 +- [x] 可定制分页 +- [x] 自动播放 +- [x] 控制器 +- [x] 外部分页指示器 +- [ ] 更多布局方式 + + +## 更新日志 + +>参考:[CHANGELOG.md](https://github.com/feicien/flutter_swiper_view/blob/master/CHANGELOG-ZH.md) + +## 目录 + +- [安装](#安装) +- [基本使用](#基本使用) +- [构建](#构建) + + [基本构造函数](#基本构造函数) + + [分页指示器](#分页指示器) + + [控制按钮](#控制按钮) + + [控制器](#控制器) + + [自动播放](#自动播放) ++ [内建的布局](#内建的布局) ++ [一些常用代码示例](#代码) + +### 安装 + +增加 + +```dart +dependencies: + flutter_swiper_view: ^1.1.8 +``` +到项目根目录下的 pubspec.yaml ,并且根目录运行命令行 + +```dart +flutter pub get +``` + + +### 基本使用 + +使用命令行创建一个新项目: + +```dart +flutter create myapp +``` + +编辑 lib/main.dart: + +```dart +import 'package:flutter/material.dart'; + +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Swiper( + itemBuilder: (context, index){ + return Image.network("https://via.placeholder.com/350x150",fit: BoxFit.fill,); + }, + itemCount: 3, + pagination: const SwiperPagination(), + control: const SwiperControl(), + ), + ); + } +} +``` + + + +### 构建 + + +#### 基本参数 + +| 参数 | 默认值 | 描述 | +| :-------------- |:-----------------:| :------------------------| +| scrollDirection | Axis.horizontal |滚动方向,设置为 Axis.vertical 如果需要垂直滚动 | +| loop | true |无限轮播模式开关 | +| index | 0 |初始的时候下标位置 | +| autoplay | false |自动播放开关. | +| onIndexChanged | void onIndexChanged(int index) | 当用户手动拖拽或者自动播放引起下标改变的时候调用 | +| onTap | void onTap(int index) | 当用户点击某个轮播的时候调用 | +| duration | 300.0 | 动画时间,单位是毫秒 | +| pagination | null | 设置 `SwiperPagination()` 展示默认分页指示器 +| control | null | 设置 `SwiperControl()` 展示默认分页按钮 + + +#### 分页指示器 + +分页指示器继承自 `SwiperPlugin`,`SwiperPlugin` 为 `Swiper` 提供额外的界面.设置为`SwiperPagination()` 展示默认分页. + + +| 参数 | 默认值 | 描述 | +| :------------ |:---------------:| :-----| +| alignment | Alignment.bottomCenter | 如果要将分页指示器放到其他位置,那么可以修改这个参数 | +| margin | const EdgeInsets.all(10.0) | 分页指示器与容器边框的距离 | +| builder | SwiperPagination.dots | 目前已经定义了三个默认的分页指示器样式: `SwiperPagination.dots` 、 `SwiperPagination.fraction`、 `SwiperPagination.rect`,都可以做进一步的自定义. | + +如果需要定制自己的分页指示器,那么可以这样写: + +```dart +Swiper( + ..., + pagination: SwiperCustomPagination( + builder: (context, config){ + return YourOwnPaginatipon(); + }, + ) +); +``` + + + +#### 控制按钮 + +控制按钮组件也是继承自 `SwiperPlugin`,设置 `SwiperControl()` 展示默认控制按钮. + + +| 参数 | 默认值 | 描述 | +| :------------ |:---------------:| :-----| +| iconPrevious | Icons.arrow_back_ios | 上一页的IconData | +| iconNext | Icons.arrow_forward_ios | 下一页的IconData | +| color | Theme.of(context).primaryColor | 控制按钮颜色 | +| size | 30.0 | 控制按钮的大小 | +| padding | const EdgeInsets.all(5.0) | 控制按钮与容器的距离 | + + +#### 控制器(SwiperController) + +`SwiperController` 用于控制 Swiper的`index`属性, 停止和开始自动播放. 通过 `SwiperController()` 创建一个 SwiperController 实例,并保存,以便将来能使用。 + + +| 方法 | 描述 | +| :------------ |:-----| +| void move(int index, {bool animation: true}) | 移动到指定下标,设置是否播放动画| +| void next({bool animation: true}) | 下一页 | +| void previous({bool animation: true}) | 上一页 | +| void startAutoplay() | 开始自动播放 | +| void stopAutoplay() | 停止自动播放 | + + + +#### 自动播放 + +| 参数 | 默认值 | 描述 | +| :------------ |:---------------:| :-----| +| autoplayDely | 3000 | 自动播放延迟毫秒数. | +| autoplayDisableOnInteraction | true | 当用户拖拽的时候,是否停止自动播放. | + + + +## 内建的布局 +![](https://github.com/jzoom/images/raw/master/layout1.gif) + +```dart +Swiper( + itemBuilder: (context, index) { + return Image.network( + "https://via.placeholder.com/288x188", + fit: BoxFit.fill, + ); + }, + itemCount: 10, + viewportFraction: 0.8, + scale: 0.9, +) + +``` + + + +![](https://github.com/jzoom/images/raw/master/layout2.gif) + +```dart +Swiper( + itemBuilder: (context, index) { + return Image.network( + "https://via.placeholder.com/288x188", + fit: BoxFit.fill, + ); + }, + itemCount: 10, + itemWidth: 300.0, + layout: SwiperLayout.STACK, +) +``` + +![](https://github.com/jzoom/images/raw/master/layout3.gif) + +```dart +Swiper( + itemBuilder: (context, index) { + return Image.network( + "https://via.placeholder.com/288x188", + fit: BoxFit.fill, + ); + }, + itemCount: 10, + itemWidth: 300.0, + itemHeight: 400.0, + layout: SwiperLayout.TINDER, +) +``` + + + +![](https://github.com/jzoom/images/raw/master/layout4.gif) + +构建你自己的动画十分简单: + +```dart +Swiper( + layout: SwiperLayout.CUSTOM, + customLayoutOption: CustomLayoutOption(startIndex: -1, stateCount: 3) + ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) + ..addTranslate([ + const Offset(-370.0, -40.0), + const Offset(0.0, 0.0), + const Offset(370.0, -40.0) + ]), + itemWidth: 300.0, + itemHeight: 200.0, + itemBuilder: (context, index) { + return Container( + color: Colors.grey, + child: Center( + child: Text("$index"), + ), + ); + }, + itemCount: 10, +), +``` + + +`CustomLayoutOption` 被设计用来描述布局和动画,很简单的可以指定每一个元素的状态. + +```dart +CustomLayoutOption( + startIndex: -1, /// 开始下标 + stateCount: 3 /// 下面的数组长度 + )..addRotate([ // 每个元素的角度 + -45.0/180, + 0.0, + 45.0/180 + ])..addTranslate([ /// 每个元素的偏移 + const Offset(-370.0, -40.0), + const Offset(0.0, 0.0), + const Offset(370.0, -40.0) + ]) +``` + +## 代码 + + +![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) + + +```dart +ConstrainedBox( + constraints: BoxConstraints.loose(Size(screenWidth, 170.0)) + child: Swiper( + outer: false, + itemBuilder: (c, i) { + return Wrap( + runSpacing: 6.0, + children: [0,1,2,3,4,5,6,7,8,9].map((i){ + return SizedBox( + width: MediaQuery.of(context).size.width/5, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: MediaQuery.of(context).size.width * 0.12, + width: MediaQuery.of(context).size.width * 0.12, + child: Image.network("https://fuss10.elemecdn.com/c/db/d20d49e5029281b9b73db1c5ec6f9jpeg.jpeg%3FimageMogr/format/webp/thumbnail/!90x90r/gravity/Center/crop/90x90"), + ), + Padding( + padding: const EdgeInsets.only(top: 6.0), + child: Text("$i"), + ) + ], + ), + ); + }).toList(), + ); + }, + pagination: const SwiperPagination(margin: EdgeInsets.all(5.0)), + itemCount: 10, + ), +), +``` + + + +这里可以找到所有的定制选项 + +>https://github.com/feicien/flutter_swiper_view/blob/master/example/lib/src/example_custom.dart \ No newline at end of file diff --git a/package/flutter_swiper_view/README.md b/package/flutter_swiper_view/README.md new file mode 100644 index 00000000..3365bd8c --- /dev/null +++ b/package/flutter_swiper_view/README.md @@ -0,0 +1,393 @@ +![Logo](banner.jpg) + +

+ + Build Status + + + Coverage Status + + + PRs Welcome + + + pub package + +

+

+ + 中文说明 + +

+ + + +# flutter_swiper_view + +The best swiper for flutter , with multiple layouts, infinite loop. Compatible with Android & iOS. + + + +# :sparkles::sparkles: New Features:PageTransformer + +Finally, we have `PageTransformer` like android, just set a `transformer` to `Swiper`, +it returns a widget that has been transformed. For now, only support for layout `DEFAULT`. +Thanks to @FlutterRocks ,you've done great job 👏. + + + +# :sparkles::sparkles: New Features:Layout + + +![](https://github.com/jzoom/images/raw/master/layout1.gif) + +![](https://github.com/jzoom/images/raw/master/layout2.gif) + +![](https://github.com/jzoom/images/raw/master/layout3.gif) + +[See More](#build-in-layouts) + + +# Showcases + +![Horizontal](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/1.gif) + +![Vertical](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/2.gif) + +![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/3.gif) + +![Custom Pagination](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/4.gif) + +![Phone](https://github.com/feicien/flutter_swiper_view/raw/master/example/res/5.gif) + +![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) + +[See More](#codes) + + +## Changelogs + +>see:[CHANGELOG.md](https://github.com/feicien/flutter_swiper_view/blob/master/CHANGELOG.md) + +## Getting Started + +- [Installation](#installation) +- [Basic Usage](#basic-usage) +- [Constructor](#constructor) + + [Basic](#basic) + + [Pagination](#pagination) + + [Control buttons](#control-buttons) + + [Controller](#controller) + + [Autoplay](#autoplay) +- [Build in layouts](#build-in-layouts) +- [Codes](#codes) + +### Installation + +Add + +```dart +dependencies: + flutter_swiper_view: ^1.1.8 +``` +to your pubspec.yaml ,and run + +```dart +flutter pub get +``` +in your project's root directory. + +### Basic Usage + +Create a new project with command + +```dart +flutter create myapp +``` + +Edit lib/main.dart like this: + +```dart +import 'package:flutter/material.dart'; + +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Swiper( + itemBuilder: (context, index){ + return Image.network("https://via.placeholder.com/350x150",fit: BoxFit.fill,); + }, + itemCount: 3, + pagination: const SwiperPagination(), + control: const SwiperControl(), + ), + ); + } +} +``` + + + +### Constructor + + +#### Basic + +| Parameter | Default | Description | +| :------------ |:---------------:| :-----| +| scrollDirection | Axis.horizontal | If `Axis.horizontal`, the scroll view's children are arranged horizontally in a row instead of vertically in a column. | +| loop | true |Set to `false` to disable continuous loop mode. | +| index | 0 | Index number of initial slide. | +| autoplay | false |Set to `true` enable auto play mode. | +| onIndexChanged | void onIndexChanged(int index) | Called with the new index when the user swiped or autoplay | +| onTap | void onTap(int index) | Called when user tap ui. | +| duration | 300.0 | The milliscends of every transaction animation costs | +| pagination | null | set `SwiperPagination()` to show default pagination +| control | null | set `SwiperControl()` to show default control buttons + + +#### Pagination + +The pagination extends from `SwiperPlugin`,the `SwiperPlugin` provides extra ui for `Swiper`.Set `SwiperPagination()` to show default pagination. + + +| Parameter | Default | Description | +| :------------ |:---------------:| :-----| +| alignment | Alignment.bottomCenter | Change this value if you what to put pagination in other place | +| margin | const EdgeInsets.all(10.0) | The distance between inner side of the parent container. | +| builder | SwiperPagination.dots | There are three default styles `SwiperPagination.dots` and `SwiperPagination.fraction`、 `SwiperPagination.rect`,both can be customized. | + +If you'd like to customize your own pagination, you can do like this: + +```dart +Swiper( + ..., + pagination: SwiperCustomPagination( + builder: (context, config){ + return YourOwnPaginatipon(); + }, + ) +); +``` + + + +#### Control buttons + +The control also extends from `SwiperPlugin`,set `SwiperControl()` to show default control buttons. + + +| Parameter | Default | Description | +| :------------ |:---------------:| :-----| +| iconPrevious | Icons.arrow_back_ios | The icon data to display `previous` control button | +| iconNext | Icons.arrow_forward_ios | The icon data to display `next`. | +| color | Theme.of(context).primaryColor | Control button color | +| size | 30.0 | Control button size | +| padding | const EdgeInsets.all(5.0) | Control button padding | + + +#### Controller + +The `Controller` is used to control the `index` of the Swiper, start or stop autoplay.You can create a controller by `SwiperController()` and save the instance by futher usage. + + +| Method | Description | +| :------------ |:-----| +| void move(int index, {bool animation: true}) | Move to the spicified `index`,with animation or not | +| void next({bool animation: true}) | Move to next | +| void previous({bool animation: true}) | Move to previous | +| void startAutoplay() | Start autoplay | +| void stopAutoplay() | Stop autoplay | + + + +#### Autoplay + +| Parameter | Default | Description | +| :------------ |:---------------:| :-----| +| autoplayDelay | 3000 | Autoplay delay milliseconds. | +| autoplayDisableOnInteraction | true | If set true, `autoplay` is disabled when use swipes. | + +## Build in layouts +![](https://github.com/jzoom/images/raw/master/layout1.gif) + +```dart +Swiper( + itemBuilder: (context, index) { + return Image.network( + "https://via.placeholder.com/288x188", + fit: BoxFit.fill, + ); + }, + itemCount: 10, + viewportFraction: 0.8, + scale: 0.9, +) +``` + + + +![](https://github.com/jzoom/images/raw/master/layout2.gif) + +```dart +Swiper( + itemBuilder: (context, index) { + return Image.network( + "https://via.placeholder.com/288x188", + fit: BoxFit.fill, + ); + }, + itemCount: 10, + itemWidth: 300.0, + layout: SwiperLayout.STACK, +) +``` + +![](https://github.com/jzoom/images/raw/master/layout3.gif) + +```dart +Swiper( + itemBuilder: (context, index) { + return Image.network( + "https://via.placeholder.com/288x188", + fit: BoxFit.fill, + ); + }, + itemCount: 10, + itemWidth: 300.0, + itemHeight: 400.0, + layout: SwiperLayout.TINDER, +) +``` + + +![](https://github.com/jzoom/images/raw/master/layout4.gif) + +Very easy to create you own custom animation: + +```dart +Swiper( + layout: SwiperLayout.CUSTOM, + customLayoutOption: CustomLayoutOption(startIndex: -1, stateCount: 3) + ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) + ..addTranslate([ + const Offset(-370.0, -40.0), + const Offset(0.0, 0.0), + const Offset(370.0, -40.0) + ]), + itemWidth: 300.0, + itemHeight: 200.0, + itemBuilder: (context, index) { + return Container( + color: Colors.grey, + child: Center( + child: Text("$index"), + ), + ); + }, + itemCount: 10, +), +``` + +The `CustomLayoutOption` is designed to describe animations. +It is very easy to specify every state of items in Swiper. + +```dart +CustomLayoutOption( + startIndex: -1, /// Which index is the first item of array below + stateCount: 3 /// array length + )..addRotate([ // rotation of every item + -45.0/180, + 0.0, + 45.0/180 + ])..addTranslate([ /// offset of every item + const Offset(-370.0, -40.0), + const Offset(0.0, 0.0), + const Offset(370.0, -40.0) + ]) +``` + +## Codes + +![Example](https://github.com/jzoom/images/raw/master/swiper-example.gif) + +```dart +ConstrainedBox( + constraints: BoxConstraints.loose(Size(screenWidth, 170.0)) + child: Swiper( + outer: false, + itemBuilder: (c, i) { + return Wrap( + runSpacing: 6.0, + children: [0,1,2,3,4,5,6,7,8,9].map((i){ + return SizedBox( + width: MediaQuery.of(context).size.width/5, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: MediaQuery.of(context).size.width * 0.12, + width: MediaQuery.of(context).size.width * 0.12, + child: Image.network("https://fuss10.elemecdn.com/c/db/d20d49e5029281b9b73db1c5ec6f9jpeg.jpeg%3FimageMogr/format/webp/thumbnail/!90x90r/gravity/Center/crop/90x90"), + ), + Padding( + padding: const EdgeInsets.only(top: 6.0), + child: Text("$i"), + ) + ], + ), + ); + }).toList(), + ); + }, + pagination: const SwiperPagination(margin: EdgeInsets.all(5.0)), + itemCount: 10, + ), +), +``` + + + + + +You can find all custom options here: + +>https://github.com/feicien/flutter_swiper_view/blob/master/example/lib/src/example_custom.dart + + + + + diff --git a/package/flutter_swiper_view/analysis_options.yaml b/package/flutter_swiper_view/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/package/flutter_swiper_view/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/package/flutter_swiper_view/banner.jpg b/package/flutter_swiper_view/banner.jpg new file mode 100644 index 00000000..73c4eb89 Binary files /dev/null and b/package/flutter_swiper_view/banner.jpg differ diff --git a/package/flutter_swiper_view/dev/bots/travis_install.sh b/package/flutter_swiper_view/dev/bots/travis_install.sh new file mode 100755 index 00000000..a834cb95 --- /dev/null +++ b/package/flutter_swiper_view/dev/bots/travis_install.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo "$PWD" +export ROOT="$PWD" + +mkdir ~/development + +cd ~/development +wget https://storage.googleapis.com/flutter_infra/releases/beta/linux/flutter_linux_v0.4.4-beta.tar.xz +tar xf ~/development/flutter_linux_v0.4.4-beta.tar.xz + +export PATH=~/development/flutter/bin:$PATH + + +cd $ROOT +flutter packages get + +gem install coveralls-lcov diff --git a/package/flutter_swiper_view/dev/bots/travis_script.sh b/package/flutter_swiper_view/dev/bots/travis_script.sh new file mode 100755 index 00000000..8eea42df --- /dev/null +++ b/package/flutter_swiper_view/dev/bots/travis_script.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -ex + +export PATH=~/development/flutter/bin:$PATH +export ROOT="$PWD" + +if [[ "$SHARD" == "dartfmt" ]]; then + echo 'Formating code' + cd $ROOT + flutter format . || exit $? +else + # tests shard + cd $ROOT + + flutter test --coverage test/* || exit $? + +fi diff --git a/package/flutter_swiper_view/example/README.md b/package/flutter_swiper_view/example/README.md new file mode 100644 index 00000000..64a12f6b --- /dev/null +++ b/package/flutter_swiper_view/example/README.md @@ -0,0 +1,8 @@ +# example + +A new Flutter project. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](https://flutter.io/). diff --git a/package/flutter_swiper_view/example/images/1.png b/package/flutter_swiper_view/example/images/1.png new file mode 100755 index 00000000..9b87ff2a Binary files /dev/null and b/package/flutter_swiper_view/example/images/1.png differ diff --git a/package/flutter_swiper_view/example/images/2.png b/package/flutter_swiper_view/example/images/2.png new file mode 100755 index 00000000..d08e334d Binary files /dev/null and b/package/flutter_swiper_view/example/images/2.png differ diff --git a/package/flutter_swiper_view/example/images/3.png b/package/flutter_swiper_view/example/images/3.png new file mode 100755 index 00000000..2ad5a781 Binary files /dev/null and b/package/flutter_swiper_view/example/images/3.png differ diff --git a/package/flutter_swiper_view/example/images/bg.jpeg b/package/flutter_swiper_view/example/images/bg.jpeg new file mode 100644 index 00000000..76ee2df0 Binary files /dev/null and b/package/flutter_swiper_view/example/images/bg.jpeg differ diff --git a/package/flutter_swiper_view/example/images/bg0.jpeg b/package/flutter_swiper_view/example/images/bg0.jpeg new file mode 100644 index 00000000..bf3b7436 Binary files /dev/null and b/package/flutter_swiper_view/example/images/bg0.jpeg differ diff --git a/package/flutter_swiper_view/example/images/bg1.jpeg b/package/flutter_swiper_view/example/images/bg1.jpeg new file mode 100644 index 00000000..2c4f2d64 Binary files /dev/null and b/package/flutter_swiper_view/example/images/bg1.jpeg differ diff --git a/package/flutter_swiper_view/example/images/bg2.jpeg b/package/flutter_swiper_view/example/images/bg2.jpeg new file mode 100644 index 00000000..b560cc57 Binary files /dev/null and b/package/flutter_swiper_view/example/images/bg2.jpeg differ diff --git a/package/flutter_swiper_view/example/lib/inner_swiper.dart b/package/flutter_swiper_view/example/lib/inner_swiper.dart new file mode 100644 index 00000000..c7087387 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/inner_swiper.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: InnerSwiper(), + ); + } +} + +class InnerSwiper extends StatefulWidget { + const InnerSwiper({Key? key}) : super(key: key); + + @override + State createState() { + return _InnerSwiperState(); + } +} + +class _InnerSwiperState extends State { + late SwiperController controller; + + late List autoPlayer; + + late List controllers; + + @override + void initState() { + controller = SwiperController(); + autoPlayer = List.generate(10, (index) => false); + controllers = List.generate(10, (index) => SwiperController()); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Swiper( + loop: false, + itemCount: 10, + controller: controller, + pagination: const SwiperPagination(), + itemBuilder: (context, index) { + return Column( + children: [ + SizedBox( + height: 300.0, + child: Swiper( + controller: controllers[index], + pagination: const SwiperPagination(), + itemCount: 4, + itemBuilder: (context, index) { + return Container( + color: Colors.greenAccent, + child: const Text("jkfjkldsfjd"), + ); + }, + autoplay: autoPlayer[index], + ), + ), + ElevatedButton( + onPressed: () { + setState(() { + autoPlayer[index] = true; + }); + }, + child: const Text("Start autoplay"), + ), + ElevatedButton( + onPressed: () { + setState(() { + autoPlayer[index] = false; + }); + }, + child: const Text("End autoplay"), + ), + Text("is autoplay: ${autoPlayer[index]}") + ], + ); + }, + ), + ); + } +} diff --git a/package/flutter_swiper_view/example/lib/listener_test.dart b/package/flutter_swiper_view/example/lib/listener_test.dart new file mode 100644 index 00000000..f2dc2136 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/listener_test.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MyHomePage(title: 'Flutter Swiper'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Swiper( + itemCount: 10, + itemBuilder: (c, i) { + return Text("$i"); + }, + plugins: const [], + ), + ); + } +} diff --git a/package/flutter_swiper_view/example/lib/main.dart b/package/flutter_swiper_view/example/lib/main.dart new file mode 100644 index 00000000..1b088745 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/main.dart @@ -0,0 +1,409 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; +import 'src/example_custom.dart'; +import 'src/config.dart'; +import 'src/example_swiper_in_scrollview.dart'; + +void main() => runApp(const MyApp()); + +class MyScrollBehavior extends MaterialScrollBehavior { + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }; +} + +class MyApp extends StatelessWidget { + const MyApp({Key? key}) : super(key: key); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + scrollBehavior: MyScrollBehavior(), + title: 'Flutter Demo', + theme: ThemeData.light(), + home: const MyHomePage(title: 'Flutter Swiper'), + //home: buildHome(), + routes: { + '/example01': (context) => const ExampleHorizontal(), + '/example02': (context) => const ExampleVertical(), + '/example03': (context) => const ExampleFraction(), + '/example04': (context) => const ExampleCustomPagination(), + '/example05': (context) => const ExamplePhone(), + '/example06': (context) => const ScaffoldWidget( + title: "ScrollView", child: ExampleSwiperInScrollView()), + '/example07': (context) => const ScaffoldWidget( + title: "Custom All", + child: ExampleCustom(), + ), + '/example08': (context) => const ExampleRect(), + }, + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + List render(BuildContext context, List> children) { + return ListTile.divideTiles( + context: context, + tiles: children.map((data) { + return buildListTile(context, data[0], data[1], data[2]); + }), + ).toList(); + } + + Widget buildListTile( + BuildContext context, String title, String subtitle, String url) { + return ListTile( + onTap: () { + Navigator.of(context).pushNamed(url); + }, + isThreeLine: true, + dense: false, + leading: null, + title: Text(title), + subtitle: Text(subtitle), + trailing: const Icon( + Icons.arrow_right, + color: Colors.blueAccent, + ), + ); + } + + @override + Widget build(BuildContext context) { + // DateTime moonLanding = DateTime.parse("1969-07-20"); + + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: ListView( + children: render(context, [ + ["Horizontal", "Scroll Horizontal", "/example01"], + ["Vertical", "Scroll Vertical", "/example02"], + ["Fraction", "Fraction style", "/example03"], + ["Rect", "Rect style", "/example08"], + ["Custom Pagination", "Custom Pagination", "/example04"], + ["Phone", "Phone view", "/example05"], + ["ScrollView ", "In a ScrollView", "/example06"], + ["Custom", "Custom all properties", "/example07"], + ]), + ), + ); + } +} + +const List titles = [ + "Flutter Swiper is awosome", + "Really nice", + "Yeap" +]; + +class ExampleHorizontal extends StatelessWidget { + const ExampleHorizontal({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("ExampleHorizontal"), + ), + body: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + indicatorLayout: PageIndicatorLayout.COLOR, + autoplay: true, + itemCount: images.length, + pagination: const SwiperPagination(), + control: const SwiperControl(), + )); + } +} + +class ExampleVertical extends StatelessWidget { + const ExampleVertical({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("ExampleVertical"), + ), + body: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + scrollDirection: Axis.vertical, + pagination: const SwiperPagination(alignment: Alignment.centerRight), + control: const SwiperControl(), + )); + } +} + +class ExampleFraction extends StatelessWidget { + const ExampleFraction({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("ExampleFraction"), + ), + body: Column( + children: [ + Expanded( + child: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + pagination: + const SwiperPagination(builder: SwiperPagination.fraction), + control: const SwiperControl(), + )), + Expanded( + child: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + scrollDirection: Axis.vertical, + pagination: const SwiperPagination( + alignment: Alignment.centerRight, + builder: SwiperPagination.fraction), + )) + ], + )); + } +} + +class ExampleRect extends StatelessWidget { + const ExampleRect({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("ExampleFraction"), + ), + body: Column( + children: [ + Expanded( + child: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + pagination: + const SwiperPagination(builder: SwiperPagination.rect), + control: const SwiperControl(), + ), + ), + Expanded( + child: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + scrollDirection: Axis.vertical, + pagination: const SwiperPagination( + alignment: Alignment.centerRight, + builder: SwiperPagination.rect, + ), + ), + ) + ], + )); + } +} + +class ExampleCustomPagination extends StatelessWidget { + const ExampleCustomPagination({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Custom Pagination"), + ), + body: Column( + children: [ + Expanded( + child: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + pagination: SwiperPagination( + margin: const EdgeInsets.all(0.0), + builder: SwiperCustomPagination(builder: + (BuildContext context, SwiperPluginConfig config) { + return ConstrainedBox( + constraints: const BoxConstraints.expand(height: 50.0), + child: Container( + color: Colors.white, + child: Text( + "${titles[config.activeIndex]} ${config.activeIndex + 1}/${config.itemCount}", + style: const TextStyle(fontSize: 20.0), + )), + ); + })), + control: const SwiperControl(), + ), + ), + Expanded( + child: Swiper( + itemBuilder: (context, index) { + return Image.asset( + images[index], + fit: BoxFit.fill, + ); + }, + autoplay: true, + itemCount: images.length, + pagination: SwiperPagination( + margin: const EdgeInsets.all(0.0), + builder: SwiperCustomPagination(builder: + (BuildContext context, SwiperPluginConfig config) { + return ConstrainedBox( + constraints: const BoxConstraints.expand(height: 50.0), + child: Row( + children: [ + Text( + "${titles[config.activeIndex]} ${config.activeIndex + 1}/${config.itemCount}", + style: const TextStyle(fontSize: 20.0), + ), + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: const DotSwiperPaginationBuilder( + color: Colors.black12, + activeColor: Colors.black, + size: 10.0, + activeSize: 20.0) + .build(context, config), + ), + ) + ], + ), + ); + })), + control: const SwiperControl(color: Colors.redAccent), + ), + ) + ], + )); + } +} + +class ExamplePhone extends StatelessWidget { + const ExamplePhone({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("Phone"), + ), + body: Stack( + children: [ + ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: Image.asset( + "images/bg.jpeg", + fit: BoxFit.fill, + ), + ), + Swiper.children( + autoplay: false, + pagination: const SwiperPagination( + margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 30.0), + builder: DotSwiperPaginationBuilder( + color: Colors.white30, + activeColor: Colors.white, + size: 20.0, + activeSize: 20.0)), + children: [ + Image.asset( + "images/1.png", + fit: BoxFit.contain, + ), + Image.asset( + "images/2.png", + fit: BoxFit.contain, + ), + Image.asset("images/3.png", fit: BoxFit.contain) + ], + ) + ], + ), + ); + } +} + +class ScaffoldWidget extends StatelessWidget { + final Widget child; + final String title; + final List? actions; + + const ScaffoldWidget({ + Key? key, + required this.title, + required this.child, + this.actions, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(title), + actions: actions, + ), + body: child, + ); + } +} diff --git a/package/flutter_swiper_view/example/lib/src/config.dart b/package/flutter_swiper_view/example/lib/src/config.dart new file mode 100644 index 00000000..fcea1725 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/src/config.dart @@ -0,0 +1,5 @@ +const List images = [ + "images/bg0.jpeg", + "images/bg1.jpeg", + "images/bg2.jpeg", +]; diff --git a/package/flutter_swiper_view/example/lib/src/example_custom.dart b/package/flutter_swiper_view/example/lib/src/example_custom.dart new file mode 100644 index 00000000..5c32d2e4 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/src/example_custom.dart @@ -0,0 +1,365 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +import 'config.dart'; +import 'forms/form_widget.dart'; + +class ExampleCustom extends StatefulWidget { + const ExampleCustom({Key? key}) : super(key: key); + + @override + State createState() { + return _ExampleCustomState(); + } +} + +class _ExampleCustomState extends State { + //properties want to custom + late int _itemCount; + + late bool _loop; + + late bool _autoplay; + + late int _autoplayDelay; + + late double _padding; + + late bool _outer; + + late double _radius; + + late double _viewportFraction; + + late SwiperLayout _layout; + + late int _currentIndex; + + late double _scale; + + late Axis _scrollDirection; + + late AxisDirection _axisDirection; + + late Curve _curve; + + late double _fade; + + late bool _autoplayDisableOnInteraction; + + late CustomLayoutOption customLayoutOption; + + late SwiperController _controller; + + TextEditingController numberController = TextEditingController(); + + Widget _buildItem(BuildContext context, int index) { + return ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(_radius)), + child: Image.asset( + images[index % images.length], + fit: BoxFit.fill, + ), + ); + } + + @override + void didUpdateWidget(ExampleCustom oldWidget) { + customLayoutOption = CustomLayoutOption(startIndex: -1, stateCount: 3) + ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) + ..addTranslate([ + const Offset(-370.0, -40.0), + const Offset(0.0, 0.0), + const Offset(370.0, -40.0) + ]); + super.didUpdateWidget(oldWidget); + } + + @override + void initState() { + customLayoutOption = CustomLayoutOption(startIndex: -1, stateCount: 3) + ..addRotate([-25.0 / 180, 0.0, 25.0 / 180]) + ..addTranslate([ + const Offset(-350.0, 0.0), + const Offset(0.0, 0.0), + const Offset(350.0, 0.0) + ]); + _fade = 1.0; + _currentIndex = 0; + _curve = Curves.ease; + _scale = 0.8; + _autoplay = false; + _controller = SwiperController(); + _layout = SwiperLayout.TINDER; + _radius = 10.0; + _padding = 0.0; + _loop = true; + _itemCount = 3; + _autoplayDelay = 3000; + _viewportFraction = 0.8; + _outer = false; + _scrollDirection = Axis.horizontal; + _axisDirection = AxisDirection.left; + _autoplayDisableOnInteraction = false; + super.initState(); + } + +// maintain the index + + Widget buildSwiper() { + return Swiper( + onTap: (index) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) { + return Scaffold( + appBar: AppBar( + title: const Text('New page'), + ), + body: Container(), + ); + }, + )); + }, + customLayoutOption: customLayoutOption, + fade: _fade, + index: _currentIndex, + onIndexChanged: (index) { + setState(() { + _currentIndex = index; + }); + }, + curve: _curve, + scale: _scale, + itemWidth: 300.0, + controller: _controller, + layout: _layout, + outer: _outer, + itemHeight: 200.0, + viewportFraction: _viewportFraction, + autoplayDelay: _autoplayDelay, + loop: _loop, + autoplay: _autoplay, + itemBuilder: _buildItem, + itemCount: _itemCount, + scrollDirection: _scrollDirection, + axisDirection: _axisDirection, + indicatorLayout: PageIndicatorLayout.COLOR, + autoplayDisableOnInteraction: _autoplayDisableOnInteraction, + pagination: const SwiperPagination( + builder: DotSwiperPaginationBuilder( + size: 20.0, activeSize: 20.0, space: 10.0)), + ); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + color: Colors.black87, + child: SizedBox( + height: 300.0, + width: double.infinity, + child: buildSwiper(), + ), + ), + Expanded( + child: ListView( + children: [ + Text('Index:$_currentIndex'), + Row( + children: [ + ElevatedButton( + onPressed: () { + _controller.previous(animation: true); + }, + child: const Text('Prev'), + ), + ElevatedButton( + onPressed: () { + _controller.next(animation: true); + }, + child: const Text('Next'), + ), + Expanded( + child: TextField( + controller: numberController, + )), + ElevatedButton( + onPressed: () { + final text = numberController.text; + setState(() { + _currentIndex = int.tryParse(text) ?? 0; + }); + }, + child: const Text('Update'), + ), + ], + ), + FormWidget( + label: 'layout', + child: FormSelect( + placeholder: 'Select layout', + value: _layout, + values: const [ + SwiperLayout.DEFAULT, + SwiperLayout.STACK, + SwiperLayout.TINDER, + SwiperLayout.CUSTOM + ], + valueChanged: (value) { + _layout = value; + setState(() {}); + }, + ), + ), + FormWidget( + label: 'scrollDirection', + child: Switch( + value: _scrollDirection == Axis.horizontal, + onChanged: (value) => setState(() => _scrollDirection = + value ? Axis.horizontal : Axis.vertical)), + ), + if (_layout == SwiperLayout.STACK) + FormWidget( + label: 'axisDirection (left <-> right)', + child: Switch( + value: _axisDirection == AxisDirection.right, + onChanged: (value) => setState(() => _axisDirection = + value ? AxisDirection.right : AxisDirection.left)), + ), + FormWidget( + label: 'autoplayDisableOnInteraction', + child: Switch( + value: _autoplayDisableOnInteraction, + onChanged: (value) => + setState(() => _autoplayDisableOnInteraction = value)), + ), + //Pannel Begin + FormWidget( + label: 'loop', + child: Switch( + value: _loop, + onChanged: (value) => setState(() => _loop = value)), + ), + FormWidget( + label: 'outer', + child: Switch( + value: _outer, + onChanged: (value) => setState(() => _outer = value)), + ), + //Pannel Begin + FormWidget( + label: 'autoplay', + child: Switch( + value: _autoplay, + onChanged: (value) => setState(() => _autoplay = value)), + ), + + FormWidget( + label: 'padding', + child: NumberPad( + number: _padding, + step: 5.0, + min: 0.0, + max: 30.0, + onChangeValue: (value) { + _padding = value.toDouble(); + setState(() {}); + }, + ), + ), + FormWidget( + label: 'scale', + child: NumberPad( + number: _scale, + step: 0.1, + min: 0.0, + max: 1.0, + onChangeValue: (value) { + _scale = value.toDouble(); + setState(() {}); + }, + ), + ), + FormWidget( + label: 'fade', + child: NumberPad( + number: _fade, + step: 0.1, + min: 0.0, + max: 1.0, + onChangeValue: (value) { + _fade = value.toDouble(); + setState(() {}); + }, + ), + ), + FormWidget( + label: 'itemCount', + child: NumberPad( + number: _itemCount, + step: 1, + min: 0, + max: 100, + onChangeValue: (value) { + _itemCount = value.toInt(); + setState(() {}); + }, + ), + ), + + FormWidget( + label: 'radius', + child: NumberPad( + number: _radius, + step: 1.0, + min: 0.0, + max: 30.0, + onChangeValue: (value) { + _radius = value.toDouble(); + setState(() {}); + }, + ), + ), + + FormWidget( + label: 'viewportFraction', + child: NumberPad( + number: _viewportFraction, + step: 0.1, + max: 1.0, + min: 0.5, + onChangeValue: (value) { + _viewportFraction = value.toDouble(); + setState(() {}); + }, + ), + ), + + FormWidget( + label: 'curve', + child: FormSelect( + placeholder: 'Select curve', + value: _curve, + values: const [ + Curves.easeInOut, + Curves.ease, + Curves.bounceInOut, + Curves.bounceOut, + Curves.bounceIn, + Curves.fastOutSlowIn + ], + valueChanged: (value) { + _curve = value; + setState(() {}); + }, + ), + ), + ], + ), + ) + ], + ); + } +} diff --git a/package/flutter_swiper_view/example/lib/src/example_swiper_in_scrollview.dart b/package/flutter_swiper_view/example/lib/src/example_swiper_in_scrollview.dart new file mode 100644 index 00000000..fbf531b3 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/src/example_swiper_in_scrollview.dart @@ -0,0 +1,276 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +class ExampleSwiperInScrollView extends StatefulWidget { + const ExampleSwiperInScrollView({Key? key}) : super(key: key); + + @override + State createState() { + return _ExampleState(); + } +} + +class _ExampleState extends State + with TickerProviderStateMixin { + late AnimationController controller; + late Animation _animation10; + late Animation _animation11; + late Animation _animation12; + late Animation _animation13; + + _ExampleState(); + + @override + void dispose() { + controller.dispose(); + + super.dispose(); + } + + @override + void initState() { + controller = AnimationController(vsync: this); + _animation10 = Tween(begin: 0.0, end: 1.0).animate(controller); + _animation11 = Tween(begin: 0.0, end: 1.0).animate(controller); + _animation12 = Tween(begin: 0.0, end: 1.0).animate(controller); + _animation13 = Tween(begin: 0.0, end: 1.0).animate(controller); + controller.animateTo(1.0, duration: const Duration(seconds: 3)); + super.initState(); + } + + Widget _buildDynamicCard() { + return Card( + elevation: 2.0, + color: Colors.white, + child: Stack( + children: [ + Column( + children: [ + Container( + padding: const EdgeInsets.all(10.0), + ), + ], + ), + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 40.0), + ), + ScaleTransition( + scale: _animation10, + alignment: FractionalOffset.center, + ), + ], + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 160.0), + ), + ScaleTransition( + scale: _animation11, + alignment: FractionalOffset.center, + ), + ], + ), + Container( + padding: const EdgeInsets.all(1.0), + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 160.0), + ), + ScaleTransition( + scale: _animation12, + alignment: FractionalOffset.center, + ), + ], + ), + Column( + children: [ + Container( + padding: const EdgeInsets.only(top: 40.0), + ), + ScaleTransition( + scale: _animation13, + alignment: FractionalOffset.center, + ), + ], + ), + ]), + Container( + padding: const EdgeInsets.all(10.0), + ), + ], + ) + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + color: Theme.of(context).primaryColorLight, + child: CustomScrollView( + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate((c, i) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 100.0, + child: Swiper( + scale: 0.8, + fade: 0.8, + itemBuilder: (c, i) { + return Container( + color: Colors.grey, + child: Text("$i"), + ); + }, + itemCount: 10, + pagination: const SwiperPagination(), + ), + ), + SizedBox( + height: 100.0, + child: Swiper( + scale: 0.8, + fade: 0.8, + itemBuilder: (c, i) { + return Container( + color: Colors.grey, + child: Text("$i"), + ); + }, + pagination: const SwiperPagination( + builder: SwiperPagination.fraction), + itemCount: 0), + ), + SizedBox( + height: 100.0, + child: Swiper( + scale: 0.8, + fade: 0.8, + itemBuilder: (c, i) { + return Container( + color: Colors.grey, + child: Text("$i"), + ); + }, + pagination: const SwiperPagination( + builder: SwiperPagination.fraction), + itemCount: 10000), + ), + SizedBox( + height: 100.0, + child: Swiper( + outer: true, + scale: 0.8, + fade: 0.8, + itemBuilder: (c, i) { + return Container( + color: Colors.grey, + child: Text("$i"), + ); + }, + pagination: const SwiperPagination(), + itemCount: 10), + ), + const Text("Image from network"), + SizedBox( + height: 300.0, + child: Swiper( + itemCount: 10, + itemBuilder: (BuildContext context, int index) { + return Image.network( + "https://ss3.baidu.com/9fo3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=87d6daed02f41bd5c553eef461d881a0/f9198618367adab4b025268587d4b31c8601e47b.jpg"); + }, + ), + ), + SizedBox( + height: 100.0, + child: Swiper( + outer: true, + scale: 0.8, + fade: 0.8, + itemBuilder: (c, i) { + return Card( + elevation: 2.0, + child: Stack( + alignment: AlignmentDirectional.center, + children: [ + Image.network( + "https://ss3.baidu.com/9fo3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=87d6daed02f41bd5c553eef461d881a0/f9198618367adab4b025268587d4b31c8601e47b.jpg"), + FractionalTranslation( + translation: const Offset(0.0, 0.0), + child: Container( + alignment: const FractionalOffset(0.0, 0.0), + decoration: BoxDecoration( + border: Border.all( + color: Colors.lightBlue.withOpacity(0.5), + width: 100.0, + ), + shape: BoxShape.circle, + ), + ), + ), + Container( + //padding: const EdgeInsets.only(bottom:10.0), + margin: const EdgeInsets.all(140.0), + + child: const Icon(Icons.location_on, + color: Colors.white, size: 25.0), + ), + ], + ), + ); + }, + pagination: const SwiperPagination( + alignment: Alignment.topCenter), + itemCount: 10), + ), + SizedBox( + height: 400.0, + child: Swiper( + outer: true, + itemBuilder: (c, i) { + return _buildDynamicCard(); + }, + pagination: const SwiperPagination( + alignment: Alignment.topCenter), + itemCount: 10), + ), + SizedBox( + height: 100.0, + child: Swiper( + outer: true, + fade: 0.8, + viewportFraction: 0.8, + scale: 0.8, + itemBuilder: (c, i) { + return Container( + color: Colors.grey, + child: Text("$i"), + ); + }, + pagination: const SwiperPagination( + alignment: Alignment.topCenter), + itemCount: 10), + ), + ], + ); + }, childCount: 1)) + ], + ), + ); + } +} diff --git a/package/flutter_swiper_view/example/lib/src/forms/form_widget.dart b/package/flutter_swiper_view/example/lib/src/forms/form_widget.dart new file mode 100644 index 00000000..d5e91eb8 --- /dev/null +++ b/package/flutter_swiper_view/example/lib/src/forms/form_widget.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; + +class FormWidget extends StatelessWidget { + final String label; + + final Widget child; + + const FormWidget({ + Key? key, + required this.label, + required this.child, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: Row( + children: [ + Text(label, style: const TextStyle(fontSize: 14.0)), + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: child, + ), + ) + ], + ), + ); + } +} + +class FormSelect extends StatefulWidget { + final String placeholder; + final ValueChanged valueChanged; + final List values; + final T value; + + const FormSelect({ + Key? key, + required this.placeholder, + required this.valueChanged, + required this.value, + required this.values, + }) : super(key: key); + + @override + State createState() { + return _FormSelectState(); + } +} + +class _FormSelectState extends State> { + int _selectedIndex = 0; + + @override + void initState() { + for (int i = 0; i < widget.values.length; ++i) { + if (widget.values[i] == widget.value) { + _selectedIndex = i; + break; + } + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + final placeholder = widget.placeholder; + final values = widget.values; + + return InkWell( + child: Text( + _selectedIndex < 0 ? placeholder : values[_selectedIndex].toString()), + onTap: () { + _selectedIndex = 0; + showBottomSheet( + context: context, + builder: (context) { + return SizedBox( + height: values.length * 30.0 + 200.0, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: values.length * 30.0 + 70.0, + child: CupertinoPicker( + itemExtent: 30.0, + children: values.map((value) { + return Text(value.toString()); + }).toList(), + onSelectedItemChanged: (index) { + _selectedIndex = index; + }, + ), + ), + Center( + child: ElevatedButton( + onPressed: () { + if (_selectedIndex >= 0) { + widget.valueChanged( + widget.values[_selectedIndex], + ); + } + + setState(() {}); + + Navigator.of(context).pop(); + }, + child: const Text('ok'), + ), + ) + ], + ), + ); + }); + }, + ); + } +} + +class NumberPad extends StatelessWidget { + final num number; + final num step; + final num max; + final num min; + final ValueChanged onChangeValue; + + const NumberPad({ + Key? key, + required this.number, + required this.step, + required this.onChangeValue, + required this.max, + required this.min, + }) : super(key: key); + + void onAdd() { + onChangeValue(number + step > max ? max : number + step); + } + + void onSub() { + onChangeValue(number - step < min ? min : number - step); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton(icon: const Icon(Icons.exposure_neg_1), onPressed: onSub), + Text( + number is int ? number.toString() : number.toStringAsFixed(1), + style: const TextStyle(fontSize: 14.0), + ), + IconButton(icon: const Icon(Icons.exposure_plus_1), onPressed: onAdd) + ], + ); + } +} diff --git a/package/flutter_swiper_view/example/pubspec.yaml b/package/flutter_swiper_view/example/pubspec.yaml new file mode 100644 index 00000000..3504f4bd --- /dev/null +++ b/package/flutter_swiper_view/example/pubspec.yaml @@ -0,0 +1,35 @@ +name: example +description: A new Flutter project. + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + + flutter_swiper_view: + path: ../ + + +dev_dependencies: + flutter_lints: ^2.0.1 + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true + + + assets: + - images/bg0.jpeg + - images/bg1.jpeg + - images/bg2.jpeg + - images/bg.jpeg + - images/1.png + - images/2.png + - images/3.png diff --git a/package/flutter_swiper_view/example/res/1.gif b/package/flutter_swiper_view/example/res/1.gif new file mode 100644 index 00000000..51de191b Binary files /dev/null and b/package/flutter_swiper_view/example/res/1.gif differ diff --git a/package/flutter_swiper_view/example/res/2.gif b/package/flutter_swiper_view/example/res/2.gif new file mode 100644 index 00000000..821df059 Binary files /dev/null and b/package/flutter_swiper_view/example/res/2.gif differ diff --git a/package/flutter_swiper_view/example/res/3.gif b/package/flutter_swiper_view/example/res/3.gif new file mode 100644 index 00000000..a8f50a61 Binary files /dev/null and b/package/flutter_swiper_view/example/res/3.gif differ diff --git a/package/flutter_swiper_view/example/res/4.gif b/package/flutter_swiper_view/example/res/4.gif new file mode 100644 index 00000000..43bde554 Binary files /dev/null and b/package/flutter_swiper_view/example/res/4.gif differ diff --git a/package/flutter_swiper_view/example/res/5.gif b/package/flutter_swiper_view/example/res/5.gif new file mode 100644 index 00000000..a9b87b28 Binary files /dev/null and b/package/flutter_swiper_view/example/res/5.gif differ diff --git a/package/flutter_swiper_view/example/test/widget_test.dart b/package/flutter_swiper_view/example/test/widget_test.dart new file mode 100644 index 00000000..43218bbf --- /dev/null +++ b/package/flutter_swiper_view/example/test/widget_test.dart @@ -0,0 +1,29 @@ +// This is a basic Flutter widget test. +// To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter +// provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to +// find child widgets in the widget tree, read text, and verify that the values of widget properties +// are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:example/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/package/flutter_swiper_view/lib/flutter_swiper_view.dart b/package/flutter_swiper_view/lib/flutter_swiper_view.dart new file mode 100644 index 00000000..8dc90daa --- /dev/null +++ b/package/flutter_swiper_view/lib/flutter_swiper_view.dart @@ -0,0 +1,9 @@ +library flutter_swiper_view; + +export 'src/flutter_page_indicator.dart'; +export 'src/index_controller.dart'; +export 'src/swiper.dart'; +export 'src/swiper_pagination.dart'; +export 'src/swiper_control.dart'; +export 'src/swiper_controller.dart'; +export 'src/swiper_plugin.dart'; diff --git a/package/flutter_swiper_view/lib/src/custom_layout.dart b/package/flutter_swiper_view/lib/src/custom_layout.dart new file mode 100644 index 00000000..68eab940 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/custom_layout.dart @@ -0,0 +1,485 @@ +part of 'swiper.dart'; + +abstract class _CustomLayoutStateBase extends State + with SingleTickerProviderStateMixin { + late double _swiperWidth; + late double _swiperHeight; + late Animation _animation; + late AnimationController _animationController; + SwiperController get _controller => widget.controller; + late int _startIndex; + int? _animationCount; + int _currentIndex = 0; + + @override + void initState() { + _currentIndex = widget.index ?? 0; + if (widget.itemWidth == null) { + throw Exception( + '==============\n\nwidget.itemWidth must not be null when use stack layout.\n========\n', + ); + } + + _createAnimationController(); + _controller.addListener(_onController); + super.initState(); + } + + void _createAnimationController() { + _animationController = AnimationController(vsync: this, value: 0.5); + Tween tween = Tween(begin: 0.0, end: 1.0); + _animation = tween.animate(_animationController); + } + + @override + void didChangeDependencies() { + WidgetsBinding.instance.addPostFrameCallback(_getSize); + super.didChangeDependencies(); + } + + void _getSize(Duration _) { + if (!mounted) return; + afterRender(); + } + + @mustCallSuper + void afterRender() { + RenderObject renderObject = context.findRenderObject()!; + Size size = renderObject.paintBounds.size; + _swiperWidth = size.width; + _swiperHeight = size.height; + setState(() {}); + } + + @override + void didUpdateWidget(T oldWidget) { + if (widget.controller != oldWidget.controller) { + oldWidget.controller.removeListener(_onController); + widget.controller.addListener(_onController); + } + + if (widget.loop != oldWidget.loop) { + if (!widget.loop) { + _currentIndex = _ensureIndex(_currentIndex); + } + } + + if (widget.axisDirection != oldWidget.axisDirection) { + afterRender(); + } + + super.didUpdateWidget(oldWidget); + } + + int _ensureIndex(int index) { + var res = index; + res = index % widget.itemCount; + if (res < 0) { + res += widget.itemCount; + } + return res; + } + + @override + void dispose() { + widget.controller.removeListener(_onController); + _animationController.dispose(); + super.dispose(); + } + + Widget _buildItem(int i, int realIndex, double animationValue); + + Widget _buildContainer(List list) { + return Stack( + children: list, + ); + } + + Widget _buildAnimation(BuildContext context, Widget? w) { + List list = []; + + double animationValue = _animation.value; + + for (int i = 0; i < _animationCount! && widget.itemCount > 0; ++i) { + int realIndex = _currentIndex + i + _startIndex; + realIndex = realIndex % widget.itemCount; + if (realIndex < 0) { + realIndex += widget.itemCount; + } + + if (widget.axisDirection == AxisDirection.right) { + list.insert(0, _buildItem(i, realIndex, animationValue)); + } else { + list.add(_buildItem(i, realIndex, animationValue)); + } + } + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onPanStart: _onPanStart, + onPanEnd: _onPanEnd, + onPanUpdate: _onPanUpdate, + child: ClipRect( + child: Center( + child: _buildContainer(list), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + if (_animationCount == null) { + return Container(); + } + return AnimatedBuilder( + animation: _animationController, + builder: _buildAnimation, + ); + } + + late double _currentValue; + late double _currentPos; + + bool _lockScroll = false; + + Future _move(double position, {int? nextIndex}) async { + if (_lockScroll) return; + try { + _lockScroll = true; + await _animationController.animateTo( + position, + duration: Duration(milliseconds: widget.duration!), + curve: widget.curve, + ); + if (nextIndex != null) { + widget.onIndexChanged!(widget.getCorrectIndex(nextIndex)); + } + } catch (e) { + debugPrint('error animating _animationController ${e.toString()}'); + } finally { + if (nextIndex != null) { + try { + _animationController.value = 0.5; + } catch (e) { + debugPrint( + 'error setting _animationController.value ${e.toString()}'); + } + + _currentIndex = nextIndex; + } + _lockScroll = false; + } + } + + int _getProperNewIndex(int newIndex) { + var res = newIndex; + if (!widget.loop && newIndex >= widget.itemCount - 1) { + res = widget.itemCount - 1; + } else if (!widget.loop && newIndex < 0) { + res = 0; + } + return res; + } + + void _onController() { + SwiperController controller = widget.controller; + final event = controller.event; + if (event is StepBasedIndexControllerEvent) { + final newIndex = event.calcNextIndex( + currentIndex: _currentIndex, + itemCount: widget.itemCount, + loop: widget.loop, + reverse: false, + ); + _move(event.targetPosition, nextIndex: newIndex); + } else if (event is MoveIndexControllerEvent) { + _move( + event.targetPosition, + nextIndex: _getProperNewIndex(event.newIndex), + ); + } + } + + void _onPanEnd(DragEndDetails details) { + if (_lockScroll) return; + + final bool _isReversedGesture = widget.axisDirection == AxisDirection.right; + + double velocity = widget.scrollDirection == Axis.horizontal + ? details.velocity.pixelsPerSecond.dx + : details.velocity.pixelsPerSecond.dy; + + if (_isReversedGesture) { + // Reversed: swipe right (value >= 0.75) = next, swipe left (value < 0.25) = previous + if (_animationController.value >= 0.75 || velocity > 500.0) { + if (_currentIndex >= widget.itemCount - 1 && !widget.loop) { + _move(0.5); + return; + } + _move(1.0, nextIndex: _currentIndex + 1); + } else if (_animationController.value < 0.25 || velocity < -500.0) { + if (_currentIndex <= 0 && !widget.loop) { + _move(0.5); + return; + } + _move(0.0, nextIndex: _currentIndex - 1); + } else { + _move(0.5); + } + } else { + // Default: swipe right (value >= 0.75) = previous, swipe left (value < 0.25) = next + if (_animationController.value >= 0.75 || velocity > 500.0) { + if (_currentIndex <= 0 && !widget.loop) { + return; + } + _move(1.0, nextIndex: _currentIndex - 1); + } else if (_animationController.value < 0.25 || velocity < -500.0) { + if (_currentIndex >= widget.itemCount - 1 && !widget.loop) { + return; + } + _move(0.0, nextIndex: _currentIndex + 1); + } else { + _move(0.5); + } + } + } + + void _onPanStart(DragStartDetails details) { + if (_lockScroll) return; + _currentValue = _animationController.value; + _currentPos = widget.scrollDirection == Axis.horizontal + ? details.globalPosition.dx + : details.globalPosition.dy; + } + + void _onPanUpdate(DragUpdateDetails details) { + if (_lockScroll) return; + + double value = _currentValue + + ((widget.scrollDirection == Axis.horizontal + ? details.globalPosition.dx + : details.globalPosition.dy) - + _currentPos) / + _swiperWidth / + 2; + // no loop ? + if (!widget.loop) { + if (widget.axisDirection == AxisDirection.right) { + // Reversed bounds: block dragging left past first, right past last + if (_currentIndex <= 0) { + if (value < 0.5) { + value = 0.5; + } + } else if (_currentIndex >= widget.itemCount - 1) { + if (value > 0.5) { + value = 0.5; + } + } + } else { + if (_currentIndex >= widget.itemCount - 1) { + if (value < 0.5) { + value = 0.5; + } + } else if (_currentIndex <= 0) { + if (value > 0.5) { + value = 0.5; + } + } + } + } + + _animationController.value = value; + } +} + +double _getValue(List values, double animationValue, int index) { + double s = values[index]; + if (animationValue >= 0.5) { + if (index < values.length - 1) { + s = s + (values[index + 1] - s) * (animationValue - 0.5) * 2.0; + } + } else { + if (index != 0) { + s = s - (s - values[index - 1]) * (0.5 - animationValue) * 2.0; + } + } + return s; +} + +Offset _getOffsetValue(List values, double animationValue, int index) { + Offset s = values[index]; + double dx = s.dx; + double dy = s.dy; + if (animationValue >= 0.5) { + if (index < values.length - 1) { + dx = dx + (values[index + 1].dx - dx) * (animationValue - 0.5) * 2.0; + dy = dy + (values[index + 1].dy - dy) * (animationValue - 0.5) * 2.0; + } + } else { + if (index != 0) { + dx = dx - (dx - values[index - 1].dx) * (0.5 - animationValue) * 2.0; + dy = dy - (dy - values[index - 1].dy) * (0.5 - animationValue) * 2.0; + } + } + return Offset(dx, dy); +} + +abstract class TransformBuilder { + final List values; + + TransformBuilder({required this.values}); + + Widget build(int i, double animationValue, Widget widget); +} + +class ScaleTransformBuilder extends TransformBuilder { + final Alignment alignment; + + ScaleTransformBuilder({ + required List values, + this.alignment = Alignment.center, + }) : super(values: values); + + @override + Widget build(int i, double animationValue, Widget widget) { + double s = _getValue(values, animationValue, i); + return Transform.scale(scale: s, child: widget); + } +} + +class OpacityTransformBuilder extends TransformBuilder { + OpacityTransformBuilder({required List values}) + : super(values: values); + + @override + Widget build(int i, double animationValue, Widget widget) { + double v = _getValue(values, animationValue, i); + return Opacity( + opacity: v, + child: widget, + ); + } +} + +class RotateTransformBuilder extends TransformBuilder { + RotateTransformBuilder({required List values}) + : super(values: values); + + @override + Widget build(int i, double animationValue, Widget widget) { + double v = _getValue(values, animationValue, i); + return Transform.rotate( + angle: v, + child: widget, + ); + } +} + +class TranslateTransformBuilder extends TransformBuilder { + TranslateTransformBuilder({required List values}) + : super(values: values); + + @override + Widget build(int i, double animationValue, Widget widget) { + Offset s = _getOffsetValue(values, animationValue, i); + return Transform.translate( + offset: s, + child: widget, + ); + } +} + +class CustomLayoutOption { + final List builders = []; + final int startIndex; + final int? stateCount; + + CustomLayoutOption({this.stateCount, required this.startIndex}); + + void addOpacity(List values) { + builders.add(OpacityTransformBuilder(values: values)); + } + + void addTranslate(List values) { + builders.add(TranslateTransformBuilder(values: values)); + } + + void addScale(List values, Alignment alignment) { + builders.add(ScaleTransformBuilder(values: values, alignment: alignment)); + } + + void addRotate(List values) { + builders.add(RotateTransformBuilder(values: values)); + } +} + +class _CustomLayoutSwiper extends _SubSwiper { + final CustomLayoutOption option; + + const _CustomLayoutSwiper({ + required this.option, + double? itemWidth, + required bool loop, + double? itemHeight, + ValueChanged? onIndexChanged, + Key? key, + IndexedWidgetBuilder? itemBuilder, + required Curve curve, + int? duration, + int? index, + required int itemCount, + Axis? scrollDirection, + required SwiperController controller, + }) : super( + loop: loop, + onIndexChanged: onIndexChanged, + itemWidth: itemWidth, + itemHeight: itemHeight, + key: key, + itemBuilder: itemBuilder, + curve: curve, + duration: duration, + index: index, + itemCount: itemCount, + controller: controller, + scrollDirection: scrollDirection); + + @override + State createState() { + return _CustomLayoutState(); + } +} + +class _CustomLayoutState extends _CustomLayoutStateBase<_CustomLayoutSwiper> { + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _startIndex = widget.option.startIndex; + _animationCount = widget.option.stateCount; + } + + @override + void didUpdateWidget(_CustomLayoutSwiper oldWidget) { + _startIndex = widget.option.startIndex; + _animationCount = widget.option.stateCount; + super.didUpdateWidget(oldWidget); + } + + @override + Widget _buildItem(int index, int realIndex, double animationValue) { + List builders = widget.option.builders; + + Widget child = SizedBox( + width: widget.itemWidth ?? double.infinity, + height: widget.itemHeight ?? double.infinity, + child: widget.itemBuilder!(context, realIndex)); + + for (int i = builders.length - 1; i >= 0; --i) { + TransformBuilder builder = builders[i]; + child = builder.build(index, animationValue, child); + } + + return child; + } +} diff --git a/package/flutter_swiper_view/lib/src/flutter_page_indicator.dart b/package/flutter_swiper_view/lib/src/flutter_page_indicator.dart new file mode 100644 index 00000000..3384a35e --- /dev/null +++ b/package/flutter_swiper_view/lib/src/flutter_page_indicator.dart @@ -0,0 +1,355 @@ +// ignore_for_file: constant_identifier_names + +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +class WarmPainter extends BasePainter { + WarmPainter(PageIndicator widget, double page, int index, Paint paint) + : super(widget, page, index, paint); + + @override + void draw(Canvas canvas, double space, double size, double radius) { + double progress = page - index; + double distance = size + space; + double start = index * (size + space); + + if (progress > 0.5) { + double right = start + size + distance; + //progress=>0.5-1.0 + //left:0.0=>distance + + double left = index * distance + distance * (progress - 0.5) * 2; + canvas.drawRRect( + RRect.fromLTRBR(left, 0.0, right, size, Radius.circular(radius)), + _paint); + } else { + double right = start + size + distance * progress * 2; + + canvas.drawRRect( + RRect.fromLTRBR(start, 0.0, right, size, Radius.circular(radius)), + _paint); + } + } +} + +class DropPainter extends BasePainter { + DropPainter(PageIndicator widget, double page, int index, Paint paint) + : super(widget, page, index, paint); + + @override + void draw(Canvas canvas, double space, double size, double radius) { + double progress = page - index; + double dropHeight = widget.dropHeight; + double rate = (0.5 - progress).abs() * 2; + double scale = widget.scale; + + //lerp(begin, end, progress) + + canvas.drawCircle( + Offset(radius + ((page) * (size + space)), + radius - dropHeight * (1 - rate)), + radius * (scale + rate * (1.0 - scale)), + _paint); + } +} + +class NonePainter extends BasePainter { + NonePainter(PageIndicator widget, double page, int index, Paint paint) + : super(widget, page, index, paint); + + @override + void draw(Canvas canvas, double space, double size, double radius) { + double progress = page - index; + double secondOffset = index == widget.count - 1 + ? radius + : radius + ((index + 1) * (size + space)); + + if (progress > 0.5) { + canvas.drawCircle(Offset(secondOffset, radius), radius, _paint); + } else { + canvas.drawCircle( + Offset(radius + (index * (size + space)), radius), radius, _paint); + } + } +} + +class SlidePainter extends BasePainter { + SlidePainter(PageIndicator widget, double page, int index, Paint paint) + : super(widget, page, index, paint); + + @override + void draw(Canvas canvas, double space, double size, double radius) { + canvas.drawCircle( + Offset(radius + (page * (size + space)), radius), radius, _paint); + } +} + +class ScalePainter extends BasePainter { + ScalePainter(PageIndicator widget, double page, int index, Paint paint) + : super(widget, page, index, paint); + + // 连续的两个点,含有最后一个和第一个 + @override + bool _shouldSkip(int i) { + if (index == widget.count - 1) { + return i == 0 || i == index; + } + return (i == index || i == index + 1); + } + + @override + void paint(Canvas canvas, Size size) { + _paint.color = widget.color; + double space = widget.space; + double size = widget.size; + double radius = size / 2; + int c = widget.count; + for (int i = 0; i < c; ++i) { + if (_shouldSkip(i)) { + continue; + } + canvas.drawCircle(Offset(i * (size + space) + radius, radius), + radius * widget.scale, _paint); + } + + _paint.color = widget.activeColor; + draw(canvas, space, size, radius); + } + + @override + void draw(Canvas canvas, double space, double size, double radius) { + double secondOffset = index == widget.count - 1 + ? radius + : radius + ((index + 1) * (size + space)); + + double progress = page - index; + _paint.color = Color.lerp(widget.activeColor, widget.color, progress)!; + //last + canvas.drawCircle(Offset(radius + (index * (size + space)), radius), + lerp(radius, radius * widget.scale, progress), _paint); + //first + _paint.color = Color.lerp(widget.color, widget.activeColor, progress)!; + canvas.drawCircle(Offset(secondOffset, radius), + lerp(radius * widget.scale, radius, progress), _paint); + } +} + +class ColorPainter extends BasePainter { + ColorPainter(PageIndicator widget, double page, int index, Paint paint) + : super(widget, page, index, paint); + + // 连续的两个点,含有最后一个和第一个 + @override + bool _shouldSkip(int i) { + if (index == widget.count - 1) { + return i == 0 || i == index; + } + return (i == index || i == index + 1); + } + + @override + void draw(Canvas canvas, double space, double size, double radius) { + double progress = page - index; + double secondOffset = index == widget.count - 1 + ? radius + : radius + ((index + 1) * (size + space)); + + _paint.color = Color.lerp(widget.activeColor, widget.color, progress)!; + //left + canvas.drawCircle( + Offset(radius + (index * (size + space)), radius), radius, _paint); + //right + _paint.color = Color.lerp(widget.color, widget.activeColor, progress)!; + canvas.drawCircle(Offset(secondOffset, radius), radius, _paint); + } +} + +abstract class BasePainter extends CustomPainter { + final PageIndicator widget; + final double page; + final int index; + final Paint _paint; + + double lerp(double begin, double end, double progress) { + return begin + (end - begin) * progress; + } + + BasePainter(this.widget, this.page, this.index, this._paint); + + void draw(Canvas canvas, double space, double size, double radius); + + bool _shouldSkip(int i) { + return false; + } + //double secondOffset = index == widget.count-1 ? radius : radius + ((index + 1) * (size + space)); + + @override + void paint(Canvas canvas, Size size) { + _paint.color = widget.color; + double space = widget.space; + double size = widget.size; + double radius = size / 2; + int c = widget.count; + for (int i = 0; i < c; ++i) { + if (_shouldSkip(i)) { + continue; + } + canvas.drawCircle( + Offset(i * (size + space) + radius, radius), radius, _paint); + } + + double page = this.page; + if (page < index) { + page = 0.0; + } + _paint.color = widget.activeColor; + draw(canvas, space, size, radius); + } + + @override + bool shouldRepaint(BasePainter oldDelegate) { + return oldDelegate.page != page; + } +} + +class _PageIndicatorState extends State { + int index = 0; + double page = 0; + final Paint _paint = Paint(); + + BasePainter _createPainter() { + switch (widget.layout) { + case PageIndicatorLayout.NONE: + return NonePainter(widget, page, index, _paint); + case PageIndicatorLayout.SLIDE: + return SlidePainter(widget, page, index, _paint); + case PageIndicatorLayout.WARM: + return WarmPainter(widget, page, index, _paint); + case PageIndicatorLayout.COLOR: + return ColorPainter(widget, page, index, _paint); + case PageIndicatorLayout.SCALE: + return ScalePainter(widget, page, index, _paint); + case PageIndicatorLayout.DROP: + return DropPainter(widget, page, index, _paint); + default: + throw Exception("Not a valid layout"); + } + } + + @override + Widget build(BuildContext context) { + Widget child = SizedBox( + width: widget.count * widget.size + (widget.count - 1) * widget.space, + height: widget.size, + child: CustomPaint( + painter: _createPainter(), + ), + ); + + if (widget.layout == PageIndicatorLayout.SCALE || + widget.layout == PageIndicatorLayout.COLOR) { + child = ClipRect( + child: child, + ); + } + + return IgnorePointer( + child: child, + ); + } + + void _setInitialPage() { + // use the initial page index but cut off + // the offset specified when looping (kMiddleValue) + index = widget.controller.initialPage % kMiddleValue; + page = index.toDouble(); + } + + void _onController() { + if (!widget.controller.hasClients) return; + page = widget.controller.page ?? 0.0; + index = page.floor(); + + setState(() {}); + } + + @override + void initState() { + super.initState(); + widget.controller.addListener(_onController); + _setInitialPage(); + } + + @override + void didUpdateWidget(PageIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.controller != oldWidget.controller) { + oldWidget.controller.removeListener(_onController); + widget.controller.addListener(_onController); + } + } + + @override + void dispose() { + widget.controller.removeListener(_onController); + super.dispose(); + } +} + +enum PageIndicatorLayout { + NONE, + SLIDE, + WARM, + COLOR, + SCALE, + DROP, +} + +class PageIndicator extends StatefulWidget { + /// size of the dots + final double size; + + /// space between dots. + final double space; + + /// count of dots + final int count; + + /// active color + final Color activeColor; + + /// normal color + final Color color; + + /// layout of the dots,default is [PageIndicatorLayout.SLIDE] + final PageIndicatorLayout? layout; + + // Only valid when layout==PageIndicatorLayout.scale + final double scale; + + // Only valid when layout==PageIndicatorLayout.drop + final double dropHeight; + + final PageController controller; + + final double activeSize; + + const PageIndicator({ + Key? key, + this.size = 20.0, + this.space = 5.0, + required this.count, + this.activeSize = 20.0, + required this.controller, + this.color = Colors.white30, + this.layout = PageIndicatorLayout.SLIDE, + this.activeColor = Colors.white, + this.scale = 0.6, + this.dropHeight = 20.0, + }) : super(key: key); + + @override + State createState() { + return _PageIndicatorState(); + } +} diff --git a/package/flutter_swiper_view/lib/src/index_controller.dart b/package/flutter_swiper_view/lib/src/index_controller.dart new file mode 100644 index 00000000..f8ea30aa --- /dev/null +++ b/package/flutter_swiper_view/lib/src/index_controller.dart @@ -0,0 +1,119 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +abstract class IndexControllerEventBase { + final bool animation; + + final completer = Completer(); + Future get future => completer.future; + void complete() { + if (!completer.isCompleted) { + completer.complete(); + } + } + + IndexControllerEventBase({ + required this.animation, + }); +} + +mixin TargetedPositionControllerEvent on IndexControllerEventBase { + double get targetPosition; +} +mixin StepBasedIndexControllerEvent on TargetedPositionControllerEvent { + int get step; + int calcNextIndex({ + required int currentIndex, + required int itemCount, + required bool loop, + required bool reverse, + }) { + var cIndex = currentIndex; + if (reverse) { + cIndex -= step; + } else { + cIndex += step; + } + + if (!loop) { + if (cIndex >= itemCount) { + cIndex = 0; + } else if (cIndex < 0) { + cIndex = itemCount - 1; + } + } + return cIndex; + } +} + +class NextIndexControllerEvent extends IndexControllerEventBase + with TargetedPositionControllerEvent, StepBasedIndexControllerEvent { + NextIndexControllerEvent({ + required bool animation, + }) : super( + animation: animation, + ); + + @override + int get step => 1; + + @override + double get targetPosition => 1; +} + +class PrevIndexControllerEvent extends IndexControllerEventBase + with TargetedPositionControllerEvent, StepBasedIndexControllerEvent { + PrevIndexControllerEvent({ + required bool animation, + }) : super( + animation: animation, + ); + @override + int get step => -1; + + @override + double get targetPosition => 0; +} + +class MoveIndexControllerEvent extends IndexControllerEventBase + with TargetedPositionControllerEvent { + final int newIndex; + final int oldIndex; + MoveIndexControllerEvent({ + required this.newIndex, + required this.oldIndex, + required bool animation, + }) : super( + animation: animation, + ); + @override + double get targetPosition => newIndex > oldIndex ? 1 : 0; +} + +class IndexController extends ChangeNotifier { + IndexControllerEventBase? event; + int index = 0; + Future move(int index, {bool animation = true}) { + final e = event = MoveIndexControllerEvent( + animation: animation, + newIndex: index, + oldIndex: this.index, + ); + notifyListeners(); + return e.future; + } + + Future next({bool animation = true}) { + final e = event = NextIndexControllerEvent(animation: animation); + notifyListeners(); + return e.future; + } + + Future previous({bool animation = true}) { + final e = event = PrevIndexControllerEvent(animation: animation); + notifyListeners(); + return e.future; + } +} diff --git a/package/flutter_swiper_view/lib/src/parallax.dart b/package/flutter_swiper_view/lib/src/parallax.dart new file mode 100644 index 00000000..905920f4 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/parallax.dart @@ -0,0 +1,152 @@ +import 'package:flutter/widgets.dart'; + +import 'transformer_page_view.dart'; + +typedef PaintCallback = void Function(Canvas canvas, Size size); + +class ColorPainter extends CustomPainter { + final Paint _paint; + final TransformInfo info; + final List colors; + + ColorPainter(this._paint, this.info, this.colors); + + @override + void paint(Canvas canvas, Size size) { + int index = info.fromIndex; + _paint.color = colors[index]; + canvas.drawRect(Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); + if (info.done!) { + return; + } + int alpha; + int color; + double opacity; + double? position = info.position; + if (info.forward!) { + if (index < colors.length - 1) { + color = colors[index + 1].value & 0x00ffffff; + opacity = (position! <= 0 + ? (-position / info.viewportFraction!) + : 1 - position / info.viewportFraction!); + if (opacity > 1) { + opacity -= 1.0; + } + if (opacity < 0) { + opacity += 1.0; + } + alpha = (0xff * opacity).toInt(); + + _paint.color = Color((alpha << 24) | color); + canvas.drawRect( + Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); + } + } else { + if (index > 0) { + color = colors[index - 1].value & 0x00ffffff; + opacity = (position! > 0 + ? position / info.viewportFraction! + : (1 + position / info.viewportFraction!)); + if (opacity > 1) { + opacity -= 1.0; + } + if (opacity < 0) { + opacity += 1.0; + } + alpha = (0xff * opacity).toInt(); + + _paint.color = Color((alpha << 24) | color); + canvas.drawRect( + Rect.fromLTWH(0.0, 0.0, size.width, size.height), _paint); + } + } + } + + @override + bool shouldRepaint(ColorPainter oldDelegate) { + return oldDelegate.info != info; + } +} + +class _ParallaxColorState extends State { + Paint paint = Paint(); + + @override + Widget build(BuildContext context) { + return CustomPaint( + painter: ColorPainter(paint, widget.info, widget.colors), + child: widget.child, + ); + } +} + +class ParallaxColor extends StatefulWidget { + final Widget child; + + final List colors; + + final TransformInfo info; + + const ParallaxColor({ + Key? key, + required this.colors, + required this.info, + required this.child, + }) : super(key: key); + + @override + State createState() { + return _ParallaxColorState(); + } +} + +class ParallaxContainer extends StatelessWidget { + final Widget child; + final double position; + final double translationFactor; + final double opacityFactor; + + const ParallaxContainer({ + Key? key, + required this.child, + required this.position, + this.translationFactor = 100.0, + this.opacityFactor = 1.0, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Opacity( + opacity: (1 - position.abs()).clamp(0.0, 1.0) * opacityFactor, + child: Transform.translate( + offset: Offset(position * translationFactor, 0.0), + child: child, + ), + ); + } +} + +class ParallaxImage extends StatelessWidget { + final Image image; + final double imageFactor; + + ParallaxImage.asset( + String name, { + Key? key, + required double position, + this.imageFactor = 0.3, + }) : image = Image.asset( + name, + fit: BoxFit.cover, + alignment: FractionalOffset( + 0.5 + position * imageFactor, + 0.5, + ), + ), + super(key: key); + + @override + Widget build(BuildContext context) { + return image; + } +} diff --git a/package/flutter_swiper_view/lib/src/swiper.dart b/package/flutter_swiper_view/lib/src/swiper.dart new file mode 100644 index 00000000..1b55ae73 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/swiper.dart @@ -0,0 +1,1003 @@ +// ignore_for_file: constant_identifier_names + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +import 'transformer_page_view.dart'; + +part 'custom_layout.dart'; + +typedef SwiperOnTap = void Function(int index); + +typedef SwiperDataBuilder = Widget Function( + BuildContext context, T data, int index); + +/// default auto play delay +const int kDefaultAutoplayDelayMs = 3000; + +/// Default auto play transition duration (in millisecond) +const int kDefaultAutoplayTransactionDuration = 300; + +const int kMaxValue = 2000000000; +const int kMiddleValue = 1000000000; + +enum SwiperLayout { + DEFAULT, + STACK, + TINDER, + CUSTOM, +} + +class Swiper extends StatefulWidget { + /// If set true , the pagination will display 'outer' of the 'content' container. + final bool outer; + + /// Inner item height, this property is valid if layout=STACK or layout=TINDER or LAYOUT=CUSTOM, + final double? itemHeight; + + /// Inner item width, this property is valid if layout=STACK or layout=TINDER or LAYOUT=CUSTOM, + final double? itemWidth; + + // height of the inside container,this property is valid when outer=true,otherwise the inside container size is controlled by parent widget + final double? containerHeight; + // width of the inside container,this property is valid when outer=true,otherwise the inside container size is controlled by parent widget + final double? containerWidth; + + /// Build item on index + final IndexedWidgetBuilder? itemBuilder; + + /// Support transform like Android PageView did + /// `itemBuilder` and `transformItemBuilder` must have one not null + final PageTransformer? transformer; + + /// count of the display items + final int itemCount; + + final ValueChanged? onIndexChanged; + + ///auto play config + final bool autoplay; + + ///Duration of the animation between transactions (in millisecond). + final int autoplayDelay; + + ///disable auto play when interaction + final bool autoplayDisableOnInteraction; + + ///auto play transition duration (in millisecond) + final int duration; + + ///horizontal/vertical + final Axis scrollDirection; + + ///left/right for Stack Layout + final AxisDirection axisDirection; + + ///transition curve + final Curve curve; + + /// Set to false to disable continuous loop mode. + final bool loop; + + ///Index number of initial slide. + ///If not set , the `Swiper` is 'uncontrolled', which means manage index by itself + ///If set , the `Swiper` is 'controlled', which means the index is fully managed by parent widget. + final int? index; + + ///Called when tap + final SwiperOnTap? onTap; + + ///The swiper pagination plugin + final SwiperPlugin? pagination; + + ///the swiper control button plugin + final SwiperPlugin? control; + + ///other plugins, you can custom your own plugin + final List? plugins; + + /// + final SwiperController? controller; + + final ScrollPhysics? physics; + + /// + final double viewportFraction; + + /// Build in layouts + final SwiperLayout layout; + + /// this value is valid when layout == SwiperLayout.CUSTOM + final CustomLayoutOption? customLayoutOption; + + // This value is valid when viewportFraction is set and < 1.0 + final double? scale; + + // This value is valid when viewportFraction is set and < 1.0 + final double? fade; + + final PageIndicatorLayout indicatorLayout; + + final bool allowImplicitScrolling; + + const Swiper({ + this.itemBuilder, + this.indicatorLayout = PageIndicatorLayout.NONE, + + /// + this.transformer, + required this.itemCount, + bool autoplay = false, + this.layout = SwiperLayout.DEFAULT, + this.autoplayDelay = kDefaultAutoplayDelayMs, + this.autoplayDisableOnInteraction = true, + this.duration = kDefaultAutoplayTransactionDuration, + this.onIndexChanged, + this.index, + this.onTap, + this.control, + bool loop = true, + this.curve = Curves.ease, + this.scrollDirection = Axis.horizontal, + this.axisDirection = AxisDirection.left, + this.pagination, + this.plugins, + this.physics, + Key? key, + this.controller, + this.customLayoutOption, + + /// since v1.0.0 + this.containerHeight, + this.containerWidth, + this.viewportFraction = 1.0, + this.itemHeight, + this.itemWidth, + this.outer = false, + this.scale, + this.fade, + this.allowImplicitScrolling = false, + }) : assert( + itemBuilder != null || transformer != null, + 'itemBuilder and transformItemBuilder must not be both null', + ), + assert( + !loop || + ((loop && + layout == SwiperLayout.DEFAULT && + (indicatorLayout == PageIndicatorLayout.SCALE || + indicatorLayout == PageIndicatorLayout.COLOR || + indicatorLayout == PageIndicatorLayout.NONE)) || + (loop && layout != SwiperLayout.DEFAULT)), + "Only support `PageIndicatorLayout.SCALE` and `PageIndicatorLayout.COLOR`when layout==SwiperLayout.DEFAULT in loop mode"), + autoplay = (autoplay && itemCount > 1), + loop = (loop && itemCount > 1), + super(key: key); + + factory Swiper.children({ + required List children, + bool autoplay = false, + PageTransformer? transformer, + int autoplayDelay = kDefaultAutoplayDelayMs, + bool autoplayDisableOnInteraction = true, + int duration = kDefaultAutoplayTransactionDuration, + ValueChanged? onIndexChanged, + int? index, + SwiperOnTap? onTap, + bool loop = true, + Curve curve = Curves.ease, + Axis scrollDirection = Axis.horizontal, + AxisDirection axisDirection = AxisDirection.left, + SwiperPlugin? pagination, + SwiperPlugin? control, + List? plugins, + SwiperController? controller, + Key? key, + CustomLayoutOption? customLayoutOption, + ScrollPhysics? physics, + double? containerHeight, + double? containerWidth, + double viewportFraction = 1.0, + double? itemHeight, + double? itemWidth, + bool outer = false, + double scale = 1.0, + double? fade, + PageIndicatorLayout indicatorLayout = PageIndicatorLayout.NONE, + SwiperLayout layout = SwiperLayout.DEFAULT, + }) => + Swiper( + fade: fade, + indicatorLayout: indicatorLayout, + layout: layout, + transformer: transformer, + customLayoutOption: customLayoutOption, + containerHeight: containerHeight, + containerWidth: containerWidth, + viewportFraction: viewportFraction, + itemHeight: itemHeight, + itemWidth: itemWidth, + outer: outer, + scale: scale, + autoplay: autoplay, + autoplayDelay: autoplayDelay, + autoplayDisableOnInteraction: autoplayDisableOnInteraction, + duration: duration, + onIndexChanged: onIndexChanged, + index: index, + onTap: onTap, + curve: curve, + scrollDirection: scrollDirection, + axisDirection: axisDirection, + pagination: pagination, + control: control, + controller: controller, + loop: loop, + plugins: plugins, + physics: physics, + key: key, + itemBuilder: (context, index) { + return children[index]; + }, + itemCount: children.length, + ); + + static Swiper list({ + PageTransformer? transformer, + required List list, + CustomLayoutOption? customLayoutOption, + required SwiperDataBuilder builder, + bool autoplay = false, + int autoplayDelay = kDefaultAutoplayDelayMs, + bool reverse = false, + bool autoplayDisableOnInteraction = true, + int duration = kDefaultAutoplayTransactionDuration, + ValueChanged? onIndexChanged, + int? index, + SwiperOnTap? onTap, + bool loop = true, + Curve curve = Curves.ease, + Axis scrollDirection = Axis.horizontal, + AxisDirection axisDirection = AxisDirection.left, + SwiperPlugin? pagination, + SwiperPlugin? control, + List? plugins, + SwiperController? controller, + Key? key, + ScrollPhysics? physics, + double? containerHeight, + double? containerWidth, + double viewportFraction = 1.0, + double? itemHeight, + double? itemWidth, + bool outer = false, + double scale = 1.0, + double? fade, + PageIndicatorLayout indicatorLayout = PageIndicatorLayout.NONE, + SwiperLayout layout = SwiperLayout.DEFAULT, + }) => + Swiper( + fade: fade, + indicatorLayout: indicatorLayout, + layout: layout, + transformer: transformer, + customLayoutOption: customLayoutOption, + containerHeight: containerHeight, + containerWidth: containerWidth, + viewportFraction: viewportFraction, + itemHeight: itemHeight, + itemWidth: itemWidth, + outer: outer, + scale: scale, + autoplay: autoplay, + autoplayDelay: autoplayDelay, + autoplayDisableOnInteraction: autoplayDisableOnInteraction, + duration: duration, + onIndexChanged: onIndexChanged, + index: index, + onTap: onTap, + curve: curve, + key: key, + scrollDirection: scrollDirection, + axisDirection: axisDirection, + pagination: pagination, + control: control, + controller: controller, + loop: loop, + plugins: plugins, + physics: physics, + itemBuilder: (context, index) { + return builder(context, list[index], index); + }, + itemCount: list.length, + ); + + @override + State createState() => _SwiperState(); +} + +abstract class _SwiperTimerMixin extends State { + Timer? _timer; + + late SwiperController _controller; + + @override + void initState() { + super.initState(); + _controller = widget.controller ?? SwiperController(); + _controller.addListener(_onController); + if (widget.autoplay) { + _controller.startAutoplay(); + } else { + _controller.stopAutoplay(); + } + } + + void _onController() { + final event = _controller.event; + if (event is AutoPlaySwiperControllerEvent) { + if (event.autoplay) { + if (_timer == null) { + _startAutoplay(); + } + } else { + _stopAutoplay(); + } + } + } + + @override + void didUpdateWidget(Swiper oldWidget) { + super.didUpdateWidget(oldWidget); + if (_controller != oldWidget.controller) { + final oldController = oldWidget.controller; + if (oldController != null) { + oldController.removeListener(_onController); + _controller = oldController; + _controller.addListener(_onController); + } + } + if (widget.autoplay != oldWidget.autoplay) { + if (widget.autoplay) { + _controller.startAutoplay(); + } else { + _controller.stopAutoplay(); + } + } + } + + @override + void dispose() { + _controller.removeListener(_onController); + _stopAutoplay(); + super.dispose(); + } + + void _startAutoplay() { + _stopAutoplay(); + _timer = Timer.periodic( + Duration( + milliseconds: widget.autoplayDelay, + ), + _onTimer, + ); + } + + void _onTimer(Timer timer) { + _controller.next(animation: true); + } + + void _stopAutoplay() { + _timer?.cancel(); + _timer = null; + } +} + +class _SwiperState extends _SwiperTimerMixin { + late int _activeIndex; + + TransformerPageController? _pageController; + + Widget _wrapTap(BuildContext context, int index) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => widget.onTap!(index), + child: widget.itemBuilder!(context, index), + ); + } + + @override + void initState() { + super.initState(); + _activeIndex = widget.index ?? widget.controller?.index ?? 0; + if (_isPageViewLayout()) { + _pageController = TransformerPageController( + initialPage: widget.index ?? widget.controller?.index ?? 0, + loop: widget.loop, + itemCount: widget.itemCount, + reverse: widget.transformer?.reverse ?? false, + viewportFraction: widget.viewportFraction, + ); + } + } + + bool _isPageViewLayout() { + return widget.layout == SwiperLayout.DEFAULT; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + } + + bool _getReverse(Swiper widget) => widget.transformer?.reverse ?? false; + + @override + void didUpdateWidget(Swiper oldWidget) { + super.didUpdateWidget(oldWidget); + if (_isPageViewLayout()) { + if (_pageController == null || + (widget.index != oldWidget.index || + widget.loop != oldWidget.loop || + widget.itemCount != oldWidget.itemCount || + widget.viewportFraction != oldWidget.viewportFraction || + _getReverse(widget) != _getReverse(oldWidget))) { + _pageController = TransformerPageController( + initialPage: widget.index ?? widget.controller?.index ?? 0, + loop: widget.loop, + itemCount: widget.itemCount, + reverse: _getReverse(widget), + viewportFraction: widget.viewportFraction, + ); + } + } else { + scheduleMicrotask(() { + // So that we have a chance to do `removeListener` in child widgets. + if (_pageController != null) { + _pageController!.dispose(); + _pageController = null; + } + }); + } + if (widget.index != null && widget.index != _activeIndex) { + _activeIndex = widget.index!; + } + } + + void _onIndexChanged(int index) { + setState(() { + _activeIndex = index; + }); + widget.onIndexChanged?.call(index); + } + + Widget _buildSwiper() { + IndexedWidgetBuilder? itemBuilder; + if (widget.onTap != null) { + itemBuilder = _wrapTap; + } else { + itemBuilder = widget.itemBuilder; + } + + if (widget.layout == SwiperLayout.STACK) { + return _StackSwiper( + loop: widget.loop, + itemWidth: widget.itemWidth, + itemHeight: widget.itemHeight, + itemCount: widget.itemCount, + itemBuilder: itemBuilder, + index: _activeIndex, + curve: widget.curve, + duration: widget.duration, + onIndexChanged: _onIndexChanged, + controller: _controller, + scrollDirection: widget.scrollDirection, + axisDirection: widget.axisDirection, + ); + } else if (_isPageViewLayout()) { + PageTransformer? transformer = widget.transformer; + if (widget.scale != null || widget.fade != null) { + transformer = + ScaleAndFadeTransformer(scale: widget.scale, fade: widget.fade); + } + + Widget child = TransformerPageView( + pageController: _pageController, + loop: widget.loop, + itemCount: widget.itemCount, + itemBuilder: itemBuilder, + transformer: transformer, + viewportFraction: widget.viewportFraction, + index: _activeIndex, + duration: Duration(milliseconds: widget.duration), + scrollDirection: widget.scrollDirection, + onPageChanged: _onIndexChanged, + curve: widget.curve, + physics: widget.physics, + controller: _controller, + allowImplicitScrolling: widget.allowImplicitScrolling, + ); + if (widget.autoplayDisableOnInteraction && widget.autoplay) { + return NotificationListener( + onNotification: (notification) { + if (notification is ScrollStartNotification) { + if (notification.dragDetails != null) { + //by human + if (_timer != null) _stopAutoplay(); + } + } else if (notification is ScrollEndNotification) { + if (_timer == null) _startAutoplay(); + } + + return false; + }, + child: child, + ); + } + + return child; + } else if (widget.layout == SwiperLayout.TINDER) { + return _TinderSwiper( + loop: widget.loop, + itemWidth: widget.itemWidth, + itemHeight: widget.itemHeight, + itemCount: widget.itemCount, + itemBuilder: itemBuilder, + index: _activeIndex, + curve: widget.curve, + duration: widget.duration, + onIndexChanged: _onIndexChanged, + controller: _controller, + scrollDirection: widget.scrollDirection, + ); + } else if (widget.layout == SwiperLayout.CUSTOM) { + return _CustomLayoutSwiper( + loop: widget.loop, + option: widget.customLayoutOption!, + itemWidth: widget.itemWidth, + itemHeight: widget.itemHeight, + itemCount: widget.itemCount, + itemBuilder: itemBuilder, + index: _activeIndex, + curve: widget.curve, + duration: widget.duration, + onIndexChanged: _onIndexChanged, + controller: _controller, + scrollDirection: widget.scrollDirection, + ); + } else { + return const SizedBox.shrink(); + } + } + + SwiperPluginConfig _ensureConfig(SwiperPluginConfig? config) { + final con = config ?? + SwiperPluginConfig( + outer: widget.outer, + itemCount: widget.itemCount, + layout: widget.layout, + indicatorLayout: widget.indicatorLayout, + pageController: _pageController, + activeIndex: _activeIndex, + scrollDirection: widget.scrollDirection, + axisDirection: widget.axisDirection, + controller: _controller, + loop: widget.loop, + ); + return con; + } + + List? _ensureListForStack({ + required Widget swiper, + required List? listForStack, + required Widget widget, + }) { + final resList = []; + if (listForStack == null) { + resList.addAll([swiper, widget]); + } else { + resList.addAll([...listForStack, widget]); + } + return resList; + } + + @override + Widget build(BuildContext context) { + Widget swiper = _buildSwiper(); + List? listForStack; + SwiperPluginConfig? config; + if (widget.control != null) { + //Stack + config = _ensureConfig(config); + listForStack = _ensureListForStack( + swiper: swiper, + listForStack: listForStack, + widget: widget.control!.build(context, config), + ); + } + + if (widget.plugins != null) { + config = _ensureConfig(config); + for (SwiperPlugin plugin in widget.plugins!) { + listForStack = _ensureListForStack( + swiper: swiper, + listForStack: listForStack, + widget: plugin.build(context, config), + ); + } + } + if (widget.pagination != null) { + config = _ensureConfig(config); + if (widget.outer) { + return _buildOuterPagination( + widget.pagination! as SwiperPagination, + listForStack == null ? swiper : Stack(children: listForStack), + config); + } else { + listForStack = _ensureListForStack( + swiper: swiper, + listForStack: listForStack, + widget: widget.pagination!.build(context, config), + ); + } + } + + if (listForStack != null) { + return Stack( + children: listForStack, + ); + } + + return swiper; + } + + Widget _buildOuterPagination( + SwiperPagination pagination, + Widget swiper, + SwiperPluginConfig config, + ) { + final list = []; + //Only support bottom yet! + if (widget.containerHeight != null || widget.containerWidth != null) { + list.add(swiper); + } else { + list.add(Expanded(child: swiper)); + } + + list.add(Align( + alignment: Alignment.center, + child: pagination.build(context, config), + )); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: list, + ); + } +} + +abstract class _SubSwiper extends StatefulWidget { + final IndexedWidgetBuilder? itemBuilder; + final int itemCount; + final int? index; + final ValueChanged? onIndexChanged; + final SwiperController controller; + final int? duration; + final Curve curve; + final double? itemWidth; + final double? itemHeight; + final bool loop; + final Axis? scrollDirection; + final AxisDirection? axisDirection; + + const _SubSwiper({ + Key? key, + required this.loop, + this.itemHeight, + this.itemWidth, + this.duration, + required this.curve, + this.itemBuilder, + required this.controller, + this.index, + required this.itemCount, + this.scrollDirection = Axis.horizontal, + this.axisDirection = AxisDirection.left, + this.onIndexChanged, + }) : super(key: key); + + @override + State createState(); + + int getCorrectIndex(int indexNeedsFix) { + if (itemCount == 0) return 0; + var value = indexNeedsFix % itemCount; + if (value < 0) { + value += itemCount; + } + return value; + } +} + +class _TinderSwiper extends _SubSwiper { + const _TinderSwiper({ + Key? key, + required Curve curve, + int? duration, + required SwiperController controller, + ValueChanged? onIndexChanged, + double? itemHeight, + double? itemWidth, + IndexedWidgetBuilder? itemBuilder, + int? index, + required bool loop, + required int itemCount, + Axis? scrollDirection, + }) : assert(itemWidth != null && itemHeight != null), + super( + loop: loop, + key: key, + itemWidth: itemWidth, + itemHeight: itemHeight, + itemBuilder: itemBuilder, + curve: curve, + duration: duration, + controller: controller, + index: index, + onIndexChanged: onIndexChanged, + itemCount: itemCount, + scrollDirection: scrollDirection); + + @override + State createState() { + return _TinderState(); + } +} + +class _StackSwiper extends _SubSwiper { + const _StackSwiper({ + Key? key, + required Curve curve, + int? duration, + required SwiperController controller, + ValueChanged? onIndexChanged, + double? itemHeight, + double? itemWidth, + IndexedWidgetBuilder? itemBuilder, + int? index, + required bool loop, + required int itemCount, + Axis? scrollDirection, + AxisDirection? axisDirection, + }) : super( + loop: loop, + key: key, + itemWidth: itemWidth, + itemHeight: itemHeight, + itemBuilder: itemBuilder, + curve: curve, + duration: duration, + controller: controller, + index: index, + onIndexChanged: onIndexChanged, + itemCount: itemCount, + scrollDirection: scrollDirection, + axisDirection: axisDirection, + ); + + @override + State createState() => _StackViewState(); +} + +class _TinderState extends _CustomLayoutStateBase<_TinderSwiper> { + late List scales; + late List offsetsX; + late List offsetsY; + late List opacity; + late List rotates; + + double getOffsetY(double scale) { + return widget.itemHeight! - widget.itemHeight! * scale; + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + } + + @override + void didUpdateWidget(_TinderSwiper oldWidget) { + _updateValues(); + super.didUpdateWidget(oldWidget); + } + + @override + void afterRender() { + super.afterRender(); + + _startIndex = -3; + _animationCount = 5; + opacity = [0.0, 0.9, 0.9, 1.0, 0.0, 0.0]; + scales = [0.80, 0.80, 0.85, 0.90, 1.0, 1.0, 1.0]; + rotates = [0.0, 0.0, 0.0, 0.0, 20.0, 25.0]; + _updateValues(); + } + + void _updateValues() { + if (widget.scrollDirection == Axis.horizontal) { + offsetsX = [0.0, 0.0, 0.0, 0.0, _swiperWidth, _swiperWidth]; + offsetsY = [ + 0.0, + 0.0, + -5.0, + -10.0, + -15.0, + -20.0, + ]; + } else { + offsetsX = [ + 0.0, + 0.0, + 5.0, + 10.0, + 15.0, + 20.0, + ]; + + offsetsY = [0.0, 0.0, 0.0, 0.0, _swiperHeight, _swiperHeight]; + } + } + + @override + Widget _buildItem(int i, int realIndex, double animationValue) { + double s = _getValue(scales, animationValue, i); + double f = _getValue(offsetsX, animationValue, i); + double fy = _getValue(offsetsY, animationValue, i); + double o = _getValue(opacity, animationValue, i); + double a = _getValue(rotates, animationValue, i); + + Alignment alignment = widget.scrollDirection == Axis.horizontal + ? Alignment.bottomCenter + : Alignment.centerLeft; + + return Opacity( + opacity: o, + child: Transform.rotate( + angle: a / 180.0, + child: Transform.translate( + key: ValueKey(_currentIndex + i), + offset: Offset(f, fy), + child: Transform.scale( + scale: s, + alignment: alignment, + child: SizedBox( + width: widget.itemWidth ?? double.infinity, + height: widget.itemHeight ?? double.infinity, + child: widget.itemBuilder!(context, realIndex), + ), + ), + ), + ), + ); + } +} + +class _StackViewState extends _CustomLayoutStateBase<_StackSwiper> { + late List scales; + late List offsets; + late List opacity; + @override + void didChangeDependencies() { + super.didChangeDependencies(); + } + + void _updateValues() { + if (widget.scrollDirection == Axis.horizontal) { + double space = (_swiperWidth - widget.itemWidth!) / 2; + offsets = widget.axisDirection == AxisDirection.left + ? [-space, -space / 3 * 2, -space / 3, 0.0, _swiperWidth] + : [_swiperWidth, 0.0, -space / 3, -space / 3 * 2, -space]; + } else { + double space = (_swiperHeight - widget.itemHeight!) / 2; + offsets = [-space, -space / 3 * 2, -space / 3, 0.0, _swiperHeight]; + } + } + + @override + void didUpdateWidget(_StackSwiper oldWidget) { + _updateValues(); + super.didUpdateWidget(oldWidget); + } + + @override + void afterRender() { + super.afterRender(); + final isRightSide = widget.axisDirection == AxisDirection.right; + + //length of the values array below + _animationCount = 5; + + //Array below this line, '0' index is 1.0, which is the first item show in swiper. + _startIndex = isRightSide ? -1 : -3; + scales = + isRightSide ? [1.0, 1.0, 0.9, 0.8, 0.7] : [0.7, 0.8, 0.9, 1.0, 1.0]; + opacity = + isRightSide ? [1.0, 1.0, 1.0, 0.5, 0.0] : [0.0, 0.5, 1.0, 1.0, 1.0]; + + _updateValues(); + } + + @override + Widget _buildItem(int i, int realIndex, double animationValue) { + double s = _getValue(scales, animationValue, i); + double f = _getValue(offsets, animationValue, i); + double o = _getValue(opacity, animationValue, i); + + Offset offset = widget.scrollDirection == Axis.horizontal + ? widget.axisDirection == AxisDirection.left + ? Offset(f, 0.0) + : Offset(-f, 0.0) + : Offset(0.0, f); + + Alignment alignment = widget.scrollDirection == Axis.horizontal + ? widget.axisDirection == AxisDirection.left + ? Alignment.centerLeft + : Alignment.centerRight + : Alignment.topCenter; + + return Opacity( + opacity: o, + child: Transform.translate( + key: ValueKey(_currentIndex + i), + offset: offset, + child: Transform.scale( + scale: s, + alignment: alignment, + child: SizedBox( + width: widget.itemWidth ?? double.infinity, + height: widget.itemHeight ?? double.infinity, + child: widget.itemBuilder!(context, realIndex), + ), + ), + ), + ); + } +} + +class ScaleAndFadeTransformer extends PageTransformer { + final double? _scale; + final double? _fade; + + ScaleAndFadeTransformer({double? fade = 0.3, double? scale = 0.8}) + : _fade = fade, + _scale = scale; + + @override + Widget transform(Widget child, TransformInfo info) { + double? position = info.position; + Widget newChild = child; + if (_scale != null) { + double scaleFactor = (1 - position!.abs()) * (1 - _scale!); + double scale = _scale! + scaleFactor; + + newChild = Transform.scale( + scale: scale, + child: child, + ); + } + + if (_fade != null) { + double fadeFactor = (1 - position!.abs()) * (1 - _fade!); + double opacity = _fade! + fadeFactor; + newChild = Opacity( + opacity: opacity, + child: newChild, + ); + } + + return newChild; + } +} diff --git a/package/flutter_swiper_view/lib/src/swiper_control.dart b/package/flutter_swiper_view/lib/src/swiper_control.dart new file mode 100644 index 00000000..b3b3b125 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/swiper_control.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +class SwiperControl extends SwiperPlugin { + ///IconData for previous + final IconData iconPrevious; + + ///iconData fopr next + final IconData iconNext; + + ///icon size + final double size; + + ///Icon normal color, The theme's [ThemeData.primaryColor] by default. + final Color? color; + + ///if set loop=false on Swiper, this color will be used when swiper goto the last slide. + ///The theme's [ThemeData.disabledColor] by default. + final Color? disableColor; + + final EdgeInsetsGeometry padding; + + final Key? key; + + const SwiperControl({ + this.iconPrevious = Icons.arrow_back_ios, + this.iconNext = Icons.arrow_forward_ios, + this.color, + this.disableColor, + this.key, + this.size = 30.0, + this.padding = const EdgeInsets.all(5.0), + }); + + Widget buildButton({ + required SwiperPluginConfig? config, + required Color color, + required IconData iconDaga, + required int quarterTurns, + required bool previous, + }) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (previous) { + config!.controller.previous(animation: true); + } else { + config!.controller.next(animation: true); + } + }, + child: Padding( + padding: padding, + child: RotatedBox( + quarterTurns: quarterTurns, + child: Icon( + iconDaga, + semanticLabel: previous ? "Previous" : "Next", + size: size, + color: color, + ))), + ); + } + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + ThemeData themeData = Theme.of(context); + + Color color = this.color ?? themeData.primaryColor; + Color disableColor = this.disableColor ?? themeData.disabledColor; + Color prevColor; + Color nextColor; + + if (config.loop) { + prevColor = nextColor = color; + } else { + bool next = config.activeIndex < config.itemCount - 1; + bool prev = config.activeIndex > 0; + prevColor = prev ? color : disableColor; + nextColor = next ? color : disableColor; + } + + Widget child; + if (config.scrollDirection == Axis.horizontal) { + child = Row( + key: key, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildButton( + config: config, + color: prevColor, + iconDaga: iconPrevious, + quarterTurns: 0, + previous: true, + ), + buildButton( + config: config, + color: nextColor, + iconDaga: iconNext, + quarterTurns: 0, + previous: false, + ) + ], + ); + } else { + child = Column( + key: key, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + buildButton( + config: config, + color: prevColor, + iconDaga: iconPrevious, + quarterTurns: -3, + previous: true, + ), + buildButton( + config: config, + color: nextColor, + iconDaga: iconNext, + quarterTurns: -3, + previous: false, + ) + ], + ); + } + + return SizedBox( + height: double.infinity, + width: double.infinity, + child: child, + ); + } +} diff --git a/package/flutter_swiper_view/lib/src/swiper_controller.dart b/package/flutter_swiper_view/lib/src/swiper_controller.dart new file mode 100644 index 00000000..13363edf --- /dev/null +++ b/package/flutter_swiper_view/lib/src/swiper_controller.dart @@ -0,0 +1,44 @@ +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +class SwipeIndexControllerEvent extends IndexControllerEventBase { + SwipeIndexControllerEvent({ + required this.pos, + required bool animation, + }) : super(animation: animation); + final double pos; +} + +class BuildIndexControllerEvent extends IndexControllerEventBase { + BuildIndexControllerEvent({ + required bool animation, + required this.config, + }) : super(animation: animation); + final SwiperPluginConfig config; +} + +class AutoPlaySwiperControllerEvent extends IndexControllerEventBase { + AutoPlaySwiperControllerEvent({ + required bool animation, + required this.autoplay, + }) : super(animation: animation); + + AutoPlaySwiperControllerEvent.start({ + required bool animation, + }) : this(animation: animation, autoplay: true); + AutoPlaySwiperControllerEvent.stop({ + required bool animation, + }) : this(animation: animation, autoplay: false); + final bool autoplay; +} + +class SwiperController extends IndexController { + void startAutoplay({bool animation = true}) { + event = AutoPlaySwiperControllerEvent.start(animation: animation); + notifyListeners(); + } + + void stopAutoplay({bool animation = true}) { + event = AutoPlaySwiperControllerEvent.stop(animation: animation); + notifyListeners(); + } +} diff --git a/package/flutter_swiper_view/lib/src/swiper_pagination.dart b/package/flutter_swiper_view/lib/src/swiper_pagination.dart new file mode 100644 index 00000000..51a18e81 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/swiper_pagination.dart @@ -0,0 +1,310 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +class FractionPaginationBuilder extends SwiperPlugin { + ///color ,if set null , will be Theme.of(context).scaffoldBackgroundColor + final Color? color; + + ///color when active,if set null , will be Theme.of(context).primaryColor + final Color? activeColor; + + ////font size + final double fontSize; + + ///font size when active + final double activeFontSize; + + final Key? key; + + const FractionPaginationBuilder({ + this.color, + this.fontSize = 20.0, + this.key, + this.activeColor, + this.activeFontSize = 35.0, + }); + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + int itemCount = config.itemCount; + if (itemCount <= 1) { + return Container(); + } + + ThemeData themeData = Theme.of(context); + Color activeColor = this.activeColor ?? themeData.primaryColor; + Color color = this.color ?? themeData.scaffoldBackgroundColor; + + if (Axis.vertical == config.scrollDirection) { + return Column( + key: key, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${config.activeIndex + 1}", + style: TextStyle(color: activeColor, fontSize: activeFontSize), + ), + Text( + "/", + style: TextStyle(color: color, fontSize: fontSize), + ), + Text( + "$itemCount", + style: TextStyle(color: color, fontSize: fontSize), + ) + ], + ); + } else { + return Row( + key: key, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${config.activeIndex + 1}", + style: TextStyle(color: activeColor, fontSize: activeFontSize), + ), + Text( + " / $itemCount", + style: TextStyle(color: color, fontSize: fontSize), + ) + ], + ); + } + } +} + +class RectSwiperPaginationBuilder extends SwiperPlugin { + ///color when current index,if set null , will be Theme.of(context).primaryColor + final Color? activeColor; + + ///,if set null , will be Theme.of(context).scaffoldBackgroundColor + final Color? color; + + ///Size of the rect when activate + final Size activeSize; + + ///Size of the rect + final Size size; + + /// Space between rects + final double space; + + final Key? key; + + const RectSwiperPaginationBuilder({ + this.activeColor, + this.color, + this.key, + this.size = const Size(10.0, 3.0), + this.activeSize = const Size(10.0, 3.0), + this.space = 2.0, + }); + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + int itemCount = config.itemCount; + if (itemCount <= 1) { + return Container(); + } + + ThemeData themeData = Theme.of(context); + Color activeColor = this.activeColor ?? themeData.primaryColor; + Color color = this.color ?? themeData.scaffoldBackgroundColor; + + List list = []; + + int activeIndex = config.activeIndex; + if (itemCount > 20) { + debugPrint( + "The itemCount is too big, we suggest use FractionPaginationBuilder instead of DotSwiperPaginationBuilder in this situation"); + } + + for (int i = 0; i < itemCount; ++i) { + bool active = i == activeIndex; + Size size = active ? activeSize : this.size; + list.add(Container( + width: size.width, + height: size.height, + key: Key("pagination_$i"), + margin: EdgeInsets.all(space), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(1.5), + color: active ? activeColor : color, + ), + )); + } + + if (config.scrollDirection == Axis.vertical) { + return Column( + key: key, + mainAxisSize: MainAxisSize.min, + children: list, + ); + } else { + return Row( + key: key, + mainAxisSize: MainAxisSize.min, + children: list, + ); + } + } +} + +class DotSwiperPaginationBuilder extends SwiperPlugin { + ///color when current index,if set null , will be Theme.of(context).primaryColor + final Color? activeColor; + + ///,if set null , will be Theme.of(context).scaffoldBackgroundColor + final Color? color; + + ///Size of the dot when activate + final double activeSize; + + ///Size of the dot + final double size; + + /// Space between dots + final double space; + + final Key? key; + + const DotSwiperPaginationBuilder({ + this.activeColor, + this.color, + this.key, + this.size = 10.0, + this.activeSize = 10.0, + this.space = 3.0, + }); + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + + int itemCount = config.itemCount; + if (itemCount <= 1) { + return Container(); + } + + if (config.itemCount > 20) { + debugPrint( + "The itemCount is too big, we suggest use FractionPaginationBuilder instead of DotSwiperPaginationBuilder in this sitituation"); + } + Color? activeColor = this.activeColor; + Color? color = this.color; + + if (activeColor == null || color == null) { + ThemeData themeData = Theme.of(context); + activeColor = this.activeColor ?? themeData.primaryColor; + color = this.color ?? themeData.scaffoldBackgroundColor; + } + + if (config.indicatorLayout != PageIndicatorLayout.NONE && + config.layout == SwiperLayout.DEFAULT) { + return PageIndicator( + count: config.itemCount, + controller: config.pageController!, + layout: config.indicatorLayout, + size: size, + activeColor: activeColor, + color: color, + space: space, + ); + } + + List list = []; + + int? activeIndex = config.activeIndex; + + for (int i = 0; i < itemCount; ++i) { + bool active = i == activeIndex; + list.add(Container( + key: Key("pagination_$i"), + margin: EdgeInsets.all(space), + child: ClipOval( + child: Container( + color: active ? activeColor : color, + width: active ? activeSize : size, + height: active ? activeSize : size, + ), + ), + )); + } + + if (config.scrollDirection == Axis.vertical) { + return Column( + key: key, + mainAxisSize: MainAxisSize.min, + children: list, + ); + } else { + return Row( + key: key, + mainAxisSize: MainAxisSize.min, + children: list, + ); + } + } +} + +typedef SwiperPaginationBuilder = Widget Function( + BuildContext context, SwiperPluginConfig config); + +class SwiperCustomPagination extends SwiperPlugin { + final SwiperPaginationBuilder builder; + + const SwiperCustomPagination({required this.builder}); + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + return builder(context, config); + } +} + +class SwiperPagination extends SwiperPlugin { + /// dot style pagination + static const SwiperPlugin dots = DotSwiperPaginationBuilder(); + + /// fraction style pagination + static const SwiperPlugin fraction = FractionPaginationBuilder(); + + /// round rect style pagination + static const SwiperPlugin rect = RectSwiperPaginationBuilder(); + + /// Alignment.bottomCenter by default when scrollDirection== Axis.horizontal + /// Alignment.centerRight by default when scrollDirection== Axis.vertical + final Alignment? alignment; + + /// Distance between pagination and the container + final EdgeInsetsGeometry margin; + + /// Build the widget + final SwiperPlugin builder; + + final Key? key; + + const SwiperPagination({ + this.alignment, + this.key, + this.margin = const EdgeInsets.all(10.0), + this.builder = SwiperPagination.dots, + }); + + @override + Widget build(BuildContext context, SwiperPluginConfig config) { + Alignment defaultAlignment = config.scrollDirection == Axis.horizontal + ? Alignment.bottomCenter + : Alignment.centerRight; + Widget child = Container( + margin: margin, + child: builder.build(context, config), + ); + if (!config.outer!) { + child = Align( + key: key, + alignment: alignment ?? defaultAlignment, + child: child, + ); + } + return child; + } +} diff --git a/package/flutter_swiper_view/lib/src/swiper_plugin.dart b/package/flutter_swiper_view/lib/src/swiper_plugin.dart new file mode 100644 index 00000000..1a6fe003 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/swiper_plugin.dart @@ -0,0 +1,52 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; + +/// plugin to display swiper components +/// +abstract class SwiperPlugin { + const SwiperPlugin(); + + Widget build(BuildContext context, SwiperPluginConfig config); +} + +class SwiperPluginConfig { + final Axis scrollDirection; + final AxisDirection? axisDirection; + final SwiperController controller; + final int activeIndex; + final int itemCount; + final PageIndicatorLayout? indicatorLayout; + final bool loop; + final bool? outer; + final PageController? pageController; + final SwiperLayout? layout; + + const SwiperPluginConfig({ + required this.scrollDirection, + required this.controller, + required this.activeIndex, + required this.itemCount, + this.axisDirection, + this.indicatorLayout, + this.outer, + this.pageController, + this.layout, + this.loop = false, + }); +} + +class SwiperPluginView extends StatelessWidget { + final SwiperPlugin plugin; + final SwiperPluginConfig config; + + const SwiperPluginView({ + Key? key, + required this.plugin, + required this.config, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return plugin.build(context, config); + } +} diff --git a/package/flutter_swiper_view/lib/src/transformer_page_view.dart b/package/flutter_swiper_view/lib/src/transformer_page_view.dart new file mode 100644 index 00000000..a970b3f2 --- /dev/null +++ b/package/flutter_swiper_view/lib/src/transformer_page_view.dart @@ -0,0 +1,595 @@ +import 'package:flutter/widgets.dart'; + +import 'index_controller.dart'; + +/// +/// NOTICE:: +/// +/// In order to make package smaller,currently we're not supporting any build-in page transformers +/// You can find build in transforms here: +/// +/// +/// + +const int kMaxValue = 2000000000; +const int kMiddleValue = 1000000000; + +/// Default auto play transition duration (in millisecond) +const int kDefaultTransactionDuration = 300; + +class TransformInfo { + /// The `width` of the `TransformerPageView` + final double? width; + + /// The `height` of the `TransformerPageView` + final double? height; + + /// The `position` of the widget pass to [PageTransformer.transform] + /// A `position` describes how visible the widget is. + /// The widget in the center of the screen' which is full visible, position is 0.0. + /// The widge in the left ,may be hidden, of the screen's position is less than 0.0, -1.0 when out of the screen. + /// The widge in the right ,may be hidden, of the screen's position is greater than 0.0, 1.0 when out of the screen + /// + /// + final double? position; + + /// The `index` of the widget pass to [PageTransformer.transform] + final int? index; + + /// The `activeIndex` of the PageView + final int? activeIndex; + + /// The `activeIndex` of the PageView, from user start to swipe + /// It will change when user end drag + final int fromIndex; + + /// Next `index` is greater than this `index` + final bool? forward; + + /// User drag is done. + final bool? done; + + /// Same as [TransformerPageView.viewportFraction] + final double? viewportFraction; + + /// Copy from [TransformerPageView.scrollDirection] + final Axis? scrollDirection; + + TransformInfo({ + this.index, + this.position, + this.width, + this.height, + this.activeIndex, + required this.fromIndex, + this.forward, + this.done, + this.viewportFraction, + this.scrollDirection, + }); +} + +abstract class PageTransformer { + /// + final bool reverse; + + PageTransformer({this.reverse = false}); + + /// Return a transformed widget, based on child and TransformInfo + Widget transform(Widget child, TransformInfo info); +} + +typedef PageTransformerBuilderCallback = Widget Function( + Widget child, TransformInfo info); + +class PageTransformerBuilder extends PageTransformer { + final PageTransformerBuilderCallback builder; + + PageTransformerBuilder({bool reverse = false, required this.builder}) + : super(reverse: reverse); + + @override + Widget transform(Widget child, TransformInfo info) { + return builder(child, info); + } +} + +class TransformerPageController extends PageController { + final bool loop; + final int itemCount; + final bool reverse; + + TransformerPageController({ + int initialPage = 0, + bool keepPage = true, + double viewportFraction = 1.0, + this.loop = false, + this.itemCount = 0, + this.reverse = false, + }) : super( + initialPage: TransformerPageController._getRealIndexFromRenderIndex( + initialPage, loop, itemCount, reverse), + keepPage: keepPage, + viewportFraction: viewportFraction, + ); + + int getRenderIndexFromRealIndex(num index) { + return _getRenderIndexFromRealIndex(index, loop, itemCount, reverse); + } + + int? getRealItemCount() { + if (itemCount == 0) return 0; + return loop ? itemCount + kMaxValue : itemCount; + } + + static int _getRenderIndexFromRealIndex( + num index, + bool loop, + int itemCount, + bool reverse, + ) { + if (itemCount == 0) return 0; + int renderIndex; + if (loop) { + renderIndex = (index - kMiddleValue).toInt(); + renderIndex = renderIndex % itemCount; + if (renderIndex < 0) { + renderIndex += itemCount; + } + } else { + renderIndex = index.toInt(); + } + if (reverse) { + renderIndex = itemCount - renderIndex - 1; + } + + return renderIndex; + } + + double get realPage => super.page ?? 0.0; + + static double? _getRenderPageFromRealPage( + double page, + bool loop, + int itemCount, + bool reverse, + ) { + double? renderPage; + if (loop) { + renderPage = page - kMiddleValue; + renderPage = renderPage % itemCount; + if (renderPage < 0) { + renderPage += itemCount; + } + } else { + renderPage = page; + } + if (reverse) { + renderPage = itemCount - renderPage - 1; + } + + return renderPage; + } + + @override + double? get page { + return loop + ? _getRenderPageFromRealPage(realPage, loop, itemCount, reverse) + : realPage; + } + + int getRealIndexFromRenderIndex(num index) { + return _getRealIndexFromRenderIndex(index, loop, itemCount, reverse); + } + + static int _getRealIndexFromRenderIndex( + num index, bool loop, int itemCount, bool reverse) { + int result = reverse ? itemCount - index - 1 as int : index as int; + if (loop) { + result += kMiddleValue; + } + return result; + } +} + +class TransformerPageView extends StatefulWidget { + /// Create a `transformed` widget base on the widget that has been passed to the [PageTransformer.transform]. + /// See [TransformInfo] + /// + final PageTransformer? transformer; + + /// Same as [PageView.scrollDirection] + /// + /// Defaults to [Axis.horizontal]. + final Axis scrollDirection; + + /// Same as [PageView.physics] + final ScrollPhysics? physics; + + /// Set to false to disable page snapping, useful for custom scroll behavior. + /// Same as [PageView.pageSnapping] + final bool pageSnapping; + + /// Called whenever the page in the center of the viewport changes. + /// Same as [PageView.onPageChanged] + final ValueChanged? onPageChanged; + + final IndexedWidgetBuilder? itemBuilder; + + // See [IndexController.mode],[IndexController.next],[IndexController.previous] + final IndexController? controller; + + /// Animation duration + final Duration duration; + + /// Animation curve + final Curve curve; + + final TransformerPageController? pageController; + + /// Set true to open infinity loop mode. + final bool loop; + + /// This value is only valid when `pageController` is not set, + final int itemCount; + + /// This value is only valid when `pageController` is not set, + final double viewportFraction; + + /// If not set, it is controlled by this widget. + final int? index; + + final bool allowImplicitScrolling; + + /// Creates a scrollable list that works page by page using widgets that are + /// created on demand. + /// + /// This constructor is appropriate for page views with a large (or infinite) + /// number of children because the builder is called only for those children + /// that are actually visible. + /// + /// Providing a non-null [itemCount] lets the [PageView] compute the maximum + /// scroll extent. + /// + /// [itemBuilder] will be called only with indices greater than or equal to + /// zero and less than [itemCount]. + const TransformerPageView({ + Key? key, + this.index, + Duration? duration, + this.curve = Curves.ease, + this.viewportFraction = 1.0, + required this.loop, + this.scrollDirection = Axis.horizontal, + this.physics, + this.pageSnapping = true, + this.onPageChanged, + this.controller, + this.transformer, + this.allowImplicitScrolling = false, + this.itemBuilder, + this.pageController, + required this.itemCount, + }) : assert(itemCount == 0 || itemBuilder != null || transformer != null), + duration = duration ?? + const Duration(milliseconds: kDefaultTransactionDuration), + super(key: key); + + factory TransformerPageView.children({ + Key? key, + int? index, + Duration? duration, + Curve curve = Curves.ease, + double viewportFraction = 1.0, + bool loop = false, + Axis scrollDirection = Axis.horizontal, + ScrollPhysics? physics, + bool pageSnapping = true, + ValueChanged? onPageChanged, + IndexController? controller, + PageTransformer? transformer, + bool allowImplicitScrolling = false, + required List children, + TransformerPageController? pageController, + }) { + return TransformerPageView( + itemCount: children.length, + itemBuilder: (context, index) { + return children[index]; + }, + pageController: pageController, + transformer: transformer, + pageSnapping: pageSnapping, + key: key, + index: index, + loop: loop, + duration: duration, + curve: curve, + viewportFraction: viewportFraction, + scrollDirection: scrollDirection, + physics: physics, + allowImplicitScrolling: allowImplicitScrolling, + onPageChanged: onPageChanged, + controller: controller, + ); + } + + @override + State createState() => _TransformerPageViewState(); + + static int getRealIndexFromRenderIndex({ + required bool reverse, + int index = 0, + int itemCount = 0, + required bool loop, + }) { + int initPage = reverse ? (itemCount - index - 1) : index; + if (loop) { + initPage += kMiddleValue; + } + return initPage; + } + + static PageController createPageController({ + required bool reverse, + int index = 0, + int itemCount = 0, + required bool loop, + required double viewportFraction, + }) { + return PageController( + initialPage: getRealIndexFromRenderIndex( + reverse: reverse, + index: index, + itemCount: itemCount, + loop: loop, + ), + viewportFraction: viewportFraction, + ); + } +} + +class _TransformerPageViewState extends State { + Size? _size; + int _activeIndex = 0; + late double _currentPixels; + bool _done = false; + + ///This value will not change until user end drag. + late int _fromIndex; + + PageTransformer? _transformer; + + late TransformerPageController _pageController; + + Widget _buildItemNormal(BuildContext context, int index) { + int renderIndex = _pageController.getRenderIndexFromRealIndex(index); + return widget.itemBuilder!(context, renderIndex); + } + + Widget _buildItem(BuildContext context, int index) { + return AnimatedBuilder( + animation: _pageController, + builder: (c, w) { + int renderIndex = _pageController.getRenderIndexFromRealIndex(index); + Widget child = widget.itemBuilder?.call(context, renderIndex) ?? + const SizedBox.shrink(); + if (_size == null) { + return child; + } + + double position; + + double page = _pageController.realPage; + + if (_transformer!.reverse) { + position = page - index; + } else { + position = index - page; + } + position *= widget.viewportFraction; + + TransformInfo info = TransformInfo( + index: renderIndex, + width: _size!.width, + height: _size!.height, + position: position.clamp(-1.0, 1.0), + activeIndex: + _pageController.getRenderIndexFromRealIndex(_activeIndex), + fromIndex: _fromIndex, + forward: _pageController.position.pixels - _currentPixels >= 0, + done: _done, + scrollDirection: widget.scrollDirection, + viewportFraction: widget.viewportFraction, + ); + return _transformer!.transform(child, info); + }); + } + + double? _calcCurrentPixels() { + _currentPixels = _pageController.getRenderIndexFromRealIndex(_activeIndex) * + _pageController.position.viewportDimension * + widget.viewportFraction; + + // print("activeIndex:$_activeIndex , pix:$_currentPixels"); + + return _currentPixels; + } + + @override + Widget build(BuildContext context) { + IndexedWidgetBuilder builder = + _transformer == null ? _buildItemNormal : _buildItem; + Widget child = PageView.builder( + allowImplicitScrolling: widget.allowImplicitScrolling, + itemBuilder: builder, + itemCount: _pageController.getRealItemCount(), + onPageChanged: _onIndexChanged, + controller: _pageController, + scrollDirection: widget.scrollDirection, + physics: widget.physics, + pageSnapping: widget.pageSnapping, + reverse: _pageController.reverse, + ); + if (_transformer == null) { + return child; + } + return NotificationListener( + onNotification: (notification) { + if (notification is ScrollStartNotification) { + _calcCurrentPixels(); + _done = false; + _fromIndex = _activeIndex; + } else if (notification is ScrollEndNotification) { + _calcCurrentPixels(); + _fromIndex = _activeIndex; + _done = true; + } + + return false; + }, + child: child, + ); + } + + void _onIndexChanged(int index) { + _activeIndex = index; + widget.onPageChanged + ?.call(_pageController.getRenderIndexFromRealIndex(index)); + } + + void _onGetSize(Duration _) { + if (!mounted) return; + Size? size; + RenderObject? renderObject = context.findRenderObject(); + if (renderObject != null) { + Rect bounds = renderObject.paintBounds; + size = bounds.size; + } + _calcCurrentPixels(); + onGetSize(size); + } + + void onGetSize(Size? size) { + if (mounted) { + setState(() { + _size = size; + }); + } + } + + IndexController? _controller; + + @override + void initState() { + _transformer = widget.transformer; + // int index = widget.index ?? 0; + _pageController = widget.pageController ?? + TransformerPageController( + initialPage: widget.index ?? 0, + itemCount: widget.itemCount, + loop: widget.loop, + reverse: widget.transformer?.reverse ?? false, + ); + // int initPage = _getRealIndexFromRenderIndex(index); + // _pageController = PageController(initialPage: initPage,viewportFraction: widget.viewportFraction); + _fromIndex = _activeIndex = _pageController.initialPage; + + _controller = widget.controller; + _controller?.addListener(onChangeNotifier); + super.initState(); + } + + @override + void didUpdateWidget(TransformerPageView oldWidget) { + _transformer = widget.transformer; + int index = widget.index ?? 0; + bool created = false; + if (_pageController != widget.pageController) { + if (widget.pageController != null) { + _pageController = widget.pageController!; + } else { + created = true; + _pageController = TransformerPageController( + initialPage: widget.index ?? 0, + itemCount: widget.itemCount, + loop: widget.loop, + reverse: widget.transformer?.reverse ?? false, + ); + } + } + + if (_pageController.getRenderIndexFromRealIndex(_activeIndex) != index) { + _fromIndex = _activeIndex = _pageController.initialPage; + if (!created) { + int initPage = _pageController.getRealIndexFromRenderIndex(index); + if (_pageController.hasClients) { + _pageController.animateToPage( + initPage, + duration: widget.duration, + curve: widget.curve, + ); + } + } + } + if (_transformer != null) { + WidgetsBinding.instance.addPostFrameCallback(_onGetSize); + } + + if (_controller != widget.controller) { + _controller?.removeListener(onChangeNotifier); + _controller = widget.controller; + _controller?.addListener(onChangeNotifier); + } + super.didUpdateWidget(oldWidget); + } + + @override + void didChangeDependencies() { + if (_transformer != null) { + WidgetsBinding.instance.addPostFrameCallback(_onGetSize); + } + super.didChangeDependencies(); + } + + void onChangeNotifier() { + final controller = widget.controller!; + final event = controller.event; + int index; + if (event == null) return; + if (event is MoveIndexControllerEvent) { + index = _pageController.getRealIndexFromRenderIndex(event.newIndex); + } else if (event is StepBasedIndexControllerEvent) { + index = event.calcNextIndex( + currentIndex: _activeIndex, + itemCount: _pageController.itemCount, + loop: _pageController.loop, + reverse: _pageController.reverse, + ); + } else { + //ignore other events + return; + } + if (_pageController.hasClients) { + if (event.animation) { + _pageController + .animateToPage( + index, + duration: widget.duration, + curve: widget.curve, + ) + .whenComplete(event.complete); + } else { + event.complete(); + } + } else { + event.complete(); + } + } + + @override + void dispose() { + _controller?.removeListener(onChangeNotifier); + super.dispose(); + } +} diff --git a/package/flutter_swiper_view/pubspec.yaml b/package/flutter_swiper_view/pubspec.yaml new file mode 100644 index 00000000..a9de80ee --- /dev/null +++ b/package/flutter_swiper_view/pubspec.yaml @@ -0,0 +1,20 @@ +name: flutter_swiper_view +description: The best swiper(carousel) for flutter, with multiple layouts, infinite loop. Compatible with Android & iOS. +version: 1.1.8 +homepage: https://github.com/feicien/flutter_swiper_view + +dependencies: + flutter: + sdk: flutter + + +environment: + sdk: '>=2.12.0 <3.0.0' + flutter: ">=1.17.0" + +dev_dependencies: + flutter_lints: ^2.0.1 + flutter_test: + sdk: flutter + +flutter: diff --git a/package/flutter_swiper_view/test/control_test.dart b/package/flutter_swiper_view/test/control_test.dart new file mode 100644 index 00000000..7766de47 --- /dev/null +++ b/package/flutter_swiper_view/test/control_test.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Control horizontal', (WidgetTester tester) async { + SwiperController controller = SwiperController(); + + SwiperPluginConfig config = SwiperPluginConfig( + activeIndex: 0, + controller: controller, + itemCount: 10, + loop: true, + scrollDirection: Axis.horizontal); + + Key key = UniqueKey(); + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: Builder(builder: (BuildContext context) { + return SwiperControl(key: key).build(context, config); + })), + )); + + expect(find.byKey(key), findsOneWidget); + + bool first = true; + + await tester.tap(find.byWidgetPredicate((Widget widget) { + if (widget is GestureDetector && first) { + first = false; + return true; + } + + return false; + })); + }); + + testWidgets('Control vertical', (WidgetTester tester) async { + SwiperController controller = SwiperController(); + + SwiperPluginConfig config = SwiperPluginConfig( + activeIndex: 0, + controller: controller, + itemCount: 10, + loop: true, + scrollDirection: Axis.vertical); + + Key key = UniqueKey(); + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: Builder(builder: (BuildContext context) { + return SwiperControl( + key: key, color: Colors.white, disableColor: Colors.black87) + .build(context, config); + })), + )); + + expect(find.byKey(key), findsOneWidget); + + bool first = true; + + await tester.tap(find.byWidgetPredicate((Widget widget) { + if (widget is GestureDetector && first) { + first = false; + return true; + } + + return false; + })); + }); +} diff --git a/package/flutter_swiper_view/test/flutter_swiper_test.dart b/package/flutter_swiper_view/test/flutter_swiper_test.dart new file mode 100644 index 00000000..4e26daca --- /dev/null +++ b/package/flutter_swiper_view/test/flutter_swiper_test.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Default Swiper', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MaterialApp( + home: Swiper( + itemBuilder: (context, index) { + return const Text("0"); + }, + itemCount: 10))); + + expect(find.text("0", skipOffstage: false), findsOneWidget); + }); + + testWidgets('Default Swiper loop:false', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MaterialApp( + home: Swiper( + onTap: (int inde) {}, + itemBuilder: (context, index) { + return const Text("0"); + }, + itemCount: 10, + loop: false, + ))); + + expect(find.text("0", skipOffstage: true), findsOneWidget); + }); + + testWidgets('Create Swiper with children', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MaterialApp( + home: Swiper.children( + children: const [Text("0"), Text("1")], + ))); + + expect(find.text("0", skipOffstage: false), findsOneWidget); + }); + + testWidgets('Create Swiper with list', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(MaterialApp( + home: Swiper.list( + list: const ["0", "1"], + builder: (BuildContext context, dynamic data, int index) { + return Text(data); + }, + ))); + + expect(find.text("0", skipOffstage: false), findsOneWidget); + }); + + testWidgets('Swiper with default plugins', (WidgetTester tester) async { + // Build our app and trigger a frame. + SwiperController controller = SwiperController(); + await tester.pumpWidget(MaterialApp( + home: Swiper( + controller: controller, + itemBuilder: (context, index) { + return const Text("0"); + }, + itemCount: 10, + pagination: const SwiperPagination(), + control: const SwiperControl(), + ))); + + expect(find.text("0", skipOffstage: false), findsOneWidget); + }); + + const List titles = [ + "Flutter Swiper is awosome", + "Really nice", + "Yeap" + ]; + + testWidgets('Customize pagination', (WidgetTester tester) async { + // Build our app and trigger a frame. + SwiperController controller = SwiperController(); + await tester.pumpWidget(MaterialApp( + home: Swiper( + controller: controller, + itemBuilder: (context, index) { + return const Text("0"); + }, + itemCount: 10, + pagination: SwiperCustomPagination( + builder: (BuildContext context, SwiperPluginConfig config) { + return ConstrainedBox( + constraints: const BoxConstraints.expand(height: 50.0), + child: Row( + children: [ + Text( + "${titles[config.activeIndex]} ${config.activeIndex + 1}/${config.itemCount}", + style: const TextStyle(fontSize: 20.0), + ), + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: const DotSwiperPaginationBuilder( + color: Colors.black12, + activeColor: Colors.black, + size: 10.0, + activeSize: 20.0) + .build(context, config), + ), + ) + ], + ), + ); + }), + control: const SwiperControl(), + ))); + + controller.startAutoplay(); + + controller.stopAutoplay(); + + await controller.move(0, animation: false); + await controller.move(0, animation: false); + + await controller.next(animation: false); + await controller.previous(animation: false); + + expect(find.text("0", skipOffstage: false), findsOneWidget); + }); + + testWidgets('Swiper fraction', (WidgetTester tester) async { + // Build our app and trigger a frame. + SwiperController controller = SwiperController(); + await tester.pumpWidget(MaterialApp( + home: Swiper( + controller: controller, + itemBuilder: (context, index) { + return const Text("0"); + }, + itemCount: 10, + pagination: const SwiperPagination(builder: SwiperPagination.fraction), + control: const SwiperControl(), + ))); + + expect(find.text("0", skipOffstage: false), findsOneWidget); + }); + + testWidgets('Zero itemCount', (WidgetTester tester) async { + // Build our app and trigger a frame. + SwiperController controller = SwiperController(); + await tester.pumpWidget(MaterialApp( + home: Swiper( + controller: controller, + itemBuilder: (context, index) { + return const Text("0"); + }, + itemCount: 0, + pagination: const SwiperPagination(builder: SwiperPagination.fraction), + control: const SwiperControl(), + ))); + + expect(find.text("0", skipOffstage: false), findsNothing); + }); +} diff --git a/package/flutter_swiper_view/test/layout_test.dart b/package/flutter_swiper_view/test/layout_test.dart new file mode 100644 index 00000000..1120101c --- /dev/null +++ b/package/flutter_swiper_view/test/layout_test.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('STACK', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Swiper( + layout: SwiperLayout.STACK, + itemWidth: 300.0, + itemHeight: 200.0, + itemBuilder: (context, index) { + return Container( + color: Colors.grey, + child: Center( + child: Text("$index"), + ), + ); + }, + itemCount: 10))); + }); + + testWidgets('TINDER', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Swiper( + layout: SwiperLayout.TINDER, + itemWidth: 300.0, + itemHeight: 200.0, + itemBuilder: (context, index) { + return Container( + color: Colors.grey, + child: Center( + child: Text("$index"), + ), + ); + }, + itemCount: 10))); + }); + + testWidgets('DEFAULT', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + home: Swiper( + layout: SwiperLayout.DEFAULT, + viewportFraction: 0.8, + scale: 0.9, + itemBuilder: (context, index) { + return Container( + color: Colors.grey, + child: Center( + child: Text("$index"), + ), + ); + }, + itemCount: 10))); + }); + + testWidgets('CUSTOM', (WidgetTester tester) async { + CustomLayoutOption customLayoutOption; + customLayoutOption = CustomLayoutOption(startIndex: -1, stateCount: 3) + ..addRotate([-45.0 / 180, 0.0, 45.0 / 180]) + ..addTranslate([ + const Offset(-370.0, -40.0), + const Offset(0.0, 0.0), + const Offset(370.0, -40.0) + ]); + await tester.pumpWidget(MaterialApp( + home: Swiper( + layout: SwiperLayout.CUSTOM, + itemWidth: 300.0, + itemHeight: 200.0, + customLayoutOption: customLayoutOption, + itemBuilder: (context, index) { + return Container( + color: Colors.grey, + child: Center( + child: Text("$index"), + ), + ); + }, + itemCount: 10))); + }); +} diff --git a/package/flutter_swiper_view/test/pagination_test.dart b/package/flutter_swiper_view/test/pagination_test.dart new file mode 100644 index 00000000..f34551ab --- /dev/null +++ b/package/flutter_swiper_view/test/pagination_test.dart @@ -0,0 +1,129 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_view/flutter_swiper_view.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Pagination', (WidgetTester tester) async { + SwiperController controller = SwiperController(); + + SwiperPluginConfig config = SwiperPluginConfig( + activeIndex: 0, + controller: controller, + itemCount: 10, + scrollDirection: Axis.horizontal); + + Key key = UniqueKey(); + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: Builder(builder: (BuildContext context) { + return DotSwiperPaginationBuilder( + key: key, + activeColor: const Color(0xff000000), + color: const Color(0xffffffff), + space: 10.0, + size: 10.0, + activeSize: 20.0) + .build(context, config); + })), + )); + + for (int i = 0; i < 10; ++i) { + expect(find.byWidgetPredicate((Widget widget) { + if (widget.key != null && + widget.key is ValueKey && + (widget.key as ValueKey).value == 'pagination_$i') return true; + + return false; + }), findsOneWidget); + } + + expect(find.byKey(key), findsOneWidget); + }); + + testWidgets('Pagination vertical', (WidgetTester tester) async { + SwiperController controller = SwiperController(); + + SwiperPluginConfig config = SwiperPluginConfig( + activeIndex: 0, + controller: controller, + itemCount: 10, + scrollDirection: Axis.vertical); + + Key key = UniqueKey(); + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: Builder(builder: (BuildContext context) { + return DotSwiperPaginationBuilder( + key: key, + activeColor: const Color(0xff000000), + color: const Color(0xffffffff), + space: 10.0, + size: 10.0, + activeSize: 20.0) + .build(context, config); + })), + )); + + for (int i = 0; i < 10; ++i) { + expect(find.byWidgetPredicate((Widget widget) { + if (widget.key != null && + widget.key is ValueKey && + (widget.key as ValueKey).value == 'pagination_$i') return true; + + return false; + }), findsOneWidget); + } + + expect(find.byKey(key), findsOneWidget); + }); + + testWidgets('Pagination fraction', (WidgetTester tester) async { + SwiperController controller = SwiperController(); + + SwiperPluginConfig config = SwiperPluginConfig( + activeIndex: 0, + controller: controller, + itemCount: 10, + scrollDirection: Axis.horizontal); + + Key key = UniqueKey(); + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: Builder(builder: (BuildContext context) { + return FractionPaginationBuilder( + key: key, + activeColor: const Color(0xff000000), + color: const Color(0xffffffff), + ).build(context, config); + })), + )); + + expect(find.text("1"), findsOneWidget); + expect(find.text(" / 10"), findsOneWidget); + + expect(find.byKey(key), findsOneWidget); + }); + + testWidgets('Pagination fraction vertical', (WidgetTester tester) async { + SwiperController controller = SwiperController(); + + SwiperPluginConfig config = SwiperPluginConfig( + activeIndex: 0, + controller: controller, + itemCount: 10, + scrollDirection: Axis.vertical); + + Key key = UniqueKey(); + await tester.pumpWidget(MaterialApp( + home: Scaffold(body: Builder(builder: (BuildContext context) { + return FractionPaginationBuilder( + key: key, + activeColor: const Color(0xff000000), + color: const Color(0xffffffff), + ).build(context, config); + })), + )); + + expect(find.text("1"), findsOneWidget); + expect(find.text("10"), findsOneWidget); + + expect(find.byKey(key), findsOneWidget); + }); +} diff --git a/pubspec.yaml b/pubspec.yaml index 3b112673..833c5f4c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,8 +2,8 @@ name: hmg_patient_app_new description: "New HMG Patient App" publish_to: 'none' # Remove this line if you wish to publish to pub.dev -#version: 0.0.15+12 -version: 0.0.1+16 +version: 0.0.16+13 +#version: 0.0.1+16 environment: sdk: ">=3.6.0 <4.0.0" @@ -84,7 +84,8 @@ dependencies: keyboard_actions: ^4.2.0 path_provider: ^2.0.8 open_filex: ^4.7.0 - flutter_swiper_view: ^1.1.8 + flutter_swiper_view: + path: package/flutter_swiper_view flutter_callkit_incoming: ^3.0.0 in_app_update: ^4.2.5 # device_preview: ^1.3.1 @@ -126,6 +127,19 @@ flutter: - assets/animations/lottie/ fonts: + - family: CairoArabic + fonts: + - asset: assets/fonts/cairo/Cairo-ExtraBold.ttf + weight: 700 + - asset: assets/fonts/cairo/Cairo-Bold.ttf + weight: 600 + - asset: assets/fonts/cairo/Cairo-SemiBold.ttf + weight: 500 + - asset: assets/fonts/cairo/Cairo-Medium.ttf + weight: 400 + - asset: assets/fonts/cairo/Cairo-Regular.ttf + weight: 300 + - family: Poppins fonts: # - asset: assets/fonts/poppins/Poppins-Black.ttf @@ -148,7 +162,7 @@ flutter: # weight: 100 - - family: GESSTwo + - family: GESSTwo22 fonts: - asset: assets/fonts/gess_two/GE_SS_Two_Bold.otf weight: 600