Design changes on fold

faiz_dev
faizatflutter 10 hours ago
parent 1a6dff29f1
commit cb299c5bca

@ -601,7 +601,7 @@ SPEC CHECKSUMS:
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
PayFortSDK: 233eabe9a45601fdbeac67fa6e5aae46ed8faf82
PayFortSDK: e12f79051ae15b2e29536771a45354900180bfcb
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
@ -612,7 +612,7 @@ SPEC CHECKSUMS:
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153
SwiftProtobuf: 9e106a71456f4d3f6a3b0c8fd87ef0be085efc38
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
Turf: aa2ede4298009639d10db36aba1a7ebaad072a5e
url_launcher_ios: bb13df5870e8c4234ca12609d04010a21be43dfa

@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart';
@ -16,7 +17,6 @@ import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:http/http.dart' as http;
import '../exceptions/api_failure.dart';
abstract class ApiClient {
static final NavigationService _navigationService = getIt.get<NavigationService>();

@ -9,18 +9,17 @@ import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_callkit_incoming/entities/android_params.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/entities/call_kit_params.dart';
import 'package:flutter_callkit_incoming/entities/ios_params.dart';
import 'package:flutter_callkit_incoming/entities/notification_params.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_ios_voip_kit_karmm/call_state_type.dart';
import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uuid/uuid.dart';
@ -33,7 +32,7 @@ String? _currentSessionId; // Store session ID for API call
// |--> Push Notification Background
@pragma('vm:entry-point')
Future<dynamic> backgroundMessageHandler(dynamic message) async {
print("Firebase backgroundMessageHandler!!!");
log("Firebase backgroundMessageHandler!!!");
await Firebase.initializeApp();
fir.RemoteMessage message_;
@ -47,20 +46,20 @@ Future<dynamic> backgroundMessageHandler(dynamic message) async {
callPage(String sessionID, String token) async {}
_incomingCall(Map<String, dynamic> data) async {
print('the value of the _incomingCall remote message is $data');
log('the value of the _incomingCall remote message is $data');
// Check if there's already a call in progress to prevent duplicates
if (_isCallInProgress && _currentCallId != null) {
print('⚠️ Call already in progress (ID: $_currentCallId), ignoring duplicate notification');
log('⚠️ Call already in progress (ID: $_currentCallId), ignoring duplicate notification');
return;
}
String roomID = data['session_id'] ?? '';
String callTypeID = data['AppointmentNo'] ?? '';
print('🔍 Extracted from notification data:');
print(' - roomID (session_id): "$roomID"');
print(' - callTypeID (AppointmentNo): "$callTypeID"');
log('🔍 Extracted from notification data:');
log(' - roomID (session_id): "$roomID"');
log(' - callTypeID (AppointmentNo): "$callTypeID"');
// Generate unique call ID
var _currentUuid = Uuid().v4();
@ -68,16 +67,16 @@ _incomingCall(Map<String, dynamic> data) async {
_isCallInProgress = true;
_currentSessionId = roomID; // Store session ID for decline API call
print('📞 Starting new call with ID: $_currentCallId, Session: $_currentSessionId');
log('📞 Starting new call with ID: $_currentCallId, Session: $_currentSessionId');
await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, roomID);
await Utils.saveStringFromPrefs(CacheConst.callTypeID, callTypeID);
await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, true);
print('💾 Saved to cache:');
print(' - CacheConst.zoomRoomID: "$roomID"');
print(' - CacheConst.callTypeID: "$callTypeID"');
print(' - CacheConst.isAppOpenedFromCall: true');
log('💾 Saved to cache:');
log(' - CacheConst.zoomRoomID: "$roomID"');
log(' - CacheConst.callTypeID: "$callTypeID"');
log(' - CacheConst.isAppOpenedFromCall: true');
WidgetsFlutterBinding.ensureInitialized();
@ -148,7 +147,7 @@ _incomingCall(Map<String, dynamic> data) async {
// Start 60-second timeout timer to auto-dismiss call
_callTimeoutTimer?.cancel(); // Cancel any existing timer
_callTimeoutTimer = Timer(Duration(seconds: 40), () async {
print('⏱️ Call timeout (60 seconds) - auto dismissing call notification');
log('⏱️ Call timeout (60 seconds) - auto dismissing call notification');
await _endCallAndCleanup(_currentUuid);
});
@ -158,8 +157,6 @@ _incomingCall(Map<String, dynamic> data) async {
// Helper method to end call and cleanup
Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async {
try {
print('🧹 Cleaning up call: $callId');
// If call was declined, update session status via API
if (isDeclined && _currentSessionId != null && _currentSessionId!.isNotEmpty) {
await _updateSessionStatus(_currentSessionId!, 3, 'Patient');
@ -184,10 +181,7 @@ Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async
await Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, false);
await Utils.saveStringFromPrefs(CacheConst.zoomRoomID, '');
await Utils.saveStringFromPrefs(CacheConst.callTypeID, '');
print('✅ Call and push notification cleanup completed successfully');
} catch (e) {
print('❌ Error during call cleanup: $e');
// Force reset flags even if there's an error
_isCallInProgress = false;
_currentCallId = null;
@ -200,27 +194,20 @@ Future<void> _endCallAndCleanup(String callId, {bool isDeclined = false}) async
// Helper method to update session status via API
Future<void> _updateSessionStatus(String sessionId, int sessionStatus, String sessionEndedBy) async {
try {
final apiClient = getIt.get<ApiClient>();
Map<String, dynamic> requestBody = {
"Open_SessionID": sessionId,
"SessionStatus": sessionStatus,
"SessionEndedBy": sessionEndedBy
};
Map<String, dynamic> requestBody = {"Open_SessionID": sessionId, "SessionStatus": sessionStatus, "SessionEndedBy": sessionEndedBy};
await apiClient.post(
CHANGE_PATIENT_ER_SESSION,
body: requestBody,
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
print('✅ Session status updated successfully: $response');
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {},
onFailure: (error, statusCode, {messageStatus, failureType}) {
print('❌ Failed to update session status: $error');
log('❌ Failed to update session status: $error');
},
);
} catch (e) {
print('❌ Exception updating session status: $e');
log('❌ Exception updating session status: $e');
}
}
@ -252,7 +239,7 @@ Future<void> openCallPage(BuildContext context) async {
// );
// }
} catch (err) {
print(err);
log(err.toString());
// await PlatformExceptionAlertDialog(
// exception: Exception(err),
// ).show(context);
@ -351,7 +338,7 @@ class PushNotificationHandler {
// int seconds = 30,
// }) async {
// timeOutTimer = Timer(Duration(seconds: seconds), () async {
// print('🎈 example: timeOut');
// log('🎈 example: timeOut');
// final incomingCallerName = await voIPKit.getIncomingCallerName();
// voIPKit.unansweredIncomingCall(
// skipLocalNotification: false,
@ -366,19 +353,19 @@ class PushNotificationHandler {
if (Platform.isIOS) {
voIPKit.getVoIPToken().then((value) {
print("🎈 APNS VOIP KIT TOKEN: $value");
log("🎈 APNS VOIP KIT TOKEN: $value");
Utils.saveStringFromPrefs(CacheConst.voipToken, value ?? "");
// AppSharedPreferences().setString(APNS_TOKEN, value!);
});
voIPKit.onDidUpdatePushToken = (String token) {
print('🎈 example: onDidUpdatePushToken: $token');
log('🎈 example: onDidUpdatePushToken: $token');
};
voIPKit.onDidReceiveIncomingPush = (
Map<String, dynamic> payload,
) async {
print('🎈 example: onDidReceiveIncomingPush $payload');
log('🎈 example: onDidReceiveIncomingPush $payload');
// _timeOut();
};
@ -387,20 +374,22 @@ class PushNotificationHandler {
String callerId,
) async {
try {
print('🎈 example: onDidRejectIncomingCall $uuid - $callerId');
log('🎈 example: onDidRejectIncomingCall $uuid - $callerId');
timeOutTimer.cancel();
// Cleanup on reject with API call
if (_currentCallId != null) {
await _endCallAndCleanup(_currentCallId!, isDeclined: true);
}
} catch (err) {}
} catch (err) {
log(err.toString());
}
};
voIPKit.onDidAcceptIncomingCall = (
String uuid,
String callerId,
) async {
print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId');
log('🎈 example: onDidAcceptIncomingCall $uuid - $callerId');
await voIPKit.acceptIncomingCall(callerState: CallStateType.calling);
await voIPKit.callConnected();
@ -430,12 +419,12 @@ class PushNotificationHandler {
if (Platform.isAndroid) {
try {
final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) {
print(err);
log(err);
});
if (fcmToken != null) onToken(fcmToken);
// }
} catch (ex) {
print("Notification Exception: $ex");
log("Notification Exception: $ex");
}
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
}
@ -467,7 +456,7 @@ class PushNotificationHandler {
} catch (ex) {}
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
print("Firebase onMessage!!!");
log("Firebase onMessage!!!");
// showCallkitIncoming();
if (Platform.isIOS) {
await Future.delayed(Duration(milliseconds: 3000)).then((value) {
@ -479,7 +468,7 @@ class PushNotificationHandler {
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
print("Firebase onMessageOpenedApp!!!");
log("Firebase onMessageOpenedApp!!!");
if (Platform.isIOS) {
await Future.delayed(Duration(milliseconds: 3000)).then((value) {
newMessage(message);
@ -491,21 +480,21 @@ class PushNotificationHandler {
if (Platform.isIOS) {
FirebaseMessaging.instance.getAPNSToken().then((String? token) {
print("Push Notification getAPNSToken: ${token!}");
log("Push Notification getAPNSToken: ${token!}");
}).catchError((err) {
print("Push Notification getAPNSToken ERR: ${err.toString()}");
log("Push Notification getAPNSToken ERR: ${err.toString()}");
});
}
FirebaseMessaging.instance.getToken().then((String? token) {
print("Push Notification getToken: ${token!}");
log("Push Notification getToken: ${token!}");
onToken(token!);
}).catchError((err) {
print(err);
log(err);
});
FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) {
print("Push Notification onTokenRefresh: $fcm_token");
log("Push Notification onTokenRefresh: $fcm_token");
onToken(fcm_token);
});
@ -521,11 +510,11 @@ class PushNotificationHandler {
}
newMessage(RemoteMessage remoteMessage) async {
print("Remote Message: ${remoteMessage.data}");
log("Remote Message: ${remoteMessage.data}");
if (remoteMessage.data.isEmpty) {
return;
}
debugPrint('the value of the remote message is ${remoteMessage.data}');
log('the value of the remote message is ${remoteMessage.data}');
if (remoteMessage.data['is_call'] == 'true' || remoteMessage.data['is_call'] == true) {
_incomingCall(remoteMessage.data);
// showCallkitIncoming();
@ -547,7 +536,7 @@ class PushNotificationHandler {
}
onToken(String token) async {
print("Push Notification Token: $token");
log("Push Notification Token: $token");
await Utils.saveStringFromPrefs(CacheConst.pushToken, token);
}
@ -569,11 +558,11 @@ class PushNotificationHandler {
// Permission.audio,
// Permission.microphone,
].request();
print("=-=-=-=-=-=-=-=-=-=-");
print(statuses[Permission.notification]);
log("=-=-=-=-=-=-=-=-=-=-");
log(statuses[Permission.notification].toString());
}
} catch (_) {
debugPrint(_.toString());
log(_.toString());
}
}
}

@ -115,12 +115,14 @@ extension EmailValidator on String {
bool isCenter = false,
double? height,
double? letterSpacing,
TextOverflow? textOverflow,
int maxLine = 0}) =>
Text(
this,
textAlign: isCenter ? TextAlign.center : textAlignment,
maxLines: (maxLine > 0) ? maxLine : null,
style: TextStyle(
overflow: textOverflow,
fontSize: 12.f,
fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal),
color: color ?? AppColors.blackColor,

@ -262,7 +262,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Expanded(
child: CollapsingListView(
title: LocaleKeys.appointmentDetails.tr(context: context),
report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) && widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment==false && widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! ==true && widget.patientAppointmentHistoryResponseModel.isActiveDoctor! == true
report: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) &&
widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment == false &&
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! == true &&
widget.patientAppointmentHistoryResponseModel.isActiveDoctor! == true
? () {
contactUsViewModel.setSelectedFeedbackType(FeedbackType(id: 1, nameEN: "Complaint for appointment", nameAR: 'شكوى على موعد'));
contactUsViewModel.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
@ -280,8 +283,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
children: [
AppointmentDoctorCard(
// renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
renderWidgetForERDisplay:
((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) || !Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)),
renderWidgetForERDisplay: ((widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID)),
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onAskDoctorTap: () async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingDoctorAvailability.tr(context: context));
@ -416,14 +419,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row(
mainAxisSize: MainAxisSize.max,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.prescription_reminder_icon, width: 40.w, height: 40.h, applyThemeColor: false),
Utils.buildSvgWithAssets(
icon: AppAssets.prescription_reminder_icon, width: 40.w, height: 40.h, applyThemeColor: false),
SizedBox(width: 8.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4.h,
children: [
LocaleKeys.setReminder.tr(context: context).toText13(isBold: true),
LocaleKeys.notifyMeBeforeAppointment.tr(context: context).toText11(color: AppColors.textColorLight, isBold: true),
LocaleKeys.notifyMeBeforeAppointment
.tr(context: context)
.toText11(color: AppColors.textColorLight, isBold: true),
],
),
const Spacer(),
@ -438,48 +444,53 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
inactiveCircleColor: AppColors.greyTextColor,
activeIconColor: AppColors.bgGreenColor,
inactiveIconColor: AppColors.greyTextColor,
activeIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h),
inactiveIcon: Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h),
activeIcon:
Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h),
inactiveIcon:
Utils.buildSvgWithAssets(icon: AppAssets.bell, iconColor: AppColors.whiteColor, width: 12.w, height: 12.h),
onChanged: (newValue) async {
CalenderUtilsNew calender = CalenderUtilsNew.instance;
bool isEventAddedOrRemoved = false;
if (newValue == true) {
DateTime startDate = DateTime.now();
DateTime endDate = DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate);
DateTime endDate =
DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate);
// Show reminder bottom sheet and check if permission was granted
bool permissionGranted = await BottomSheetUtils().showReminderBottomSheet(
context,
endDate,
widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "",
"${widget.patientAppointmentHistoryResponseModel.appointmentNo}" ?? "",
"",
"",
title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}",
description:
"${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}",
onSuccess: () {
setState(() {
myAppointmentsViewModel.setAppointmentReminder(newValue, widget.patientAppointmentHistoryResponseModel);
});
},
isMultiAllowed: true,
onMultiDateSuccess: (int selectedIndex) async {
isEventAddedOrRemoved = await calender.createOrUpdateEvent(
title:
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
description:
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}",
scheduleDateTime: DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate),
eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
location: '',
reminderMinutes: selectedIndex);
setState(() {
myAppointmentsViewModel.setAppointmentReminder(isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
});
},
isForAppointment: true
);
context,
endDate,
widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "",
"${widget.patientAppointmentHistoryResponseModel.appointmentNo}" ?? "",
"",
"",
title: "Appointment with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj}",
description:
"${widget.patientAppointmentHistoryResponseModel.doctorNameObj} will be having an appointment on ${widget.patientAppointmentHistoryResponseModel.appointmentDate}",
onSuccess: () {
setState(() {
myAppointmentsViewModel.setAppointmentReminder(
newValue, widget.patientAppointmentHistoryResponseModel);
});
},
isMultiAllowed: true,
onMultiDateSuccess: (int selectedIndex) async {
isEventAddedOrRemoved = await calender.createOrUpdateEvent(
title:
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} on ${DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate)}, Appointment #${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
description:
"Appointment Reminder with ${widget.patientAppointmentHistoryResponseModel.doctorNameObj} in ${widget.patientAppointmentHistoryResponseModel.projectName}",
scheduleDateTime:
DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate),
eventId: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
location: '',
reminderMinutes: selectedIndex);
setState(() {
myAppointmentsViewModel.setAppointmentReminder(
isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
});
},
isForAppointment: true);
// If permission was not granted, revert the switch back to OFF
if (!permissionGranted) {
@ -490,7 +501,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
id: "${widget.patientAppointmentHistoryResponseModel.appointmentNo}",
);
setState(() {
myAppointmentsViewModel.setAppointmentReminder(!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
myAppointmentsViewModel.setAppointmentReminder(
!isEventAddedOrRemoved, widget.patientAppointmentHistoryResponseModel);
});
}
},
@ -581,8 +593,12 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
LocaleKeys.appointmentStatus.tr(context: context).toText16(isBold: true),
SizedBox(height: 4.h),
(!AppointmentType.isConfirmed(widget.patientAppointmentHistoryResponseModel)
? LocaleKeys.notConfirmed.tr(context: context).toText12(color: AppColors.primaryRedColor, isBold: true)
: LocaleKeys.confirmed.tr(context: context).toText12(color: AppColors.successColor, isBold: true)),
? LocaleKeys.notConfirmed
.tr(context: context)
.toText12(color: AppColors.primaryRedColor, isBold: true)
: LocaleKeys.confirmed
.tr(context: context)
.toText12(color: AppColors.successColor, isBold: true)),
SizedBox(height: 16.h),
],
),
@ -640,7 +656,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.doctorWillCallYou.tr(context: context).toText14(color: AppColors.greyTextColor, isBold: true),
LocaleKeys.doctorWillCallYou
.tr(context: context)
.toText14(color: AppColors.greyTextColor, isBold: true),
],
),
),
@ -648,13 +666,17 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
)
: Stack(
children: [
ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
fit: BoxFit.contain,
SizedBox(
width: double.infinity,
child: ClipRRect(
clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲
child: Image.network(
"https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}",
fit: BoxFit.cover,
width: double.infinity,
),
),
),
Positioned(
@ -663,7 +685,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
width: MediaQuery.of(context).size.width - 85.w,
child: CustomButton(
onPressed: () async {
if (widget.patientAppointmentHistoryResponseModel.projectID == 130 || widget.patientAppointmentHistoryResponseModel.projectID == 120) {
if (widget.patientAppointmentHistoryResponseModel.projectID == 130 ||
widget.patientAppointmentHistoryResponseModel.projectID == 120) {
showDirectionsBottomSheet();
} else {
await MapLauncher.showMarker(
@ -683,7 +706,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
},
text: LocaleKeys.getDirections.tr(context: context),
backgroundColor: AppColors.bookAppointment.withValues(alpha: 0.8),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
borderColor: AppointmentType.getNextActionButtonColor(
widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor: Colors.white,
fontSize: 14.f,
fontWeight: FontWeight.w600,
@ -974,7 +999,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
);
Navigator.of(context).push(
CustomPageRoute(
page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel),
page: PrescriptionDetailPage(
isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel),
),
);
} else {
@ -1289,7 +1315,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 || widget.patientAppointmentHistoryResponseModel.nextAction == 20)
if (widget.patientAppointmentHistoryResponseModel.nextAction == 15 ||
widget.patientAppointmentHistoryResponseModel.nextAction == 20)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -1297,7 +1324,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.amountBeforeTax.tr(context: context).toText18(isBold: true),
Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
Utils.getPaymentAmountWithSymbol(
widget.patientAppointmentHistoryResponseModel.patientShare!.toString().toText16(isBold: true),
AppColors.blackColor,
13,
isSaudiCurrency: true),
],
),
@ -1305,8 +1335,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})".toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64),
Expanded(
child: LocaleKeys.upcomingPaymentNow.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor)),
"VAT 15%(${widget.patientAppointmentHistoryResponseModel.patientTaxAmount})"
.toText14(isBold: true, color: AppColors.greyTextColor, letterSpacing: -0.64),
],
),
SizedBox(height: 18.h),
@ -1320,7 +1352,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Utils.getPaymentAmountWithSymbol(widget.patientAppointmentHistoryResponseModel.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
Utils.getPaymentAmountWithSymbol(
widget.patientAppointmentHistoryResponseModel.patientShareWithTax!.toString().toText24(isBold: true),
AppColors.blackColor,
17,
isSaudiCurrency: true),
],
),
@ -1358,7 +1393,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction);
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor: widget.patientAppointmentHistoryResponseModel.nextAction == 15 ? AppColors.textColor : Colors.white,
fontSize: 16.f,
fontWeight: FontWeight.w600,
@ -1398,7 +1434,10 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
text: LocaleKeys.insideHospital.tr(context: context),
onPressed: () {
Navigator.pop(context);
initPenguinSDK(widget.patientAppointmentHistoryResponseModel.projectID == 130 ? 1 : (widget.patientAppointmentHistoryResponseModel.projectID == 120 ? 3 : -1),
initPenguinSDK(
widget.patientAppointmentHistoryResponseModel.projectID == 130
? 1
: (widget.patientAppointmentHistoryResponseModel.projectID == 120 ? 3 : -1),
clinicID: widget.patientAppointmentHistoryResponseModel.clinicID.toString());
},
backgroundColor: AppColors.primaryRedColor,
@ -1417,12 +1456,14 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Navigator.pop(context);
await MapLauncher.showMarker(
mapType: MapType.google,
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
).catchError((err) {
MapLauncher.showMarker(
mapType: Platform.isIOS ? MapType.apple : MapType.google,
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!), double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
coords: Coords(double.parse(widget.patientAppointmentHistoryResponseModel.latitude!),
double.parse(widget.patientAppointmentHistoryResponseModel.longitude!)),
title: widget.patientAppointmentHistoryResponseModel.projectName ?? "Habib Hospital",
);
});
@ -1456,7 +1497,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
Permission.bluetoothScan,
Permission.activityRecognition,
].request().whenComplete(() {
PenguinMethodChannel().launch("penguin", getIt.get<AppState>().isArabic() ? "ar" : "en", getIt.get<AppState>().getAuthenticatedUser()?.patientId?.toString() ?? "", true, details: data);
PenguinMethodChannel().launch("penguin", getIt.get<AppState>().isArabic() ? "ar" : "en",
getIt.get<AppState>().getAuthenticatedUser()?.patientId?.toString() ?? "", true,
details: data);
});
}
}
@ -1514,12 +1557,20 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheet(context, child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) {
showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
Navigator.of(context).pop();
}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isAutoDismiss: true, isCloseButtonVisible: true, isDismissible: false, isFullScreen: false, isSuccessDialog: true);
},
title: "",
height: ResponsiveExtension.screenHeight * 0.3,
isAutoDismiss: true,
isCloseButtonVisible: true,
isDismissible: false,
isFullScreen: false,
isSuccessDialog: true);
});
// LoaderBottomSheet.hideLoader();
case 15:
@ -1539,7 +1590,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(
height: 12,
),

