diff --git a/lib/core/utils/size_utils.dart b/lib/core/utils/size_utils.dart index 0be751e..6566641 100644 --- a/lib/core/utils/size_utils.dart +++ b/lib/core/utils/size_utils.dart @@ -11,23 +11,108 @@ extension ResponsiveExtension on num { double get _screenHeight => SizeUtils.height; - /// Scale text size + /// Check if device is likely a foldable + bool get _isFoldable { + double aspectRatio = _screenWidth / _screenHeight; + // Foldable devices typically have aspect ratios close to 1:1 when unfolded + return (aspectRatio > 0.9 && aspectRatio < 1.1) && (_screenWidth > 700 || _screenHeight > 700); + } + + /// Scale text size - enhanced for foldable devices double get f { double aspectRatio = _screenWidth / _screenHeight; double scale = (_screenWidth < _screenHeight ? _screenWidth : _screenHeight) / figmaDesignWidth; - double clamp = (aspectRatio > 1.3 || aspectRatio < 0.77) ? 1.6 : 1.2; + + // Enhanced clamping for different device types + double clamp; + if (SizeUtils.deviceType == DeviceType.tablet || _isFoldable) { + // More conservative scaling for tablets and foldables + clamp = (aspectRatio > 1.5 || aspectRatio < 0.67) ? 1.4 : 1.1; + } else { + // Original logic for phones + clamp = (aspectRatio > 1.3 || aspectRatio < 0.77) ? 1.6 : 1.2; + } + if (scale > clamp) scale = clamp; return this * scale; } - /// Scale horizontally (width-based) - double get w => (this * _screenWidth) / figmaDesignWidth; + /// Scale horizontally (width-based) - enhanced for foldable devices + double get w { + double baseScale = (this * _screenWidth) / figmaDesignWidth; - /// Scale vertically (height-based) - double get h => (this * _screenHeight) / figmaDesignHeight; + if (_isFoldable) { + // For foldables, use more conservative width scaling + double scale = _screenWidth / figmaDesignWidth; + scale = scale.clamp(0.8, 1.4); + return this * scale; + } - //radius - double get r => (this * _screenWidth) / figmaDesignWidth; + return baseScale; + } + + /// Scale vertically (height-based) - enhanced for foldable devices + double get h { + double baseScale = (this * _screenHeight) / figmaDesignHeight; + + if (_isFoldable) { + // For foldables, use height-based scaling but with constraints + double scale = (_screenHeight / figmaDesignHeight).clamp(0.8, 1.4); + return this * scale; + } + + return baseScale; + } + + /// Radius - enhanced for foldable devices + double get r { + double baseScale = (this * _screenWidth) / figmaDesignWidth; + + if (_isFoldable) { + // Use the same logic as enhanced width for foldables + double scale = _screenWidth / figmaDesignWidth; + scale = scale.clamp(0.8, 1.4); + return this * scale; + } + + return baseScale; + } + + // New enhanced getters (additional options) + + /// Enhanced font scaling with device-specific adjustments + double get fh { + double baseScale = _screenHeight / figmaDesignHeight; + + if (_isFoldable) { + // Special handling for foldable devices - use more conservative scaling + baseScale = baseScale.clamp(0.8, 1.3); + } else if (SizeUtils.deviceType == DeviceType.tablet) { + // Tablet-specific scaling + baseScale = baseScale.clamp(0.9, 1.5); + } else { + // Phone scaling + baseScale = baseScale.clamp(0.8, 1.8); + } + + return this * baseScale; + } + + /// Adaptive scaling - automatically chooses best scaling method + double get adaptive { + if (_isFoldable) { + return fh; + } else if (SizeUtils.deviceType == DeviceType.tablet) { + return f * 0.9; // Slightly smaller for tablets + } + return f; + } + + /// Minimum size constraint (useful for touch targets) + double get minSize { + double scaled = adaptive; + return scaled < 44 ? 44 : scaled; // Minimum 44pt for accessibility + } /// Optional: direct accessors for full width/height static double get screenWidth => SizeUtils.width; @@ -129,6 +214,7 @@ class SizeUtils { } log("longerSide: $longerSide"); + log("shorterSide: $shorterSide"); log("isTablet: $isTablet"); } } @@ -139,3 +225,9 @@ bool get isTablet => SizeUtils.deviceType == DeviceType.tablet; bool get isMobile => SizeUtils.deviceType == DeviceType.mobile; bool get isDesktop => SizeUtils.deviceType == DeviceType.desktop; + +bool get isFoldable { + double aspectRatio = SizeUtils.width / SizeUtils.height; + // Foldable devices typically have aspect ratios close to 1:1 when unfolded + return (aspectRatio > 0.9 && aspectRatio < 1.1) && (SizeUtils.width > 700 || SizeUtils.height > 700); +} diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index a88d9c2..491aa49 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -633,17 +633,19 @@ class Utils { required String url, required Color iconColor, bool isDisabled = false, - double width = 24, - double height = 24, + double? width, + double? height, }) { + final iconH = height ?? 24.h; + final iconW = width ?? 24.w; return SvgPicture.network( url, colorFilter: ColorFilter.mode( isDisabled ? iconColor.withOpacity(0.5) : iconColor, BlendMode.srcIn, ), - width: width, - height: height, + width: iconW, + height: iconH, ); } @@ -662,7 +664,7 @@ class Utils { return Container( decoration: BoxDecoration( border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, - borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 0) : null, + borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, ), child: Image.asset(icon, width: iconW, height: iconH, fit: fit), ); @@ -670,17 +672,27 @@ class Utils { } /// Widget to build an SVG from network - static Widget buildImgWithNetwork({required String url, required Color iconColor, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover, ImageErrorWidgetBuilder? errorBuilder}) { + static Widget buildImgWithNetwork({ + required String url, + required Color iconColor, + bool isDisabled = false, + double? width, + double? height, + BoxFit fit = BoxFit.cover, + ImageErrorWidgetBuilder? errorBuilder, + }) { + final iconH = height ?? 24.h; + final iconW = width ?? 24.w; return Image.network( url, - width: width, - height: height, + width: iconW, + height: iconH, fit: fit, - errorBuilder: errorBuilder??(_,__,___){ - //todo change the error builder icon that it is returning - return Utils.buildSvgWithAssets(width: width, - height: height,icon: AppAssets.no_visit_icon); - }, + errorBuilder: errorBuilder ?? + (_, __, ___) { + //todo change the error builder icon that it is returning + return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon); + }, ); } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 030770a..26220fc 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -167,16 +167,17 @@ extension EmailValidator on String { decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText14( - {Color? color, - bool isUnderLine = false, - bool isBold = false, - bool isCenter = false, - FontWeight? weight, - int? maxlines, - double? letterSpacing = 0, - double? height, - TextOverflow? textOverflow}) => + Widget toText14({ + Color? color, + bool isUnderLine = false, + bool isBold = false, + bool isCenter = false, + FontWeight? weight, + int? maxlines, + double? letterSpacing = 0, + double? height, + TextOverflow? textOverflow, + }) => Text( this, textAlign: isCenter ? TextAlign.center : null, diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart index 8d9adec..7481bf2 100644 --- a/lib/presentation/appointments/my_appointments_page.dart +++ b/lib/presentation/appointments/my_appointments_page.dart @@ -16,18 +16,16 @@ import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/AppointmentFilter.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart'; -import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart'; -import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; +import 'package:hmg_patient_app_new/widgets/date_range_selector/date_range_calender.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; -import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; -import '../../widgets/common_bottom_sheet.dart' - show showCommonBottomSheetWithoutHeight; +import '../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; class MyAppointmentsPage extends StatefulWidget { const MyAppointmentsPage({super.key}); @@ -86,18 +84,14 @@ class _MyAppointmentsPageState extends State { } Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) { - return getAppointList( - myAppointmentsVM, myAppointmentsVM.filteredAppointmentList); + return getAppointList(myAppointmentsVM, myAppointmentsVM.filteredAppointmentList); } - Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, - List filteredAppointmentList) { + Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, List filteredAppointmentList) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Visibility( - visible: myAppointmentsVM.availableFilters.isNotEmpty, - child: getAppointmentFilters(myAppointmentsVM)), + Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)), ListView.separated( padding: EdgeInsets.only(top: 24.h), shrinkWrap: true, @@ -110,14 +104,9 @@ class _MyAppointmentsPageState extends State { itemBuilder: (context, index) { return myAppointmentsVM.isMyAppointmentsLoading ? Container( - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: AppointmentCard( - patientAppointmentHistoryResponseModel: - PatientAppointmentHistoryResponseModel(), + patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), myAppointmentsViewModel: myAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: true, @@ -134,16 +123,10 @@ class _MyAppointmentsPageState extends State { child: AnimatedContainer( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.h, - hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), child: AppointmentCard( - patientAppointmentHistoryResponseModel: - filteredAppointmentList[index], - myAppointmentsViewModel: - myAppointmentsViewModel, + patientAppointmentHistoryResponseModel: filteredAppointmentList[index], + myAppointmentsViewModel: myAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: false, @@ -154,8 +137,7 @@ class _MyAppointmentsPageState extends State { ) : Utils.getNoDataWidget( context, - noDataText: "You don't have any appointments yet." - .needTranslation, + noDataText: "You don't have any appointments yet.".needTranslation, callToActionButton: CustomButton( text: LocaleKeys.bookAppo.tr(context: context), onPressed: () { @@ -168,18 +150,17 @@ class _MyAppointmentsPageState extends State { backgroundColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA), textColor: Color(0xffED1C2B), - fontSize: 14, + fontSize: 14.f, fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), + height: 40.h, icon: AppAssets.add_icon, iconColor: AppColors.primaryRedColor, ).paddingSymmetrical(48.h, 0.h), ); }, - separatorBuilder: (BuildContext cxt, int index) => - SizedBox(height: 16.h), + separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h), ), SizedBox(height: 24.h), ], @@ -189,45 +170,41 @@ class _MyAppointmentsPageState extends State { Widget getAppointmentFilters(MyAppointmentsViewModel myAppointmentsVM) { return SizedBox( child: Row( - children: [ - Expanded( - child: ListView.separated( - separatorBuilder: (_, index) => SizedBox( - width: 8.h, - ), - scrollDirection: Axis.horizontal, - itemCount: myAppointmentsVM.availableFilters.length, - itemBuilder: (_, index) => AppointmentFilters( - selectedFilter: myAppointmentsVM.selectedFilter, - item: myAppointmentsVM.availableFilters[index], - onClicked: () { - if (myAppointmentsVM.availableFilters[index] == - AppointmentListingFilters.DATESELECTION) { - showCommonBottomSheetWithoutHeight( - title: "Set The Date Range".needTranslation, - context, - child: DateRangeSelector( - onRangeSelected: (start, end) { - // if (start != null) { - myAppointmentsVM.getSelectedDateRange( - start, end); - // } - }, - ), - isFullScreen: false, - isCloseButtonVisible: true, - callBackFunc: () {}, - ); - } else { - myAppointmentsVM.setSelectedFilter( - myAppointmentsVM.availableFilters[index]); - myAppointmentsVM.filterTheListAsPerSelection(); - } - }, - )), - ), - ], - )).paddingOnly(top: 24.h, left: 24.h, right: 24.h); + children: [ + Expanded( + child: ListView.separated( + separatorBuilder: (_, index) => SizedBox( + width: 8.h, + ), + scrollDirection: Axis.horizontal, + itemCount: myAppointmentsVM.availableFilters.length, + itemBuilder: (_, index) => AppointmentFilters( + selectedFilter: myAppointmentsVM.selectedFilter, + item: myAppointmentsVM.availableFilters[index], + onClicked: () { + if (myAppointmentsVM.availableFilters[index] == AppointmentListingFilters.DATESELECTION) { + showCommonBottomSheetWithoutHeight( + title: "Set The Date Range".needTranslation, + context, + child: DateRangeSelector( + onRangeSelected: (start, end) { + // if (start != null) { + myAppointmentsVM.getSelectedDateRange(start, end); + // } + }, + ), + isFullScreen: false, + isCloseButtonVisible: true, + callBackFunc: () {}, + ); + } else { + myAppointmentsVM.setSelectedFilter(myAppointmentsVM.availableFilters[index]); + myAppointmentsVM.filterTheListAsPerSelection(); + } + }, + )), + ), + ], + )).paddingOnly(top: 24.h, left: 24.h, right: 24.h); } } - diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 3c0f4f6..fe8f025 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -106,392 +106,388 @@ class _LandingPageState extends State { insuranceViewModel = Provider.of(context, listen: false); immediateLiveCareViewModel = Provider.of(context, listen: false); appState = getIt.get(); - return Scaffold( - backgroundColor: AppColors.bgScaffoldColor, - body: SingleChildScrollView( - padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24), - child: Column( - spacing: 16.h, - children: [ - Row( - spacing: 8.h, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - appState.isAuthenticated - ? WelcomeWidget( - onTap: () { - Navigator.of(context).push(springPageRoute(ProfileSettings())); - }, - name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), - imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, - ).expanded - : CustomButton( - text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () async { - await authVM.onLoginPressed(); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 14.f, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), - height: 40.h, - ), - Row( - mainAxisSize: MainAxisSize.min, - spacing: 12.h, - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - // page: LoginScreen(), - ), - ); - }), - Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 18.h, width: 18.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - // page: LoginScreen(), - ), - ); - }), - Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - // page: LoginScreen(), + return PopScope( + canPop: false, + child: Scaffold( + backgroundColor: AppColors.bgScaffoldColor, + body: SingleChildScrollView( + padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24), + child: Column( + spacing: 16.h, + children: [ + Row( + spacing: 8.h, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + appState.isAuthenticated + ? WelcomeWidget( + onTap: () { + Navigator.of(context).push(springPageRoute(ProfileSettings())); + }, + name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), + imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, + ).expanded + : CustomButton( + text: LocaleKeys.loginOrRegister.tr(context: context), + onPressed: () async { + await authVM.onLoginPressed(); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + height: 40.h, ), - ); - }), - ], - ) - ], - ).paddingSymmetrical(24.h, 0.h), - appState.isAuthenticated - ? Column( + Row( + mainAxisSize: MainAxisSize.min, + spacing: 12.h, children: [ - SizedBox(height: 12.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Appointments & Visits".toText16(isBold: true), - Row( - children: [ - LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], + Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: MedicalFilePage(), + // page: LoginScreen(), ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { + ); + }), + Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 18.h, width: 18.h).onPress(() { Navigator.of(context).push( CustomPageRoute( - page: MyAppointmentsPage(), + page: MedicalFilePage(), + // page: LoginScreen(), ), ); }), - SizedBox(height: 16.h), - Consumer(builder: (context, myAppointmentsVM, child) { - return myAppointmentsVM.isMyAppointmentsLoading - ? Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), - child: AppointmentCard( - patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), - myAppointmentsViewModel: myAppointmentsViewModel, - bookAppointmentsViewModel: bookAppointmentsViewModel, - isLoading: true, - isFromHomePage: true, - ), - ).paddingSymmetrical(24.h, 0.h) - : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty - ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 - ? Container( - decoration: - RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), - child: AppointmentCard( - patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, - myAppointmentsViewModel: myAppointmentsViewModel, - bookAppointmentsViewModel: bookAppointmentsViewModel, - isLoading: false, - isFromHomePage: true, - ), - ).paddingSymmetrical(24.h, 0.h) - : Swiper( - itemCount: myAppointmentsVM.isMyAppointmentsLoading - ? 3 - : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 - ? myAppointmentsVM.patientAppointmentsHistoryList.length - : 3, - layout: SwiperLayout.STACK, - loop: true, - itemWidth: MediaQuery.of(context).size.width - 48.h, - indicatorLayout: PageIndicatorLayout.COLOR, - axisDirection: AxisDirection.right, - controller: _controller, - itemHeight: 210 + 25, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - margin: EdgeInsets.only(top: 210 + 8 + 24), - builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), - ), - itemBuilder: (BuildContext context, int index) { - return Container( - decoration: RoundedRectangleBorder() - .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() { + Navigator.of(context).push( + CustomPageRoute( + page: MedicalFilePage(), + // page: LoginScreen(), + ), + ); + }), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h), + appState.isAuthenticated + ? Column( + children: [ + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Appointments & Visits".toText16(isBold: true), + Row( + children: [ + LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage())); + }), + SizedBox(height: 16.h), + Consumer( + builder: (context, myAppointmentsVM, child) { + return myAppointmentsVM.isMyAppointmentsLoading + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), + myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, + isLoading: true, + isFromHomePage: true, + ), + ).paddingSymmetrical(24.h, 0.h) + : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty + ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 + ? Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), child: AppointmentCard( - patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], + patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, myAppointmentsViewModel: myAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel, isLoading: false, isFromHomePage: true, ), - ); - }, - ) - : Container( - width: double.infinity, - decoration: - RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true), - child: Padding( - padding: EdgeInsets.all(12.h), - child: Column( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), - SizedBox(height: 12.h), - "You do not have any upcoming appointment. Please book an appointment".needTranslation.toText12(isCenter: true), - SizedBox(height: 12.h), - CustomButton( - text: LocaleKeys.bookAppo.tr(context: context), - onPressed: () { - Navigator.of(context).push( - CustomPageRoute( - page: BookAppointmentPage(), + ).paddingSymmetrical(24.h, 0.h) + : Swiper( + itemCount: myAppointmentsVM.isMyAppointmentsLoading + ? 3 + : myAppointmentsVM.patientAppointmentsHistoryList.length < 3 + ? myAppointmentsVM.patientAppointmentsHistoryList.length + : 3, + layout: SwiperLayout.STACK, + loop: true, + itemWidth: MediaQuery.of(context).size.width - 48.h, + indicatorLayout: PageIndicatorLayout.COLOR, + axisDirection: AxisDirection.right, + controller: _controller, + itemHeight: 210 + 25, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only(top: 210 + 8 + 24), + builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), + ), + itemBuilder: (BuildContext context, int index) { + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), + child: AppointmentCard( + patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], + myAppointmentsViewModel: myAppointmentsViewModel, + bookAppointmentsViewModel: bookAppointmentsViewModel, + isLoading: false, + isFromHomePage: true, ), ); }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 14.f, - fontWeight: FontWeight.w500, - padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), - icon: AppAssets.add_icon, - iconColor: AppColors.primaryRedColor, - ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h); - }), - Consumer(builder: (context, immediateLiveCareVM, child) { - return immediateLiveCareVM.patientHasPendingLiveCareRequest - ? Column( - children: [ - SizedBox(height: 12.h), - Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 20.h, - hasShadow: true, - side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), - ), - width: double.infinity, - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ) + : Container( + width: double.infinity, + decoration: + RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true), + child: Padding( + padding: EdgeInsets.all(12.h), + child: Column( children: [ - AppCustomChipWidget( - labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, - backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), - textColor: AppColors.alertColor, + Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), + SizedBox(height: 12.h), + "You do not have any upcoming appointment. Please book an appointment".needTranslation.toText12(isCenter: true), + SizedBox(height: 12.h), + CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage())); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14.f, + fontWeight: FontWeight.w500, + padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + height: (isFoldable || isTablet) ? 56.h : 46.h, ), - Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), - // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 40.h, height: 40.h, fit: BoxFit.contain), ], ), - SizedBox(height: 8.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + ), + ).paddingSymmetrical(24.h, 0.h); + }, + ), + Consumer( + builder: (context, immediateLiveCareVM, child) { + return immediateLiveCareVM.patientHasPendingLiveCareRequest + ? Column( + children: [ + SizedBox(height: 12.h), + Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.r, + hasShadow: true, + side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h), + ), + width: double.infinity, + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - "You have a pending LiveCare request".needTranslation.toText12(isBold: true), - Utils.buildSvgWithAssets( - icon: AppAssets.forward_arrow_icon_small, - iconColor: AppColors.blackColor, - width: 20.h, - height: 15.h, - fit: BoxFit.contain, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppCustomChipWidget( + labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, + backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), + textColor: AppColors.alertColor, + ), + Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h), + // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 40.h, height: 40.h, fit: BoxFit.contain), + ], + ), + SizedBox(height: 8.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "You have a pending LiveCare request".needTranslation.toText12(isBold: true), + Utils.buildSvgWithAssets( + icon: AppAssets.forward_arrow_icon_small, + iconColor: AppColors.blackColor, + width: 20.h, + height: 15.h, + fit: BoxFit.contain, + ), + ], ), ], ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: ImmediateLiveCarePendingRequestPage(), + ), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage())); + }), + SizedBox(height: 12.h), + ], + ) + : SizedBox(height: 12.h); + }, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Quick Links".needTranslation.toText16(isBold: true), + Row( + children: [ + "View medical file".needTranslation.toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], + ), + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage())); + }), + SizedBox(height: 16.h), + Container( + height: 120.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getLoggedInServiceCardsList[index].icon, + title: LandingPageData.getLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, + serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, + ), + ), ), ); - }), - SizedBox(height: 12.h), - ], - ) - : SizedBox(height: 12.h); - }), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Quick Links".needTranslation.toText16(isBold: true), - Row( - children: [ - "View medical file".needTranslation.toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + }, + separatorBuilder: (BuildContext cxt, int index) => 10.width, + ), + ), ], ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push( - CustomPageRoute( - page: MedicalFilePage(), - ), - ); - }), - SizedBox(height: 16.h), - Container( - height: 120.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: EdgeInsets.only(left: 16.h, right: 16.h), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getLoggedInServiceCardsList[index].icon, - title: LandingPageData.getLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, - serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, - ), + ).paddingSymmetrical(24.h, 0.h), + ], + ) + : Container( + height: 127.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 16.h, right: 16.h), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName, + icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, + title: LandingPageData.getNotLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, ), ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 10.width, - ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), - ], - ), - ).paddingSymmetrical(24.h, 0.h), + ), + ], + ), + ).paddingSymmetrical(24.h, 0.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Services".toText16(isBold: true), + Row( + children: [ + "View all services".toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), ], - ) - : Container( - height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24.r, - ), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: EdgeInsets.only(left: 16.h, right: 16.h), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName, - icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, - title: LandingPageData.getNotLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, - ), - ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, + ), + ], + ).paddingSymmetrical(24.h, 0.h), + SizedBox( + height: 340.h, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getServiceCardsList.length, + shrinkWrap: true, + padding: EdgeInsets.only(left: 24.w, right: 24.w), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: LargeServiceCard( + image: LandingPageData.getServiceCardsList[index].icon, + title: LandingPageData.getServiceCardsList[index].title, + subtitle: LandingPageData.getServiceCardsList[index].subtitle, + icon: LandingPageData.getServiceCardsList[index].largeCardIcon, ), ), - ], - ), - ).paddingSymmetrical(24.h, 0.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Services".toText16(isBold: true), - Row( - children: [ - "View all services".toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], - ), - ], - ).paddingSymmetrical(24.h, 0.h), - SizedBox( - height: 350.h, - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getServiceCardsList.length, - shrinkWrap: true, - padding: EdgeInsets.only(left: 24.w, right: 24.w), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: LargeServiceCard( - image: LandingPageData.getServiceCardsList[index].icon, - title: LandingPageData.getServiceCardsList[index].title, - subtitle: LandingPageData.getServiceCardsList[index].subtitle, - icon: LandingPageData.getServiceCardsList[index].largeCardIcon, - ), ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.w), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.w), + ), ), - ), - appState.isAuthenticated ? HabibWalletCard() : SizedBox(), - ], + appState.isAuthenticated ? HabibWalletCard() : SizedBox(), + ], + ), ), ), ); @@ -503,19 +499,21 @@ class _LandingPageState extends State { title: "", isCloseButtonVisible: false, - child: StatefulBuilder(builder: (context, setState) { - return QuickLogin( - isDone: isDone, - onPressed: () { - // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); - authVM.loginWithFingerPrintFace(() { - isDone = true; - cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); - setState(() {}); - }); - }, - ); - }), + child: StatefulBuilder( + builder: (context, setState) { + return QuickLogin( + isDone: isDone, + onPressed: () { + // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); + authVM.loginWithFingerPrintFace(() { + isDone = true; + cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); + setState(() {}); + }); + }, + ); + }, + ), // height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3, isFullScreen: false, callBackFunc: () { diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index af03168..3989523 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -26,40 +26,37 @@ class LargeServiceCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( + return Container( + width: 150.w, padding: EdgeInsets.symmetric(horizontal: 3.w), - child: Container( - width: 150.w, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.transparent, borderRadius: 16.r), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Image.asset(AppAssets.livecare_service, width: 220.w, height: 220.h, fit: BoxFit.contain), - SizedBox(height: 3.h), - Row( - children: [ - Utils.buildSvgWithAssets(icon: icon, width: 24.w, height: 24.h), - title.toText14(color: AppColors.blackColor, isBold: true), - ], - ), - subtitle.toText11(color: AppColors.blackColor), - SizedBox(height: 10.h), - CustomButton( - width: 150.w, - text: LocaleKeys.bookNow.tr(context: context), - onPressed: () {}, - backgroundColor: AppColors.borderOnlyColor, - borderColor: AppColors.borderOnlyColor, - textColor: AppColors.whiteColor, - fontSize: 14.f, - fontWeight: FontWeight.bold, - borderRadius: 12.r, - padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), - height: 40.h, - ), - ], - ), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.transparent, borderRadius: 16.r), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Image.asset(AppAssets.livecare_service, width: 220.w, fit: BoxFit.contain), + SizedBox(height: 10.h), + Row( + children: [ + Utils.buildSvgWithAssets(icon: icon, width: 24.w, height: 24.h), + Flexible(child: title.toText14(color: AppColors.blackColor, isBold: true, textOverflow: TextOverflow.clip, maxlines: 1)), + ], + ), + subtitle.toText11(color: AppColors.blackColor), + SizedBox(height: 10.h), + CustomButton( + text: LocaleKeys.bookNow.tr(context: context), + onPressed: () {}, + backgroundColor: AppColors.borderOnlyColor, + borderColor: AppColors.borderOnlyColor, + textColor: AppColors.whiteColor, + fontSize: 14.f, + fontWeight: FontWeight.bold, + borderRadius: 12.r, + padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), + height: 40.h, + ), + ], ), ); } diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 719e667..07bd7f3 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -221,12 +221,13 @@ class _MedicalFilePageState extends State { children: [ AppCustomChipWidget( labelText: "${appState.getAuthenticatedUser()!.age} Years Old", + labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w), ), AppCustomChipWidget( icon: AppAssets.blood_icon, - labelText: "Blood: ${appState.getUserBloodGroup}", + labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}", iconColor: AppColors.primaryRedColor, - labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), + labelPadding: EdgeInsetsDirectional.only(end: 8.w), ), Consumer(builder: (context, insuranceVM, child) { return AppCustomChipWidget( @@ -237,7 +238,7 @@ class _MedicalFilePageState extends State { iconSize: 12.w, backgroundColor: insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1), - labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), + labelPadding: EdgeInsetsDirectional.only(end: 8.w), ); }), ], @@ -377,7 +378,7 @@ class _MedicalFilePageState extends State { fontWeight: FontWeight.w500, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), - height: 40.h, + height: isFoldable ? 50.h : 40.h, icon: AppAssets.add_icon, iconColor: AppColors.primaryRedColor, ), @@ -821,7 +822,7 @@ class _MedicalFilePageState extends State { fontWeight: FontWeight.w500, borderRadius: 12.r, padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), - height: 56.h, + height: isFoldable ? 50.h : 40.h, ).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h), ), ).paddingSymmetrical(24.w, 0.h); @@ -863,19 +864,21 @@ class _MedicalFilePageState extends State { ); }), MedicalFileCard( - label: "My Invoices List".needTranslation, - textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.eye_result_icon, - isLargeText: true, - iconSize: 36.w), + label: "My Invoices List".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: true, + iconSize: 36.w, + ), MedicalFileCard( - label: "Ancillary Orders List".needTranslation, - textColor: AppColors.blackColor, - backgroundColor: AppColors.whiteColor, - svgIcon: AppAssets.eye_result_icon, - isLargeText: true, - iconSize: 36.w), + label: "Ancillary Orders List".needTranslation, + textColor: AppColors.blackColor, + backgroundColor: AppColors.whiteColor, + svgIcon: AppAssets.eye_result_icon, + isLargeText: true, + iconSize: 36.w, + ), ], ).paddingSymmetrical(24.w, 0.0), SizedBox(height: 16.h), diff --git a/lib/presentation/onboarding/onboarding_screen.dart b/lib/presentation/onboarding/onboarding_screen.dart index 2bf898b..c1e714e 100644 --- a/lib/presentation/onboarding/onboarding_screen.dart +++ b/lib/presentation/onboarding/onboarding_screen.dart @@ -53,7 +53,7 @@ class _OnboardingScreenState extends State { left: false, right: false, child: Column( - spacing: 24, + spacing: 24.h, children: [ PageView( controller: pageController, @@ -75,24 +75,24 @@ class _OnboardingScreenState extends State { }, ).expanded, Row( - spacing: 4.h, + spacing: 4.w, children: [ AnimatedContainer( duration: const Duration(milliseconds: 250), height: 6.h, - width: selectedIndex == 0 ? 18.h : 6.h, + width: selectedIndex == 0 ? 18.w : 6.w, decoration: - BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), + BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)), ), AnimatedContainer( duration: const Duration(milliseconds: 250), height: 6.h, - width: selectedIndex == 1 ? 18.h : 6.h, + width: selectedIndex == 1 ? 18.w : 6.w, decoration: - BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), + BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)), ), ], - ).paddingOnly(left: 24.h, right: 24.h), + ).paddingOnly(left: 24.w, right: 24.w), Row( children: [ AnimatedSwitcher( @@ -102,20 +102,20 @@ class _OnboardingScreenState extends State { ? CustomButton( text: "Skip".needTranslation, onPressed: () => goToHomePage(), - width: 86.h, + width: 86.w, height: 56.h, backgroundColor: Color(0xffFEE9EA), textColor: AppColors.primaryRedColor, borderColor: Colors.transparent, - ).paddingOnly(left: 24.h) + ).paddingOnly(left: 24.w) : const SizedBox.shrink(), ), const Spacer(), AnimatedContainer( duration: const Duration(milliseconds: 400), curve: Curves.easeInOut, - width: selectedIndex == 0 ? 86.h : MediaQuery.of(context).size.width - 48.h, - margin: EdgeInsets.only(left: 24.h, right: 24.h), + width: selectedIndex == 0 ? 86.w : MediaQuery.of(context).size.width - 48.w, + margin: EdgeInsets.only(left: 24.w, right: 24.w), decoration: BoxDecoration( color: AppColors.primaryRedColor, borderRadius: BorderRadius.circular(12.r), @@ -126,8 +126,8 @@ class _OnboardingScreenState extends State { child: selectedIndex == 0 ? CustomButton( icon: getIt.get().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, - iconSize: 32.h, - width: 86.h, + iconSize: 32.w, + width: 86.w, height: 56.h, text: "".needTranslation, backgroundColor: Colors.transparent, @@ -146,7 +146,7 @@ class _OnboardingScreenState extends State { ), ), ], - ), + ).paddingOnly(bottom: 10.h), ], ), ), @@ -157,7 +157,7 @@ class _OnboardingScreenState extends State { return Column( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.start, - spacing: 12, + spacing: 12.h, children: [ Align( alignment: Alignment.bottomCenter, @@ -167,10 +167,9 @@ class _OnboardingScreenState extends State { repeat: true, reverse: false, frameRate: FrameRate(60), - width: MediaQuery.sizeOf(context).width - 50, - height: MediaQuery.sizeOf(context).width - 50))) + width: MediaQuery.sizeOf(context).width - 50.w, + height: MediaQuery.sizeOf(context).width - 50.w))) .expanded, - // 12.height, Text( heading, style: TextStyle(fontSize: 36.f, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1), @@ -180,6 +179,6 @@ class _OnboardingScreenState extends State { style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), ), ], - ).paddingOnly(left: 24.h, right: 24.h); + ).paddingOnly(left: 24.w, right: 24.w); } } diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index 59e59b1..f5e2ef8 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -51,6 +51,33 @@ class _ProfileSettingsState extends State { super.dispose(); } + double dynamicItemHeight(BuildContext context) { + final double w = SizeUtils.width; + final double h = SizeUtils.height; + + double longer = w > h ? w : h; + double shorter = w < h ? w : h; + + final double aspect = longer / (shorter == 0 ? 1 : shorter); + + // Choose multiplier based on aspect ratio (handles near-square / foldable) + double multiplier; + if (aspect < 1.05) { + multiplier = 0.28; // nearly square / foldable -> smaller card height + } else if (aspect > 1.8) { + multiplier = 0.40; // very tall/wide -> larger height + } else { + multiplier = 0.34; // normal phones/tablets + } + + // Compute and clamp using sensible bounds (uses .h extension) + final double minH = 210.h; + final double maxH = 380.h; + final double computed = (shorter * multiplier); + + return computed.clamp(minH, maxH); + } + int length = 3; final SwiperController _controller = SwiperController(); @@ -72,11 +99,11 @@ class _ProfileSettingsState extends State { itemCount: medicalVm.patientFamilyFiles.length, layout: SwiperLayout.STACK, loop: true, - itemWidth: SizeUtils.width - 42.w, + itemHeight: dynamicItemHeight(context), + itemWidth: SizeUtils.width - 30.w, indicatorLayout: PageIndicatorLayout.COLOR, axisDirection: AxisDirection.right, controller: _controller, - itemHeight: 220.h, pagination: SwiperPagination( alignment: Alignment.bottomCenter, margin: EdgeInsets.only(top: (210.h + 8.h + 24.h)), @@ -97,13 +124,14 @@ class _ProfileSettingsState extends State { onFamilySwitchPress: (FamilyFileResponseModelLists profile) { medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); }, - ).paddingOnly(right: 16.w); + ).paddingOnly(right: 16.w, left: 8.w); }, ), + SizedBox(height: 5.h), GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 9.w, mainAxisSpacing: 9.h), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: isTablet ? 3 : 2), physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 24.w, top: 10.w), + padding: EdgeInsets.only(left: 24.w, right: 24.w, bottom: 24.h), shrinkWrap: true, children: [ Container( @@ -115,14 +143,14 @@ class _ProfileSettingsState extends State { ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - spacing: 4.h, + // spacing: 4.h, children: [ Row( spacing: 8.w, crossAxisAlignment: CrossAxisAlignment.center, children: [ Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h), - "Habib Wallet".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded, + "Habib Wallet".needTranslation.toText16(weight: FontWeight.w600, maxlines: 2).expanded, Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward), ], ), @@ -131,10 +159,11 @@ class _ProfileSettingsState extends State { return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false) .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); }), + Spacer(), CustomButton( height: 40.h, icon: AppAssets.recharge_icon, - iconSize: 24.h, + iconSize: 22.w, iconColor: AppColors.infoColor, textColor: AppColors.infoColor, text: "Recharge".needTranslation, @@ -152,7 +181,7 @@ class _ProfileSettingsState extends State { ).onPress(() { Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); }), - ) + ), ], ), "Quick Actions" @@ -220,7 +249,12 @@ class _ProfileSettingsState extends State { ], ), ), - CustomButton(icon: AppAssets.minus, text: "Deactivate account".needTranslation, onPressed: () {}).paddingAll(24.w), + CustomButton( + height: 56.h, + icon: AppAssets.minus, + text: "Deactivate account".needTranslation, + onPressed: () {}, + ).paddingAll(24.w), ], ); }, @@ -275,7 +309,11 @@ class FamilyCardWidget extends StatelessWidget { final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false; final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); return Container( - decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.r, hasShadow: true), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: true, + ), child: Column( children: [ Column( @@ -284,7 +322,6 @@ class FamilyCardWidget extends StatelessWidget { children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, - // children: [ Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h), Column( @@ -404,7 +441,7 @@ class FamilyCardWidget extends StatelessWidget { borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor, textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, - height: 56.h, + height: isFoldable ? 50.h : 40.h, fontSize: 14.f, ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); } @@ -420,7 +457,7 @@ class FamilyCardWidget extends StatelessWidget { textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {}, - height: 40.h, + height: isFoldable ? 50.h : 40.h, fontSize: 14.f, ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); } diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index 28d6554..6f33eda 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -56,7 +56,7 @@ class CustomButton extends StatelessWidget { return GestureDetector( onTap: isDisabled ? null : onPressed, child: Container( - height: height ?? (50.h), + height: height ?? (56.h), width: width, padding: padding, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( diff --git a/lib/widgets/custom_tab_bar.dart b/lib/widgets/custom_tab_bar.dart index a148cc7..6f8c47f 100644 --- a/lib/widgets/custom_tab_bar.dart +++ b/lib/widgets/custom_tab_bar.dart @@ -88,7 +88,7 @@ class CustomTabBarState extends State { bool isSelected = selectedIndex == currentIndex; return Container( height: 54.h, - padding: EdgeInsets.only(top: 4.h, bottom: 4.h, left: 16.w, right: 16.w), + padding: EdgeInsets.only(top: 4.h, bottom: 4.h, left: 14.w, right: 14.w), alignment: Alignment.center, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor,