import ' dart:async ' ;
import ' dart:developer ' ;
import ' dart:ui ' as ui ;
import ' package:clarity_flutter/clarity_flutter.dart ' ;
import ' package:easy_localization/easy_localization.dart ' ;
import ' package:flutter/material.dart ' ;
import ' package:flutter_staggered_animations/flutter_staggered_animations.dart ' ;
import ' package:flutter_swiper_view/flutter_swiper_view.dart ' ;
import ' package:get_it/get_it.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/cache_consts.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/int_extensions.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/features/authentication/authentication_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/hospital/hospital_selection_view_model.dart ' ;
import ' package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart ' ;
import ' package:hmg_patient_app_new/features/insurance/insurance_view_model.dart ' ;
import ' package:hmg_patient_app_new/features/my_appointments/appointment_rating_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/features/notifications/notifications_view_model.dart ' ;
import ' package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart ' ;
import ' package:hmg_patient_app_new/features/paytabs/paytabs_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/appointments/appointment_queue_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart ' ;
import ' package:hmg_patient_app_new/presentation/authentication/login.dart ' ;
import ' package:hmg_patient_app_new/presentation/authentication/quick_login.dart ' ;
import ' package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/contact_us/contact_us.dart ' ;
import ' package:hmg_patient_app_new/presentation/hmg_services/services_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/app_update_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/widgets/lakum_wallet_card.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart ' ;
import ' package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart ' ;
import ' package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart ' ;
import ' package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/my_family/my_family.dart ' ;
import ' package:hmg_patient_app_new/presentation/notifications/notifications_list_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/offers_and_discounts/offers_and_discounts_page.dart ' ;
import ' package:hmg_patient_app_new/presentation/offers_and_discounts/widgets/offers_and_discounts.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 ' ;
import ' package:hmg_patient_app_new/services/navigation_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 ' ;
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/routes/custom_page_route.dart ' ;
import ' package:hmg_patient_app_new/widgets/user_avatar_widget.dart ' ;
import ' package:lottie/lottie.dart ' ;
import ' package:provider/provider.dart ' ;
import ' package:smooth_corner/smooth_corner.dart ' ;
import ' ../emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart ' ;
class LandingPage extends StatefulWidget {
const LandingPage ( { super . key } ) ;
@ override
State < LandingPage > createState ( ) = > _LandingPageState ( ) ;
}
class _LandingPageState extends State < LandingPage > {
late final AuthenticationViewModel authVM ;
bool isDone = false ;
late final HabibWalletViewModel habibWalletVM ;
late AppState appState ;
late MyAppointmentsViewModel myAppointmentsViewModel ;
final CacheService cacheService = GetIt . instance < CacheService > ( ) ;
late AppointmentRatingViewModel appointmentRatingViewModel ;
late InsuranceViewModel insuranceViewModel ;
late ImmediateLiveCareViewModel immediateLiveCareViewModel ;
late BookAppointmentsViewModel bookAppointmentsViewModel ;
late EmergencyServicesViewModel emergencyServicesViewModel ;
late TodoSectionViewModel todoSectionViewModel ;
late NotificationsViewModel notificationsViewModel ;
final SwiperController _controller = SwiperController ( ) ;
final ScrollController _horizontalScrollController = ScrollController ( ) ;
final ScrollController _scrollController = ScrollController ( ) ;
double _scrollOpacity = 0.0 ;
@ override
void dispose ( ) {
_horizontalScrollController . dispose ( ) ;
_scrollController . dispose ( ) ;
super . dispose ( ) ;
}
@ override
void initState ( ) {
Clarity . setCurrentScreenName ( ' Landing Page ' ) ;
authVM = context . read < AuthenticationViewModel > ( ) ;
habibWalletVM = context . read < HabibWalletViewModel > ( ) ;
appointmentRatingViewModel = context . read < AppointmentRatingViewModel > ( ) ;
authVM . savePushTokenToAppState ( ) ;
if ( mounted ) {
authVM . checkLastLoginStatus ( ( ) {
showQuickLogin ( context ) ;
} ) ;
}
_scrollController . addListener ( ( ) {
final scrollOffset = _scrollController . offset ;
final newOpacity = ( scrollOffset / 50 ) . clamp ( 0.0 , 1.0 ) ;
if ( _scrollOpacity ! = newOpacity ) {
setState ( ( ) {
_scrollOpacity = newOpacity ;
} ) ;
}
} ) ;
scheduleMicrotask ( ( ) async {
if ( ! appState . isAuthenticated ) {
// LoaderBottomSheet.showLoader();
await authVM . handleNewImeiRegistration ( isNavigate: false ) ;
}
if ( appState . isAuthenticated ) {
habibWalletVM . initHabibWalletProvider ( ) ;
habibWalletVM . getPatientBalanceAmount ( ) ;
habibWalletVM . getLakumAccountInformation ( ) ;
todoSectionViewModel . initializeTodoSectionViewModel ( ) ;
immediateLiveCareViewModel . initImmediateLiveCare ( ) ;
immediateLiveCareViewModel . getPatientLiveCareHistory ( ) ;
myAppointmentsViewModel . initAppointmentsViewModel ( ) ;
myAppointmentsViewModel . getPatientAppointments ( true , false ) ;
emergencyServicesViewModel . checkPatientERAdvanceBalance ( ) ;
// myAppointmentsViewModel.getPatientAppointmentQueueDetails();
notificationsViewModel . initNotificationsViewModel ( ) ;
insuranceViewModel . initInsuranceProvider ( ) ;
if ( ! appState . isRatedVisible ) {
appointmentRatingViewModel . getLastRatingAppointment (
onSuccess: ( response ) {
if ( appointmentRatingViewModel . appointmentRatedList . isNotEmpty ) {
final lastAppointment = appointmentRatingViewModel . appointmentRatedList . last ;
final appointmentNo = lastAppointment . appointmentNo ;
final projectID = lastAppointment . projectID ;
if ( appointmentNo = = null | | projectID = = null ) return ;
appointmentRatingViewModel . getAppointmentDetails (
appointmentNo ,
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 ( ) ;
}
@ override
Widget build ( BuildContext context ) {
bookAppointmentsViewModel = Provider . of < BookAppointmentsViewModel > ( context , listen: false ) ;
myAppointmentsViewModel = Provider . of < MyAppointmentsViewModel > ( context , listen: false ) ;
insuranceViewModel = Provider . of < InsuranceViewModel > ( context , listen: false ) ;
immediateLiveCareViewModel = Provider . of < ImmediateLiveCareViewModel > ( context , listen: false ) ;
emergencyServicesViewModel = Provider . of < EmergencyServicesViewModel > ( context , listen: false ) ;
todoSectionViewModel = Provider . of < TodoSectionViewModel > ( context , listen: false ) ;
notificationsViewModel = Provider . of < NotificationsViewModel > ( context , listen: false ) ;
appState = getIt . get < AppState > ( ) ;
return PopScope (
canPop: false ,
child: Scaffold (
backgroundColor: AppColors . bgScaffoldColor ,
body: Consumer < InsuranceViewModel > ( builder: ( context , insuranceVM , child ) {
return Stack (
children: [
RefreshIndicator (
color: AppColors . primaryRedColor ,
onRefresh: ( ) async {
if ( appState . isAuthenticated ) {
// Refresh Appointments Data
myAppointmentsViewModel . setIsAppointmentDataToBeLoaded ( true ) ;
myAppointmentsViewModel . initAppointmentsViewModel ( ) ;
myAppointmentsViewModel . getPatientAppointments ( true , false ) ;
// Refresh Appointments Data
habibWalletVM . initHabibWalletProvider ( ) ;
habibWalletVM . getPatientBalanceAmount ( ) ;
habibWalletVM . getLakumAccountInformation ( ) ;
// Refresh Ancillary Orders
todoSectionViewModel . setIsAncillaryOrdersNeedReloading ( true ) ;
todoSectionViewModel . initializeTodoSectionViewModel ( ) ;
// Refresh Immediate LiveCare Data
immediateLiveCareViewModel . initImmediateLiveCare ( ) ;
immediateLiveCareViewModel . getPatientLiveCareHistory ( ) ;
// 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);
// }),
// );
// }
// },
// );
}
} ,
child: SingleChildScrollView (
controller: _scrollController ,
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: ( ) {
// ...existing navigation code...
Navigator . of ( context ) . push (
CustomPageRoute (
direction: AxisDirection . down ,
page: FamilyMedicalScreen ( ) ,
) ,
) ;
} ,
name: ( ' ${ appState . getAuthenticatedUser ( ) ! . firstName ! } ${ appState . getAuthenticatedUser ( ) ! . lastName ! } ' ) ,
imageWidget: Selector < ProfileSettingsViewModel , String ? > (
selector: ( _ , profileVM ) = > profileVM . profileImageData ? ? appState . getProfileImageData ,
shouldRebuild: ( previous , next ) = > previous ! = next ,
builder: ( context , profileImageData , child ) {
// Only rebuild when profile image data changes
return UserAvatarWidget (
key: ValueKey ( ' landing_avatar_ ${ profileImageData ? . hashCode ? ? 0 } ' ) ,
width: 42. w ,
height: 42. h ,
fit: BoxFit . cover ,
isCircular: true ,
customProfileImageData: profileImageData ,
) ;
} ,
) ,
) . expanded
: CustomButton (
text: LocaleKeys . loginOrRegister . tr ( context: context ) ,
onPressed: ( ) async {
await authVM . onLoginPressed ( ) ;
// Navigator.of(context).push(
// CustomPageRoute(
// // page: NotificationsListPage(),
// page: AppUpdatePage(
// appUpdateMessage:
// "A new update for Dr.Alhabib app is now available with new features and services.\r\nFor technical support:\r\neservices.hmg@drsulaimanalhabib.com"),
// ),
// );
// ...existing commented code...
} ,
backgroundColor: AppColors . secondaryLightRedColor ,
borderColor: AppColors . secondaryLightRedColor ,
textColor: AppColors . primaryRedColor ,
fontSize: 14. f ,
fontWeight: FontWeight . w700 ,
borderRadius: 12. r ,
padding: EdgeInsets . fromLTRB ( 12. h , 0 , 12. h , 0 ) ,
height: 40. h ,
) ,
Consumer < TodoSectionViewModel > ( 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 & & todoSectionVM . notificationsCount ! = null & & todoSectionVM . notificationsCount ! = " 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 ? ? " 0 " ,
style: TextStyle (
color: Colors . white ,
fontFamily: " Poppins " ,
fontSize: 10. f ,
fontWeight: FontWeight . w600 ,
) ,
textAlign: TextAlign . center ,
) ,
) ,
)
: SizedBox . shrink ( ) ,
] ) ,
SizedBox (
width: 24. w ,
) ,
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
? Row ( children: [
SizedBox ( width: 24. w ) ,
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 ( )
] ,
) ;
} ) ,
] ,
) . 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: [
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 ,
) ,
] ,
) . expanded
] ,
) ,
) ,
) . paddingSymmetrical ( 24. w , 0. h )
: SizedBox . shrink ( ) ,
// Offers And Discounts Carousel - Auto-scrolling from right to left
appState . isAuthenticated & & appState . isEnabledOffersAndDiscountsCarousel
? Column (
children: [
SizedBox ( height: 12. h ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
( " ${ LocaleKeys . offers . tr ( context: context ) } & ${ LocaleKeys . discount . tr ( context: context ) } " ) . toText16 ( isBold: true ) ,
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: OffersAndDiscountsPage ( ) ) ) ;
} ) ,
SizedBox ( height: 16. h ) ,
OffersAndDiscountsCarousel ( ) . paddingSymmetrical ( 24. h , 0. h ) ,
SizedBox ( height: 18. h ) ,
] ,
)
: SizedBox . shrink ( ) ,
appState . isAuthenticated
? Column (
children: [
SizedBox ( height: 12. h ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
LocaleKeys . appointmentsAndVisits . tr ( context: context ) . toText16 ( isBold: true ) ,
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 ( ( ) {
myAppointmentsViewModel . onTabChange ( 0 ) ;
myAppointmentsViewModel . updateListWRTTab ( 0 ) ;
Navigator . of ( context ) . push ( CustomPageRoute ( page: MyAppointmentsPage ( ) ) ) ;
// getIt.get<PayTabsViewModel>().setPaymentConfiguration("Appointment Payment", 15.5);
// getIt.get<PayTabsViewModel>().startCardPayment();
} ) ,
Consumer3 < MyAppointmentsViewModel , ImmediateLiveCareViewModel , TodoSectionViewModel > (
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: isTablet ? 290. h : 255. h ,
child: ListView . separated (
scrollDirection: Axis . horizontal ,
itemCount: myAppointmentsVM . patientAppointmentsHistoryList . length < 3
? myAppointmentsVM . patientAppointmentsHistoryList . length
: 3 ,
shrinkWrap: true ,
padding: EdgeInsets . only ( left: 16. h , right: 16. h ) ,
itemBuilder: ( context , index ) {
return SizedBox (
height: isTablet ? 290. h : 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: Builder (
builder: ( context ) {
final int swiperItemCount = myAppointmentsVM . isMyAppointmentsLoading
? 3
: myAppointmentsVM . patientAppointmentsHistoryList . length < 3
? myAppointmentsVM . patientAppointmentsHistoryList . length
: 3 ;
final bool isArabic = appState . isArabic ( ) ;
return Swiper (
itemCount: swiperItemCount ,
layout: SwiperLayout . STACK ,
loop: false ,
itemWidth: MediaQuery . of ( context ) . size . width - 48. h ,
indicatorLayout: PageIndicatorLayout . COLOR ,
axisDirection: isArabic ? AxisDirection . right : AxisDirection . left ,
controller: _controller ,
index: isArabic ? swiperItemCount - 1 : 0 ,
itemHeight: 255. h + 20 ,
// extra space for shadow
pagination: SwiperPagination (
alignment: Alignment . bottomCenter ,
margin: EdgeInsets . only ( top: 220. h + 20 + 8 + 24 ) ,
builder: _DirectionalDotPaginationBuilder (
color: Color ( 0xffD9D9D9 ) ,
activeColor: AppColors . blackBgColor ,
isRTL: isArabic ,
totalCount: swiperItemCount ,
) ,
) ,
itemBuilder: ( BuildContext context , int index ) {
final int actualIndex = isArabic ? ( swiperItemCount - 1 - index ) : index ;
return Padding (
padding: const EdgeInsets . symmetric ( vertical: 10 ) ,
child: getIndexSwiperCard ( actualIndex ) ,
) ;
} ,
) ;
} ,
) ,
)
: immediateLiveCareVM . patientHasPendingLiveCareRequest
? _buildLiveCareRequestCard ( ) . paddingSymmetrical ( 24. h , 16. 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 < BookAppointmentsViewModel > ( ) . 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 ,
) ,
] ,
) ,
) ,
) . paddingSymmetrical ( 24. h , 16. h ) ;
} ,
) ,
// Consumer for ER Online Check-In pending request
// Consumer<EmergencyServicesViewModel>(
// 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<AppState>().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<EmergencyServicesViewModel>().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 . viewMedicalFileLandingPage . 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: MedicalFilePage ( ) ) ) ;
} ) ,
SizedBox ( height: 16. h ) ,
Consumer ( builder: ( BuildContext context , TodoSectionViewModel todoSectionVM , Widget ? child ) {
return Container (
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 ( ) ) ) ;
} ,
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 . shrink ( ) ,
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: 200 ) ,
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 ) = > 10. width ,
) . paddingSymmetrical ( 16. 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 ,
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 ) ,
] ,
) ,
) . paddingSymmetrical ( 24. h , 0. h ) ,
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
LocaleKeys . services2 . tr ( context: context ) . toText18 ( isBold: true ) ,
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 ) ,
] ,
) . onPress ( ( ) {
Navigator . of ( context ) . push ( CustomPageRoute ( page: ServicesPage ( ) ) ) ;
} ) ,
] ,
) . paddingSymmetrical ( 24. w , 0. h ) ,
ConstrainedBox (
constraints: BoxConstraints ( maxHeight: isFoldable ? 450. h : ( isTablet ? 440. h : 431. h ) , minHeight: 411. 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 ,
isPNG: LandingPageData . getServiceCardsList [ index ] . isPNG ,
) ,
) ,
) ,
) ;
} ,
separatorBuilder: ( BuildContext cxt , int index ) = > SizedBox ( width: 16. w ) ,
) ,
) ,
appState . isAuthenticated
? Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
LocaleKeys . myBalanceSubtitle . tr ( context: context ) . toText16 ( isBold: true ) . paddingSymmetrical ( 24. h , 0. h ) ,
SizedBox (
height: 175. h ,
child: ListView . separated (
scrollDirection: Axis . horizontal ,
itemCount: 2 ,
padding: EdgeInsetsDirectional . only ( start: 24. h , end: 24. h ) ,
itemBuilder: ( BuildContext context , int index ) {
return index = = 0 ? HabibWalletCard ( ) : LakumWalletCard ( ) ;
} ,
separatorBuilder: ( BuildContext context , int index ) = > SizedBox ( width: 12. h ) ,
) ,
) ,
] ,
)
: SizedBox ( ) ,
] ,
) ,
) ,
) ,
AnimatedOpacity (
opacity: _scrollOpacity ,
duration: Duration ( milliseconds: 200 ) ,
child: Container (
height: MediaQuery . paddingOf ( context ) . top + 10. h ,
decoration: BoxDecoration (
gradient: LinearGradient (
begin: Alignment . topCenter ,
end: Alignment . bottomCenter ,
colors: [
AppColors . scaffoldBgColor ,
AppColors . scaffoldBgColor . withValues ( alpha: 0.9 ) ,
AppColors . scaffoldBgColor . withValues ( alpha: 0.0 ) ,
] ,
stops: [ 0.0 , 0.7 , 1.0 ] ,
) ,
) ,
) ,
) ,
( appState . isAuthenticated & & ! insuranceVM . isInsuranceLoading & & insuranceVM . isInsuranceExpired & & insuranceVM . isInsuranceExpiryBannerShown )
? Container (
height: MediaQuery . paddingOf ( context ) . top + 50. h ,
decoration: ShapeDecoration (
color: AppColors . secondaryLightRedBorderColor ,
shape: SmoothRectangleBorder (
side: BorderSide ( width: 1 , color: AppColors . primaryRedColor . withAlpha ( 20 ) ) ,
// borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
smoothness: 1 ,
) ,
) ,
child: Column (
mainAxisAlignment: MainAxisAlignment . end ,
children: [
Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
LocaleKeys . insuranceExpiredOrInactive . tr ( context: context ) . toText14 ( color: AppColors . primaryRedColor , isBold: true ) . paddingSymmetrical ( 0. h , 0. h ) ,
Row (
children: [
CustomButton (
text: LocaleKeys . updateInsurance . tr ( context: context ) ,
onPressed: ( ) {
insuranceVM . setIsInsuranceUpdateDetailsLoading ( true ) ;
insuranceVM . getPatientInsuranceDetailsForUpdate (
appState . getAuthenticatedUser ( ) ! . patientId . toString ( ) , appState . getAuthenticatedUser ( ) ! . patientIdentificationNo . toString ( ) ) ;
showCommonBottomSheetWithoutHeight ( context ,
child: PatientInsuranceCardUpdateCard ( ) , callBackFunc: ( ) { } , title: " " , isCloseButtonVisible: false , isFullScreen: false ) ;
} ,
backgroundColor: AppColors . primaryRedColor ,
borderColor: AppColors . secondaryLightRedBorderColor ,
textColor: Colors . white ,
fontSize: 12. f ,
fontWeight: FontWeight . bold ,
borderRadius: 8. r ,
padding: EdgeInsets . fromLTRB ( 15 , 0 , 15 , 0 ) ,
height: 36. h ,
) . paddingSymmetrical ( 12. h , 0. h ) ,
Icon ( Icons . close , color: AppColors . primaryRedColor ) . onPress ( ( ) {
insuranceVM . setIsInsuranceExpiryBannerShown ( false ) ;
} ) ,
] ,
) ,
] ,
) ,
SizedBox (
height: 10. h ,
)
] ,
) . paddingSymmetrical ( 24. h , 0. h ) ,
)
: SizedBox . shrink ( )
] ,
) ;
} ) ,
) ,
) ;
}
// Main card selector based on index and priority
Widget getIndexSwiperCard ( int index ) {
// Index 0: Priority cards (Queue > LiveCare > Upcoming Appointment > History)
if ( index = = 0 ) {
return _buildFirstCardSlot ( ) ;
}
// Index 1: Secondary cards based on first card type
if ( index = = 1 ) {
return _buildSecondCardSlot ( ) ;
}
// Index 2: Tertiary cards
if ( index = = 2 ) {
return _buildThirdCardSlot ( ) ;
}
return Container ( ) ;
}
// First slot: Shows highest priority card
Widget _buildFirstCardSlot ( ) {
if ( myAppointmentsViewModel . isPatientHasQueueAppointment ) {
return _buildQueueCard ( ) ;
}
if ( immediateLiveCareViewModel . patientHasPendingLiveCareRequest ) {
return _buildLiveCareRequestCard ( ) ;
}
if ( myAppointmentsViewModel . patientUpcomingAppointmentsHistoryList . isNotEmpty ) {
return _buildAppointmentCardWrapper (
myAppointmentsViewModel . patientUpcomingAppointmentsHistoryList . first ,
) ;
}
return _buildAppointmentCardWrapper (
myAppointmentsViewModel . patientAppointmentsHistoryList [ 0 ] ,
) ;
}
// Second slot: Shows appointment or ancillary orders
Widget _buildSecondCardSlot ( ) {
final hasQueueOrLiveCare = myAppointmentsViewModel . isPatientHasQueueAppointment | | immediateLiveCareViewModel . patientHasPendingLiveCareRequest ;
if ( hasQueueOrLiveCare ) {
return _buildAppointmentCardWrapper (
myAppointmentsViewModel . patientAppointmentsHistoryList . first ,
) ;
}
// if (todoSectionViewModel.patientAncillaryOrdersList.isNotEmpty) {
// return _buildAncillaryOrderCardWrapper(
// todoSectionViewModel.patientAncillaryOrdersList.first,
// );
// }
// Safety check: ensure list has at least 2 items
if ( myAppointmentsViewModel . patientAppointmentsHistoryList . length > 1 ) {
return _buildAppointmentCardWrapper (
myAppointmentsViewModel . patientAppointmentsHistoryList [ 1 ] ,
) ;
}
return Container ( ) ; // Return empty container if not enough items
}
// Third slot: Shows ancillary orders or appointment
Widget _buildThirdCardSlot ( ) {
final hasQueueOrLiveCare = myAppointmentsViewModel . isPatientHasQueueAppointment | | immediateLiveCareViewModel . patientHasPendingLiveCareRequest ;
final hasAncillaryOrders = todoSectionViewModel . patientAncillaryOrdersList . isNotEmpty ;
if ( hasQueueOrLiveCare & & hasAncillaryOrders ) {
// return _buildAncillaryOrderCardWrapper(
// todoSectionViewModel.patientAncillaryOrdersList.first,
// );
}
// Safety check: ensure list has at least 3 items
if ( myAppointmentsViewModel . patientAppointmentsHistoryList . length > 2 ) {
return _buildAppointmentCardWrapper (
myAppointmentsViewModel . patientAppointmentsHistoryList [ 2 ] ,
) ;
}
return Container ( ) ; // Return empty container if not enough items
}
// Queue Card Widget
Widget _buildQueueCard ( ) {
final currentQueue = myAppointmentsViewModel . currentPatientQueueDetails ;
final currentStatus = myAppointmentsViewModel . currentQueueStatus ;
return Container (
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration (
color: AppColors . whiteColor ,
borderRadius: 20. h ,
hasShadow: true ,
side: BorderSide (
color: Utils . getCardBorderColor ( currentStatus ) ,
width: 2. w ,
) ,
) ,
child: Padding (
padding: EdgeInsets . all ( 16. h ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
_buildQueueHeader ( currentStatus ) ,
SizedBox ( height: 8. h ) ,
_buildQueueGreeting ( ) ,
SizedBox ( height: 2. h ) ,
LocaleKeys . thankYouForPatience . tr ( ) . toText12 (
isBold: true ,
color: AppColors . textColorLight ,
) ,
SizedBox ( height: 8. h ) ,
( currentQueue . queueNo ? ? " IM-123 " ) . toText28 ( isBold: true ) . toShimmer2 ( isShow: myAppointmentsViewModel . patientQueueDetailsList . isEmpty ) ,
SizedBox ( height: 6. h ) ,
_buildServingNowSection ( ) ,
SizedBox ( height: 5. h ) ,
_buildQueueActionButton ( currentStatus , currentQueue . roomNo ? ? " " ) . toShimmer2 ( isShow: myAppointmentsViewModel . patientQueueDetailsList . isEmpty ) ,
] ,
) ,
) ,
) . onPress ( ( ) {
Navigator . of ( context ) . push (
CustomPageRoute ( page: AppointmentQueuePage ( ) ) ,
) ;
} ) ;
}
// Queue Card: Header with status chip and icon
Widget _buildQueueHeader ( int currentStatus ) {
return Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
AppCustomChipWidget (
labelText: currentStatus = = 0 ? LocaleKeys . inQueue . tr ( ) : LocaleKeys . yourTurn . tr ( ) ,
backgroundColor: Utils . getCardBorderColor ( currentStatus ) . withValues ( alpha: 0.20 ) ,
textColor: Utils . getCardBorderColor ( currentStatus ) ,
) . toShimmer2 ( isShow: myAppointmentsViewModel . patientQueueDetailsList . isEmpty ) ,
Container (
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration (
color: AppColors . greyColor ,
borderRadius: 10. h ,
hasShadow: false ,
) ,
padding: EdgeInsets . all ( 6. h ) ,
child: Lottie . asset ( AppAnimations . hourGlass , repeat: true , reverse: false , frameRate: FrameRate ( 60 ) , width: 40. h , height: 40. h , fit: BoxFit . fill ) ) ,
] ,
) ;
}
// Queue Card: Greeting text
Widget _buildQueueGreeting ( ) {
return LocaleKeys . halaFirstName . tr ( namedArgs: { ' firstName ' : appState . getAuthenticatedUser ( ) ! . firstName ! } ) . toText16 ( isBold: true ) ;
}
// Queue Card: Currently serving section
Widget _buildServingNowSection ( ) {
if ( myAppointmentsViewModel . patientQueueDetailsList . isEmpty ) {
return SizedBox ( height: 24. h ) ;
}
final servingQueue = myAppointmentsViewModel . patientQueueDetailsList . first ;
final isVitalCall = servingQueue . callType = = 1 ;
return Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
" ${ LocaleKeys . servingNow . tr ( ) } : " . toText14 ( isBold: true ) ,
Row (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
servingQueue . queueNo ! . toText12 ( isBold: true ) ,
SizedBox ( width: 8. w ) ,
AppCustomChipWidget (
deleteIcon: isVitalCall ? AppAssets . call_for_vitals : AppAssets . call_for_doctor ,
labelText: isVitalCall ? LocaleKeys . callForVitalSigns . tr ( ) : LocaleKeys . callForDoctor . tr ( ) ,
iconColor: isVitalCall ? AppColors . primaryRedColor : AppColors . successColor ,
textColor: isVitalCall ? AppColors . primaryRedColor : AppColors . successColor ,
iconSize: 14. w ,
backgroundColor: isVitalCall ? AppColors . primaryRedColor . withValues ( alpha: 0.1 ) : AppColors . successColor . withValues ( alpha: 0.1 ) ,
labelPadding: EdgeInsetsDirectional . only (
start: 8. h ,
end: - 2. h ,
) ,
) ,
] ,
) ,
] ,
) ;
}
// Queue Card: Action button
Widget _buildQueueActionButton ( int currentStatus , String roomNo ) {
return CustomButton (
text: Utils . getCardButtonText ( currentStatus , roomNo ) ,
onPressed: ( ) { } ,
backgroundColor: Utils . getCardButtonColor ( currentStatus ) ,
borderColor: Utils . getCardButtonColor ( currentStatus ) . withValues ( alpha: 0.01 ) ,
textColor: Utils . getCardButtonTextColor ( currentStatus ) ,
fontSize: 12. f ,
fontWeight: FontWeight . w600 ,
borderRadius: 12. r ,
padding: EdgeInsets . symmetric ( horizontal: 10. w ) ,
height: 40. h ,
iconColor: AppColors . whiteColor ,
iconSize: 18. h ,
) ;
}
// LiveCare Request Card Widget
Widget _buildLiveCareRequestCard ( ) {
final liveCareData = immediateLiveCareViewModel . patientLiveCareHistoryList [ 0 ] ;
return Column (
children: [
SizedBox ( height: 12. h ) ,
Container (
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration (
color: AppColors . whiteColor ,
borderRadius: 20. r ,
hasShadow: true ,
// side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
) ,
child: Padding (
padding: EdgeInsets . all ( 16. h ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
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: 14. 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 . warningColorYellow . withValues ( alpha: 0.01 ) ,
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(),
] ,
) ,
) ,
) . paddingSymmetrical ( 0. h , 0. h ) . onPress ( ( ) {
Navigator . of ( context ) . push (
CustomPageRoute ( page: ImmediateLiveCarePendingRequestPage ( ) ) ,
) ;
} ) ,
SizedBox ( height: 12. h ) ,
] ,
) ;
}
// LiveCare Card: Header with status and date
Widget _buildLiveCareHeader ( liveCareData ) {
return Row (
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
children: [
Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
AppCustomChipWidget (
labelText: liveCareData . 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 ( liveCareData . arrivalTime ) ,
false ,
) ,
) ,
SizedBox ( width: 8. w ) ,
AppCustomChipWidget (
richText: ValueListenableBuilder < Duration > (
valueListenable: immediateLiveCareViewModel . durationNotifier ,
builder: ( context , duration , child ) {
final hours = duration . inHours . toString ( ) . padLeft ( 2 , ' 0 ' ) ;
final minutes = duration . inMinutes . remainder ( 60 ) . toString ( ) . padLeft ( 2 , ' 0 ' ) ;
final seconds = duration . inSeconds . remainder ( 60 ) . toString ( ) . padLeft ( 2 , ' 0 ' ) ;
return Column (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
' $ hours : $ minutes : $ seconds ' . toText10 ( isBold: true ) ,
] ,
) ;
} ,
) ,
// labelText: DateUtil.formatDateToDate(
// DateUtil.convertStringToDate(liveCareData.arrivalTime),
// false,
// ),
) ,
] ,
) ,
Container (
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration (
color: AppColors . greyColor ,
borderRadius: 10. h ,
hasShadow: false ,
) ,
padding: EdgeInsets . all ( 6. h ) ,
child: Lottie . asset ( AppAnimations . hourGlass , repeat: true , reverse: false , frameRate: FrameRate ( 60 ) , width: 40. h , height: 40. h , fit: BoxFit . fill ) ) ,
// Utils.buildSvgWithAssets(
// icon: AppAssets.waiting_icon,
// width: 24.h,
// height: 24.h,
// ),
] ,
) ;
}
// LiveCare Card: Greeting
Widget _buildLiveCareGreeting ( ) {
return LocaleKeys . halaFirstName . tr (
namedArgs: { ' firstName ' : appState . getAuthenticatedUser ( ) ! . firstName ! } ,
context: context ,
) . toText16 ( isBold: true ) ;
}
// LiveCare Card: Queue position info
Widget _buildLiveCareQueueInfo ( int ? patCount ) {
return LocaleKeys . yourTurnIsAfterPatients . tr ( namedArgs: { ' count ' : ( patCount ? ? 0 ) . toString ( ) } , context: context ) . toText14 ( isBold: true ) ;
}
// LiveCare Card: Waiting time counter
Widget _buildLiveCareWaitingTime ( ) {
return Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
" ${ LocaleKeys . waitingTime . tr ( ) } : " . toText12 ( isBold: true ) ,
SizedBox ( height: 7. h ) ,
ValueListenableBuilder < Duration > (
valueListenable: immediateLiveCareViewModel . durationNotifier ,
builder: ( context , duration , child ) {
return Column (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
buildTime ( duration ) ,
] ,
) ;
} ,
) ,
] ,
) ;
}
// Appointment Card Wrapper (reusable)
Widget _buildAppointmentCardWrapper ( appointment ) {
return Container (
decoration: RoundedRectangleBorder ( ) . toSmoothCornerDecoration ( color: AppColors . whiteColor , borderRadius: 24. r , hasShadow: true , hasDenseShadow: true ) ,
child: AppointmentCard (
patientAppointmentHistoryResponseModel: appointment ,
myAppointmentsViewModel: myAppointmentsViewModel ,
bookAppointmentsViewModel: bookAppointmentsViewModel ,
isLoading: false ,
isFromHomePage: true ,
) ,
) ;
}
// Ancillary Order Card Wrapper (reusable)
Widget _buildAncillaryOrderCardWrapper ( order ) {
return AncillaryOrderCard (
order: order ,
isLoading: false ,
isOrdersList: false ,
onCheckIn: ( ) {
log ( " Check-in for order: ${ order . orderNo } " ) ;
} ,
onViewDetails: ( ) {
Navigator . of ( context ) . push (
CustomPageRoute (
page: AncillaryOrderDetailsList (
appointmentNoVida: order . appointmentNo ? ? 0 ,
orderNo: order . orderNo ? ? 0 ,
projectID: order . projectID ? ? 0 ,
projectName: order . projectName ? ? " " ,
setupID: order . setupID ? ? " " ,
) ,
) ,
) ;
} ,
) ;
}
void showQuickLogin ( BuildContext context ) {
showCommonBottomSheetWithoutHeight (
getIt < NavigationService > ( ) . navigatorKey . currentContext ! ,
// title: "",
isCloseButtonVisible: false ,
child: StatefulBuilder (
builder: ( context , setState ) {
return QuickLogin (
isDone: isDone ,
onPressed: ( ) {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
authVM . loginWithFingerPrintFace ( ( ) async {
isDone = true ;
cacheService . saveBool ( key: CacheConst . quickLoginEnabled , value: true ) ;
setState ( ( ) { } ) ;
await Future . delayed ( Duration ( milliseconds: 2000 ) ) . then ( ( value ) {
if ( mounted ) Navigator . pop ( context ) ;
} ) ;
} ) ;
} ,
) ;
} ,
) ,
// height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
isFullScreen: false ,
callBackFunc: ( ) {
isDone = true ;
setState ( ( ) { } ) ;
} ,
) ;
}
openLastRating ( ) {
showCommonBottomSheetWithoutHeight (
context ,
titleWidget: Selector < AppointmentRatingViewModel , String ? > (
selector: ( _ , vm ) = > vm . title ,
builder: ( context , title , child ) {
final displayTitle = title ? ? ' ' ;
return Column (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
displayTitle . toText20 ( isBold: true ) ,
( context . select < AppointmentRatingViewModel , String ? > ( ( vm ) = > vm . subTitle ) ? ? ' ' ) . toText12 ( ) ,
] ,
) ;
} ,
) ,
isCloseButtonVisible: true ,
child: StatefulBuilder (
builder: ( context , setState ) {
return RateAppointmentDoctor ( ) ;
} ,
) ,
isFullScreen: false ,
) ;
}
void openIndoorNavigationBottomSheet ( BuildContext context ) {
showCommonBottomSheetWithoutHeight (
title: LocaleKeys . selectHospital . tr ( ) ,
context ,
child: ChangeNotifierProvider (
create: ( context ) = > HospitalSelectionBottomSheetViewModel ( getIt ( ) ) ,
child: Consumer < HospitalSelectionBottomSheetViewModel > (
builder: ( _ , vm , __ ) = > HospitalBottomSheetBody (
searchText: vm . searchController ,
displayList: vm . displayList ,
onFacilityClicked: ( value ) {
vm . setSelectedFacility ( value ) ;
vm . getDisplayList ( ) ;
} ,
onHospitalClicked: ( hospital ) {
Navigator . pop ( context ) ;
vm . openPenguin ( hospital ) ;
} ,
onHospitalSearch: ( value ) {
vm . searchHospitals ( value ? ? " " ) ;
} ,
selectedFacility: vm . selectedFacility ,
hmcCount: vm . hmcCount ,
hmgCount: vm . hmgCount ,
) ,
) ,
) ,
isFullScreen: false ,
isCloseButtonVisible: true ,
hasBottomPadding: false ,
backgroundColor: AppColors . bottomSheetBgColor ,
callBackFunc: ( ) {
context . read < EmergencyServicesViewModel > ( ) . clearSearchText ( ) ;
} ,
) ;
}
}
/// A custom pagination builder that reverses the active-dot index
/// when the UI is in RTL (Arabic) mode, so the dots visually match
/// the swipe direction.
class _DirectionalDotPaginationBuilder extends SwiperPlugin {
final Color color ;
final Color activeColor ;
final double size ;
final double activeSize ;
final double space ;
final bool isRTL ;
final int totalCount ;
const _DirectionalDotPaginationBuilder ( {
required this . color ,
required this . activeColor ,
required this . isRTL ,
required this . totalCount ,
this . size = 10.0 ,
this . activeSize = 10.0 ,
this . space = 3.0 ,
} ) ;
@ override
Widget build ( BuildContext context , SwiperPluginConfig config ) {
return Row (
mainAxisSize: MainAxisSize . min ,
textDirection: ui . TextDirection . ltr , // always lay out dots left-to-right
children: List . generate ( config . itemCount , ( i ) {
final bool isActive = i = = config . activeIndex ;
return Container (
margin: EdgeInsets . symmetric ( horizontal: space ) ,
width: isActive ? activeSize : size ,
height: isActive ? activeSize : size ,
decoration: BoxDecoration (
color: isActive ? activeColor : color ,
shape: BoxShape . circle ,
) ,
) ;
} ) ,
) ;
}
}