@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -18,6 +20,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_vie
import 'package:hmg_patient_app_new/features/my_appointments/utils/appointment_type.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_details_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/eye_measurement_details_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -26,8 +29,6 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'dart:ui' as ui;
import 'package:hmg_patient_app_new/presentation/appointments/appointment_payment_page.dart';
import 'package:lottie/lottie.dart';
class AppointmentCard extends StatefulWidget {
@ -42,6 +43,7 @@ class AppointmentCard extends StatefulWidget {
final ContactUsViewModel? contactUsViewModel;
final BookAppointmentsViewModel bookAppointmentsViewModel;
final bool isForRate;
// bool isAppointmentWithin4Hours = false;
const AppointmentCard(
@ -189,20 +191,28 @@ class _AppointmentCardState extends State<AppointmentCard> {
runSpacing: 6.h,
children: [
AppCustomChipWidget(
icon: widget.isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon),
icon:
widget.isLoading ? AppAssets.walkin_appointment_icon : (isLiveCare ? AppAssets.small_livecare_icon : AppAssets.walkin_appointment_icon),
iconColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor),
labelText: widget.isLoading ? LocaleKeys.walkin.tr(context: context) : (isLiveCare ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context)),
labelText: widget.isLoading
? LocaleKeys.walkin.tr(context: context)
: (isLiveCare ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context)),
backgroundColor: widget.isLoading ? AppColors.greyColor : (isLiveCare ? AppColors.successColor : AppColors.greyColor),
textColor: widget.isLoading ? AppColors.textColor : (isLiveCare ? Colors.white : AppColors.textColor),
).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
labelText:
widget.isLoading ? 'OutPatient' : (appState.isArabic() ? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN! : widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
labelText: widget.isLoading
? 'OutPatient'
: (appState.isArabic()
? widget.patientAppointmentHistoryResponseModel.isInOutPatientDescriptionN!
: widget.patientAppointmentHistoryResponseModel.isInOutPatientDescription!),
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.1),
textColor: AppColors.warningColorYellow,
).toShimmer2(isShow: widget.isLoading),
AppCustomChipWidget(
labelText: widget.isLoading ? 'Booked' : AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
labelText: widget.isLoading
? 'Booked'
: AppointmentType.getAppointmentStatusType(widget.patientAppointmentHistoryResponseModel.patientStatusType!),
backgroundColor: AppColors.successColor.withValues(alpha: 0.1),
textColor: AppColors.successColor,
).toShimmer2(isShow: widget.isLoading),
@ -218,7 +228,9 @@ class _AppointmentCardState extends State<AppointmentCard> {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.network(
widget.isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
widget.isLoading
? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png'
: widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
width: 63.h,
height: 63.h,
fit: BoxFit.cover,
@ -242,8 +254,10 @@ class _AppointmentCardState extends State<AppointmentCard> {
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.w, height: 15.h, iconColor: AppColors.ratingColorYellow),
SizedBox(height: 2.h),
(isFoldable || isTablet)
? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}".toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true)
: "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
? "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate}"
.toText9(isBold: true, color: AppColors.textColor, isEnglishOnly: true)
: "${widget.patientAppointmentHistoryResponseModel.decimalDoctorRate ?? "0.0"}"
.toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
],
),
).circle(100).toShimmer2(isShow: widget.isLoading),
@ -258,10 +272,16 @@ class _AppointmentCardState extends State<AppointmentCard> {
Row(
children: [
(widget.isLoading ? 'Dr' : "${widget.patientAppointmentHistoryResponseModel.doctorTitle}").toText16(isBold: true, maxlines: 1),
(widget.isLoading ? 'John Doe' : " ${widget.patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}")
.toText16(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")),
Expanded(
child: (widget.isLoading ? 'John Doe' : " ${widget.patientAppointmentHistoryResponseModel.doctorNameObj!.truncate(20)}").toText16(
isBold: true,
maxlines: 1,
textOverflow: TextOverflow.ellipsis,
isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "John Doe")),
),
SizedBox(width: 12.w),
(widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty)
(widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null &&
widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty)
? Image.network(
widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png",
width: 20.h,
@ -419,40 +439,40 @@ class _AppointmentCardState extends State<AppointmentCard> {
// } else {
return CustomButton(
text: widget.isFromMedicalReport ? LocaleKeys.selectAppointment.tr(context: context) : LocaleKeys.viewDetails.tr(context: context),
onPressed: () {
if (widget.isFromMedicalReport) {
if (widget.isForFeedback) {
widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
} else {
widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
}
Navigator.pop(context, false);
onPressed: () {
if (widget.isFromMedicalReport) {
if (widget.isForFeedback) {
widget.contactUsViewModel!.setPatientFeedbackSelectedAppointment(widget.patientAppointmentHistoryResponseModel);
} else {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((_) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
widget.medicalFileViewModel!.setSelectedMedicalReportAppointment(widget.patientAppointmentHistoryResponseModel);
}
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
// height: isTablet || isFoldable ? 46.h : 40.h,
height: 40.h,
icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
Navigator.pop(context, false);
} else {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((_) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
// height: isTablet || isFoldable ? 46.h : 40.h,
height: 40.h,
icon: widget.isFromMedicalReport ? AppAssets.checkmark_icon : null,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
);
// }
} else {
if (widget.isFromMedicalReport) {
@ -486,7 +506,7 @@ class _AppointmentCardState extends State<AppointmentCard> {
}
return (widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true)
? (AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) &&
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false)
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false)
? // Show only View Details button without arrow when rebooking not allowed
_getArrivedButton(context)
: Row(
@ -498,8 +518,10 @@ class _AppointmentCardState extends State<AppointmentCard> {
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () => handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context),
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600,
@ -589,10 +611,10 @@ class _AppointmentCardState extends State<AppointmentCard> {
// Show Rebook button
return CustomButton(
borderSide: BorderSide(
color: AppColors.textColor,
width: 1.2,
),
borderSide: BorderSide(
color: AppColors.textColor,
width: 1.2,
),
text: LocaleKeys.rebookSameDoctor.tr(context: context),
onPressed: () => openDoctorScheduleCalendar(context),
backgroundColor: AppColors.transparent,
@ -620,7 +642,8 @@ class _AppointmentCardState extends State<AppointmentCard> {
);
} else {
if (!AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)) {
widget.bookAppointmentsViewModel.getAppointmentNearestGate(projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID);
widget.bookAppointmentsViewModel.getAppointmentNearestGate(
projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID);
}
Navigator.of(context)
.push(
@ -708,46 +731,47 @@ class _AppointmentCardState extends State<AppointmentCard> {
children: [
Lottie.asset(AppAnimations.warningAnimation,
repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 12,),
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
color: AppColors.textColor,
isCenter: true,
SizedBox(
height: 12,
),
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
color: AppColors.textColor,
isCenter: true,
),
SizedBox(height: 24.h),
// Countdown Timer - DD : HH : MM : SS format with labels
Directionality(
textDirection: ui.TextDirection.ltr,
child:Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Days
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
LocaleKeys.days.tr(context: context),
),
_buildTimeSeparator(),
// Hours
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
LocaleKeys.hours.tr(context: context),
),
_buildTimeSeparator(),
// Minutes
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.minutes.tr(context: context),
),
_buildTimeSeparator(),
// Seconds
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.seconds.tr(context: context),
),
],
)),
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Days
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
LocaleKeys.days.tr(context: context),
),
_buildTimeSeparator(),
// Hours
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
LocaleKeys.hours.tr(context: context),
),
_buildTimeSeparator(),
// Minutes
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.minutes.tr(context: context),
),
_buildTimeSeparator(),
// Seconds
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.seconds.tr(context: context),
),
],
)),
SizedBox(height: 24.h),
// Green Acknowledge button with checkmark icon
CustomButton(
@ -800,4 +824,3 @@ class _AppointmentCardState extends State<AppointmentCard> {
}
}
}

