Merge branch 'refs/heads/master' into fatima

# Conflicts:
#	lib/core/utils/utils.dart
#	lib/presentation/emergency_services/call_ambulance/tracking_screen.dart
#	lib/presentation/home/landing_page.dart
pull/152/head
Fatimah.Alshammari 4 months ago
commit 27ad4e0764

@ -1,16 +1,165 @@
# hmg_patient_app_new # HMG Patient App
New HMG Patient App A comprehensive Flutter-based mobile application for HMG (Habib Medical Group) patients,
providing seamless healthcare services and patient management features.
## Getting Started ## 📱 Features
This project is a starting point for a Flutter application. - **Patient Registration & Authentication**: Secure login and registration system
- **Appointment Management**: Book, reschedule, and manage medical appointments
- **Medical Records**: Access to personal health records and medical history
- **Doctor Consultation**: Video consultations and chat with healthcare providers
- **Health Monitoring**: Track vital signs and health metrics
- **Prescription Management**: View and manage prescriptions
- **Payment Integration**: Secure payment processing for medical services
- **Multi-language Support**: Available in English and Arabic
- **Push Notifications**: Real-time updates for appointments and health reminders
- **Calendar Integration**: Sync appointments with device calendar
- **Location Services**: Find nearby hospitals and clinics
A few resources to get you started if this is your first Flutter project: ## 🚀 Getting Started
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) ### Prerequisites
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the - Flutter SDK (>=3.0.0)
[online documentation](https://docs.flutter.dev/), which offers tutorials, - Dart SDK (>=3.0.0)
samples, guidance on mobile development, and a full API reference. - Android Studio / VS Code
- Android SDK (for Android development)
- Xcode (for iOS development, macOS only)
### Installation
1. **Clone the repository:**
```bash
git clone <repository-url>
cd HMG_Patient_App_New
```
2. **Install dependencies:**
```bash
flutter pub get
```
3. **Configure Firebase:**
- Add your `google-services.json` file to `android/app/`
- Add your `GoogleService-Info.plist` file to `ios/Runner/`
4. **Run the application:**
```bash
flutter run
```
## 🏗️ Project Structure
```
lib/
├── core/ # Core utilities and constants
├── extensions/ # Dart extensions
├── features/ # Feature-based modules
├── generated/ # Generated files (localization, etc.)
├── presentation/ # UI screens and widgets
├── routes/ # App navigation and routing
├── services/ # Business logic and API services
├── theme/ # App theming and styling
├── widgets/ # Reusable UI components
├── main.dart # App entry point
└── splashPage.dart # Splash screen
assets/
├── animations/ # Lottie animations
├── fonts/ # Custom fonts (Poppins, Gess Two)
├── images/ # PNG and SVG images
├── json/ # JSON data files
└── langs/ # Localization files
```
## 🛠️ Technologies Used
- **Framework**: Flutter
- **Language**: Dart
- **State Management**: [Your state management solution]
- **Backend Services**: Firebase
- **Authentication**: Firebase Auth
- **Database**: Cloud Firestore
- **Push Notifications**: Firebase Cloud Messaging
- **Maps**: Google Maps
- **Payment**: Amazon PayFort
- **Video Calling**: Flutter Zoom Video SDK
- **Local Storage**: SQLite, Shared Preferences
## 📱 Supported Platforms
- ✅ Android (API level 21+)
- ✅ iOS (iOS 12.0+)
- ✅ Foldable devices support
- ✅ Tablet optimization
## 🌐 Localization
The app supports multiple languages:
- English (en-US)
- Arabic (ar-SA)
## 🔧 Configuration
### Environment Setup
1. **API Keys**: Configure your API keys in the appropriate configuration files
2. **Firebase**: Set up Firebase project and add configuration files
3. **Maps**: Add Google Maps API key
4. **Payment**: Configure PayFort credentials
### Build Variants
- **Debug**: Development build with debugging enabled
- **Release**: Production-ready optimized build
## 📝 Development Guidelines
### Code Style
- Follow Dart/Flutter best practices
- Use consistent naming conventions
- Implement proper error handling
- Write comprehensive documentation
### Testing
```bash
# Run unit tests
flutter test
# Run integration tests
flutter test integration_test/
```
### Building for Production
**Android:**
```bash
flutter build apk --release
# or
flutter build appbundle --release
```
**iOS:**
```bash
flutter build ios --release
```
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/new-feature`)
3. Commit your changes (`git commit -am 'Add new feature'`)
4. Push to the branch (`git push origin feature/new-feature`)
5. Create a Pull Request
## 📄 License
This project is proprietary software developed for HMG Healthcare Group.
**Built with ❤️ for better healthcare accessibility**

@ -637,7 +637,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = 3A359E86ZF; DEVELOPMENT_TEAM = 3A359E86ZF;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;

@ -1,5 +1,5 @@
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:amazon_payfort/amazon_payfort.dart'; import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
var MAX_SMALL_SCREEN = 660; var MAX_SMALL_SCREEN = 660;
final OPENTOK_API_KEY = '46209962'; final OPENTOK_API_KEY = '46209962';
@ -570,7 +570,8 @@ var GET_CUSTOMER_INFO = "VerifyCustomer";
//Pharmacy //Pharmacy
var GET_PHARMACY_CATEGORISE = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0'; var GET_PHARMACY_CATEGORISE =
'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0';
var GET_OFFERS_CATEGORISE = 'discountcategories'; var GET_OFFERS_CATEGORISE = 'discountcategories';
var GET_OFFERS_PRODUCTS = 'offerproducts/'; var GET_OFFERS_PRODUCTS = 'offerproducts/';
var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id='; var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=';
@ -722,7 +723,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In
//family Files //family Files
const FAMILY_FILES= 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus'; const FAMILY_FILES = 'Services/Authentication.svc/REST/GetAllSharedRecordsByStatus';
var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp'; var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatbot_SendMedicationInstructionByWhatsApp';
@ -789,7 +790,6 @@ class ApiConsts {
RCBaseUrl = 'https://rc.hmg.com/test/'; RCBaseUrl = 'https://rc.hmg.com/test/';
break; break;
case AppEnvironmentTypeEnum.preProd: case AppEnvironmentTypeEnum.preProd:
baseUrl = "https://webservices.hmg.com/"; baseUrl = "https://webservices.hmg.com/";
payFortEnvironment = FortEnvironment.production; payFortEnvironment = FortEnvironment.production;
applePayMerchantId = "merchant.com.hmgwebservices"; applePayMerchantId = "merchant.com.hmgwebservices";
@ -833,7 +833,7 @@ class ApiConsts {
static final String getUserAgreementContent = 'Services/Patients.svc/REST/GetUsageAgreementText'; static final String getUserAgreementContent = 'Services/Patients.svc/REST/GetUsageAgreementText';
static final String checkPatientForRegistration = 'Services/Authentication.svc/REST/CheckPatientForRegisteration'; static final String checkPatientForRegistration = 'Services/Authentication.svc/REST/CheckPatientForRegisteration';
static final String checkUserStatus= 'Services/NHIC.svc/REST/GetPatientInfo'; static final String checkUserStatus = 'Services/NHIC.svc/REST/GetPatientInfo';
static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI'; static final String insertPatientDeviceIMEIData = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo'; static final String insertPatientMobileData = 'Services/MobileNotifications.svc/REST/Insert_PatientMobileDeviceInfo';

@ -11,22 +11,109 @@ extension ResponsiveExtension on num {
double get _screenHeight => SizeUtils.height; double get _screenHeight => SizeUtils.height;
/// Scale horizontally (width-based) /// Check if device is likely a foldable
double get w => (this * _screenWidth) / figmaDesignWidth; bool get _isFoldable {
double aspectRatio = _screenWidth / _screenHeight;
/// Scale vertically (height-based) // Foldable devices typically have aspect ratios close to 1:1 when unfolded
double get h => (this * _screenHeight) / figmaDesignHeight; return (aspectRatio > 0.9 && aspectRatio < 1.1) && (_screenWidth > 700 || _screenHeight > 700);
}
//radius
double get r => (this * _screenWidth) / figmaDesignWidth;
/// Scale text size /// Scale text size - enhanced for foldable devices
double get f { double get f {
double scale = _screenWidth / figmaDesignWidth; double aspectRatio = _screenWidth / _screenHeight;
if (scale > 1.6) scale = 1.6; // optional clamp for tablets double scale = (_screenWidth < _screenHeight ? _screenWidth : _screenHeight) / figmaDesignWidth;
// Enhanced clamping for different device types
double clamp;
if (SizeUtils.deviceType == DeviceType.tablet || _isFoldable) {
// More conservative scaling for tablets and foldables
clamp = (aspectRatio > 1.5 || aspectRatio < 0.67) ? 1.4 : 1.1;
} else {
// Original logic for phones
clamp = (aspectRatio > 1.3 || aspectRatio < 0.77) ? 1.6 : 1.2;
}
if (scale > clamp) scale = clamp;
return this * scale; return this * scale;
} }
/// Scale horizontally (width-based) - enhanced for foldable devices
double get w {
double baseScale = (this * _screenWidth) / figmaDesignWidth;
if (_isFoldable) {
// For foldables, use more conservative width scaling
double scale = _screenWidth / figmaDesignWidth;
scale = scale.clamp(0.8, 1.4);
return this * scale;
}
return baseScale;
}
/// Scale vertically (height-based) - enhanced for foldable devices
double get h {
double baseScale = (this * _screenHeight) / figmaDesignHeight;
if (_isFoldable) {
// For foldables, use height-based scaling but with constraints
double scale = (_screenHeight / figmaDesignHeight).clamp(0.8, 1.4);
return this * scale;
}
return baseScale;
}
/// Radius - enhanced for foldable devices
double get r {
double baseScale = (this * _screenWidth) / figmaDesignWidth;
if (_isFoldable) {
// Use the same logic as enhanced width for foldables
double scale = _screenWidth / figmaDesignWidth;
scale = scale.clamp(0.8, 1.4);
return this * scale;
}
return baseScale;
}
// New enhanced getters (additional options)
/// Enhanced font scaling with device-specific adjustments
double get fh {
double baseScale = _screenHeight / figmaDesignHeight;
if (_isFoldable) {
// Special handling for foldable devices - use more conservative scaling
baseScale = baseScale.clamp(0.8, 1.3);
} else if (SizeUtils.deviceType == DeviceType.tablet) {
// Tablet-specific scaling
baseScale = baseScale.clamp(0.9, 1.5);
} else {
// Phone scaling
baseScale = baseScale.clamp(0.8, 1.8);
}
return this * baseScale;
}
/// Adaptive scaling - automatically chooses best scaling method
double get adaptive {
if (_isFoldable) {
return fh;
} else if (SizeUtils.deviceType == DeviceType.tablet) {
return f * 0.9; // Slightly smaller for tablets
}
return f;
}
/// Minimum size constraint (useful for touch targets)
double get minSize {
double scaled = adaptive;
return scaled < 44 ? 44 : scaled; // Minimum 44pt for accessibility
}
/// Optional: direct accessors for full width/height /// Optional: direct accessors for full width/height
static double get screenWidth => SizeUtils.width; static double get screenWidth => SizeUtils.width;
@ -91,10 +178,7 @@ class SizeUtils {
/// Device's Width /// Device's Width
static double width = 375; static double width = 375;
static void setScreenSize( static void setScreenSize(BoxConstraints constraints, Orientation currentOrientation) {
BoxConstraints constraints,
Orientation currentOrientation,
) {
boxConstraints = constraints; boxConstraints = constraints;
orientation = currentOrientation; orientation = currentOrientation;
@ -130,7 +214,9 @@ class SizeUtils {
} }
log("longerSide: $longerSide"); log("longerSide: $longerSide");
log("shorterSide: $shorterSide");
log("isTablet: $isTablet"); log("isTablet: $isTablet");
log("isFoldable: $isFoldable");
} }
} }
@ -140,3 +226,9 @@ bool get isTablet => SizeUtils.deviceType == DeviceType.tablet;
bool get isMobile => SizeUtils.deviceType == DeviceType.mobile; bool get isMobile => SizeUtils.deviceType == DeviceType.mobile;
bool get isDesktop => SizeUtils.deviceType == DeviceType.desktop; 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);
}

@ -633,17 +633,19 @@ class Utils {
required String url, required String url,
required Color iconColor, required Color iconColor,
bool isDisabled = false, bool isDisabled = false,
double width = 24, double? width,
double height = 24, double? height,
}) { }) {
final iconH = height ?? 24.h;
final iconW = width ?? 24.w;
return SvgPicture.network( return SvgPicture.network(
url, url,
colorFilter: ColorFilter.mode( colorFilter: ColorFilter.mode(
isDisabled ? iconColor.withOpacity(0.5) : iconColor, isDisabled ? iconColor.withOpacity(0.5) : iconColor,
BlendMode.srcIn, BlendMode.srcIn,
), ),
width: width, width: iconW,
height: height, height: iconH,
); );
} }
@ -662,7 +664,7 @@ class Utils {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, 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), child: Image.asset(icon, width: iconW, height: iconH, fit: fit),
); );
@ -670,17 +672,27 @@ class Utils {
} }
/// Widget to build an SVG from network /// Widget to build an SVG from network
static Widget buildImgWithNetwork({required String url, bool isDisabled = false, double width = 24, double height = 24, BoxFit fit = BoxFit.cover, ImageErrorWidgetBuilder? errorBuilder}) { static Widget buildImgWithNetwork({
required String url,
required Color iconColor,
bool isDisabled = false,
double? width,
double? height,
BoxFit fit = BoxFit.cover,
ImageErrorWidgetBuilder? errorBuilder,
}) {
final iconH = height ?? 24.h;
final iconW = width ?? 24.w;
return Image.network( return Image.network(
url, url,
width: width, width: iconW,
height: height, height: iconH,
fit: fit, fit: fit,
errorBuilder: errorBuilder??(_,__,___){ errorBuilder: errorBuilder ??
//todo change the error builder icon that it is returning (_, __, ___) {
return Utils.buildSvgWithAssets(width: width, //todo change the error builder icon that it is returning
height: height,icon: AppAssets.no_visit_icon); return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon);
}, },
); );
} }

@ -167,16 +167,17 @@ extension EmailValidator on String {
decoration: isUnderLine ? TextDecoration.underline : null), decoration: isUnderLine ? TextDecoration.underline : null),
); );
Widget toText14( Widget toText14({
{Color? color, Color? color,
bool isUnderLine = false, bool isUnderLine = false,
bool isBold = false, bool isBold = false,
bool isCenter = false, bool isCenter = false,
FontWeight? weight, FontWeight? weight,
int? maxlines, int? maxlines,
double? letterSpacing = 0, double? letterSpacing = 0,
double? height, double? height,
TextOverflow? textOverflow}) => TextOverflow? textOverflow,
}) =>
Text( Text(
this, this,
textAlign: isCenter ? TextAlign.center : null, textAlign: isCenter ? TextAlign.center : null,

@ -37,9 +37,9 @@ import '../../core/dependencies.dart';
import '../medical_file/widgets/medical_file_card.dart'; import '../medical_file/widgets/medical_file_card.dart';
class AppointmentDetailsPage extends StatefulWidget { class AppointmentDetailsPage extends StatefulWidget {
AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel}); const AppointmentDetailsPage({super.key, required this.patientAppointmentHistoryResponseModel});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@override @override
State<AppointmentDetailsPage> createState() => _AppointmentDetailsPageState(); State<AppointmentDetailsPage> createState() => _AppointmentDetailsPageState();
@ -115,7 +115,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 20.h, borderRadius: 20.r,
hasShadow: false, hasShadow: false,
), ),
child: Padding( child: Padding(
@ -138,7 +138,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
? Row( ? Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.livecare_clinic_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h), SizedBox(width: 12.w),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -155,7 +155,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [ children: [
ClipRRect( ClipRRect(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲
child: Image.network( child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
fit: BoxFit.contain, fit: BoxFit.contain,
@ -173,19 +174,19 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!),
widget.patientAppointmentHistoryResponseModel.projectName); widget.patientAppointmentHistoryResponseModel.projectName);
}, },
backgroundColor: AppColors.textColor.withOpacity(0.8), backgroundColor: AppColors.textColor.withValues(alpha: 0.8),
borderColor: borderColor: AppointmentType.getNextActionButtonColor(
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction) widget.patientAppointmentHistoryResponseModel.nextAction)
.withOpacity(0.01), .withValues(alpha: 0.01),
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h, height: 40.h,
icon: AppAssets.directions_icon, icon: AppAssets.directions_icon,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
iconSize: 13.h, iconSize: 14.h,
).paddingAll(12.h), ).paddingAll(12.h),
), ),
), ),
@ -199,7 +200,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 20.h, borderRadius: 20.r,
hasShadow: false, hasShadow: false,
), ),
child: Row( child: Row(
@ -211,12 +212,14 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
"Notify me before the appointment".needTranslation.toText11(color: AppColors.textColorLight, weight: FontWeight.w500), "Notify me before the appointment"
.needTranslation
.toText11(color: AppColors.textColorLight, weight: FontWeight.w500),
], ],
), ),
const Spacer(), const Spacer(),
Switch( Switch(
activeColor: AppColors.successColor, activeThumbColor: AppColors.successColor,
activeTrackColor: AppColors.successColor.withValues(alpha: .15), activeTrackColor: AppColors.successColor.withValues(alpha: .15),
value: widget.patientAppointmentHistoryResponseModel.hasReminder!, value: widget.patientAppointmentHistoryResponseModel.hasReminder!,
onChanged: (newValue) { onChanged: (newValue) {
@ -237,8 +240,12 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
"Lab & Radiology".needTranslation.toText18(isBold: true), "Lab & Radiology".needTranslation.toText18(isBold: true),
SizedBox(height: 16.h), SizedBox(height: 16.h),
GridView( GridView(
padding: EdgeInsets.zero,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisSpacing: 13.h, mainAxisSpacing: 13.h, childAspectRatio: 7 / 6), crossAxisCount: isTablet || isFoldable ? 3 : 2,
crossAxisSpacing: 13.w,
mainAxisSpacing: 13.w,
),
physics: NeverScrollableScrollPhysics(), physics: NeverScrollableScrollPhysics(),
shrinkWrap: true, shrinkWrap: true,
children: [ children: [
@ -247,19 +254,36 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon, svgIcon: AppAssets.lab_result_icon,
iconSize: 40, iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: "Radiology Results".needTranslation,
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon,
iconSize: 40.w,
isLargeText: true,
),
MedicalFileCard(
label: LocaleKeys.labResults.tr(context: context),
textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.lab_result_icon,
iconSize: 40.w,
isLargeText: true, isLargeText: true,
), ),
MedicalFileCard( MedicalFileCard(
label: LocaleKeys.radiology.tr(context: context), label: "Radiology Results".needTranslation,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.radiology_icon, svgIcon: AppAssets.radiology_icon,
iconSize: 40, iconSize: 40.w,
isLargeText: true, isLargeText: true,
), ),
], ],
), ),
SizedBox(height: 16.h),
LocaleKeys.prescriptions.tr().toText18(isBold: true), LocaleKeys.prescriptions.tr().toText18(isBold: true),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) { Consumer<PrescriptionsViewModel>(builder: (context, prescriptionVM, child) {
@ -268,153 +292,178 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
: Container( : Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white, color: Colors.white,
borderRadius: 20.h, borderRadius: 20.r,
), ),
child: Padding( padding: EdgeInsets.all(16.w),
padding: EdgeInsets.all(16.h), child: Column(
child: Column( children: [
children: [ ListView.separated(
ListView.separated( itemCount: prescriptionVM.prescriptionDetailsList.length,
itemCount: prescriptionVM.prescriptionDetailsList.length, shrinkWrap: true,
shrinkWrap: true, padding: EdgeInsets.only(right: 8.w),
padding: const EdgeInsets.only(left: 0, right: 8), physics: NeverScrollableScrollPhysics(),
physics: NeverScrollableScrollPhysics(), itemBuilder: (context, index) {
itemBuilder: (context, index) { return AnimationConfiguration.staggeredList(
return AnimationConfiguration.staggeredList( position: index,
position: index, duration: const Duration(milliseconds: 500),
duration: const Duration(milliseconds: 500), child: SlideAnimation(
child: SlideAnimation( verticalOffset: 100.0,
verticalOffset: 100.0, child: FadeInAnimation(
child: FadeInAnimation( child: Row(
child: Row( children: [
children: [ Utils.buildSvgWithAssets(
Utils.buildSvgWithAssets( icon: AppAssets.prescription_item_icon,
icon: AppAssets.prescription_item_icon, width: 40.h,
width: 40.h, height: 40.h,
height: 40.h, ),
), SizedBox(width: 8.h),
SizedBox(width: 8.h), Row(
Row( mainAxisSize: MainAxisSize.max,
mainAxisSize: MainAxisSize.max, children: [
children: [ Column(
Column( children: [
children: [ prescriptionVM.prescriptionDetailsList[index].itemDescription!
SizedBox( .toText12(isBold: true, maxLine: 1),
width: 150.h, "Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}"
child: prescriptionVM.prescriptionDetailsList[index].itemDescription! .needTranslation
.toText12(isBold: true, maxLine: 1)), .toText10(
SizedBox( weight: FontWeight.w500,
width: 150.h, color: AppColors.greyTextColor,
child: letterSpacing: -0.4),
"Prescribed By: ${widget.patientAppointmentHistoryResponseModel.doctorTitle} ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}" ],
.needTranslation ),
.toText10( SizedBox(width: 68.w),
weight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: -0.4), Transform.flip(
), flipX: appState.isArabic(),
], child: Utils.buildSvgWithAssets(
), icon: AppAssets.forward_arrow_icon,
SizedBox(width: 68.h), iconColor: AppColors.blackColor,
Transform.flip( width: 18.w,
flipX: appState.isArabic(), height: 13.h,
child: Utils.buildSvgWithAssets( fit: BoxFit.contain,
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.blackColor,
width: 18.h,
height: 13.h,
fit: BoxFit.contain,
),
), ),
], ),
), ],
], ),
), ],
), ),
), ),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
).onPress(() {
prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push(
CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
), ),
); );
}), },
SizedBox(height: 16.h), separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
const Divider(color: AppColors.dividerColor), ).onPress(() {
SizedBox(height: 16.h), prescriptionVM.setPrescriptionsDetailsLoading();
Row( Navigator.of(context).push(
children: [ CustomPageRoute(
// Expanded( page: PrescriptionDetailPage(prescriptionsResponseModel: getPrescriptionRequestModel()),
// child: CustomButton( ),
// text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context), );
// onPressed: () {}, }),
// backgroundColor: AppColors.secondaryLightRedColor, SizedBox(height: 16.h),
// borderColor: AppColors.secondaryLightRedColor, const Divider(color: AppColors.dividerColor),
// textColor: AppColors.primaryRedColor, SizedBox(height: 16.h),
// fontSize: 14, Wrap(
// fontWeight: FontWeight.w500, runSpacing: 6.w,
// borderRadius: 12.h, children: [
// height: 40.h, // Expanded(
// icon: AppAssets.appointment_calendar_icon, // child: CustomButton(
// iconColor: AppColors.primaryRedColor, // text: widget.prescriptionsResponseModel.isHomeMedicineDeliverySupported! ? LocaleKeys.resendOrder.tr(context: context) : LocaleKeys.prescriptionDeliveryError.tr(context: context),
// iconSize: 16.h, // onPressed: () {},
// ), // backgroundColor: AppColors.secondaryLightRedColor,
// ), // borderColor: AppColors.secondaryLightRedColor,
// SizedBox(width: 16.h), // textColor: AppColors.primaryRedColor,
Expanded( // fontSize: 14,
child: CustomButton( // fontWeight: FontWeight.w500,
text: "All Prescriptions".needTranslation, // borderRadius: 12.h,
onPressed: () { // height: 40.h,
Navigator.of(context) // icon: AppAssets.appointment_calendar_icon,
.push( // iconColor: AppColors.primaryRedColor,
CustomPageRoute( // iconSize: 16.h,
page: PrescriptionsListPage(), // ),
), // ),
) // SizedBox(width: 16.h),
.then((val) { Expanded(
prescriptionsViewModel.setPrescriptionsDetailsLoading(); child: CustomButton(
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel()); text: "Refill & Delivery".needTranslation,
}); onPressed: () {
}, Navigator.of(context)
backgroundColor: AppColors.secondaryLightRedColor, .push(
borderColor: AppColors.secondaryLightRedColor, CustomPageRoute(
textColor: AppColors.primaryRedColor, page: PrescriptionsListPage(),
fontSize: 14, ),
fontWeight: FontWeight.w500, )
borderRadius: 12.h, .then((val) {
height: 40.h, prescriptionsViewModel.setPrescriptionsDetailsLoading();
icon: AppAssets.requests, prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
iconColor: AppColors.primaryRedColor, });
iconSize: 16.h, },
), backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
), ),
], ),
),
], SizedBox(width: 16.w),
), Expanded(
child: CustomButton(
text: "All Prescriptions".needTranslation,
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: PrescriptionsListPage(),
),
)
.then((val) {
prescriptionsViewModel.setPrescriptionsDetailsLoading();
prescriptionsViewModel.getPrescriptionDetails(getPrescriptionRequestModel());
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
icon: AppAssets.requests,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
),
),
],
),
],
), ),
); );
}), }),
], ],
), ),
], ],
).paddingSymmetrical(24.h, 24.h), ).paddingAll(24.w),
), ),
), ),
), ),
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 24.h, borderRadius: 24.r,
hasShadow: true, hasShadow: true,
), ),
child: SizedBox( child: SizedBox(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20) if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 ||
widget.patientAppointmentHistoryResponseModel.nextAction == 20)
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -423,7 +472,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [ children: [
"Amount before tax".needTranslation.toText18(isBold: true), "Amount before tax".needTranslation.toText18(isBold: true),
Utils.getPaymentAmountWithSymbol( Utils.getPaymentAmountWithSymbol(
widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true), isSaudiCurrency: true),
], ],
), ),
@ -432,8 +483,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: child: LocaleKeys.upcomingPaymentNow
LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)), .tr(context: context)
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor)),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})" "VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})"
.needTranslation .needTranslation
.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -2), .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -2),
@ -470,10 +522,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
fontSize: 16, fontSize: 16.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h, height: 50.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
@ -486,13 +538,14 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction); handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
}, },
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), borderColor:
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : AppColors.whiteColor, textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : AppColors.whiteColor,
fontSize: 16, fontSize: 16.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 50.h, height: 56.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction), icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
iconSize: 18.h, iconSize: 18.h,

@ -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/AppointmentFilter.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.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/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/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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.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/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/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../widgets/common_bottom_sheet.dart' import '../../widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
show showCommonBottomSheetWithoutHeight;
class MyAppointmentsPage extends StatefulWidget { class MyAppointmentsPage extends StatefulWidget {
const MyAppointmentsPage({super.key}); const MyAppointmentsPage({super.key});
@ -86,18 +84,14 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
} }
Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) { Widget getSelectedTabData(int index, MyAppointmentsViewModel myAppointmentsVM) {
return getAppointList( return getAppointList(myAppointmentsVM, myAppointmentsVM.filteredAppointmentList);
myAppointmentsVM, myAppointmentsVM.filteredAppointmentList);
} }
Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, Widget getAppointList(MyAppointmentsViewModel myAppointmentsVM, List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Visibility( Visibility(visible: myAppointmentsVM.availableFilters.isNotEmpty, child: getAppointmentFilters(myAppointmentsVM)),
visible: myAppointmentsVM.availableFilters.isNotEmpty,
child: getAppointmentFilters(myAppointmentsVM)),
ListView.separated( ListView.separated(
padding: EdgeInsets.only(top: 24.h), padding: EdgeInsets.only(top: 24.h),
shrinkWrap: true, shrinkWrap: true,
@ -110,14 +104,9 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
itemBuilder: (context, index) { itemBuilder: (context, index) {
return myAppointmentsVM.isMyAppointmentsLoading return myAppointmentsVM.isMyAppointmentsLoading
? Container( ? Container(
decoration: RoundedRectangleBorder() decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
PatientAppointmentHistoryResponseModel(),
myAppointmentsViewModel: myAppointmentsViewModel, myAppointmentsViewModel: myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: true, isLoading: true,
@ -134,16 +123,10 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
child: AnimatedContainer( child: AnimatedContainer(
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
decoration: RoundedRectangleBorder() decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
.toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.h,
hasShadow: true),
child: AppointmentCard( child: AppointmentCard(
patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel: filteredAppointmentList[index],
filteredAppointmentList[index], myAppointmentsViewModel: myAppointmentsViewModel,
myAppointmentsViewModel:
myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: false, isLoading: false,
isFromHomePage: false, isFromHomePage: false,
@ -154,8 +137,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
) )
: Utils.getNoDataWidget( : Utils.getNoDataWidget(
context, context,
noDataText: "You don't have any appointments yet." noDataText: "You don't have any appointments yet.".needTranslation,
.needTranslation,
callToActionButton: CustomButton( callToActionButton: CustomButton(
text: LocaleKeys.bookAppo.tr(context: context), text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () { onPressed: () {
@ -168,18 +150,17 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA), borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B), textColor: Color(0xffED1C2B),
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40, height: 40.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
).paddingSymmetrical(48.h, 0.h), ).paddingSymmetrical(48.h, 0.h),
); );
}, },
separatorBuilder: (BuildContext cxt, int index) => separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
SizedBox(height: 16.h),
), ),
SizedBox(height: 24.h), SizedBox(height: 24.h),
], ],
@ -189,45 +170,41 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
Widget getAppointmentFilters(MyAppointmentsViewModel myAppointmentsVM) { Widget getAppointmentFilters(MyAppointmentsViewModel myAppointmentsVM) {
return SizedBox( return SizedBox(
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
child: ListView.separated( child: ListView.separated(
separatorBuilder: (_, index) => SizedBox( separatorBuilder: (_, index) => SizedBox(
width: 8.h, width: 8.h,
), ),
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: myAppointmentsVM.availableFilters.length, itemCount: myAppointmentsVM.availableFilters.length,
itemBuilder: (_, index) => AppointmentFilters( itemBuilder: (_, index) => AppointmentFilters(
selectedFilter: myAppointmentsVM.selectedFilter, selectedFilter: myAppointmentsVM.selectedFilter,
item: myAppointmentsVM.availableFilters[index], item: myAppointmentsVM.availableFilters[index],
onClicked: () { onClicked: () {
if (myAppointmentsVM.availableFilters[index] == if (myAppointmentsVM.availableFilters[index] == AppointmentListingFilters.DATESELECTION) {
AppointmentListingFilters.DATESELECTION) { showCommonBottomSheetWithoutHeight(
showCommonBottomSheetWithoutHeight( title: "Set The Date Range".needTranslation,
title: "Set The Date Range".needTranslation, context,
context, child: DateRangeSelector(
child: DateRangeSelector( onRangeSelected: (start, end) {
onRangeSelected: (start, end) { // if (start != null) {
// if (start != null) { myAppointmentsVM.getSelectedDateRange(start, end);
myAppointmentsVM.getSelectedDateRange( // }
start, end); },
// } ),
}, isFullScreen: false,
), isCloseButtonVisible: true,
isFullScreen: false, callBackFunc: () {},
isCloseButtonVisible: true, );
callBackFunc: () {}, } else {
); myAppointmentsVM.setSelectedFilter(myAppointmentsVM.availableFilters[index]);
} else { myAppointmentsVM.filterTheListAsPerSelection();
myAppointmentsVM.setSelectedFilter( }
myAppointmentsVM.availableFilters[index]); },
myAppointmentsVM.filterTheListAsPerSelection(); )),
} ),
}, ],
)), )).paddingOnly(top: 24.h, left: 24.h, right: 24.h);
),
],
)).paddingOnly(top: 24.h, left: 24.h, right: 24.h);
} }
} }

@ -23,339 +23,312 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:smooth_corner/smooth_corner.dart';
class AppointmentCard extends StatefulWidget { class AppointmentCard extends StatelessWidget {
AppointmentCard( final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
{super.key, final MyAppointmentsViewModel myAppointmentsViewModel;
required this.patientAppointmentHistoryResponseModel, final bool isLoading;
required this.myAppointmentsViewModel, final bool isFromHomePage;
required this.bookAppointmentsViewModel, final bool isFromMedicalReport;
this.isLoading = false, final MedicalFileViewModel? medicalFileViewModel;
this.isFromHomePage = false, final BookAppointmentsViewModel bookAppointmentsViewModel;
this.isFromMedicalReport = false,
this.medicalFileViewModel});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; const AppointmentCard({
MyAppointmentsViewModel myAppointmentsViewModel; super.key,
bool isLoading; required this.patientAppointmentHistoryResponseModel,
bool isFromHomePage; required this.myAppointmentsViewModel,
bool isFromMedicalReport; required this.bookAppointmentsViewModel,
MedicalFileViewModel? medicalFileViewModel; this.isLoading = false,
BookAppointmentsViewModel bookAppointmentsViewModel; this.isFromHomePage = false,
this.isFromMedicalReport = false,
this.medicalFileViewModel,
});
@override
State<AppointmentCard> createState() => _AppointmentCardState();
}
class _AppointmentCardState extends State<AppointmentCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>(); final appState = getIt.get<AppState>();
return InkWell( return InkWell(
onTap: () { onTap: () => _goToDetails(context),
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
child: Padding( child: Padding(
padding: EdgeInsets.all(14.h), padding: EdgeInsets.all(14.h),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( _buildHeader(context, appState),
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Wrap(
alignment: WrapAlignment.start,
direction: Axis.horizontal,
spacing: 6.h,
runSpacing: 6.h,
children: [
AppCustomChipWidget(
icon: widget.isLoading
? AppAssets.walkin_appointment_icon
: (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon),
iconColor: widget.isLoading
? AppColors.textColor
: !widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? AppColors.textColor
: AppColors.whiteColor,
labelText: widget.isLoading
? "Walk In"
: widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? LocaleKeys.livecare.tr(context: context)
: "Walk In".needTranslation,
backgroundColor:
widget.isLoading ? AppColors.greyColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor),
textColor: widget.isLoading ? AppColors.textColor : (!widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor),
).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
labelText: widget.isLoading
? "OutPatient"
: appState.isArabic()
? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!,
backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
textColor: AppColors.primaryRedColor,
).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
labelText: widget.isLoading ? "Booked" : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
backgroundColor: AppColors.successColor.withOpacity(0.1),
textColor: AppColors.successColor,
).toShimmer2(isShow: widget.isLoading),
],
).toShimmer2(isShow: widget.isLoading),
),
// TODO: Implement the logic to enable/disable the switch based on reminder status
// AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
// ? SizedBox().toShimmer2(isShow: widget.isLoading)
// : Switch(
// activeColor: AppColors.successColor,
// activeTrackColor: AppColors.successColor.withValues(alpha: .15),
// thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
// (Set<WidgetState> states) {
// if (states.contains(WidgetState.selected)) {
// return const Icon(Icons.check); // Icon when switch is ON
// }
// return const Icon(Icons.close); // Icon when switch is OFF
// },
// ),
// value: widget.isLoading ? false : widget.patientAppointmentHistoryResponseModel.hasReminder!,
// onChanged: (newValue) {
// setState(() {
// widget.myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
// });
// },
// ).toShimmer2(isShow: widget.isLoading),
],
),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( _buildDoctorRow(context),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: widget.isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(widget.isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : widget.patientAppointmentHistoryResponseModel.doctorNameObj!)
.toText16(isBold: true, maxlines: 1)
.toShimmer2(isShow: widget.isLoading),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
widget.isFromHomePage
? SizedBox.shrink()
: AppCustomChipWidget(labelText: widget.isLoading ? "Cardiology" : widget.patientAppointmentHistoryResponseModel.clinicName!).toShimmer2(isShow: widget.isLoading),
widget.isFromHomePage
? SizedBox.shrink()
: AppCustomChipWidget(labelText: widget.isLoading ? "Olaya" : widget.patientAppointmentHistoryResponseModel.projectName!).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText:
widget.isLoading ? "Cardiology" : DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading),
widget.isFromMedicalReport
? SizedBox.shrink()
: AppCustomChipWidget(
icon: AppAssets.appointment_time_icon,
labelText: widget.isLoading
? "Cardiology"
: DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false))
.toShimmer2(isShow: widget.isLoading),
],
),
],
),
),
],
),
SizedBox(height: 16.h), SizedBox(height: 16.h),
widget.isFromMedicalReport _buildActionArea(context, appState),
? CustomButton(
text: "Select appointment".needTranslation,
onPressed: () {
widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
Navigator.pop(context, false);
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.checkmark_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
)
: Row(
children: [
Expanded(
flex: 6,
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton().toShimmer2(isShow: widget.isLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 15.h,
).toShimmer2(isShow: widget.isLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: 40.h,
width: 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.h,
),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.whiteColor,
width: 40.h,
height: 40.h,
fit: BoxFit.cover,
),
),
).toShimmer2(isShow: widget.isLoading).onPress(() {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((val) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
),
], ],
), ),
), ),
); );
} }
Widget getArrivedAppointmentButton() { Widget _buildHeader(BuildContext context, AppState appState) {
return DateTime.now().difference(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15 return Row(
? CustomButton( mainAxisAlignment: MainAxisAlignment.spaceBetween,
text: LocaleKeys.askDoctor.tr(context: context), children: [
onPressed: () {}, Expanded(child: _buildChips(context, appState)),
backgroundColor: AppColors.secondaryLightRedColor, ],
borderColor: AppColors.secondaryLightRedColor, );
textColor: AppColors.primaryRedColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
)
: CustomButton(
text: "Rebook with same doctor".needTranslation,
onPressed: () {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
} }
openDoctorScheduleCalendar() async { Widget _buildChips(BuildContext context, AppState appState) {
DoctorsListResponseModel doctor = DoctorsListResponseModel( final isLiveCare = !isLoading && patientAppointmentHistoryResponseModel.isLiveCareAppointment!;
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
projectID: widget.patientAppointmentHistoryResponseModel.projectID, return Wrap(
doctorID: widget.patientAppointmentHistoryResponseModel.doctorID, alignment: WrapAlignment.start,
doctorImageURL: widget.patientAppointmentHistoryResponseModel.doctorImageURL, direction: Axis.horizontal,
doctorTitle: widget.patientAppointmentHistoryResponseModel.doctorTitle, spacing: 6.w,
name: widget.patientAppointmentHistoryResponseModel.doctorNameObj, runSpacing: 6.h,
nationalityFlagURL: "https://hmgwebservices.com/Images/flag/SYR.png", children: [
speciality: [], AppCustomChipWidget(
clinicName: widget.patientAppointmentHistoryResponseModel.clinicName, icon: isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon),
projectName: widget.patientAppointmentHistoryResponseModel.projectName, iconColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor),
labelText: isLoading ? 'Walk In'.needTranslation : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : 'Walk In'.needTranslation),
backgroundColor: isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor),
textColor: isLoading ? AppColors.textColor : (isLiveCare ? AppColors.whiteColor : AppColors.textColor),
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: isLoading
? 'OutPatient'.needTranslation
: (appState.isArabic()
? patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
textColor: AppColors.primaryRedColor,
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: isLoading
? 'Booked'.needTranslation
: AppointmentType.getAppointmentStatusType(patientAppointmentHistoryResponseModel.patientStatusType!),
backgroundColor: AppColors.successColor.withValues(alpha: 0.1),
textColor: AppColors.successColor,
).toShimmer2(isShow: isLoading),
],
).toShimmer2(isShow: isLoading);
}
Widget _buildDoctorRow(BuildContext context) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
isLoading
? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png'
: patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: isLoading),
SizedBox(width: 16.h),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(isLoading
? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png'
: patientAppointmentHistoryResponseModel.doctorNameObj!)
.toText16(isBold: true, maxlines: 1)
.toShimmer2(isShow: isLoading),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 3.h,
runSpacing: 4.h,
children: [
if (!isFromHomePage)
AppCustomChipWidget(labelText: isLoading ? 'Cardiology' : patientAppointmentHistoryResponseModel.clinicName!)
.toShimmer2(isShow: isLoading),
if (!isFromHomePage)
AppCustomChipWidget(labelText: isLoading ? 'Olaya' : patientAppointmentHistoryResponseModel.projectName!)
.toShimmer2(isShow: isLoading),
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: isLoading
? 'Cardiology'
: DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false),
).toShimmer2(isShow: isLoading),
if (!isFromMedicalReport)
AppCustomChipWidget(
icon: AppAssets.appointment_time_icon,
labelText: isLoading
? 'Cardiology'
: DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false),
).toShimmer2(isShow: isLoading),
],
),
],
),
),
],
); );
widget.bookAppointmentsViewModel.setSelectedDoctor(doctor); }
LoaderBottomSheet.showLoader();
await widget.bookAppointmentsViewModel.getDoctorFreeSlots( Widget _buildActionArea(BuildContext context, AppState appState) {
isBookingForLiveCare: false, if (isFromMedicalReport) {
onSuccess: (dynamic respData) async { return CustomButton(
LoaderBottomSheet.hideLoader(); text: 'Select appointment'.needTranslation,
showCommonBottomSheetWithoutHeight( onPressed: () {
title: "Pick a Date".needTranslation, medicalFileViewModel!.setSelectedMedicalReportAppointment(patientAppointmentHistoryResponseModel);
context, Navigator.pop(context, false);
child: AppointmentCalendar(),
isFullScreen: false,
isCloseButtonVisible: true,
callBackFunc: () {},
);
}, },
onError: (err) { backgroundColor: AppColors.secondaryLightRedColor,
LoaderBottomSheet.hideLoader(); borderColor: AppColors.secondaryLightRedColor,
showCommonBottomSheetWithoutHeight( textColor: AppColors.primaryRedColor,
context, fontSize: 14.f,
child: Utils.getErrorWidget(loadingText: err), fontWeight: FontWeight.w500,
callBackFunc: () {}, borderRadius: 12.r,
isFullScreen: false, padding: EdgeInsets.symmetric(horizontal: 10.w),
isCloseButtonVisible: true, height: isTablet || isFoldable ? 46.h : 40.h,
); icon: AppAssets.checkmark_icon,
}); iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
}
return Row(
children: [
Expanded(
flex: 6,
child: (AppointmentType.isArrived(patientAppointmentHistoryResponseModel)
? _getArrivedButton(context)
: CustomButton(
text: AppointmentType.getNextActionText(patientAppointmentHistoryResponseModel.nextAction),
onPressed: () => _goToDetails(context),
backgroundColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
borderColor:
AppointmentType.getNextActionButtonColor(patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppointmentType.getNextActionIcon(patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(patientAppointmentHistoryResponseModel.nextAction),
iconSize: 15.h,
))
.toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.h),
Expanded(
flex: 1,
child: Container(
height: (isFoldable || isTablet) ? 50.h : 40.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.textColor, borderRadius: 10.h),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon,
iconColor: AppColors.whiteColor,
width: 24.w,
height: 24.h,
fit: BoxFit.contain,
),
),
).toShimmer2(isShow: isLoading).onPress(() {
_goToDetails(context);
}),
),
],
);
}
Widget _getArrivedButton(BuildContext context) {
final isRecent = DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15;
if (isRecent) {
return CustomButton(
text: LocaleKeys.askDoctor.tr(context: context),
onPressed: () {},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppAssets.ask_doctor_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
}
return CustomButton(
text: 'Rebook with same doctor'.needTranslation,
onPressed: () => openDoctorScheduleCalendar(context),
backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor,
textColor: AppColors.blackColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: isTablet || isFoldable ? 46.h : 40.h,
icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor,
iconSize: 16.h,
);
}
void _goToDetails(BuildContext context) {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel),
),
)
.then((_) {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
});
} }
void performAppointmentNextAction(nextAction) {} void openDoctorScheduleCalendar(BuildContext context) async {
final doctor = DoctorsListResponseModel(
clinicID: patientAppointmentHistoryResponseModel.clinicID,
projectID: patientAppointmentHistoryResponseModel.projectID,
doctorID: patientAppointmentHistoryResponseModel.doctorID,
doctorImageURL: patientAppointmentHistoryResponseModel.doctorImageURL,
doctorTitle: patientAppointmentHistoryResponseModel.doctorTitle,
name: patientAppointmentHistoryResponseModel.doctorNameObj,
nationalityFlagURL: 'https://hmgwebservices.com/Images/flag/SYR.png',
speciality: [],
clinicName: patientAppointmentHistoryResponseModel.clinicName,
projectName: patientAppointmentHistoryResponseModel.projectName,
);
bookAppointmentsViewModel.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsViewModel.getDoctorFreeSlots(
isBookingForLiveCare: false,
onSuccess: (respData) async {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: AppointmentCalendar(),
callBackFunc: () {},
title: 'Pick a Date'.needTranslation,
isFullScreen: false,
isCloseButtonVisible: true,
);
},
onError: (err) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: err),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
},
);
}
} }

@ -13,12 +13,17 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
class AppointmentDoctorCard extends StatelessWidget { class AppointmentDoctorCard extends StatelessWidget {
AppointmentDoctorCard({super.key, required this.patientAppointmentHistoryResponseModel, required this.onRescheduleTap, required this.onCancelTap, required this.onAskDoctorTap}); AppointmentDoctorCard(
{super.key,
required this.patientAppointmentHistoryResponseModel,
required this.onRescheduleTap,
required this.onCancelTap,
required this.onAskDoctorTap});
PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
late Function onRescheduleTap; final VoidCallback onRescheduleTap;
late Function onCancelTap; final VoidCallback onCancelTap;
late Function onAskDoctorTap; final VoidCallback onAskDoctorTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -38,11 +43,11 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [ children: [
Image.network( Image.network(
patientAppointmentHistoryResponseModel.doctorImageURL!, patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h, width: 63.w,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
).circle(100), ).circle(100.r),
SizedBox(width: 16.h), SizedBox(width: 16.w),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -50,23 +55,41 @@ class AppointmentDoctorCard extends StatelessWidget {
patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true), patientAppointmentHistoryResponseModel.doctorNameObj!.toText16(isBold: true),
Wrap( Wrap(
direction: Axis.horizontal, direction: Axis.horizontal,
spacing: 3.h, spacing: 6.w,
runSpacing: 4.h, runSpacing: 6.h,
children: [ children: [
AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.clinicName!),
AppCustomChipWidget(labelText: patientAppointmentHistoryResponseModel.projectName!),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon, labelText: patientAppointmentHistoryResponseModel.clinicName!,
labelText: labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 4.w),
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}"), ),
AppCustomChipWidget(
labelText: patientAppointmentHistoryResponseModel.projectName!,
labelPadding: EdgeInsetsDirectional.only(start: 6.w, end: 6.w),
),
AppCustomChipWidget( AppCustomChipWidget(
icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon, icon: AppAssets.doctor_calendar_icon,
labelText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}, ${DateUtil.formatDateToTimeLang(
DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate),
false,
)}",
),
AppCustomChipWidget(
icon: !patientAppointmentHistoryResponseModel.isLiveCareAppointment!
? AppAssets.walkin_appointment_icon
: AppAssets.small_livecare_icon,
iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, iconColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor,
labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? LocaleKeys.livecare.tr(context: context) : "Walk In".needTranslation, labelText: patientAppointmentHistoryResponseModel.isLiveCareAppointment!
backgroundColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor, ? LocaleKeys.livecare.tr(context: context)
: "Walk In".needTranslation,
backgroundColor:
!patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor,
textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor, textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor,
), ),
AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"), AppCustomChipWidget(
icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
], ],
), ),
], ],
@ -93,10 +116,10 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h, height: 40.h,
iconSize: 16.h, iconSize: 16.h,
icon: AppAssets.ask_doctor_icon, icon: AppAssets.ask_doctor_icon,
@ -110,10 +133,10 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.greyColor, backgroundColor: AppColors.greyColor,
borderColor: AppColors.greyColor, borderColor: AppColors.greyColor,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0), padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h, height: 40.h,
icon: AppAssets.rebook_appointment_icon, icon: AppAssets.rebook_appointment_icon,
iconColor: AppColors.blackColor, iconColor: AppColors.blackColor,
@ -129,9 +152,9 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.h, borderRadius: 12.r,
height: 40.h, height: 40.h,
icon: AppAssets.cancel, icon: AppAssets.cancel,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,
@ -148,9 +171,9 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.secondaryLightRedColor, backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor, borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.h, borderRadius: 12.r,
height: 40.h, height: 40.h,
icon: AppAssets.appointment_calendar_icon, icon: AppAssets.appointment_calendar_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
@ -167,9 +190,9 @@ class AppointmentDoctorCard extends StatelessWidget {
backgroundColor: AppColors.primaryRedColor, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor, borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
fontSize: 14, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.h, borderRadius: 12.r,
height: 40.h, height: 40.h,
icon: AppAssets.cancel, icon: AppAssets.cancel,
iconColor: AppColors.whiteColor, iconColor: AppColors.whiteColor,

@ -1,6 +1,5 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart';
@ -12,7 +11,6 @@ import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/home/landing_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart'; import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
@ -21,7 +19,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class SavedLogin extends StatefulWidget { class SavedLogin extends StatefulWidget {
const SavedLogin({Key? key}) : super(key: key); const SavedLogin({super.key});
@override @override
_SavedLogin createState() => _SavedLogin(); _SavedLogin createState() => _SavedLogin();
@ -65,248 +63,255 @@ class _SavedLogin extends State<SavedLogin> {
body: SafeArea( body: SafeArea(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h), padding: EdgeInsets.symmetric(horizontal: 24.h),
child: appState.getSelectDeviceByImeiRespModelElement != null ? Column( child: appState.getSelectDeviceByImeiRespModelElement != null
crossAxisAlignment: CrossAxisAlignment.center, ? Column(
children: [ crossAxisAlignment: CrossAxisAlignment.center,
const Spacer(flex: 2),
// Welcome back text
LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(height: 16.h),
appState.getSelectDeviceByImeiRespModelElement != null
? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase.toText26(isBold: true, height: 26 / 36, color: AppColors.textColor)
: SizedBox(),
SizedBox(height: 24.h),
Container(
padding: EdgeInsets.all(16.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
border: Border.all(color: AppColors.whiteColor),
borderRadius: BorderRadius.circular(24.h),
boxShadow: [
BoxShadow(color: Color(0x0D000000), blurRadius: 16, offset: Offset(0, 0), spreadRadius: 5),
],
),
child: Column(
children: [ children: [
// Last login info const Spacer(flex: 2),
// Welcome back text
("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}").toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1), LocaleKeys.welcomeBack.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(height: 16.h),
appState.getSelectDeviceByImeiRespModelElement != null appState.getSelectDeviceByImeiRespModelElement != null
? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null ? appState.getSelectDeviceByImeiRespModelElement!.name!.toCamelCase
? DateUtil.getFormattedDate(DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!), "d MMMM, y 'at' HH:mm") .toText26(isBold: true, height: 26 / 36, color: AppColors.textColor)
: '--')
.toText16(isBold: true, color: AppColors.textColor)
: SizedBox(), : SizedBox(),
SizedBox(height: 24.h),
Container(
padding: EdgeInsets.all(16.h),
decoration: BoxDecoration(
color: AppColors.whiteColor,
border: Border.all(color: AppColors.whiteColor),
borderRadius: BorderRadius.circular(24.h),
boxShadow: [
BoxShadow(color: Color(0x0D000000), blurRadius: 16.h, offset: Offset(0, 0), spreadRadius: 5.h),
],
),
child: Column(
children: [
// Last login info
appState.getSelectDeviceByImeiRespModelElement != null ("${LocaleKeys.lastLoginBy.tr()} ${loginType.displayName}")
? Container( .toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -1),
margin: EdgeInsets.all(16.h), appState.getSelectDeviceByImeiRespModelElement != null
child: Utils.buildSvgWithAssets( ? (appState.getSelectDeviceByImeiRespModelElement!.createdOn != null
icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!), ? DateUtil.getFormattedDate(
height: 54, DateUtil.convertStringToDate(appState.getSelectDeviceByImeiRespModelElement!.createdOn!),
width: 54, "d MMMM, y 'at' HH:mm")
iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor)) : '--')
: SizedBox(), .toText16(isBold: true, color: AppColors.textColor)
// Face ID login button : SizedBox(),
SizedBox(
height: 45, appState.getSelectDeviceByImeiRespModelElement != null
child: CustomButton( ? Container(
text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}", margin: EdgeInsets.all(16.h),
onPressed: () { child: Utils.buildSvgWithAssets(
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) { icon: getTypeIcons(appState.getSelectDeviceByImeiRespModelElement!.logInType!),
authVm.loginWithFingerPrintFace(() {}); height: 54.h,
} else { width: 54.w,
authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp); iconColor: loginType.toInt == 4 ? null : AppColors.primaryRedColor))
} : SizedBox(),
}, // Face ID login button
backgroundColor: AppColors.primaryRedColor, CustomButton(
borderColor: AppColors.primaryRedColor, text: "${LocaleKeys.loginBy.tr()} ${loginType.displayName}",
textColor: Colors.white, onPressed: () {
fontSize: 12, if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
fontWeight: FontWeight.w500, authVm.loginWithFingerPrintFace(() {});
borderRadius: 12, } else {
padding: EdgeInsets.fromLTRB(0, 10, 0, 10), authVm.checkUserAuthentication(otpTypeEnum: loginType == LoginTypeEnum.sms ? OTPTypeEnum.sms : OTPTypeEnum.whatsapp);
icon: getTypeIcons(loginType.toInt), }
//loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp, },
iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null, backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
fontSize: 12.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
height: 40.h,
padding: EdgeInsets.symmetric(vertical: 10.h),
icon: getTypeIcons(loginType.toInt),
//loginType == LoginTypeEnum.sms ? AppAssets.sms :AppAssets.whatsapp,
iconColor: loginType != LoginTypeEnum.whatsapp ? Colors.white : null,
),
],
), ),
), ),
], SizedBox(height: 24.h),
), Padding(
), padding: EdgeInsets.symmetric(horizontal: 16.w),
const SizedBox(height: 24), child: Text(
Padding( LocaleKeys.oR.tr(),
padding: EdgeInsets.symmetric(horizontal: 16.0), style: context.dynamicTextStyle(fontSize: 16.f, fontWeight: FontWeight.w500),
child: Text( ),
LocaleKeys.oR.tr(), ),
style: context.dynamicTextStyle(fontSize: 16, fontWeight: FontWeight.w500), SizedBox(height: 24.h),
), // OTP login button
), loginType.toInt != 1
const SizedBox(height: 24), ? Column(
// OTP login button children: [
loginType.toInt != 1 loginType.toInt != 1
? Column( ? CustomButton(
children: [ text: LocaleKeys.loginByOTP.tr(),
loginType.toInt != 1 onPressed: () {
? CustomButton( showModalBottomSheet(
text: LocaleKeys.loginByOTP.tr(), context: context,
onPressed: () { isScrollControlled: true,
showModalBottomSheet( isDismissible: false,
context: context, useSafeArea: true,
isScrollControlled: true, backgroundColor: Colors.transparent,
isDismissible: false, enableDrag: false,
useSafeArea: true, // Prevent dragging to avoid focus conflicts
backgroundColor: Colors.transparent, builder: (bottomSheetContext) =>
enableDrag: false, StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) {
// Prevent dragging to avoid focus conflicts return Padding(
builder: (bottomSheetContext) => StatefulBuilder(builder: (BuildContext context, StateSetter setModalState) { padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom),
return Padding( child: SingleChildScrollView(
padding: EdgeInsets.only(bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom), child: GenericBottomSheet(
child: SingleChildScrollView( countryCode: "966",
child: GenericBottomSheet( initialPhoneNumber: "",
countryCode: "966", textController: TextEditingController(),
initialPhoneNumber: "", isFromSavedLogin: true,
textController: TextEditingController(), isEnableCountryDropdown: true,
isFromSavedLogin: true, onCountryChange: (value) {},
isEnableCountryDropdown: true, onChange: (String? value) {},
onCountryChange: (value) {}, buttons: [
onChange: (String? value) {}, Padding(
buttons: [ padding: EdgeInsets.only(bottom: 10.h),
Padding( child: CustomButton(
padding: EdgeInsets.only(bottom: 10.h), text: LocaleKeys.sendOTPSMS.tr(),
child: CustomButton( onPressed: () {
text: LocaleKeys.sendOTPSMS.tr(), Navigator.of(context).pop();
onPressed: () { loginType = LoginTypeEnum.sms;
Navigator.of(context).pop(); authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms);
loginType = LoginTypeEnum.sms; },
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.sms); backgroundColor: AppColors.primaryRedColor,
}, borderColor: AppColors.primaryRedColor,
backgroundColor: AppColors.primaryRedColor, textColor: AppColors.whiteColor,
borderColor: AppColors.primaryRedColor, icon: AppAssets.sms),
textColor: AppColors.whiteColor, ),
icon: AppAssets.sms), Row(
), crossAxisAlignment: CrossAxisAlignment.center,
Row( mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, children: [
mainAxisAlignment: MainAxisAlignment.center, Padding(
children: [ padding: EdgeInsets.symmetric(horizontal: 8.h),
Padding(padding: EdgeInsets.symmetric(horizontal: 8.h), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)), child: (LocaleKeys.oR.tr()).toText16(color: AppColors.textColor)),
], ],
), ),
Padding( Padding(
padding: const EdgeInsets.only(bottom: 10, top: 10), padding: EdgeInsets.only(bottom: 10.h, top: 10.h),
child: CustomButton( child: CustomButton(
text: LocaleKeys.sendOTPWHATSAPP.tr(), text: LocaleKeys.sendOTPWHATSAPP.tr(),
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
loginType = LoginTypeEnum.whatsapp; loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp); authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
}, },
backgroundColor: AppColors.transparent, backgroundColor: AppColors.transparent,
borderColor: AppColors.textColor, borderColor: AppColors.textColor,
textColor: AppColors.textColor, textColor: AppColors.textColor,
icon: AppAssets.whatsapp, icon: AppAssets.whatsapp,
iconColor: null, iconColor: null,
),
),
],
), ),
), ),
], );
), }),
), );
); },
}), backgroundColor: AppColors.whiteColor,
); borderColor: AppColors.borderOnlyColor,
}, textColor: AppColors.textColor,
backgroundColor: AppColors.whiteColor, borderWidth: 2,
borderColor: AppColors.borderOnlyColor, padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h),
textColor: AppColors.textColor, icon: AppAssets.sms,
borderWidth: 2, iconColor: AppColors.textColor,
padding: EdgeInsets.fromLTRB(0, 14, 0, 14), )
icon: AppAssets.sms, : Container(),
iconColor: AppColors.textColor, SizedBox(
) height: 20.h,
: Container(), ),
],
)
: CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}",
icon: AppAssets.whatsapp,
iconColor: null,
onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {});
} else {
loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
}
},
backgroundColor: AppColors.whiteColor,
borderColor: Color(0xFF2E3039),
textColor: Color(0xFF2E3039),
borderWidth: 2.w,
padding: EdgeInsets.fromLTRB(0, 14.h, 0, 14.h),
),
const Spacer(flex: 2),
// OR divider
SizedBox(height: 24.h),
// Guest and Switch account
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SizedBox(
height: 56.h,
child: CustomButton(
text: LocaleKeys.guest.tr(),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 16.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
SizedBox( SizedBox(
height: 20, width: MediaQuery.of(context).size.width * 0.05,
),
Expanded(
child: SizedBox(
height: 56.h,
child: CustomButton(
text: LocaleKeys.switchAccount.tr(),
onPressed: () async {
await authVm.clearDefaultInputValues();
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 15.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(0, 10.h, 0, 10.h),
// icon: "assets/images/svg/apple-finder.svg",
),
),
), ),
], ],
)
: CustomButton(
text: "${LocaleKeys.loginBy.tr()} ${LoginTypeEnum.whatsapp.displayName}",
icon: AppAssets.whatsapp,
iconColor: null,
onPressed: () {
if (loginType == LoginTypeEnum.fingerprint || loginType == LoginTypeEnum.face) {
authVm.loginWithFingerPrintFace(() {});
} else {
loginType = LoginTypeEnum.whatsapp;
authVm.checkUserAuthentication(otpTypeEnum: OTPTypeEnum.whatsapp);
}
},
backgroundColor: AppColors.whiteColor,
borderColor: Color(0xFF2E3039),
textColor: Color(0xFF2E3039),
borderWidth: 2,
padding: EdgeInsets.fromLTRB(0, 14, 0, 14),
), ),
const Spacer(flex: 2), SizedBox(height: 20.h),
// OR divider ],
)
const SizedBox(height: 24), : SizedBox.shrink(),
// Guest and Switch account
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SizedBox(
height: 56,
child: CustomButton(
text: LocaleKeys.guest.tr(),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => LandingNavigation()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 16,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.05,
),
Expanded(
child: SizedBox(
height: 56,
child: CustomButton(
text: LocaleKeys.switchAccount.tr(),
onPressed: () async {
await authVm.clearDefaultInputValues();
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 15,
fontWeight: FontWeight.w500,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
// icon: "assets/images/svg/apple-finder.svg",
),
),
),
],
),
const SizedBox(height: 20),
],
) : SizedBox.shrink(),
), ),
), ),
); );

@ -13,7 +13,6 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart'; import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody; import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody;
@ -21,14 +20,13 @@ import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immed
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/search_doctor_by_name.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart' show showCommonBottomSheetWithoutHeight;
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart'; import '../appointments/widgets/hospital_bottom_sheet/hospital_bottom_sheet_body.dart';
@ -130,7 +128,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
], ],
), ),
Transform.flip( Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
], ],
).onPress(() { ).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true); bookAppointmentsViewModel.setIsClinicsListLoading(true);
@ -163,7 +163,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
], ],
), ),
Transform.flip( Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
], ],
).onPress(() { ).onPress(() {
bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false); bookAppointmentsViewModel.setIsDoctorSearchByNameStarted(false);
@ -194,7 +196,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
], ],
), ),
Transform.flip( Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
], ],
).onPress(() { ).onPress(() {
bookAppointmentsViewModel.setProjectID(null); bookAppointmentsViewModel.setProjectID(null);
@ -214,115 +218,121 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
Container( Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 24.h, borderRadius: 24.h,
hasShadow: false, hasShadow: false,
), ),
child: Padding( child: Padding(
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.search_by_clinic_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), "Immediate Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
], ],
), ),
], ],
), ),
Transform.flip( Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), flipX: appState.isArabic(),
], child: Utils.buildSvgWithAssets(
).onPress(() async { icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
//TODO Implement API to check for existing LiveCare Requests ],
).onPress(() async {
//TODO Implement API to check for existing LiveCare Requests
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
await immediateLiveCareViewModel.getPatientLiveCareHistory(); await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) { if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(), page: ImmediateLiveCarePendingRequestPage(),
), ),
); );
} else { } else {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: SelectImmediateLiveCareClinicPage(), page: SelectImmediateLiveCareClinicPage(),
), ),
); );
} }
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h), Utils.buildSvgWithAssets(icon: AppAssets.search_by_doctor_icon, width: 40.h, height: 40.h),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), "Scheduled Consultation".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
"Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), "Tap to select clinic".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
], ],
), ),
], ],
), ),
Transform.flip( Transform.flip(
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), flipX: appState.isArabic(),
], child: Utils.buildSvgWithAssets(
).onPress(() { icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
bookAppointmentsViewModel.setIsClinicsListLoading(true); ],
bookAppointmentsViewModel.setIsLiveCareSchedule(true); ).onPress(() {
Navigator.of(context).push( bookAppointmentsViewModel.setIsClinicsListLoading(true);
CustomPageRoute( bookAppointmentsViewModel.setIsLiveCareSchedule(true);
page: SelectClinicPage(), Navigator.of(context).push(
), CustomPageRoute(
); page: SelectClinicPage(),
}), ),
SizedBox(height: 16.h), );
Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.1), height: 1.h),
mainAxisAlignment: MainAxisAlignment.spaceBetween, SizedBox(height: 16.h),
children: [ Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h), Row(
SizedBox(width: 12.h), children: [
Column( Utils.buildSvgWithAssets(icon: AppAssets.search_by_region_icon, width: 40.h, height: 40.h),
crossAxisAlignment: CrossAxisAlignment.start, SizedBox(width: 12.h),
children: [ Column(
"Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500), crossAxisAlignment: CrossAxisAlignment.start,
"".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500), children: [
], "Pharma LiveCare".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w500),
), "".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
], ],
), ),
Transform.flip( ],
flipX: appState.isArabic(), child: Utils.buildSvgWithAssets(icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)), ),
], Transform.flip(
).onPress(() { flipX: appState.isArabic(),
openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION); child: Utils.buildSvgWithAssets(
}), icon: AppAssets.forward_arrow_icon, iconColor: AppColors.textColor, width: 40.h, height: 40.h)),
], ],
), ).onPress(() {
), openRegionListBottomSheet(context, RegionBottomSheetType.FOR_REGION);
), }),
], ],
),
),
),
],
).paddingSymmetrical(24.h, 0.h) ).paddingSymmetrical(24.h, 0.h)
: getLiveCareNotLoggedInUI(); : getLiveCareNotLoggedInUI();
default: default:
@ -335,8 +345,10 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
regionalViewModel.flush(); regionalViewModel.flush();
regionalViewModel.setBottomSheetType(type); regionalViewModel.setBottomSheetType(type);
// AppointmentViaRegionViewmodel? viewmodel = null; // AppointmentViaRegionViewmodel? viewmodel = null;
showCommonBottomSheetWithoutHeight(context, title: "", titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)), isDismissible: false, showCommonBottomSheetWithoutHeight(context,
child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) { title: "",
titleWidget: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) => getTitle(data)),
isDismissible: false, child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data); return getRegionalSelectionWidget(data);
}), callBackFunc: () {}); }), callBackFunc: () {});
} }
@ -395,7 +407,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true), "Immediate service".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"No need to wait, you will get medical consultation immediately via video call".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), "No need to wait, you will get medical consultation immediately via video call"
.needTranslation
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
], ],
), ),
), ),
@ -427,7 +441,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true), "Doctor will contact".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"A specialised doctor will contact you and will be able to view your medical history".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), "A specialised doctor will contact you and will be able to view your medical history"
.needTranslation
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
], ],
), ),
), ),
@ -443,7 +459,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true), "Free medicine delivery".needTranslation.toText18(color: AppColors.textColor, isBold: true),
"Offers free medicine delivery for the LiveCare appointment".needTranslation.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500), "Offers free medicine delivery for the LiveCare appointment"
.needTranslation
.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500),
], ],
), ),
), ),

@ -7,7 +7,6 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart'; import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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/buttons/custom_button.dart';
@ -21,49 +20,52 @@ import 'package:provider/provider.dart';
class TrackingScreen extends StatelessWidget { class TrackingScreen extends StatelessWidget {
final OrderTrackingState state = OrderTrackingState.ended; final OrderTrackingState state = OrderTrackingState.ended;
const TrackingScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
bottomNavigationBar: Visibility( bottomNavigationBar: Visibility(
visible: state == OrderTrackingState.ended, visible: state == OrderTrackingState.ended,
child: SafeArea( child: SafeArea(
child: CustomButton( child: CustomButton(
height: 56.h, height: 56.h,
backgroundColor: AppColors.bgGreenColor, backgroundColor: AppColors.bgGreenColor,
borderColor: Colors.transparent, borderColor: Colors.transparent,
text: "Close".needTranslation, text: "Close".needTranslation,
textColor: AppColors.whiteColor, textColor: AppColors.whiteColor,
onPressed: () {}, onPressed: () {},
).paddingOnly(left:16.h,right:16.h), ).paddingOnly(left: 16.h, right: 16.h),
), ),
), ),
body: CollapsingListView( body: CollapsingListView(
title: "Tracking Details".needTranslation, title: "Tracking Details".needTranslation,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
animationSection(), animationSection(),
Column( Column(
spacing: 16.h, spacing: 16.h,
children: [OrderStatus(context), OrderTrackingId(), ContactSection()], children: [orderStatus(context), orderTrackingId(), contactSection()],
).paddingAll(16.h), ).paddingAll(16.h),
], ],
))), ))),
); );
} }
animationSection(){
animationSection() {
switch (state) { switch (state) {
case OrderTrackingState.waitingForCall: case OrderTrackingState.waitingForCall:
case OrderTrackingState.dispactched: case OrderTrackingState.dispactched:
case OrderTrackingState.returning: case OrderTrackingState.returning:
return Lottie.asset(AppAnimations.ambulance, repeat: true, reverse: false, width: 260.h, height: 200.h); return Lottie.asset(AppAnimations.ambulance, repeat: true, reverse: false, width: 260.h, height: 200.h);
case OrderTrackingState.ended: case OrderTrackingState.ended:
default: return Lottie.asset(AppAnimations.checkmark, repeat: true, width: 260.h, height: 200.h);
return Lottie.asset(AppAnimations.checkmark, repeat: true, width: 260.h, height: 200.h);
} }
} }
OrderStatus(BuildContext context) { orderStatus(BuildContext context) {
return Container( return Container(
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@ -95,7 +97,9 @@ class TrackingScreen extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox(height: 16,), SizedBox(
height: 16,
),
CustomButton( CustomButton(
text: "Cancel Request".needTranslation, text: "Cancel Request".needTranslation,
onPressed: () async {}, onPressed: () async {},
@ -131,14 +135,9 @@ class TrackingScreen extends StatelessWidget {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12.h, spacing: 12.h,
children: [ children: [dividerSection, doctorSection(), mapSection(context)],
dividerSection,
doctorSection(),
mapSection(context)
],
); );
case OrderTrackingState.ended: case OrderTrackingState.ended:
default:
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12.h, spacing: 12.h,
@ -170,17 +169,14 @@ class TrackingScreen extends StatelessWidget {
return Row( return Row(
spacing: 16.h, spacing: 16.h,
children: [ children: [
Utils.buildImgWithNetwork(url: "",) Utils.buildImgWithNetwork(url: "", iconColor: Colors.transparent).circle(52.h),
.circle(52.h),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
"Dr. John Doe".toText16( "Dr. John Doe".toText16(color: AppColors.textColor, weight: FontWeight.w600),
color: AppColors.textColor, weight: FontWeight.w600),
SizedBox(height: 4.h), SizedBox(height: 4.h),
"MBBS, MD - General Medicine".toText12( "MBBS, MD - General Medicine".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
color: AppColors.greyTextColor, fontWeight: FontWeight.w500),
], ],
), ),
), ),
@ -204,23 +200,17 @@ class TrackingScreen extends StatelessWidget {
Widget mapForRequestedCoordinate(BuildContext context) { Widget mapForRequestedCoordinate(BuildContext context) {
// return SizedBox.shrink(); // return SizedBox.shrink();
if (context.read<EmergencyServicesViewModel>().isGMSAvailable || if (context.read<EmergencyServicesViewModel>().isGMSAvailable || Platform.isIOS) {
Platform.isIOS) return GMSMap(
return GMSMap( myLocationEnabled: false,
myLocationEnabled: false, currentLocation: context.read<EmergencyServicesViewModel>().getGMSLocation(),
currentLocation: onCameraMoved: (value) => context.read<EmergencyServicesViewModel>().handleGMSMapCameraMoved(value));
context.read<EmergencyServicesViewModel>().getGMSLocation(), } else {
onCameraMoved: (value) => context
.read<EmergencyServicesViewModel>()
.handleGMSMapCameraMoved(value));
else
return HMSMap( return HMSMap(
myLocationEnabled: false, myLocationEnabled: false,
currentLocation: currentLocation: context.read<EmergencyServicesViewModel>().getHMSLocation(),
context.read<EmergencyServicesViewModel>().getHMSLocation(), onCameraMoved: (value) => context.read<EmergencyServicesViewModel>().handleHMSMapCameraMoved(value));
onCameraMoved: (value) => context }
.read<EmergencyServicesViewModel>()
.handleHMSMapCameraMoved(value));
} }
informationRegardingTrackingSection() { informationRegardingTrackingSection() {
@ -229,74 +219,70 @@ class TrackingScreen extends StatelessWidget {
children: [ children: [
currentStatus(), currentStatus(),
SizedBox(height: 4.h), SizedBox(height: 4.h),
"You will receive a call from HMG for confirmation ".toText12( "You will receive a call from HMG for confirmation ".toText12(color: AppColors.greyTextColor, fontWeight: FontWeight.w500)
color: AppColors.greyTextColor, fontWeight: FontWeight.w500)
], ],
); );
} }
currentStatus() { currentStatus() {
switch (state) { switch (state) {
case OrderTrackingState.waitingForCall: case OrderTrackingState.waitingForCall:
case OrderTrackingState.dispactched: case OrderTrackingState.dispactched:
return RichText( return RichText(
text: TextSpan(children: <TextSpan>[ text: TextSpan(children: <TextSpan>[
TextSpan( TextSpan(
text: "Please wait for the call".needTranslation, text: "Please wait for the call".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 21.f, fontSize: 21.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.textColor, color: AppColors.textColor,
), ),
), ),
TextSpan( TextSpan(
text: "...".needTranslation, text: "...".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 21.f, fontSize: 21.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.errorColor, color: AppColors.errorColor,
), ),
), ),
]), ]),
); );
case OrderTrackingState.returning: case OrderTrackingState.returning:
return RichText( return RichText(
text: TextSpan(children: <TextSpan>[ text: TextSpan(children: <TextSpan>[
TextSpan( TextSpan(
text: "15:30".needTranslation, text: "15:30".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 21.f, fontSize: 21.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.textColor, color: AppColors.textColor,
), ),
), ),
TextSpan( TextSpan(
text: " mins ".needTranslation, text: " mins ".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 21.f, fontSize: 21.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.errorColor, color: AppColors.errorColor,
), ),
), ),
TextSpan( TextSpan(
text: "to hospital".needTranslation, text: "to hospital".needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 21.f, fontSize: 21.f,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: AppColors.textColor, color: AppColors.textColor,
), ),
), ),
]), ]),
); );
case OrderTrackingState.ended: case OrderTrackingState.ended:
default: return "Arrived".needTranslation.toText21(color: AppColors.textColor, weight: FontWeight.w600);
return "Arrived" }
.needTranslation
.toText21(color: AppColors.textColor, weight: FontWeight.w600);
}
} }
OrderTrackingId() { orderTrackingId() {
return Container( return Container(
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@ -308,25 +294,21 @@ class TrackingScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.h, spacing: 8.h,
children: [ children: [
"Req ID:1234567890" "Req ID:1234567890".toText16(color: AppColors.textColor, weight: FontWeight.w600),
.toText16(color: AppColors.textColor, weight: FontWeight.w600),
Row( Row(
spacing: 8.h, spacing: 8.h,
children: [ children: [
Chip("location name".needTranslation, AppAssets.location_pickup, chip("location name".needTranslation, AppAssets.location_pickup, AppColors.blackBgColor),
AppColors.blackBgColor), chip("hotel name ".needTranslation, AppAssets.hospital, AppColors.blackBgColor),
Chip("hotel name ".needTranslation, AppAssets.hospital,
AppColors.blackBgColor),
], ],
), ),
Chip("hotel name ".needTranslation, AppAssets.hospital, chip("hotel name ".needTranslation, AppAssets.hospital, AppColors.blackBgColor),
AppColors.blackBgColor),
], ],
), ),
); );
} }
Chip(String title, String iconString, Color iconColor) { chip(String title, String iconString, Color iconColor) {
return AppCustomChipWidget( return AppCustomChipWidget(
labelText: title, labelText: title,
icon: iconString, icon: iconString,
@ -335,7 +317,7 @@ class TrackingScreen extends StatelessWidget {
); );
} }
ContactSection() { contactSection() {
return Container( return Container(
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@ -350,11 +332,8 @@ class TrackingScreen extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.h, spacing: 4.h,
children: [ children: [
"Contact Rapid Response Team (RRT)".needTranslation.toText14( "Contact Rapid Response Team (RRT)".needTranslation.toText14(color: AppColors.textColor, weight: FontWeight.w600),
color: AppColors.textColor, weight: FontWeight.w600), "1223456789".needTranslation.toText12(color: AppColors.primaryRedColor, fontWeight: FontWeight.w500),
"1223456789".needTranslation.toText12(
color: AppColors.primaryRedColor,
fontWeight: FontWeight.w500),
SizedBox(height: 8.h), SizedBox(height: 8.h),
], ],
), ),
@ -390,27 +369,21 @@ class TrackingScreen extends StatelessWidget {
getTitle(OrderTrackingState state) { getTitle(OrderTrackingState state) {
switch (state) { switch (state) {
case OrderTrackingState.waitingForCall: case OrderTrackingState.waitingForCall:
return "Confirmation Call".needTranslation return "Confirmation Call".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600);
.toText16(color: AppColors.textColor, weight: FontWeight.w600);
case OrderTrackingState.dispactched: case OrderTrackingState.dispactched:
return "Pickup Up from Home".needTranslation return "Pickup Up from Home".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600);
.toText16(color: AppColors.textColor, weight: FontWeight.w600);
case OrderTrackingState.returning: case OrderTrackingState.returning:
return " On The Way To Hospital".needTranslation return " On The Way To Hospital".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600);
.toText16(color: AppColors.textColor, weight: FontWeight.w600);
case OrderTrackingState.ended: case OrderTrackingState.ended:
default: return "Arrived at Hospital".needTranslation.toText16(color: AppColors.textColor, weight: FontWeight.w600);
return "Arrived at Hospital".needTranslation
.toText16(color: AppColors.textColor, weight: FontWeight.w600);
} }
} }
mapSection(BuildContext context) { mapSection(BuildContext context) {
return SizedBox( return SizedBox(
height: 170.h, height: 170.h,
width: double.infinity, width: double.infinity,
child: ClipRRect( child: ClipRRect(borderRadius: BorderRadius.circular(20.h), child: mapForRequestedCoordinate(context)),
borderRadius: BorderRadius.circular(20.h),child: mapForRequestedCoordinate(context)),
); );
} }
} }

@ -108,408 +108,390 @@ class _LandingPageState extends State<LandingPage> {
insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false); insuranceViewModel = Provider.of<InsuranceViewModel>(context, listen: false);
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false); immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
appState = getIt.get<AppState>(); appState = getIt.get<AppState>();
return Scaffold( return PopScope(
backgroundColor: AppColors.bgScaffoldColor, canPop: false,
body: SingleChildScrollView( child: Scaffold(
padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24), backgroundColor: AppColors.bgScaffoldColor,
child: Column( body: SingleChildScrollView(
spacing: 16.h, padding: EdgeInsets.only(top: kToolbarHeight + 0.h, bottom: 24),
children: [ child: Column(
Row( spacing: 16.h,
spacing: 8.h, children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ spacing: 8.h,
appState.isAuthenticated mainAxisAlignment: MainAxisAlignment.spaceBetween,
? WelcomeWidget( children: [
onTap: () { appState.isAuthenticated
Navigator.of(context).push(springPageRoute(ProfileSettings())); ? WelcomeWidget(
}, onTap: () {
name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), Navigator.of(context).push(springPageRoute(ProfileSettings()));
imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, },
).expanded name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'),
: CustomButton( imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
text: LocaleKeys.loginOrRegister.tr(context: context), ).expanded
onPressed: () async { : CustomButton(
await authVM.onLoginPressed(); text: LocaleKeys.loginOrRegister.tr(context: context),
}, onPressed: () async {
backgroundColor: Color(0xffFEE9EA), await authVM.onLoginPressed();
borderColor: Color(0xffFEE9EA), },
textColor: Color(0xffED1C2B), backgroundColor: Color(0xffFEE9EA),
fontSize: 14.f, borderColor: Color(0xffFEE9EA),
fontWeight: FontWeight.w500, textColor: Color(0xffED1C2B),
borderRadius: 12, fontSize: 14.f,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), fontWeight: FontWeight.w500,
height: 40.h, borderRadius: 12.r,
), padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
Row( height: 40.h,
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(),
), ),
); Row(
}), mainAxisSize: MainAxisSize.min,
], spacing: 12.h,
)
],
).paddingSymmetrical(24.h, 0.h),
appState.isAuthenticated
? Column(
children: [ children: [
SizedBox(height: 12.h), Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 18.h, width: 18.h).onPress(() {
Row( Navigator.of(context).push(
mainAxisAlignment: MainAxisAlignment.spaceBetween, CustomPageRoute(
children: [ page: MedicalFilePage(),
"Appointments & Visits".toText16(isBold: true), // page: LoginScreen(),
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(() { }),
Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 18.h, width: 18.h).onPress(() {
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: MyAppointmentsPage(), page: MedicalFilePage(),
// page: LoginScreen(),
), ),
); );
}), }),
SizedBox(height: 16.h), Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() {
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { Navigator.of(context).push(
return myAppointmentsVM.isMyAppointmentsLoading CustomPageRoute(
? Container( page: MedicalFilePage(),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), // page: LoginScreen(),
child: AppointmentCard( ),
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), );
myAppointmentsViewModel: myAppointmentsViewModel, }),
bookAppointmentsViewModel: bookAppointmentsViewModel, ],
isLoading: true, ),
isFromHomePage: true, ],
), ).paddingSymmetrical(24.h, 0.h),
).paddingSymmetrical(24.h, 0.h) appState.isAuthenticated
: myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty ? Column(
? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 children: [
? Container( SizedBox(height: 12.h),
decoration: Row(
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), mainAxisAlignment: MainAxisAlignment.spaceBetween,
child: AppointmentCard( children: [
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first, "Appointments & Visits".toText16(isBold: true),
myAppointmentsViewModel: myAppointmentsViewModel, Row(
bookAppointmentsViewModel: bookAppointmentsViewModel, children: [
isLoading: false, LocaleKeys.viewAll.tr(context: context).toText12(color: AppColors.primaryRedColor),
isFromHomePage: true, SizedBox(width: 2.h),
), Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
).paddingSymmetrical(24.h, 0.h) ],
: Swiper( ),
itemCount: myAppointmentsVM.isMyAppointmentsLoading ],
? 3 ).paddingSymmetrical(24.h, 0.h).onPress(() {
: myAppointmentsVM.patientAppointmentsHistoryList.length < 3 Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
? myAppointmentsVM.patientAppointmentsHistoryList.length }),
: 3, SizedBox(height: 16.h),
layout: SwiperLayout.STACK, Consumer<MyAppointmentsViewModel>(
loop: true, builder: (context, myAppointmentsVM, child) {
itemWidth: MediaQuery.of(context).size.width - 48.h, return myAppointmentsVM.isMyAppointmentsLoading
indicatorLayout: PageIndicatorLayout.COLOR, ? Container(
axisDirection: AxisDirection.right, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
controller: _controller, color: AppColors.whiteColor,
itemHeight: 210 + 25, borderRadius: 24.r,
pagination: const SwiperPagination( hasShadow: true,
alignment: Alignment.bottomCenter, ),
margin: EdgeInsets.only(top: 210 + 8 + 24), child: AppointmentCard(
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor), patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
), myAppointmentsViewModel: myAppointmentsViewModel,
itemBuilder: (BuildContext context, int index) { bookAppointmentsViewModel: bookAppointmentsViewModel,
return Container( isLoading: true,
decoration: RoundedRectangleBorder() isFromHomePage: true,
.toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: 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( child: AppointmentCard(
patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index], patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first,
myAppointmentsViewModel: myAppointmentsViewModel, myAppointmentsViewModel: myAppointmentsViewModel,
bookAppointmentsViewModel: bookAppointmentsViewModel, bookAppointmentsViewModel: bookAppointmentsViewModel,
isLoading: false, isLoading: false,
isFromHomePage: true, isFromHomePage: true,
), ),
); ).paddingSymmetrical(24.h, 0.h)
}, : Swiper(
) itemCount: myAppointmentsVM.isMyAppointmentsLoading
: Container( ? 3
width: double.infinity, : myAppointmentsVM.patientAppointmentsHistoryList.length < 3
decoration: ? myAppointmentsVM.patientAppointmentsHistoryList.length
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24, hasShadow: true), : 3,
child: Padding( layout: SwiperLayout.STACK,
padding: EdgeInsets.all(12.h), loop: true,
child: Column( itemWidth: MediaQuery.of(context).size.width - 48.h,
children: [ indicatorLayout: PageIndicatorLayout.COLOR,
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), axisDirection: AxisDirection.right,
SizedBox(height: 12.h), controller: _controller,
"You do not have any upcoming appointment. Please book an appointment".needTranslation.toText12(isCenter: true), itemHeight: 210 + 25,
SizedBox(height: 12.h), pagination: const SwiperPagination(
CustomButton( alignment: Alignment.bottomCenter,
text: LocaleKeys.bookAppo.tr(context: context), margin: EdgeInsets.only(top: 210 + 8 + 24),
onPressed: () { builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
Navigator.of(context).push( ),
CustomPageRoute( itemBuilder: (BuildContext context, int index) {
page: BookAppointmentPage(), 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), : Container(
textColor: Color(0xffED1C2B), width: double.infinity,
fontSize: 14.f, decoration: RoundedRectangleBorder()
fontWeight: FontWeight.w500, .toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0), child: Padding(
icon: AppAssets.add_icon, padding: EdgeInsets.all(12.h),
iconColor: AppColors.primaryRedColor, child: Column(
),
],
),
),
).paddingSymmetrical(24.h, 0.h);
}),
Consumer<ImmediateLiveCareViewModel>(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,
children: [ children: [
AppCustomChipWidget( Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus, SizedBox(height: 12.h),
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20), "You do not have any upcoming appointment. Please book an appointment"
textColor: AppColors.alertColor, .needTranslation
.toText12(isCenter: true),
SizedBox(height: 12.h),
CustomButton(
text: LocaleKeys.bookAppo.tr(context: context),
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage()));
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
fontSize: 14.f,
fontWeight: FontWeight.w500,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor,
height: 46.h,
), ),
Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
// Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 40.h, height: 40.h, fit: BoxFit.contain),
], ],
), ),
SizedBox(height: 8.h), ),
Row( ).paddingSymmetrical(24.h, 0.h);
mainAxisAlignment: MainAxisAlignment.spaceBetween, },
),
Consumer<ImmediateLiveCareViewModel>(
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: [ children: [
"You have a pending LiveCare request".needTranslation.toText12(isBold: true), Row(
Utils.buildSvgWithAssets( mainAxisAlignment: MainAxisAlignment.spaceBetween,
icon: AppAssets.forward_arrow_icon_small, children: [
iconColor: AppColors.blackColor, AppCustomChipWidget(
width: 20.h, labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus,
height: 15.h, backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
fit: BoxFit.contain, 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( SizedBox(height: 12.h),
CustomPageRoute( ],
page: ImmediateLiveCarePendingRequestPage(), )
: 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), separatorBuilder: (BuildContext cxt, int index) => 10.width,
],
)
: SizedBox(height: 12.h);
}),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
CustomButton(text: "Quick Links".needTranslation,
onPressed: () {
Navigator.of(context).push(
CustomPageRoute(
page: ActiveMedicationPage(),
), ),
); ),
},
),
// "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),
).paddingSymmetrical(24.h, 0.h).onPress(() { ],
Navigator.of(context).push( )
CustomPageRoute( : Container(
page: MedicalFilePage(), height: 127.h,
), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
); child: Column(
}), children: [
SizedBox(height: 16.h), Expanded(
Container( child: ListView.separated(
height: 120.h, scrollDirection: Axis.horizontal,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( itemCount: LandingPageData.getNotLoggedInServiceCardsList.length,
color: AppColors.whiteColor, shrinkWrap: true,
borderRadius: 24, padding: EdgeInsets.only(left: 16.h, right: 16.h),
), itemBuilder: (context, index) {
child: Column( return AnimationConfiguration.staggeredList(
children: [ position: index,
Expanded( duration: const Duration(milliseconds: 1000),
child: ListView.separated( child: SlideAnimation(
scrollDirection: Axis.horizontal, horizontalOffset: 100.0,
itemCount: LandingPageData.getLoggedInServiceCardsList.length, child: FadeInAnimation(
shrinkWrap: true, child: SmallServiceCard(
padding: EdgeInsets.only(left: 16.h, right: 16.h), serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName,
itemBuilder: (context, index) { icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
return AnimationConfiguration.staggeredList( title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
position: index, subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,
duration: const Duration(milliseconds: 1000), iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor,
child: SlideAnimation( textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor,
horizontalOffset: 100.0, backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor,
child: FadeInAnimation( isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold,
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(24.h, 0.h),
],
)
: Container(
height: 127.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24,
),
child: Column(
children: [
Expanded(
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getNotLoggedInServiceCardsList.length,
shrinkWrap: true,
padding: EdgeInsets.only(left: 16.h, right: 16.h),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: SmallServiceCard(
serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName,
icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,
iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor,
textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor,
backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor,
isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold,
),
), ),
), );
); },
}, separatorBuilder: (BuildContext cxt, int index) => 0.width,
separatorBuilder: (BuildContext cxt, int index) => 0.width,
),
),
],
),
).paddingSymmetrical(24.h, 0.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Services".toText16(isBold: true),
Row(
children: [
"View all services".toText12(color: AppColors.primaryRedColor).onPress(() {
}),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],
),
],
).paddingSymmetrical(24.h, 0.h),
SizedBox(
height: 325.h,
child: Column(
children: [
Expanded(
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length,
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.h, right: 24.h),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: LargeServiceCard(
image: LandingPageData.getServiceCardsList[index].icon,
title: LandingPageData.getServiceCardsList[index].title,
subtitle: LandingPageData.getServiceCardsList[index].subtitle,
icon: LandingPageData.getServiceCardsList[index].largeCardIcon,
),
), ),
), ),
); ],
}, ),
separatorBuilder: (BuildContext cxt, int index) => 8.width, ).paddingSymmetrical(24.h, 0.h),
), Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
"Services".toText16(isBold: true),
Row(
children: [
"View all services".toText12(color: AppColors.primaryRedColor),
SizedBox(width: 2.h),
Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h),
],
), ),
], ],
).paddingSymmetrical(24.h, 0.h),
SizedBox(
height: 340.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: LandingPageData.getServiceCardsList.length,
shrinkWrap: true,
padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: LargeServiceCard(
image: LandingPageData.getServiceCardsList[index].icon,
title: LandingPageData.getServiceCardsList[index].title,
subtitle: LandingPageData.getServiceCardsList[index].subtitle,
icon: LandingPageData.getServiceCardsList[index].largeCardIcon,
),
),
),
);
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.w),
),
), ),
), appState.isAuthenticated ? HabibWalletCard() : SizedBox(),
appState.isAuthenticated ? HabibWalletCard() : SizedBox(), ],
], ),
), ),
), ),
); );
@ -521,19 +503,21 @@ class _LandingPageState extends State<LandingPage> {
title: "", title: "",
isCloseButtonVisible: false, isCloseButtonVisible: false,
child: StatefulBuilder(builder: (context, setState) { child: StatefulBuilder(
return QuickLogin( builder: (context, setState) {
isDone: isDone, return QuickLogin(
onPressed: () { isDone: isDone,
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); onPressed: () {
authVM.loginWithFingerPrintFace(() { // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
isDone = true; authVM.loginWithFingerPrintFace(() {
cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); isDone = true;
setState(() {}); cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true);
}); setState(() {});
}, });
); },
}), );
},
),
// height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3, // height: isDone == false ? ResponsiveExtension.screenHeight * 0.5 : ResponsiveExtension.screenHeight * 0.3,
isFullScreen: false, isFullScreen: false,
callBackFunc: () { callBackFunc: () {

@ -35,7 +35,6 @@ class _LandingNavigationState extends State<LandingNavigation> {
], ],
), ),
bottomNavigationBar: BottomNavigation( bottomNavigationBar: BottomNavigation(
context: context,
currentIndex: _currentIndex, currentIndex: _currentIndex,
onTap: (index) { onTap: (index) {
setState(() => _currentIndex = index); setState(() => _currentIndex = index);

@ -16,7 +16,7 @@ class LargeServiceCard extends StatelessWidget {
final String title; final String title;
final String subtitle; final String subtitle;
LargeServiceCard({ const LargeServiceCard({
super.key, super.key,
this.image = "", this.image = "",
this.icon = "", this.icon = "",
@ -26,51 +26,37 @@ class LargeServiceCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Container(
padding: EdgeInsets.symmetric(horizontal: 3.h), width: 150.w,
child: Container( padding: EdgeInsets.symmetric(horizontal: 3.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.transparent, borderRadius: 16.r),
color: Colors.transparent, child: Column(
borderRadius: 16, mainAxisAlignment: MainAxisAlignment.start,
), crossAxisAlignment: CrossAxisAlignment.start,
child: Padding( children: [
padding: EdgeInsets.symmetric(horizontal: 0.h), Image.asset(AppAssets.livecare_service, width: 220.w, fit: BoxFit.contain),
child: Column( SizedBox(height: 10.h),
mainAxisAlignment: MainAxisAlignment.center, Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset(AppAssets.livecare_service, width: 220.h, height: 220.h, fit: BoxFit.contain), Utils.buildSvgWithAssets(icon: icon, width: 24.w, height: 24.h),
SizedBox(height: 3.h), Flexible(child: title.toText14(color: AppColors.blackColor, isBold: true, textOverflow: TextOverflow.clip, maxlines: 1)),
Row(
children: [
Utils.buildSvgWithAssets(icon: icon, width: 24.h, height: 24.h),
title.toText14(color: AppColors.blackColor, isBold: true),
],
),
SizedBox(width: 220.h, child: subtitle.toText11(color: AppColors.blackColor)),
SizedBox(height: 6.h),
SizedBox(
width: 220.h,
child: CustomButton(
text: LocaleKeys.bookNow.tr(context: context),
onPressed: () {
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (BuildContext context) => LandingPage()),
// );
},
backgroundColor: AppColors.borderOnlyColor,
borderColor: AppColors.borderOnlyColor,
textColor: AppColors.whiteColor,
fontSize: 14,
fontWeight: FontWeight.bold,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
),
], ],
), ),
), 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,
),
],
), ),
); );
} }

@ -179,7 +179,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h), Image.asset(appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
width: 56.w, height: 56.h),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -226,12 +227,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
children: [ children: [
AppCustomChipWidget( AppCustomChipWidget(
labelText: "${appState.getAuthenticatedUser()!.age} Years Old", labelText: "${appState.getAuthenticatedUser()!.age} Years Old",
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w),
), ),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.blood_icon, icon: AppAssets.blood_icon,
labelText: "Blood: ${appState.getUserBloodGroup}", labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), labelPadding: EdgeInsetsDirectional.only(end: 8.w),
), ),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) { Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget( return AppCustomChipWidget(
@ -242,7 +244,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
iconSize: 12.w, iconSize: 12.w,
backgroundColor: backgroundColor:
insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1), insuranceVM.isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
labelPadding: EdgeInsetsDirectional.only(start: 4.w, end: 8.w), labelPadding: EdgeInsetsDirectional.only(end: 8.w),
); );
}), }),
], ],
@ -347,7 +349,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
}), }),
SizedBox(height: 16.h), SizedBox(height: 16.h),
Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) { Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
// Provide an explicit height so the horizontal ListView has a bounded height
return SizedBox( return SizedBox(
height: 190.h,
child: myAppointmentsVM.isMyAppointmentsLoading child: myAppointmentsVM.isMyAppointmentsLoading
? MedicalFileAppointmentCard( ? MedicalFileAppointmentCard(
patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(), patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
@ -359,7 +363,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
? Container( ? Container(
padding: EdgeInsets.all(12.w), padding: EdgeInsets.all(12.w),
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: true), decoration:
RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 12.r, hasShadow: true),
child: Column( child: Column(
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
@ -381,8 +386,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h, height: isFoldable || isTablet ? 50.h : 40.h,
icon: AppAssets.add_icon, icon: AppAssets.add_icon,
iconColor: AppColors.primaryRedColor, iconColor: AppColors.primaryRedColor,
), ),
@ -505,11 +510,13 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
spacing: 3.w, spacing: 3.w,
runSpacing: 4.w, runSpacing: 4.w,
children: [ children: [
AppCustomChipWidget(labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!), AppCustomChipWidget(
labelText: prescriptionVM.patientPrescriptionOrders[index].clinicDescription!),
AppCustomChipWidget( AppCustomChipWidget(
icon: AppAssets.doctor_calendar_icon, icon: AppAssets.doctor_calendar_icon,
labelText: DateUtil.formatDateToDate( labelText: DateUtil.formatDateToDate(
DateUtil.convertStringToDate(prescriptionVM.patientPrescriptionOrders[index].appointmentDate), DateUtil.convertStringToDate(
prescriptionVM.patientPrescriptionOrders[index].appointmentDate),
false, false,
), ),
), ),
@ -532,7 +539,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
prescriptionVM.setPrescriptionsDetailsLoading(); prescriptionVM.setPrescriptionsDetailsLoading();
Navigator.of(context).push( Navigator.of(context).push(
CustomPageRoute( CustomPageRoute(
page: PrescriptionDetailPage(prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]), page: PrescriptionDetailPage(
prescriptionsResponseModel: prescriptionVM.patientPrescriptionOrders[index]),
), ),
); );
}), }),
@ -648,7 +656,10 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fit: BoxFit.cover, fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: true, radius: 50.r), ).circle(100).toShimmer2(isShow: true, radius: 50.r),
SizedBox(height: 8.h), SizedBox(height: 8.h),
("Dr. John Smith Smith Smith").toString().toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2).toShimmer2(isShow: true), ("Dr. John Smith Smith Smith")
.toString()
.toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
.toShimmer2(isShow: true),
], ],
) )
: myAppointmentsVM.patientMyDoctorsList.isEmpty : myAppointmentsVM.patientMyDoctorsList.isEmpty
@ -731,7 +742,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
}, },
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h), separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h),
), ),
); ).paddingSymmetrical(24.w, 0);
}), }),
SizedBox(height: 24.h), SizedBox(height: 24.h),
"Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h), "Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
@ -820,10 +831,14 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}", text: "${LocaleKeys.updateInsurance.tr(context: context)} ${LocaleKeys.updateInsuranceSubtitle.tr(context: context)}",
onPressed: () { onPressed: () {
insuranceViewModel.setIsInsuranceUpdateDetailsLoading(true); insuranceViewModel.setIsInsuranceUpdateDetailsLoading(true);
insuranceViewModel.getPatientInsuranceDetailsForUpdate( insuranceViewModel.getPatientInsuranceDetailsForUpdate(appState.getAuthenticatedUser()!.patientId.toString(),
appState.getAuthenticatedUser()!.patientId.toString(), appState.getAuthenticatedUser()!.patientIdentificationNo.toString()); appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
showCommonBottomSheetWithoutHeight(context, showCommonBottomSheetWithoutHeight(context,
child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false); child: PatientInsuranceCardUpdateCard(),
callBackFunc: () {},
title: "",
isCloseButtonVisible: false,
isFullScreen: false);
}, },
backgroundColor: AppColors.bgGreenColor.withOpacity(0.20), backgroundColor: AppColors.bgGreenColor.withOpacity(0.20),
borderColor: AppColors.bgGreenColor.withOpacity(0.0), borderColor: AppColors.bgGreenColor.withOpacity(0.0),
@ -832,7 +847,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
borderRadius: 12.r, borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0), 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), ).paddingOnly(left: 12.w, right: 12.w, bottom: 12.h),
), ),
).paddingSymmetrical(24.w, 0.h); ).paddingSymmetrical(24.w, 0.h);
@ -874,19 +889,21 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
); );
}), }),
MedicalFileCard( MedicalFileCard(
label: "My Invoices List".needTranslation, label: "My Invoices List".needTranslation,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon, svgIcon: AppAssets.eye_result_icon,
isLargeText: true, isLargeText: true,
iconSize: 36.w), iconSize: 36.w,
),
MedicalFileCard( MedicalFileCard(
label: "Ancillary Orders List".needTranslation, label: "Ancillary Orders List".needTranslation,
textColor: AppColors.blackColor, textColor: AppColors.blackColor,
backgroundColor: AppColors.whiteColor, backgroundColor: AppColors.whiteColor,
svgIcon: AppAssets.eye_result_icon, svgIcon: AppAssets.eye_result_icon,
isLargeText: true, isLargeText: true,
iconSize: 36.w), iconSize: 36.w,
),
], ],
).paddingSymmetrical(24.w, 0.0), ).paddingSymmetrical(24.w, 0.0),
SizedBox(height: 16.h), SizedBox(height: 16.h),

@ -54,7 +54,10 @@ class _FamilyCardsState extends State<FamilyCards> {
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.alertSquare), Utils.buildSvgWithAssets(icon: AppAssets.alertSquare),
SizedBox(width: 8.h), SizedBox(width: 8.h),
"Who can view my medical file ?".needTranslation.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500).onPress(() { "Who can view my medical file ?"
.needTranslation
.toText14(color: AppColors.textColor, isUnderLine: true, weight: FontWeight.w500)
.onPress(() {
dialogService.showFamilyBottomSheetWithoutHWithChild( dialogService.showFamilyBottomSheetWithoutHWithChild(
label: "Manage Family".needTranslation, label: "Manage Family".needTranslation,
message: "", message: "",
@ -110,11 +113,18 @@ class _FamilyCardsState extends State<FamilyCards> {
? AppColors.textGreenColor ? AppColors.textGreenColor
: AppColors.alertColor), : AppColors.alertColor),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [ Wrap(
(profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600), alignment: WrapAlignment.start,
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)) crossAxisAlignment: WrapCrossAlignment.start,
.toText12(isBold: false, isCenter: false, maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor), runAlignment: WrapAlignment.start,
]), spacing: 0.h,
children: [
(profile.patientName ?? "").toText14(isBold: false, isCenter: false, maxlines: 1, weight: FontWeight.w600),
(getStatusTextByRequest(
FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false))
.toText12(
isBold: false, isCenter: false, maxLine: 1, fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
]),
SizedBox(height: 8.h), SizedBox(height: 8.h),
CustomChipWidget( CustomChipWidget(
height: 30.h, height: 30.h,
@ -141,7 +151,7 @@ class _FamilyCardsState extends State<FamilyCards> {
itemCount: widget.profiles.length, itemCount: widget.profiles.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
crossAxisSpacing: 10.h, crossAxisSpacing: 10.w,
mainAxisSpacing: 10.h, mainAxisSpacing: 10.h,
childAspectRatio: widget.isShowDetails ? 0.56.h : 0.65.h, childAspectRatio: widget.isShowDetails ? 0.56.h : 0.65.h,
), ),
@ -162,13 +172,14 @@ class _FamilyCardsState extends State<FamilyCards> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Utils.buildImgWithAssets( Utils.buildImgWithAssets(
icon: profile.gender == null icon: profile.gender == null
? AppAssets.dummy_user ? AppAssets.dummy_user
: profile.gender == 1 : profile.gender == 1
? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img) ? ((profile.age ?? 0) < 7 ? AppAssets.babyBoyImg : AppAssets.male_img)
: (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg), : (profile.age! < 7 ? AppAssets.babyGirlImg : AppAssets.femaleImg),
width: 80.h, width: 80.h,
height: 78.h), height: 78.h,
),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(profile.patientName ?? "Unknown").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), (profile.patientName ?? "Unknown").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -282,7 +293,9 @@ class _FamilyCardsState extends State<FamilyCards> {
alignment: WrapAlignment.start, alignment: WrapAlignment.start,
children: [ children: [
(profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600), (profile.patientName ?? "").toText14(isBold: false, isCenter: true, maxlines: 1, weight: FontWeight.w600),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false)).toText14( (getStatusTextByRequest(
FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false))
.toText14(
isBold: false, isBold: false,
isCenter: true, isCenter: true,
maxlines: 1, maxlines: 1,

@ -53,7 +53,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
left: false, left: false,
right: false, right: false,
child: Column( child: Column(
spacing: 24, spacing: 24.h,
children: [ children: [
PageView( PageView(
controller: pageController, controller: pageController,
@ -75,24 +75,24 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
}, },
).expanded, ).expanded,
Row( Row(
spacing: 4.h, spacing: 4.w,
children: [ children: [
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
height: 6.h, height: 6.h,
width: selectedIndex == 0 ? 18.h : 6.h, width: selectedIndex == 0 ? 18.w : 6.w,
decoration: decoration: BoxDecoration(
BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30.r)),
), ),
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
height: 6.h, height: 6.h,
width: selectedIndex == 1 ? 18.h : 6.h, width: selectedIndex == 1 ? 18.w : 6.w,
decoration: decoration: BoxDecoration(
BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), 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( Row(
children: [ children: [
AnimatedSwitcher( AnimatedSwitcher(
@ -102,20 +102,20 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
? CustomButton( ? CustomButton(
text: "Skip".needTranslation, text: "Skip".needTranslation,
onPressed: () => goToHomePage(), onPressed: () => goToHomePage(),
width: 86.h, width: 86.w,
height: 56.h, height: 56.h,
backgroundColor: Color(0xffFEE9EA), backgroundColor: Color(0xffFEE9EA),
textColor: AppColors.primaryRedColor, textColor: AppColors.primaryRedColor,
borderColor: Colors.transparent, borderColor: Colors.transparent,
).paddingOnly(left: 24.h) ).paddingOnly(left: 24.w)
: const SizedBox.shrink(), : const SizedBox.shrink(),
), ),
const Spacer(), const Spacer(),
AnimatedContainer( AnimatedContainer(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut, curve: Curves.easeInOut,
width: selectedIndex == 0 ? 86.h : MediaQuery.of(context).size.width - 48.h, width: selectedIndex == 0 ? 86.w : MediaQuery.of(context).size.width - 48.w,
margin: EdgeInsets.only(left: 24.h, right: 24.h), margin: EdgeInsets.only(left: 24.w, right: 24.w),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primaryRedColor, color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
@ -126,8 +126,8 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
child: selectedIndex == 0 child: selectedIndex == 0
? CustomButton( ? CustomButton(
icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward, icon: getIt.get<AppState>().isArabic() ? AppAssets.arrow_back : AppAssets.arrow_forward,
iconSize: 32.h, iconSize: 32.w,
width: 86.h, width: 86.w,
height: 56.h, height: 56.h,
text: "".needTranslation, text: "".needTranslation,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -146,7 +146,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
), ),
), ),
], ],
), ).paddingOnly(bottom: (isFoldable || isTablet) ? 20.h : 10.h),
], ],
), ),
), ),
@ -157,7 +157,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12, spacing: 12.h,
children: [ children: [
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
@ -167,10 +167,9 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
repeat: true, repeat: true,
reverse: false, reverse: false,
frameRate: FrameRate(60), frameRate: FrameRate(60),
width: MediaQuery.sizeOf(context).width - 50, width: MediaQuery.sizeOf(context).width - 50.w,
height: MediaQuery.sizeOf(context).width - 50))) height: MediaQuery.sizeOf(context).width - 50.w)))
.expanded, .expanded,
// 12.height,
Text( Text(
heading, heading,
style: TextStyle(fontSize: 36.f, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1), style: TextStyle(fontSize: 36.f, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1),
@ -180,6 +179,6 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
style: TextStyle(fontSize: 16.f, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), 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);
} }
} }

@ -51,6 +51,33 @@ class _ProfileSettingsState extends State<ProfileSettings> {
super.dispose(); 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; int length = 3;
final SwiperController _controller = SwiperController(); final SwiperController _controller = SwiperController();
@ -72,11 +99,11 @@ class _ProfileSettingsState extends State<ProfileSettings> {
itemCount: medicalVm.patientFamilyFiles.length, itemCount: medicalVm.patientFamilyFiles.length,
layout: SwiperLayout.STACK, layout: SwiperLayout.STACK,
loop: true, loop: true,
itemWidth: SizeUtils.width - 42.w, itemHeight: dynamicItemHeight(context),
itemWidth: SizeUtils.width - 30.w,
indicatorLayout: PageIndicatorLayout.COLOR, indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right, axisDirection: AxisDirection.right,
controller: _controller, controller: _controller,
itemHeight: 220.h,
pagination: SwiperPagination( pagination: SwiperPagination(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: (210.h + 8.h + 24.h)), margin: EdgeInsets.only(top: (210.h + 8.h + 24.h)),
@ -97,13 +124,14 @@ class _ProfileSettingsState extends State<ProfileSettings> {
onFamilySwitchPress: (FamilyFileResponseModelLists profile) { onFamilySwitchPress: (FamilyFileResponseModelLists profile) {
medicalVm.switchFamilyFiles(responseID: profile.responseId, patientID: profile.patientId, phoneNumber: profile.mobileNumber); 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( GridView(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 9.w, mainAxisSpacing: 9.h), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: isTablet ? 3 : 2),
physics: const NeverScrollableScrollPhysics(), 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, shrinkWrap: true,
children: [ children: [
Container( Container(
@ -115,14 +143,14 @@ class _ProfileSettingsState extends State<ProfileSettings> {
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.h, // spacing: 4.h,
children: [ children: [
Row( Row(
spacing: 8.w, spacing: 8.w,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.w, height: 40.h), 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), Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward),
], ],
), ),
@ -131,10 +159,11 @@ class _ProfileSettingsState extends State<ProfileSettings> {
return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false) return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, isExpanded: false)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h); .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.r, width: 80.w, height: 24.h);
}), }),
Spacer(),
CustomButton( CustomButton(
height: 40.h, height: 40.h,
icon: AppAssets.recharge_icon, icon: AppAssets.recharge_icon,
iconSize: 24.h, iconSize: 22.w,
iconColor: AppColors.infoColor, iconColor: AppColors.infoColor,
textColor: AppColors.infoColor, textColor: AppColors.infoColor,
text: "Recharge".needTranslation, text: "Recharge".needTranslation,
@ -152,7 +181,7 @@ class _ProfileSettingsState extends State<ProfileSettings> {
).onPress(() { ).onPress(() {
Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage()));
}), }),
) ),
], ],
), ),
"Quick Actions" "Quick Actions"
@ -220,7 +249,12 @@ class _ProfileSettingsState extends State<ProfileSettings> {
], ],
), ),
), ),
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 isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID); final canSwitch = isParentUser || (!isParentUser && profile.responseId == appState.getSuperUserID);
return Container( 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( child: Column(
children: [ children: [
Column( Column(
@ -284,7 +322,6 @@ class FamilyCardWidget extends StatelessWidget {
children: [ children: [
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
//
children: [ children: [
Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h), Image.asset(profile.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, width: 56.w, height: 56.h),
Column( Column(
@ -404,7 +441,7 @@ class FamilyCardWidget extends StatelessWidget {
borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor, borderColor: canSwitch ? AppColors.secondaryLightRedColor : AppColors.primaryRedColor,
textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, textColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor,
iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor, iconColor: canSwitch ? AppColors.primaryRedColor : AppColors.whiteColor,
height: 56.h, height: isFoldable ? 50.h : 40.h,
fontSize: 14.f, fontSize: 14.f,
).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); ).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, textColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor,
iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor, iconColor: canSwitchBack ? AppColors.whiteColor : AppColors.greyTextColor,
onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {}, onPressed: canSwitchBack ? () => onFamilySwitchPress(profile) : () {},
height: 40.h, height: isFoldable ? 50.h : 40.h,
fontSize: 14.f, fontSize: 14.f,
).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h); ).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h);
} }

@ -43,13 +43,13 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
quarterTurns: 90, quarterTurns: 90,
child: Utils.buildSvgWithAssets( child: Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back, icon: AppAssets.arrow_back,
width: 32.h, width: 32.w,
height: 32.h, height: 32.h,
), ),
) )
: Utils.buildSvgWithAssets( : Utils.buildSvgWithAssets(
icon: AppAssets.arrow_back, icon: AppAssets.arrow_back,
width: 32.h, width: 32.w,
height: 32.h, height: 32.h,
), ),
), ),
@ -57,11 +57,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
), ),
// Logo // Logo
if (!hideLogoAndLang) if (!hideLogoAndLang) Utils.buildSvgWithAssets(icon: AppAssets.habiblogo, height: 24.h),
Utils.buildSvgWithAssets(
icon: AppAssets.habiblogo,
height: 24.h,
),
if (!hideLogoAndLang) if (!hideLogoAndLang)
Expanded( Expanded(

@ -110,14 +110,18 @@ class CollapsingListView extends StatelessWidget {
color: AppColors.blackColor, color: AppColors.blackColor,
letterSpacing: -0.5), letterSpacing: -0.5),
).expanded, ).expanded,
if (logout != null) actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!), if (logout != null)
if (report != null) actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!), actionButton(context, t, title: "Logout".needTranslation, icon: AppAssets.logout).onPress(logout!),
if (report != null)
actionButton(context, t, title: "Report".needTranslation, icon: AppAssets.report_icon).onPress(report!),
if (history != null) if (history != null)
actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(history!), actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon)
.onPress(history!),
if (instructions != null) if (instructions != null)
actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!), actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!),
if (requests != null) if (requests != null)
actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon).onPress(requests!), actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon)
.onPress(requests!),
if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24), if (search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(search!).paddingOnly(right: 24),
if (trailing != null) trailing!, if (trailing != null) trailing!,
], ],
@ -151,7 +155,7 @@ class CollapsingListView extends StatelessWidget {
margin: EdgeInsets.only(right: 24.w), margin: EdgeInsets.only(right: 24.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.secondaryLightRedColor, color: AppColors.secondaryLightRedColor,
borderRadius: 12.r, borderRadius: 10.r,
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,

@ -12,9 +12,8 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
class BottomNavigation extends StatelessWidget { class BottomNavigation extends StatelessWidget {
final int currentIndex; final int currentIndex;
final ValueChanged<int> onTap; final ValueChanged<int> onTap;
BuildContext? context;
BottomNavigation({super.key, required this.currentIndex, required this.onTap, this.context}); const BottomNavigation({super.key, required this.currentIndex, required this.onTap});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -30,7 +29,9 @@ class BottomNavigation extends StatelessWidget {
iconSize: 27, iconSize: 27,
isSpecial: true, isSpecial: true,
), ),
appState.isAuthenticated ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr(context: context)) : BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()), appState.isAuthenticated
? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr(context: context))
: BottomNavItem(icon: AppAssets.news, label: LocaleKeys.news.tr()),
BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr(context: context)), BottomNavItem(icon: AppAssets.servicesBottom, label: LocaleKeys.services2.tr(context: context)),
]; ];
@ -65,11 +66,7 @@ class BottomNavigation extends StatelessWidget {
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
item.label.toText12( item.label.toText12(fontWeight: FontWeight.w500),
fontWeight: FontWeight.w500,
// color: Colors.black87,
// textAlign: TextAlign.center,
),
SizedBox(height: item.isSpecial ? 5 : 0) SizedBox(height: item.isSpecial ? 5 : 0)
], ],
), ),
@ -82,12 +79,14 @@ class BottomNavItem {
final String label; final String label;
final double iconSize; final double iconSize;
final bool isSpecial; final bool isSpecial;
final Color? color;
const BottomNavItem({ const BottomNavItem({
required this.icon, required this.icon,
required this.label, required this.label,
this.iconSize = 21, this.iconSize = 21,
this.isSpecial = false, this.isSpecial = false,
this.color,
}); });
} }

@ -56,7 +56,11 @@ class CustomButton extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: isDisabled ? null : onPressed, onTap: isDisabled ? null : onPressed,
child: Container( child: Container(
height: height ?? (50.h), height: height == null
? 56.h
: (isFoldable || isTablet)
? height! + 10.h
: height,
width: width, width: width,
padding: padding, padding: padding,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(

@ -48,63 +48,71 @@ class AppCustomChipWidget extends StatelessWidget {
final iconS = iconSize ?? 12.w; final iconS = iconSize ?? 12.w;
return GestureDetector( return GestureDetector(
onTap: onChipTap, onTap: onChipTap,
child: SizedBox( child: ChipTheme(
child: ChipTheme( data: ChipThemeData(
data: ChipThemeData( padding: EdgeInsets.zero,
padding: EdgeInsets.zero, shape: SmoothRectangleBorder(
shape: SmoothRectangleBorder( side: BorderSide(
side: BorderSide( width: 10.w,
width: 10.w, color: Colors.transparent, // Crucially, set color to transparent
color: Colors.transparent, // Crucially, set color to transparent style: BorderStyle.none,
style: BorderStyle.none,
),
borderRadius: BorderRadius.circular(8.r), // Apply a border radius of 16.0
), ),
borderRadius: BorderRadius.circular(isFoldable || isTablet ? 6.r : 8.r),
), ),
child: icon.isNotEmpty ),
? Chip( child: icon.isNotEmpty
avatar: icon.isNotEmpty ? Chip(
? Utils.buildSvgWithAssets(icon: icon, width: iconS, height: iconS, iconColor: iconHasColor ? iconColor : null, fit: BoxFit.contain) avatar: icon.isNotEmpty
: SizedBox.shrink(), ? Padding(
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), padding: EdgeInsets.only(left: 8.w, right: 6.w),
padding: padding, child: Utils.buildSvgWithAssets(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, icon: icon,
labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w),
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(
icon: deleteIcon!,
width: iconS, width: iconS,
height: iconS, height: iconS,
iconColor: deleteIconHasColor ? deleteIconColor : null, iconColor: iconHasColor ? iconColor : null,
) fit: BoxFit.contain,
: null, ),
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, )
) : SizedBox.shrink(),
: Chip( avatarBoxConstraints: BoxConstraints(),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor), padding: EdgeInsets.zero,
padding: EdgeInsets.zero, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: backgroundColor, labelPadding: EdgeInsetsDirectional.only(end: 8.w),
shape: shape ?? backgroundColor: backgroundColor,
SmoothRectangleBorder( shape: shape ??
borderRadius: BorderRadius.circular(8.r), SmoothRectangleBorder(
smoothness: 10, borderRadius: BorderRadius.circular(8.r),
side: BorderSide(color: AppColors.transparent, width: 1.5), smoothness: 10,
), side: BorderSide(color: AppColors.transparent, width: 1.5),
labelPadding: labelPadding ?? EdgeInsetsDirectional.only(start: 2.w, end: deleteIcon?.isNotEmpty == true ? 2.w : 8.w), ),
deleteIcon: deleteIcon?.isNotEmpty == true deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null) ? Utils.buildSvgWithAssets(
: null, icon: deleteIcon!,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null, width: iconS,
), height: iconS,
), iconColor: deleteIconHasColor ? deleteIconColor : null,
)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
)
: Chip(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
label: richText ?? labelText!.toText10(weight: FontWeight.w500, letterSpacing: 0, color: textColor),
padding: EdgeInsets.zero,
backgroundColor: backgroundColor,
shape: shape ??
SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8.r),
smoothness: 10,
side: BorderSide(color: AppColors.transparent, width: 1.5),
),
labelPadding: EdgeInsetsDirectional.only(start: 8.w, end: 8.w),
deleteIcon: deleteIcon?.isNotEmpty == true
? Utils.buildSvgWithAssets(icon: deleteIcon!, width: iconS, height: iconS, iconColor: deleteIconHasColor ? deleteIconColor : null)
: null,
onDeleted: deleteIcon?.isNotEmpty == true ? () {} : null,
),
), ),
); );
} }

@ -62,7 +62,7 @@ class CustomTabBarState extends State<CustomTabBar> {
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
itemBuilder: (cxt, index) => myTab(widget.tabs[index], index).expanded, itemBuilder: (cxt, index) => myTab(widget.tabs[index], index),
separatorBuilder: (cxt, index) => 4.width, separatorBuilder: (cxt, index) => 4.width,
itemCount: widget.tabs.length, itemCount: widget.tabs.length,
); );
@ -75,20 +75,20 @@ class CustomTabBarState extends State<CustomTabBar> {
} }
return Container( return Container(
height: 62.h,
padding: EdgeInsets.all(4.w), padding: EdgeInsets.all(4.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 12.r, borderRadius: 12.r,
), ),
height: 62.h,
child: Center(child: parentWidget)); child: Center(child: parentWidget));
} }
Widget myTab(CustomTabBarModel tabBar, int currentIndex) { Widget myTab(CustomTabBarModel tabBar, int currentIndex) {
bool isSelected = selectedIndex == currentIndex; bool isSelected = selectedIndex == currentIndex;
return Container( return Container(
height: 54.h, height: (isTablet || isFoldable) ? 60.h : 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, alignment: Alignment.center,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor, color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor,

@ -42,7 +42,7 @@ class _LanguageSelectorState extends State<LanguageSelector> {
return Stack(clipBehavior: Clip.none, children: [ return Stack(clipBehavior: Clip.none, children: [
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular((12.r)), borderRadius: BorderRadius.circular((10.r)),
color: AppColors.secondaryLightRedColor, color: AppColors.secondaryLightRedColor,
), ),
child: InkWell( child: InkWell(

Loading…
Cancel
Save