@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -18,10 +20,8 @@ import 'package:hmg_patient_app_new/presentation/appointments/appointment_paymen
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'dart:ui' as ui;
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
class MedicalFileAppointmentCard extends StatefulWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@ -138,169 +138,193 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
richText: Directionality(
textDirection: ui.TextDirection.ltr,
child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(widget.patientAppointmentHistoryResponseModel.appointmentDate), false)
.toText12(color: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor, isBold: true, isEnglishOnly: true)
.toText12(
color: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
isBold: true,
isEnglishOnly: true)
.paddingSymmetrical(8.w, 0),
),
icon: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppAssets.appointment_calendar_icon : AppAssets.alarm_clock_icon,
icon: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? AppAssets.appointment_calendar_icon
: AppAssets.alarm_clock_icon,
iconColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
iconSize: 16.w,
backgroundColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor,
backgroundColor:
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.greyColor : AppColors.secondaryLightRedColor,
textColor: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) ? AppColors.textColor : AppColors.primaryRedColor,
padding: EdgeInsets.only(top: 12.h, bottom: 12.h, left: 8.w, right: 8.w),
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(height: 16.h),
Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
width: 200.w,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.network(
widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 25.w,
height: 27.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(width: 8.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText14(isBold: true, maxlines: 1, isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "")).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
(widget.patientAppointmentHistoryResponseModel.clinicName ?? "")
.toText12(maxLine: 1, isBold: true, color: AppColors.greyTextColor)
.toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
],
IntrinsicWidth(
child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Image.network(
widget.patientAppointmentHistoryResponseModel.doctorImageURL ??
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 25.w,
height: 27.h,
fit: BoxFit.fill,
).circle(100).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(width: 8.w),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? "")
.toText14(
isBold: true,
maxlines: 1,
isEnglishOnly: !Utils.isArabicText(widget.patientAppointmentHistoryResponseModel.doctorNameObj ?? ""))
.toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
(widget.patientAppointmentHistoryResponseModel.clinicName ?? "")
.toText12(maxLine: 1, textOverflow: TextOverflow.ellipsis, isBold: true, color: AppColors.greyTextColor)
.toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
],
),
),
),
],
),
SizedBox(height: 12.h),
// Check if doctor is active - if not, show only View Details button
(widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true)
? // Doctor is active - check rebooking logic
(AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) &&
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false)
? // Show only the button without arrow when rebooking not allowed
widget.myAppointmentsViewModel.isMyAppointmentsLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r)
: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton(context).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context);
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading)
: // Normal flow - show button with arrow
Row(
children: [
widget.myAppointmentsViewModel.isMyAppointmentsLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r)
: Expanded(
flex: 7,
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton(context).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context);
},
backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
),
SizedBox(width: 8.w),
((((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
(widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0))) &&
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
? SizedBox.shrink()
: Expanded(
flex: 2,
child: Container(
height: 40.h,
width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.r,
),
child: Padding(
padding: EdgeInsets.all(10.w),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
iconColor: AppColors.whiteColor,
icon: AppAssets.forward_arrow_icon_small,
width: 40.w,
height: 40.h,
fit: BoxFit.contain,
),
],
),
SizedBox(height: 8.h),
// Check if doctor is active - if not, show only View Details button
(widget.patientAppointmentHistoryResponseModel.isActiveDoctor ?? true)
? // Doctor is active - check rebooking logic
(AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel) &&
widget.patientAppointmentHistoryResponseModel.isClinicReBookingAllowed == false)
? // Show only the button without arrow when rebooking not allowed
widget.myAppointmentsViewModel.isMyAppointmentsLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r)
: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton(context).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context);
},
backgroundColor:
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.15),
borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor: AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading)
: // Normal flow - show button with arrow
Row(
children: [
widget.myAppointmentsViewModel.isMyAppointmentsLoading
? Container().toShimmer2(isShow: true, height: 40.h, width: 100.w, radius: 12.r)
: Expanded(
flex: 7,
child: AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel)
? getArrivedAppointmentButton(context)
.toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading)
: CustomButton(
text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction),
onPressed: () {
handleAppointmentNextAction(widget.patientAppointmentHistoryResponseModel.nextAction, context);
},
backgroundColor:
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.15),
borderColor:
AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction)
.withValues(alpha: 0.01),
textColor:
AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
icon: AppointmentType.getNextActionIcon(widget.patientAppointmentHistoryResponseModel.nextAction),
iconColor:
AppointmentType.getNextActionTextColor(widget.patientAppointmentHistoryResponseModel.nextAction),
iconSize: 14.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
),
SizedBox(width: 8.w),
((((widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ||
(widget.patientAppointmentHistoryResponseModel.isExecludeDoctor ?? false) ||
!Utils.isClinicAllowedForRebook(widget.patientAppointmentHistoryResponseModel.clinicID ?? 0))) &&
AppointmentType.isArrived(widget.patientAppointmentHistoryResponseModel))
? SizedBox.shrink()
: Expanded(
flex: 2,
child: Container(
height: 40.h,
width: 40.w,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.textColor,
borderRadius: 10.r,
),
),
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
child: Padding(
padding: EdgeInsets.all(10.w),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
iconColor: AppColors.whiteColor,
icon: AppAssets.forward_arrow_icon_small,
width: 40.w,
height: 40.h,
fit: BoxFit.contain,
),
),
),
)
.then((val) {
// widget.myAppointmentsViewModel.initAppointmentsViewModel();
// widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
)
: // Doctor is not active - show only View Details button
CustomButton(
text: LocaleKeys.viewDetails.tr(context: context),
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((_) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
],
).paddingAll(16.w),
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading).onPress(() {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((val) {
// widget.myAppointmentsViewModel.initAppointmentsViewModel();
// widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
}),
),
],
)
: // Doctor is not active - show only View Details button
CustomButton(
text: LocaleKeys.viewDetails.tr(context: context),
onPressed: () {
Navigator.of(context)
.push(
CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
),
)
.then((_) {
widget.myAppointmentsViewModel.initAppointmentsViewModel();
widget.myAppointmentsViewModel.getPatientAppointments(true, false);
});
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
padding: EdgeInsets.symmetric(horizontal: 10.w),
height: 40.h,
).toShimmer2(isShow: widget.myAppointmentsViewModel.isMyAppointmentsLoading),
],
).paddingAll(16.w),
),
),
],
);
@ -393,16 +417,16 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
// No action needed - go to details
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {});
break;
case 10:
// Confirm appointment - go to details
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {});
break;
case 15:
@ -425,41 +449,42 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
children: [
// Message text
LocaleKeys.upcomingPaymentPending.tr(context: context).toText14(
color: AppColors.textColor,
isCenter: true,
),
color: AppColors.textColor,
isCenter: true,
),
SizedBox(height: 24.h),
// Countdown Timer - DD : HH : MM : SS format with labels
Directionality(
textDirection: ui.TextDirection.ltr, child:Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Days
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
LocaleKeys.days.tr(context: context),
),
_buildTimeSeparator(),
// Hours
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
LocaleKeys.hours.tr(context: context),
),
_buildTimeSeparator(),
// Minutes
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.minutes.tr(context: context),
),
_buildTimeSeparator(),
// Seconds
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.seconds.tr(context: context),
),
],
)),
textDirection: ui.TextDirection.ltr,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Days
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inDays.toString().padLeft(2, '0') : '00',
LocaleKeys.days.tr(context: context),
),
_buildTimeSeparator(),
// Hours
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inHours.remainder(24).toString().padLeft(2, '0') : '00',
LocaleKeys.hours.tr(context: context),
),
_buildTimeSeparator(),
// Minutes
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inMinutes.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.minutes.tr(context: context),
),
_buildTimeSeparator(),
// Seconds
_buildTimeUnit(
_timeRemaining != null ? _timeRemaining!.inSeconds.remainder(60).toString().padLeft(2, '0') : '00',
LocaleKeys.seconds.tr(context: context),
),
],
)),
SizedBox(height: 24.h),
// Green Acknowledge button with checkmark icon
CustomButton(
@ -502,26 +527,25 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
// Confirm livecare - go to details
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {});
break;
case 90:
// Check-in - go to details
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {});
break;
default:
// Default - go to details
Navigator.of(context)
.push(CustomPageRoute(
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel),
))
.then((val) {});
}
}
}

@ -99,9 +99,8 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title.toText14(isBold: true),
subTitle
.toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true)
.toShimmer2(isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading),
subTitle.toText12(color: AppColors.primaryRedColor, isBold: true, isEnglishOnly: true).toShimmer2(
isShow: (leadingIcon == AppAssets.rulerIcon || leadingIcon == AppAssets.weightScale) && hmgServicesVM.isVitalSignLoading),
],
),
],
@ -159,7 +158,9 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
}
}
} else {
name = ""; /// as per the mahmooud instruction, if user is not authenticated, we will not show any name in the greeting message
name = "";
/// as per the mahmooud instruction, if user is not authenticated, we will not show any name in the greeting message
}
return Scaffold(
@ -167,7 +168,10 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
body: Consumer2<SymptomsCheckerViewModel, HmgServicesViewModel>(
builder: (context, viewModel, hmgServicesVM, child) {
// Check if any field is empty
bool hasEmptyFields = viewModel.selectedGender == null || viewModel.selectedAge == null || viewModel.selectedHeight == null || viewModel.selectedWeight == null;
bool hasEmptyFields = viewModel.selectedGender == null ||
viewModel.selectedAge == null ||
viewModel.selectedHeight == null ||
viewModel.selectedWeight == null;
// Get display values
String genderText = _getLocalizedGender(viewModel.selectedGender, context);
@ -313,6 +317,7 @@ class _UserInfoSelectionPageState extends State<UserInfoSelectionPage> {
),
],
),
SizedBox(height: 24.h),
],
).paddingSymmetrical(24.w, 0),
),

@ -181,7 +181,7 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
color: AppColors.whiteColor,
borderRadius: BorderRadius.vertical(top: Radius.circular(24.r)),
),
padding: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h),
padding: EdgeInsets.only(left: 24.w, right: 24.w, top: 16.h, bottom: 24.h),
child: SafeArea(
top: false,
child: isSingleEdit

@ -1042,13 +1042,13 @@ packages:
source: hosted
version: "1.2.1"
image_picker_android:
dependency: transitive
dependency: "direct main"
description:
name: image_picker_android
sha256: "5e9bf126c37c117cf8094215373c6d561117a3cfb50ebc5add1a61dc6e224677"
sha256: "66810af8e99b2657ee98e5c6f02064f69bb63f7a70e343937f70946c5f8c6622"
url: "https://pub.dev"
source: hosted
version: "0.8.13+10"
version: "0.8.13+16"
image_picker_for_web:
dependency: transitive
description:
@ -1082,7 +1082,7 @@ packages:
source: hosted
version: "0.2.2+1"
image_picker_platform_interface:
dependency: transitive
dependency: "direct main"
description:
name: image_picker_platform_interface
sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c"

@ -101,9 +101,7 @@ dependencies:
flutter_widget_from_html: ^0.17.1
huawei_map: ^6.12.0+301
flutter_image_compress: ^2.3.0
scrollable_positioned_list: ^0.3.8
in_app_review: ^2.0.11
dev_dependencies:

Loading…
Cancel
Save