diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index ab7b2ca..9c1827a 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -69,4 +69,7 @@
# Penguin classes
-keep class com.peng.pennavmap.models.** { *; }
--keep class com.peng.pennavmap.db.** { *; }
\ No newline at end of file
+-keep class com.peng.pennavmap.db.** { *; }
+
+-keep class com.hiennv.flutter_callkit_incoming.** { *; }
+-keepattributes Signature, Annotation, InnerClasses, EnclosingMethod
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 6a1c47e..4f7ef74 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -30,9 +30,6 @@
-
@@ -91,6 +88,12 @@
+
+
+
+
+
+
@@ -115,6 +118,7 @@
+
+
+
diff --git a/assets/images/svg/doctor_profile_reviews_icon.svg b/assets/images/svg/doctor_profile_reviews_icon.svg
new file mode 100644
index 0000000..9ef9f71
--- /dev/null
+++ b/assets/images/svg/doctor_profile_reviews_icon.svg
@@ -0,0 +1,7 @@
+
diff --git a/assets/images/svg/waitingAppo.svg b/assets/images/svg/waitingAppo.svg
new file mode 100644
index 0000000..c9bd009
--- /dev/null
+++ b/assets/images/svg/waitingAppo.svg
@@ -0,0 +1,6 @@
+
diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart
index aa534f6..888f704 100644
--- a/lib/core/api/api_client.dart
+++ b/lib/core/api/api_client.dart
@@ -175,8 +175,7 @@ class ApiClientImp implements ApiClient {
}
// body['TokenID'] = "@dm!n";
- // body['PatientID'] = 4772429;
- // body['PatientID'] = 1231755;
+ // body['PatientID'] = 1018977;
// body['PatientTypeID'] = 1;
//
// body['PatientOutSA'] = 0;
@@ -184,9 +183,6 @@ class ApiClientImp implements ApiClient {
}
body.removeWhere((key, value) => value == null);
- log("uri: ${Uri.parse(url.trim())}");
-
- log("body: ${json.encode(body)}");
final bool networkStatus = await Utils.checkConnection(bypassConnectionCheck: bypassConnectionCheck);
@@ -204,7 +200,7 @@ class ApiClientImp implements ApiClient {
final int statusCode = response.statusCode;
log("uri: ${Uri.parse(url.trim())}");
log("body: ${json.encode(body)}");
- log("response.body: ${response.body}");
+ // log("response.body: ${response.body}");
// log("response.body: ${response.body}");
if (statusCode < 200 || statusCode >= 400) {
onFailure('Error While Fetching data', statusCode, failureType: StatusCodeFailure("Error While Fetching data"));
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index 3ae8d4c..b92461e 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -749,7 +749,7 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
- SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
+ SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
@@ -759,7 +759,7 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
- SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
+ SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
@@ -779,7 +779,7 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
- SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
+ SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
@@ -789,7 +789,7 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
- SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx';
+ SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
GET_TAMARA_INSTALLMENTS_URL = "https://epharmacy.hmg.com/tamara/Home/getinstallments";
GET_TAMARA_PAYMENT_STATUS = 'https://epharmacy.hmg.com/tamara/api/OnlineTamara/order_status?orderid=';
@@ -862,7 +862,7 @@ class ApiConsts {
// ************ static values for Api ****************
- static final double appVersionID = 19.3;
+ static final double appVersionID = 50.3;
static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958";
diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart
index 5fccc6e..f3ff8fd 100644
--- a/lib/core/app_assets.dart
+++ b/lib/core/app_assets.dart
@@ -175,6 +175,9 @@ class AppAssets {
static const String comprehensiveCheckup = '$svgBasePath/comprehensive_checkup.svg';
static const String all_payment_method = '$svgBasePath/all_payment_method.svg';
static const String ic_rrt_vehicle = '$svgBasePath/ic_rrt_vehicle.svg';
+ static const String doctor_profile_rating_icon = '$svgBasePath/doctor_profile_rating_icon.svg';
+ static const String doctor_profile_reviews_icon = '$svgBasePath/doctor_profile_reviews_icon.svg';
+ static const String waiting_appointment_icon = '$svgBasePath/waitingAppo.svg';
//bottom navigation//
diff --git a/lib/core/cache_consts.dart b/lib/core/cache_consts.dart
index 66b38d4..bcbb185 100644
--- a/lib/core/cache_consts.dart
+++ b/lib/core/cache_consts.dart
@@ -74,4 +74,7 @@ class CacheConst {
static const String patientOccupationList = 'patient-occupation-list';
static const String hasEnabledQuickLogin = 'has-enabled-quick-login';
static const String quickLoginEnabled = 'quick-login-enabled';
+
+ static const String zoomRoomID = 'zoom-room-id';
+ static String isAppOpenedFromCall = "is_app_opened_from_call";
}
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index 2a5c749..30b4ab7 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -130,7 +130,7 @@ class AppDependencies {
),
);
- getIt.registerLazySingleton(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt()));
+ getIt.registerLazySingleton(() => PrescriptionsViewModel(prescriptionsRepo: getIt(), errorHandlerService: getIt(), navServices: getIt()));
getIt.registerLazySingleton(() => InsuranceViewModel(insuranceRepo: getIt(), errorHandlerService: getIt()));
diff --git a/lib/core/utils/calender_utils_new.dart b/lib/core/utils/calender_utils_new.dart
index 28084a9..05448b1 100644
--- a/lib/core/utils/calender_utils_new.dart
+++ b/lib/core/utils/calender_utils_new.dart
@@ -16,7 +16,7 @@ class CalenderUtilsNew {
Future getCalenders() async {
CalendarPermissionStatus result = await DeviceCalendar.instance.hasPermissions();
- if(result != CalendarPermissionStatus.granted) await DeviceCalendar.instance.requestPermissions();
+ if (result != CalendarPermissionStatus.granted) await DeviceCalendar.instance.requestPermissions();
var calenders = await calender.listCalendars();
calenders.forEach((calender) {
if (!calender.readOnly) {
@@ -32,19 +32,19 @@ class CalenderUtilsNew {
var writableCalendars = writableCalender.first;
String eventResult = "";
CalendarPermissionStatus result = await DeviceCalendar.instance.hasPermissions();
- if(result != CalendarPermissionStatus.granted) await DeviceCalendar.instance.requestPermissions();
+ if (result != CalendarPermissionStatus.granted) await DeviceCalendar.instance.requestPermissions();
print(result);
// String eventId = await getEventIdIfEventExist(title!.split("#").last);
// if (eventId.isEmpty) {
- eventResult = await DeviceCalendar.instance.createEvent(
- calendarId: writableCalendars!.id,
- title: title,
- description: description,
- startDate: scheduleDateTime!,
- endDate: scheduleDateTime!.add(Duration(minutes: 30)),
- reminderMinutes: reminderMinutes);
- return eventResult.isNotEmpty;
+ eventResult = await DeviceCalendar.instance.createEvent(
+ calendarId: writableCalendars!.id,
+ title: title,
+ description: description,
+ startDate: scheduleDateTime!,
+ endDate: scheduleDateTime!.add(Duration(minutes: 30)),
+ reminderMinutes: reminderMinutes);
+ return eventResult.isNotEmpty;
// }
// await DeviceCalendar.instance.updateEvent(
@@ -86,12 +86,12 @@ class CalenderUtilsNew {
//event for number of days.
for (int j = 0; j < frequencyNumber; j++) {
statusOfOperation = await createOrUpdateEvent(
- title: "$itemDescriptionN} Medication about to due for prescription , #$prescriptionNumber",
- description: "$itemDescriptionN $frequencyNumber $route ",
- scheduleDateTime: actualDate,
- location: '', //event id with varitions
- reminderMinutes: reminderMinutes
- );
+ title: "$itemDescriptionN} Medication about to due for prescription , #$prescriptionNumber",
+ description: "$itemDescriptionN $frequencyNumber $route ",
+ scheduleDateTime: actualDate,
+ location: '',
+ //event id with varitions
+ reminderMinutes: reminderMinutes);
if (!statusOfOperation) return false;
actualDate = actualDate.add(Duration(hours: interval));
@@ -111,23 +111,21 @@ class CalenderUtilsNew {
var availableCalender = writableCalender.first;
DateTime startEventsDate = Jiffy.parseFromDateTime(DateTime.now()).subtract(days: 30).dateTime;
DateTime endEventsDate = Jiffy.parseFromDateTime(DateTime.now()).add(days: 120).dateTime;
- return await calender.listEvents(startEventsDate, endEventsDate, calendarIds: [availableCalender.id]);
-
+ return await calender.listEvents(startEventsDate, endEventsDate, calendarIds: [availableCalender.id]);
}
-
FutureOr checkIfEventExist(String admissionId) async {
if (writableCalender.isEmpty) {
await getCalenders();
}
List events = await getEvents();
- if(events.isEmpty) return false;
- for(var event in events){
+ if (events.isEmpty) return false;
+ for (var event in events) {
List title = event.title!.split("#");
- if(title.contains(admissionId)) return true;
+ if (title.contains(admissionId)) return true;
}
- return false;
+ return false;
}
FutureOr getEventIdIfEventExist(String admissionId) async {
@@ -148,11 +146,11 @@ class CalenderUtilsNew {
await getCalenders();
}
List events = await getEvents();
- if(events.isEmpty) return false;
- for(var event in events){
+ if (events.isEmpty) return false;
+ for (var event in events) {
List title = event.title!.split("#");
- if(title.contains(id)) {
+ if (title.contains(id)) {
calender.deleteEvent(eventId: event.eventId);
return true;
}
@@ -165,12 +163,12 @@ class CalenderUtilsNew {
await getCalenders();
}
List events = await getEvents();
- if(events.isEmpty) return false;
+ if (events.isEmpty) return false;
bool statusOfOperation = false;
- for(var event in events){
+ for (var event in events) {
List title = event.title.split("#");
- if(title.contains(id)) {
+ if (title.contains(id)) {
calender.deleteEvent(eventId: event.eventId);
statusOfOperation = true;
}
diff --git a/lib/core/utils/date_util.dart b/lib/core/utils/date_util.dart
index ee2b4ec..a42a44d 100644
--- a/lib/core/utils/date_util.dart
+++ b/lib/core/utils/date_util.dart
@@ -369,6 +369,29 @@ class DateUtil {
}
}
+ static String getDateStringForNearestSlot(String date) {
+ DateTime dateObj = DateUtil.convertStringToDate(date);
+ return DateUtil.getWeekDay(dateObj.weekday) +
+ ", " +
+ dateObj.day.toString() +
+ " " +
+ DateUtil.getMonth(dateObj.month) +
+ " " +
+ dateObj.year.toString() +
+ " " +
+ dateObj.hour.toString() +
+ ":" +
+ getMinute(dateObj);
+ }
+
+ static String getMinute(DateTime dateObj) {
+ if (dateObj.minute == 0) {
+ return dateObj.minute.toString() + "0";
+ } else {
+ return dateObj.minute.toString();
+ }
+ }
+
static String getMonthDayYearLangDateFormatted(DateTime dateTime, String lang) {
if (dateTime != null) {
return lang == 'en'
diff --git a/lib/core/utils/jwt.dart b/lib/core/utils/jwt.dart
new file mode 100644
index 0000000..f82c4e1
--- /dev/null
+++ b/lib/core/utils/jwt.dart
@@ -0,0 +1,38 @@
+import 'dart:math';
+
+import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
+import 'package:hmg_patient_app_new/core/utils/zoom_config.dart';
+
+String makeId(int length) {
+ String result = "";
+ String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ int charactersLength = characters.length;
+ for (var i = 0; i < length; i++) {
+ result += characters[Random().nextInt(charactersLength)];
+ }
+ return result;
+}
+
+String generateJwt(String sessionName, String roleType) {
+ try {
+ var iat = DateTime.now();
+ var exp = DateTime.now().add(Duration(days: 2));
+ final jwt = JWT(
+ {
+ 'app_key': configs["ZOOM_SDK_KEY"],
+ 'version': 1,
+ 'user_identity': makeId(10),
+ 'iat': (iat.millisecondsSinceEpoch / 1000).round(),
+ 'exp': (exp.millisecondsSinceEpoch / 1000).round(),
+ 'tpc': sessionName,
+ 'role_type': int.parse(roleType),
+ 'cloud_recording_option': 1,
+ },
+ );
+ var token = jwt.sign(SecretKey(configs["ZOOM_SDK_SECRET"]));
+ return token;
+ } catch (e) {
+ print(e);
+ return '';
+ }
+}
diff --git a/lib/core/utils/push_notification_handler.dart b/lib/core/utils/push_notification_handler.dart
index 2e0d7c3..ee05335 100644
--- a/lib/core/utils/push_notification_handler.dart
+++ b/lib/core/utils/push_notification_handler.dart
@@ -8,14 +8,22 @@ import 'package:firebase_messaging/firebase_messaging.dart' as fir;
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_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:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/utils/local_notifications.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:permission_handler/permission_handler.dart';
+import 'package:uuid/uuid.dart';
import '../cache_consts.dart';
@@ -36,16 +44,83 @@ Future backgroundMessageHandler(dynamic message) async {
callPage(String sessionID, String token) async {}
_incomingCall(Map data) async {
+ print('the value of the _incomingCall remote message is $data');
+
// LandingPage.incomingCallData = IncomingCallData.fromJson(data);
// var dataItem = await AppSharedPreferences().getObject('call_data');
// if(dataItem != null ) return; // to stop repeated attempt to invoke the call
// if (LandingPage.isOpenCallPage == false) {
// LandingPage.isOpenCallPage = true;
+
+ String roomID = data['session_id'] ?? '';
+
+ // GetIt.instance().saveString(key: CacheConst.zoomRoomID, value: roomID);
+ // GetIt.instance().saveBool(key: CacheConst.isAppOpenedFromCall, value: true);
+
+ Utils.saveStringFromPrefs(CacheConst.zoomRoomID, roomID);
+ Utils.saveBoolFromPrefs(CacheConst.isAppOpenedFromCall, true);
+
WidgetsFlutterBinding.ensureInitialized();
- // var _currentUuid = Uuid().v4();
- // await FlutterCallkitIncoming.showCallkitIncoming(callKitParams);
- // }
- // LandingPage.isOpenCallPage = false;
+
+ var _currentUuid = Uuid().v4();
+ CallKitParams callKitParams = CallKitParams(
+ id: _currentUuid,
+ nameCaller: "Dr Al Habib",
+ appName: 'Dr Al Habib',
+ avatar: 'https://play-lh.googleusercontent.com/FBNNpxb7m6eM6wtW7MV1Ffp6OXOGLI38q47zcvP29OCYA1yhYH5mZzl5itZi0TgOyZpG',
+ // handle: LandingPage.incomingCallData.name,
+ type: 1,
+ textAccept: 'Accept',
+ textDecline: 'Decline',
+ missedCallNotification: NotificationParams(
+ showNotification: true,
+ isShowCallback: false,
+ subtitle: 'Missed call',
+ callbackText: '',
+ ),
+ callingNotification: const NotificationParams(
+ showNotification: false,
+ isShowCallback: true,
+ subtitle: 'Calling...',
+ callbackText: 'Hang Up',
+ ),
+ duration: 30000,
+ extra: {'userId': '1a2b3c4d'},
+ headers: {'apiKey': 'Abc@123!', 'platform': 'flutter'},
+ android: const AndroidParams(
+ isImportant: true,
+ isShowFullLockedScreen: true,
+ isCustomNotification: true,
+ isShowLogo: false,
+ logoUrl: "https://play-lh.googleusercontent.com/FBNNpxb7m6eM6wtW7MV1Ffp6OXOGLI38q47zcvP29OCYA1yhYH5mZzl5itZi0TgOyZpG",
+ ringtonePath: 'system_ringtone_default',
+ backgroundColor: '#ED1C2B',
+ backgroundUrl: 'https://saudipedia.com/en/saudipediaen/uploads/images/2024/08/02/97358.jpg',
+ actionColor: '#4CAF50',
+ textColor: '#000000',
+ incomingCallNotificationChannelName: "Incoming Call",
+ missedCallNotificationChannelName: "Missed Call",
+ isShowCallID: false),
+ ios: IOSParams(
+ iconName: 'Meena Health',
+ handleType: 'generic',
+ supportsVideo: true,
+ maximumCallGroups: 2,
+ maximumCallsPerCallGroup: 1,
+ audioSessionMode: 'default',
+ audioSessionActive: true,
+ audioSessionPreferredSampleRate: 44100.0,
+ audioSessionPreferredIOBufferDuration: 0.005,
+ supportsDTMF: true,
+ supportsHolding: true,
+ supportsGrouping: false,
+ supportsUngrouping: false,
+ ringtonePath: 'system_ringtone_default',
+ ),
+ );
+ FlutterCallkitIncoming.unsilenceEvents();
+ await FlutterCallkitIncoming.showCallkitIncoming(callKitParams);
+
await Future.delayed(Duration(milliseconds: 500));
}
@@ -224,7 +299,9 @@ class PushNotificationHandler {
print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId');
await voIPKit.acceptIncomingCall(callerState: CallStateType.calling);
await voIPKit.callConnected();
- await Future.delayed(Duration(seconds: 1));
+ await Future.delayed(Duration(seconds: 2)).then((val) async {
+ await voIPKit.endCall();
+ });
// Navigator.pushNamed(
// locator().navigatorKey.currentContext!,
@@ -232,8 +309,6 @@ class PushNotificationHandler {
// arguments: CallArguments("hoover-dam", "123", "Patient", "40", "1", false),
// );
- await voIPKit.endCall();
-
// Navigator.pushNamed(navigatorKey.currentContext!, VIDEO_CALL_SCREEN,
// arguments: VideoArgus(
// reservationId: int.parse(callerId), token: null, isVideo: true));
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index e2c11d4..f1e1593 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -807,7 +807,11 @@ class Utils {
static Future checkConnection({bool bypassConnectionCheck = false}) async {
if (bypassConnectionCheck) return true;
List connectivityResult = await (Connectivity().checkConnectivity());
- if (connectivityResult.contains(ConnectivityResult.mobile) || connectivityResult.contains(ConnectivityResult.wifi)) {
+ if (connectivityResult.contains(ConnectivityResult.mobile) ||
+ connectivityResult.contains(ConnectivityResult.wifi) ||
+ connectivityResult.contains(ConnectivityResult.ethernet) ||
+ connectivityResult.contains(ConnectivityResult.bluetooth) ||
+ connectivityResult.contains(ConnectivityResult.vpn)) {
return true;
} else {
return false;
diff --git a/lib/core/utils/zoom_config.dart b/lib/core/utils/zoom_config.dart
new file mode 100644
index 0000000..09370c0
--- /dev/null
+++ b/lib/core/utils/zoom_config.dart
@@ -0,0 +1,4 @@
+const Map configs = {
+ 'ZOOM_SDK_KEY': 'b9T74nhfTg-ioP9urm970A',
+ 'ZOOM_SDK_SECRET': 'KOzmjBNXQ1f4IPHpnngfL29uZvJMufSy2Fk8',
+};
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index 250453d..c2a4a87 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -301,14 +301,14 @@ extension EmailValidator on String {
fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)),
);
- Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text(
+ Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false, double? letterSpacing}) => Text(
this,
textAlign: isCenter ? TextAlign.center : null,
style: TextStyle(
height: height ?? 23 / 28,
color: color ?? AppColors.blackColor,
fontSize: 28.f,
- letterSpacing: -1,
+ letterSpacing: letterSpacing ?? -1,
fontWeight: isBold ? FontWeight.bold : FontWeight.normal),
);
diff --git a/lib/features/book_appointments/book_appointments_repo.dart b/lib/features/book_appointments/book_appointments_repo.dart
index 8aafc9b..3683d57 100644
--- a/lib/features/book_appointments/book_appointments_repo.dart
+++ b/lib/features/book_appointments/book_appointments_repo.dart
@@ -14,6 +14,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/resp_model
import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/get_patient_dental_plan_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'models/resp_models/laser_body_parts.dart';
@@ -84,6 +85,22 @@ abstract class BookAppointmentsRepo {
Future>>> getLaserClinics(int laserCategoryID, int projectID, int languageID,
{Function(dynamic)? onSuccess, Function(String)? onError});
+
+ Future>> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError});
+
+ Future>> getPatientShareWalkInAppointment({required int projectID, required int clinicID, required int doctorID});
+
+ Future>> insertSpecificAppointmentForWalkIn(
+ {required int docID,
+ required int clinicID,
+ required int projectID,
+ required String selectedTime,
+ required String selectedDate,
+ required int initialSlotDuration,
+ required int genderID,
+ required int userAge,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError});
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@@ -848,4 +865,144 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future> checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ Map mapDevice = {"NFC_Code": nfcCode, "ProjectID": projectId};
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ CHECK_SCANNED_NFC_QR_CODE,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: response,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future>> getPatientShareWalkInAppointment({required int projectID, required int clinicID, required int doctorID}) async {
+ Map mapRequest = {"ProjectID": projectID, "ClinicID": clinicID, "DoctorID": doctorID};
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ GET_PATIENT_SHARE_FOR_WALKIN_APPOINTMENT,
+ body: mapRequest,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final list = response['OnlineCheckInAppointmentsWalkInModel'];
+ if (list == null || list.isEmpty) {
+ throw Exception("patient share list is empty");
+ }
+
+ final patientShareObj = PatientAppointmentShareResponseModel.fromJson(list);
+ patientShareObj.isCash = response["IsCash"];
+ patientShareObj.isEligible = response["IsEligible"];
+ patientShareObj.isInsured = response["IsInsured"];
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: patientShareObj,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future> insertSpecificAppointmentForWalkIn(
+ {required int docID,
+ required int clinicID,
+ required int projectID,
+ required String selectedTime,
+ required String selectedDate,
+ required int initialSlotDuration,
+ required int genderID,
+ required int userAge,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError}) async {
+ Map mapDevice = {
+ "IsForLiveCare": true,
+ "ProjectID": projectID,
+ "ClinicID": clinicID,
+ "DoctorID": docID,
+ "StartTime": selectedTime,
+ "SelectedTime": selectedTime,
+ "EndTime": selectedTime,
+ "InitialSlotDuration": initialSlotDuration,
+ "StrAppointmentDate": selectedDate,
+ "IsVirtual": false,
+ "BookedBy": 102,
+ "VisitType": 1,
+ "VisitFor": 1,
+ "GenderID": genderID,
+ "Age": userAge
+ };
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ INSERT_WALKIN_APPOINTMENT,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ final appointmentNo = response['AppointmentNo'];
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: response["ErrorEndUserMessage"],
+ data: response,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart
index a50b683..2413827 100644
--- a/lib/features/book_appointments/book_appointments_view_model.dart
+++ b/lib/features/book_appointments/book_appointments_view_model.dart
@@ -24,6 +24,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
@@ -112,6 +113,15 @@ class BookAppointmentsViewModel extends ChangeNotifier {
String? selectedClinicForFilters;
bool applyFilters = false;
+ bool isWaitingAppointmentAvailable = false;
+
+ bool isWaitingAppointmentSelected = false;
+ int waitingAppointmentProjectID = 0;
+ DoctorsListResponseModel? waitingAppointmentDoctor;
+ String waitingAppointmentNFCCode = "";
+
+ PatientAppointmentShareResponseModel? patientWalkInAppointmentShareResponseModel;
+
///variables for laser clinic
List femaleLaserCategory = [
LaserCategoryType(1, 'bodyString'),
@@ -171,11 +181,32 @@ class BookAppointmentsViewModel extends ChangeNotifier {
dentalChiefComplaintsList.clear();
isContinueDentalPlan = false;
isChiefComplaintsListLoading = true;
+ isWaitingAppointmentSelected = false;
bodyTypes = [maleLaserCategory, femaleLaserCategory];
// getLocation();
notifyListeners();
}
+ setIsWaitingAppointmentSelected(bool isWaitingAppointmentSelected) {
+ this.isWaitingAppointmentSelected = isWaitingAppointmentSelected;
+ notifyListeners();
+ }
+
+ setWaitingAppointmentDoctor(DoctorsListResponseModel waitingAppointmentDoctor) {
+ this.waitingAppointmentDoctor = waitingAppointmentDoctor;
+ notifyListeners();
+ }
+
+ setWaitingAppointmentNFCCode(String waitingAppointmentNFCCode) {
+ this.waitingAppointmentNFCCode = waitingAppointmentNFCCode;
+ notifyListeners();
+ }
+
+ setWaitingAppointmentProjectID(int projectID) {
+ waitingAppointmentProjectID = projectID;
+ notifyListeners();
+ }
+
setIsDoctorsListLoading(bool value) {
if (value) {
doctorsList.clear();
@@ -337,22 +368,20 @@ class BookAppointmentsViewModel extends ChangeNotifier {
//TODO: Make the API dynamic with parameters for ProjectID, isNearest, languageID, doctorId, doctorName
Future getDoctorsList(
- {int projectID = 0,
- bool isNearest = false,
- int doctorId = 0,
+ {int projectID = 0, bool isNearest = true, int doctorId = 0,
String doctorName = "",
Function(dynamic)? onSuccess,
Function(String)? onError}) async {
doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null ? int.parse(currentlySelectedHospitalFromRegionFlow!) : projectID;
- final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName,
- isContinueDentalPlan: isContinueDentalPlan);
+ final result =
+ await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, doctorName.isNotEmpty ? false : isNearest, doctorId, doctorName, isContinueDentalPlan: isContinueDentalPlan);
result.fold(
(failure) async {
isDoctorsListLoading = false;
+ if (onError != null) onError("No doctors found for the search criteria".needTranslation);
- onError!("No doctors found for the search criteria".needTranslation);
notifyListeners();
},
(apiResponse) {
@@ -468,6 +497,9 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
initialSlotDuration = apiResponse.data["InitialSlotDuration"];
freeSlotsResponse = apiResponse.data['FreeTimeSlots'];
+ // isWaitingAppointmentAvailable = apiResponse.data["IsAllowToBookWaitingAppointment"];
+ isWaitingAppointmentAvailable = true;
+
freeSlotsResponse.forEach((element) {
// date = (isLiveCareSchedule != null && isLiveCareSchedule)
// ? DateUtil.convertStringToDate(element)
@@ -1165,4 +1197,95 @@ class BookAppointmentsViewModel extends ChangeNotifier {
laserBodyPartsList = [];
duration = 0;
}
+
+ Future checkScannedNFCAndQRCode(String nfcCode, int projectId, {Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await bookAppointmentsRepo.checkScannedNFCAndQRCode(nfcCode, projectId);
+
+ result.fold(
+ (failure) async {
+ onError!("Invalid verification point scanned.".needTranslation);
+ },
+ (apiResponse) {
+ // if (apiResponse.data['returnValue'] == 0) {
+ // onError!("Invalid verification point scanned.".needTranslation);
+ // } else if (apiResponse.data['returnValue'] == 1) {
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ // }
+ },
+ );
+ }
+
+ Future getWalkInPatientShareAppointment({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ final result = await bookAppointmentsRepo.getPatientShareWalkInAppointment(projectID: waitingAppointmentProjectID, clinicID: selectedDoctor.clinicID!, doctorID: selectedDoctor.doctorID!);
+
+ result.fold(
+ (failure) async {
+ if (onError != null) {
+ onError(failure.message);
+ }
+ // await errorHandlerService.handleError(
+ // failure: failure,
+ // onOkPressed: () {
+ // onError!(failure.message);
+ // });
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage!);
+ // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
+ } else if (apiResponse.messageStatus == 1) {
+ patientWalkInAppointmentShareResponseModel = apiResponse.data!;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ Future insertSpecificAppointmentForWalkIn({Function(dynamic p1)? onSuccess, Function(dynamic p1)? onError}) async {
+ _appState = getIt();
+ final result = await bookAppointmentsRepo.insertSpecificAppointmentForWalkIn(
+ docID: selectedDoctor.doctorID!,
+ clinicID: selectedDoctor.clinicID!,
+ projectID: selectedDoctor.projectID!,
+ selectedDate: selectedAppointmentDate,
+ selectedTime: selectedAppointmentTime,
+ initialSlotDuration: initialSlotDuration,
+ genderID: _appState.getAuthenticatedUser()!.gender!,
+ userAge: _appState.getAuthenticatedUser()!.age!,
+ onError: onError);
+
+ result.fold(
+ (failure) async {
+ print(failure);
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ LoadingUtils.hideFullScreenLoader();
+ showCommonBottomSheetWithoutHeight(
+ title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
+ navigationService.navigatorKey.currentContext!,
+ child: Utils.getErrorWidget(loadingText: apiResponse.errorMessage),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ } else if (apiResponse.messageStatus == 1) {
+ if (apiResponse.data == null || apiResponse.data!.isEmpty) {
+ onError!("Unexpected Error Occurred".needTranslation);
+ return;
+ }
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
}
diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart
index ad72ef4..a713003 100644
--- a/lib/features/emergency_services/emergency_services_view_model.dart
+++ b/lib/features/emergency_services/emergency_services_view_model.dart
@@ -1054,8 +1054,8 @@ class EmergencyServicesViewModel extends ChangeNotifier {
bool result = await navServices.push(
CustomPageRoute(
page: MapUtilityScreen(
- confirmButtonString: "Submit Request ".needTranslation,
- titleString: "Select Location",
+ confirmButtonString: "Submit Request".needTranslation,
+ titleString: "Select Location".needTranslation,
subTitleString: "Please select the location".needTranslation,
isGmsAvailable: appState.isGMSAvailable,
),
diff --git a/lib/features/immediate_livecare/immediate_livecare_view_model.dart b/lib/features/immediate_livecare/immediate_livecare_view_model.dart
index 599bc39..1baa81b 100644
--- a/lib/features/immediate_livecare/immediate_livecare_view_model.dart
+++ b/lib/features/immediate_livecare/immediate_livecare_view_model.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
@@ -36,6 +38,10 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
late AppState _appState;
+ Duration countdownDuration = Duration(minutes: 1, seconds: 0);
+ late ValueNotifier durationNotifier;
+ late Timer timer;
+
initImmediateLiveCare() {
_appState = getIt();
immediateLiveCareClinicsList = [];
@@ -144,7 +150,11 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
patientLiveCareHistoryList = apiResponse.data!;
if (patientLiveCareHistoryList.isNotEmpty) {
if (patientLiveCareHistoryList[0].callStatus! < 4) {
+ countdownDuration = Duration(seconds: 0);
patientHasPendingLiveCareRequest = true;
+ countdownDuration = Duration(minutes: patientLiveCareHistoryList.first.watingtimeInteger!, seconds: 0);
+ durationNotifier = ValueNotifier(countdownDuration);
+ startTimer();
} else {
patientHasPendingLiveCareRequest = false;
}
@@ -159,4 +169,20 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
},
);
}
+
+ void startTimer() {
+ timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
+ notifyListeners();
+ }
+
+ void addTime() {
+ final seconds = durationNotifier.value.inSeconds - 1;
+ if (seconds < 0) {
+ timer?.cancel();
+ // Handle end of timer here
+ // showEndMessage();
+ } else {
+ durationNotifier.value = Duration(seconds: seconds);
+ }
+ }
}
diff --git a/lib/features/insurance/insurance_view_model.dart b/lib/features/insurance/insurance_view_model.dart
index 8460bd4..30f4c26 100644
--- a/lib/features/insurance/insurance_view_model.dart
+++ b/lib/features/insurance/insurance_view_model.dart
@@ -78,10 +78,12 @@ class InsuranceViewModel extends ChangeNotifier {
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
isInsuranceLoading = false;
+ isInsuranceDataToBeLoaded = false;
notifyListeners();
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
+ isInsuranceDataToBeLoaded = false;
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
patientInsuranceList = apiResponse.data!;
diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart
index bad4f89..24aa244 100644
--- a/lib/features/lab/lab_view_model.dart
+++ b/lib/features/lab/lab_view_model.dart
@@ -289,7 +289,7 @@ class LabViewModel extends ChangeNotifier {
);
}
- Future getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription) async {
+ Future getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription, String unitOfMeasure) async {
LoaderBottomSheet.showLoader();
mainLabResults.clear();
filteredGraphValues.clear();
@@ -339,6 +339,7 @@ class LabViewModel extends ChangeNotifier {
} catch (e) {}
});
LabResult recentResult = recentThree.first;
+ recentResult.uOM = unitOfMeasure;
checkIfGraphShouldBeDisplayed(recentResult);
recentResult.verifiedOn = resultDate(DateUtil.convertStringToDate(recentResult.verifiedOnDateTime!));
// filteredGraphValues = [filteredGraphValues.first];
diff --git a/lib/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart b/lib/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart
index 2021fff..1dc1cbe 100644
--- a/lib/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart
+++ b/lib/features/my_appointments/models/resp_models/patient_appointment_share_response_model.dart
@@ -141,7 +141,7 @@ class PatientAppointmentShareResponseModel {
doctorID = json['DoctorID'];
doctorImageURL = json['DoctorImageURL'];
doctorNameObj = json['DoctorNameObj'];
- doctorSpeciality = json['DoctorSpeciality'].cast();
+ // doctorSpeciality = json['DoctorSpeciality'].cast();
errCode = json['ErrCode'];
groupID = json['GroupID'];
iSAllowOnlineCheckedIN = json['ISAllowOnlineCheckedIN'];
diff --git a/lib/features/prescriptions/models/resp_models/prescription_delivery_response_model.dart b/lib/features/prescriptions/models/resp_models/prescription_delivery_response_model.dart
new file mode 100644
index 0000000..c221e90
--- /dev/null
+++ b/lib/features/prescriptions/models/resp_models/prescription_delivery_response_model.dart
@@ -0,0 +1,318 @@
+class PrescriptionDeliveryResponseModel {
+ int? iD;
+ int? patientId;
+ int? patientOutSa;
+ bool? isOutPatient;
+ int? projectId;
+ int? nearestProjectId;
+ dynamic longitude;
+ dynamic latitude;
+ dynamic appointmentNo;
+ dynamic dischargeId;
+ int? statusId;
+ int? serviceId;
+ int? channel;
+ Orderpayment? orderpayment;
+ dynamic orderselectedservice;
+ dynamic wforder;
+ dynamic orderapprovalobj;
+ String? created;
+ dynamic createdBy;
+ dynamic modified;
+ dynamic modifiedBy;
+ bool? isDeleted;
+ String? statusText;
+ int? paymentStatus;
+ dynamic clientRequestid;
+ dynamic paymentStatusText;
+ String? projectName;
+ String? nearestProjectName;
+ dynamic paymentAmount;
+ WFOrder? wFOrder;
+ String? serviceText;
+ bool? isSentForApproval;
+ int? exaCartOrderId;
+ bool? isTimer;
+ int? timeSeconds;
+ int? totalPendingSeconds;
+ int? timeMinute;
+ int? timeHour;
+ int? timeTotalSeconds;
+ int? timeTotalMinute;
+ int? timeTotalHour;
+ dynamic approvalStatus;
+ bool? isActive;
+ int? clickButton;
+ List? procedures;
+ dynamic pickupLocation;
+ dynamic dropOffLocation;
+ dynamic clinicName;
+ dynamic doctorName;
+ dynamic branch;
+ dynamic time;
+ dynamic notes;
+
+ PrescriptionDeliveryResponseModel(
+ {this.iD,
+ this.patientId,
+ this.patientOutSa,
+ this.isOutPatient,
+ this.projectId,
+ this.nearestProjectId,
+ this.longitude,
+ this.latitude,
+ this.appointmentNo,
+ this.dischargeId,
+ this.statusId,
+ this.serviceId,
+ this.channel,
+ this.orderpayment,
+ this.orderselectedservice,
+ this.wforder,
+ this.orderapprovalobj,
+ this.created,
+ this.createdBy,
+ this.modified,
+ this.modifiedBy,
+ this.isDeleted,
+ this.statusText,
+ this.paymentStatus,
+ this.clientRequestid,
+ this.paymentStatusText,
+ this.projectName,
+ this.nearestProjectName,
+ this.paymentAmount,
+ this.wFOrder,
+ this.serviceText,
+ this.isSentForApproval,
+ this.exaCartOrderId,
+ this.isTimer,
+ this.timeSeconds,
+ this.totalPendingSeconds,
+ this.timeMinute,
+ this.timeHour,
+ this.timeTotalSeconds,
+ this.timeTotalMinute,
+ this.timeTotalHour,
+ this.approvalStatus,
+ this.isActive,
+ this.clickButton,
+ this.procedures,
+ this.pickupLocation,
+ this.dropOffLocation,
+ this.clinicName,
+ this.doctorName,
+ this.branch,
+ this.time,
+ this.notes});
+
+ PrescriptionDeliveryResponseModel.fromJson(Map json) {
+ iD = json['ID'];
+ patientId = json['PatientId'];
+ patientOutSa = json['PatientOutSa'];
+ isOutPatient = json['IsOutPatient'];
+ projectId = json['ProjectId'];
+ nearestProjectId = json['NearestProjectId'];
+ longitude = json['Longitude'];
+ latitude = json['Latitude'];
+ appointmentNo = json['AppointmentNo'];
+ dischargeId = json['DischargeId'];
+ statusId = json['StatusId'];
+ serviceId = json['ServiceId'];
+ channel = json['Channel'];
+ orderpayment = json['orderpayment'] != null ? new Orderpayment.fromJson(json['orderpayment']) : null;
+ orderselectedservice = json['orderselectedservice'];
+ wforder = json['wforder'];
+ orderapprovalobj = json['orderapprovalobj'];
+ created = json['Created'];
+ createdBy = json['CreatedBy'];
+ modified = json['Modified'];
+ modifiedBy = json['ModifiedBy'];
+ isDeleted = json['IsDeleted'];
+ statusText = json['StatusText'];
+ paymentStatus = json['PaymentStatus'];
+ clientRequestid = json['ClientRequestid'];
+ paymentStatusText = json['PaymentStatusText'];
+ projectName = json['ProjectName'];
+ nearestProjectName = json['NearestProjectName'];
+ paymentAmount = json['PaymentAmount'];
+ wFOrder = json['WF_order'] != null ? new WFOrder.fromJson(json['WF_order']) : null;
+ serviceText = json['ServiceText'];
+ isSentForApproval = json['isSentForApproval'];
+ exaCartOrderId = json['ExaCart_OrderId'];
+ isTimer = json['isTimer'];
+ timeSeconds = json['TimeSeconds'];
+ totalPendingSeconds = json['TotalPendingSeconds'];
+ timeMinute = json['TimeMinute'];
+ timeHour = json['TimeHour'];
+ timeTotalSeconds = json['TimeTotalSeconds'];
+ timeTotalMinute = json['TimeTotalMinute'];
+ timeTotalHour = json['TimeTotalHour'];
+ approvalStatus = json['ApprovalStatus'];
+ isActive = json['isActive'];
+ clickButton = json['ClickButton'];
+ pickupLocation = json['PickupLocation'];
+ dropOffLocation = json['DropOffLocation'];
+ clinicName = json['clinicName'];
+ doctorName = json['DoctorName'];
+ branch = json['Branch'];
+ time = json['Time'];
+ notes = json['Notes'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ID'] = this.iD;
+ data['PatientId'] = this.patientId;
+ data['PatientOutSa'] = this.patientOutSa;
+ data['IsOutPatient'] = this.isOutPatient;
+ data['ProjectId'] = this.projectId;
+ data['NearestProjectId'] = this.nearestProjectId;
+ data['Longitude'] = this.longitude;
+ data['Latitude'] = this.latitude;
+ data['AppointmentNo'] = this.appointmentNo;
+ data['DischargeId'] = this.dischargeId;
+ data['StatusId'] = this.statusId;
+ data['ServiceId'] = this.serviceId;
+ data['Channel'] = this.channel;
+ if (this.orderpayment != null) {
+ data['orderpayment'] = this.orderpayment!.toJson();
+ }
+ data['orderselectedservice'] = this.orderselectedservice;
+
+ data['wforder'] = this.wforder;
+ data['orderapprovalobj'] = this.orderapprovalobj;
+ data['Created'] = this.created;
+ data['CreatedBy'] = this.createdBy;
+ data['Modified'] = this.modified;
+ data['ModifiedBy'] = this.modifiedBy;
+ data['IsDeleted'] = this.isDeleted;
+ data['StatusText'] = this.statusText;
+ data['PaymentStatus'] = this.paymentStatus;
+ data['ClientRequestid'] = this.clientRequestid;
+ data['PaymentStatusText'] = this.paymentStatusText;
+ data['ProjectName'] = this.projectName;
+ data['NearestProjectName'] = this.nearestProjectName;
+ data['PaymentAmount'] = this.paymentAmount;
+ if (this.wFOrder != null) {
+ data['WF_order'] = this.wFOrder!.toJson();
+ }
+ data['ServiceText'] = this.serviceText;
+ data['isSentForApproval'] = this.isSentForApproval;
+ data['ExaCart_OrderId'] = this.exaCartOrderId;
+ data['isTimer'] = this.isTimer;
+ data['TimeSeconds'] = this.timeSeconds;
+ data['TotalPendingSeconds'] = this.totalPendingSeconds;
+ data['TimeMinute'] = this.timeMinute;
+ data['TimeHour'] = this.timeHour;
+ data['TimeTotalSeconds'] = this.timeTotalSeconds;
+ data['TimeTotalMinute'] = this.timeTotalMinute;
+ data['TimeTotalHour'] = this.timeTotalHour;
+ data['ApprovalStatus'] = this.approvalStatus;
+ data['isActive'] = this.isActive;
+ data['ClickButton'] = this.clickButton;
+ data['PickupLocation'] = this.pickupLocation;
+ data['DropOffLocation'] = this.dropOffLocation;
+ data['clinicName'] = this.clinicName;
+ data['DoctorName'] = this.doctorName;
+ data['Branch'] = this.branch;
+ data['Time'] = this.time;
+ data['Notes'] = this.notes;
+ return data;
+ }
+}
+
+class Orderpayment {
+ int? iD;
+ int? orderId;
+ dynamic clientRequestId;
+ dynamic totalAmount;
+ int? paymentStatus;
+ dynamic order;
+ String? created;
+ dynamic createdBy;
+ dynamic modified;
+ dynamic modifiedBy;
+ bool? isDeleted;
+
+ Orderpayment({this.iD, this.orderId, this.clientRequestId, this.totalAmount, this.paymentStatus, this.order, this.created, this.createdBy, this.modified, this.modifiedBy, this.isDeleted});
+
+ Orderpayment.fromJson(Map json) {
+ iD = json['ID'];
+ orderId = json['OrderId'];
+ clientRequestId = json['ClientRequestId'];
+ totalAmount = json['TotalAmount'];
+ paymentStatus = json['PaymentStatus'];
+ order = json['Order'];
+ created = json['Created'];
+ createdBy = json['CreatedBy'];
+ modified = json['Modified'];
+ modifiedBy = json['ModifiedBy'];
+ isDeleted = json['IsDeleted'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['ID'] = this.iD;
+ data['OrderId'] = this.orderId;
+ data['ClientRequestId'] = this.clientRequestId;
+ data['TotalAmount'] = this.totalAmount;
+ data['PaymentStatus'] = this.paymentStatus;
+ data['Order'] = this.order;
+ data['Created'] = this.created;
+ data['CreatedBy'] = this.createdBy;
+ data['Modified'] = this.modified;
+ data['ModifiedBy'] = this.modifiedBy;
+ data['IsDeleted'] = this.isDeleted;
+ return data;
+ }
+}
+
+class WFOrder {
+ dynamic wfButtonsDTO;
+ int? iD;
+ int? orderId;
+ int? previousStep;
+ int? nextStep;
+ int? serviceId;
+ dynamic order;
+ String? created;
+ dynamic createdBy;
+ dynamic modified;
+ dynamic modifiedBy;
+ bool? isDeleted;
+
+ WFOrder({this.wfButtonsDTO, this.iD, this.orderId, this.previousStep, this.nextStep, this.serviceId, this.order, this.created, this.createdBy, this.modified, this.modifiedBy, this.isDeleted});
+
+ WFOrder.fromJson(Map json) {
+ wfButtonsDTO = json['wf_ButtonsDTO'];
+ iD = json['ID'];
+ orderId = json['OrderId'];
+ previousStep = json['PreviousStep'];
+ nextStep = json['NextStep'];
+ serviceId = json['ServiceId'];
+ order = json['Order'];
+ created = json['Created'];
+ createdBy = json['CreatedBy'];
+ modified = json['Modified'];
+ modifiedBy = json['ModifiedBy'];
+ isDeleted = json['IsDeleted'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['wf_ButtonsDTO'] = this.wfButtonsDTO;
+ data['ID'] = this.iD;
+ data['OrderId'] = this.orderId;
+ data['PreviousStep'] = this.previousStep;
+ data['NextStep'] = this.nextStep;
+ data['ServiceId'] = this.serviceId;
+ data['Order'] = this.order;
+ data['Created'] = this.created;
+ data['CreatedBy'] = this.createdBy;
+ data['Modified'] = this.modified;
+ data['ModifiedBy'] = this.modifiedBy;
+ data['IsDeleted'] = this.isDeleted;
+ return data;
+ }
+}
diff --git a/lib/features/prescriptions/prescriptions_repo.dart b/lib/features/prescriptions/prescriptions_repo.dart
index 2e3f1aa..e7a4f07 100644
--- a/lib/features/prescriptions/prescriptions_repo.dart
+++ b/lib/features/prescriptions/prescriptions_repo.dart
@@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:dartz/dartz.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart';
+import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_delivery_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_detail_response_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
@@ -15,7 +16,13 @@ abstract class PrescriptionsRepo {
Future>> getPrescriptionInstructionsPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel});
- Future>> getPrescriptionPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List prescriptionDetailsList});
+ Future>> getPrescriptionPDF(
+ {required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List prescriptionDetailsList});
+
+ Future>> submitPrescriptionDeliveryRequest(
+ {required String latitude, required String longitude, required String appointmentNo, required String dischargeID, required String projectID});
+
+ Future>> getPrescriptionOrdersList();
}
class PrescriptionsRepoImp implements PrescriptionsRepo {
@@ -158,7 +165,8 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
}
@override
- Future> getPrescriptionPDF({required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List prescriptionDetailsList}) async {
+ Future> getPrescriptionPDF(
+ {required PatientPrescriptionsResponseModel prescriptionsResponseModel, required List prescriptionDetailsList}) async {
Map mapDevice = {
"AppointmentDate": prescriptionsResponseModel.appointmentDate,
"ClinicName": prescriptionsResponseModel.clinicDescription,
@@ -206,4 +214,84 @@ class PrescriptionsRepoImp implements PrescriptionsRepo {
return Left(UnknownFailure(e.toString()));
}
}
+
+ @override
+ Future> submitPrescriptionDeliveryRequest(
+ {required String latitude, required String longitude, required String appointmentNo, required String dischargeID, required String projectID}) async {
+ Map mapDevice = {
+ "latitude": latitude,
+ "longitude": longitude,
+ "AppointmentNo": appointmentNo,
+ "DischargeID": dischargeID,
+ "ProjectID": projectID,
+ };
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ isRCService: true,
+ ADD_PRESCRIPTION_ORDER_RC,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: response,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
+
+ @override
+ Future> getPrescriptionOrdersList() async {
+ Map mapDevice = {};
+
+ try {
+ GenericApiModel? apiResponse;
+ Failure? failure;
+ await apiClient.post(
+ isRCService: true,
+ GET_ALL_PRESCRIPTION_ORDERS_RC,
+ body: mapDevice,
+ onFailure: (error, statusCode, {messageStatus, failureType}) {
+ failure = failureType;
+ },
+ onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
+ try {
+
+ final prescriptionOrders = response['response'].map((item) => PrescriptionDeliveryResponseModel.fromJson(item as Map)).toList().cast();
+
+ apiResponse = GenericApiModel(
+ messageStatus: messageStatus,
+ statusCode: statusCode,
+ errorMessage: null,
+ data: prescriptionOrders,
+ );
+ } catch (e) {
+ failure = DataParsingFailure(e.toString());
+ }
+ },
+ );
+ if (failure != null) return Left(failure!);
+ if (apiResponse == null) return Left(ServerFailure("Unknown error"));
+ return Right(apiResponse!);
+ } catch (e) {
+ return Left(UnknownFailure(e.toString()));
+ }
+ }
}
diff --git a/lib/features/prescriptions/prescriptions_view_model.dart b/lib/features/prescriptions/prescriptions_view_model.dart
index 0e6bce7..7517134 100644
--- a/lib/features/prescriptions/prescriptions_view_model.dart
+++ b/lib/features/prescriptions/prescriptions_view_model.dart
@@ -1,11 +1,26 @@
+import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/location_util.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
+import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
+import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
+import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/calender_utils_new.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart';
+import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_delivery_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/prescription_detail_response_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_delivery_order_summary_page.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
+import 'package:hmg_patient_app_new/services/navigation_service.dart';
+import 'package:hmg_patient_app_new/widgets/map/map_utility_screen.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
class PrescriptionsViewModel extends ChangeNotifier {
bool isPrescriptionsOrdersLoading = false;
@@ -13,6 +28,7 @@ class PrescriptionsViewModel extends ChangeNotifier {
PrescriptionsRepo prescriptionsRepo;
ErrorHandlerService errorHandlerService;
+ NavigationService navServices;
// Prescription Orders Lists
List patientPrescriptionOrders = [];
@@ -30,15 +46,22 @@ class PrescriptionsViewModel extends ChangeNotifier {
String prescriptionPDFBase64Data = "";
- PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService});
+ late GeocodeResponse locationGeocodeResponse;
+
+ bool isPrescriptionsDeliveryOrdersLoading = false;
+ List prescriptionsOrderList = [];
+
+ PrescriptionsViewModel({required this.prescriptionsRepo, required this.errorHandlerService, required this.navServices});
initPrescriptionsViewModel() {
patientPrescriptionOrders.clear();
patientPrescriptionOrdersByClinic.clear();
patientPrescriptionOrdersByHospital.clear();
patientPrescriptionOrdersViewList.clear();
+ prescriptionsOrderList.clear();
isPrescriptionsOrdersLoading = true;
isSortByClinic = true;
+ isPrescriptionsDeliveryOrdersLoading = true;
getPatientPrescriptionOrders();
notifyListeners();
}
@@ -188,4 +211,88 @@ class PrescriptionsViewModel extends ChangeNotifier {
showError(String errorMessage) {
getIt().showErrorBottomSheet(message: errorMessage);
}
+
+ Future submitPrescriptionDeliveryRequest(
+ {required String latitude,
+ required String longitude,
+ required String appointmentNo,
+ required String dischargeID,
+ required String projectID,
+ Function(dynamic)? onSuccess,
+ Function(String)? onError}) async {
+ final result = await prescriptionsRepo.submitPrescriptionDeliveryRequest(latitude: latitude, longitude: longitude, appointmentNo: appointmentNo, dischargeID: dischargeID, projectID: projectID);
+
+ result.fold(
+ (failure) async {
+ onError!(failure.message);
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ onError!(apiResponse.errorMessage!);
+ } else if (apiResponse.messageStatus == 1) {
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
+
+ void initiatePrescriptionDelivery() async {
+ getIt.get().getLocation(
+ isShowConfirmDialog: true,
+ onSuccess: (position) async {
+ bool result = await navServices.push(
+ CustomPageRoute(
+ page: MapUtilityScreen(
+ confirmButtonString: LocaleKeys.next.tr(),
+ titleString: "Select Location".needTranslation,
+ subTitleString: "Please select the location for prescription delivery".needTranslation,
+ isGmsAvailable: getIt.get().isGMSAvailable,
+ ),
+ direction: AxisDirection.down),
+ );
+ print("Location Selected: $result");
+ if (result) {
+ LocationViewModel locationViewModel = getIt.get();
+ locationGeocodeResponse = locationViewModel.geocodeResponse!;
+ navServices.push(
+ CustomPageRoute(
+ page: PrescriptionDeliveryOrderSummaryPage(),
+ ),
+ );
+ }
+ });
+ }
+
+ Future getPrescriptionOrdersList({Function(dynamic)? onSuccess, Function(String)? onError}) async {
+ isPrescriptionsDeliveryOrdersLoading = true;
+ prescriptionsOrderList.clear();
+ notifyListeners();
+
+ final result = await prescriptionsRepo.getPrescriptionOrdersList();
+
+ result.fold(
+ (failure) async {
+ isPrescriptionsDeliveryOrdersLoading = false;
+ notifyListeners();
+ onError!(failure.message);
+ },
+ (apiResponse) {
+ if (apiResponse.messageStatus == 2) {
+ isPrescriptionsDeliveryOrdersLoading = false;
+ notifyListeners();
+ onError!(apiResponse.errorMessage!);
+ } else if (apiResponse.messageStatus == 1) {
+ isPrescriptionsDeliveryOrdersLoading = false;
+ prescriptionsOrderList = apiResponse.data!;
+ notifyListeners();
+ if (onSuccess != null) {
+ onSuccess(apiResponse);
+ }
+ }
+ },
+ );
+ }
}
diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart
index d80d32e..7f863a1 100644
--- a/lib/presentation/appointments/appointment_details_page.dart
+++ b/lib/presentation/appointments/appointment_details_page.dart
@@ -752,8 +752,8 @@ class _AppointmentDetailsPageState extends State {
onPressed: () {
openDoctorScheduleCalendar();
},
- backgroundColor: AppColors.primaryRedColor,
- borderColor: AppColors.primaryRedColor,
+ backgroundColor: AppColors.successColor,
+ borderColor: AppColors.successColor,
textColor: AppColors.whiteColor,
fontSize: 16.f,
fontWeight: FontWeight.w500,
diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart
index 3cf56a6..7bc9c62 100644
--- a/lib/presentation/appointments/widgets/appointment_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_card.dart
@@ -123,22 +123,56 @@ class AppointmentCard extends StatelessWidget {
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),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Stack(
+ children: [
+ Image.network(
+ isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : patientAppointmentHistoryResponseModel.doctorImageURL!,
+ width: 63.w,
+ height: 63.h,
+ fit: BoxFit.cover,
+ ),
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ width: 63.w,
+ height: 20,
+ color: AppColors.textColorLight.withValues(alpha: 0.25),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.rating_icon,
+ width: 12.w,
+ height: 12.h,
+ fit: BoxFit.contain,
+ ),
+ SizedBox(width: 4.w),
+ isLoading ? "Rating".toText12() : patientAppointmentHistoryResponseModel.decimalDoctorRate.toString().toText12(),
+ ],
+ ),
+ ),
+ )
+ ],
+ ).circle(100).toShimmer2(isShow: isLoading),
+ // SizedBox(height: 12.h),
+ // AppCustomChipWidget(
+ // icon: AppAssets.rating_icon,
+ // iconColor: AppColors.ratingColorYellow,
+ // labelText: isLoading ? "Rating" : "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}".needTranslation)
+ // .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!)
+ (isLoading ? 'John Doe' : "${patientAppointmentHistoryResponseModel.doctorTitle} ${patientAppointmentHistoryResponseModel.doctorNameObj!}")
.toText16(isBold: true, maxlines: 1)
.toShimmer2(isShow: isLoading),
SizedBox(height: 8.h),
@@ -147,26 +181,35 @@ class AppointmentCard extends StatelessWidget {
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),
+ : "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}",
+ ).toShimmer2(isShow: isLoading),
+ AppCustomChipWidget(
+ isIconPNG: true,
+ icon: getIt.get().getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
+ iconSize: 18.h,
+ labelText: isLoading ? 'Cardiology' : "Patient: ${getIt.get().getAuthenticatedUser()!.firstName!}",
).toShimmer2(isShow: isLoading),
- if (!isFromMedicalReport)
- AppCustomChipWidget(
- icon: AppAssets.appointment_time_icon,
- labelText: isLoading
- ? 'Cardiology'
- : DateUtil.formatDateToTimeLang(
- 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),
+ // AppCustomChipWidget(
+ // icon: AppAssets.rating_icon,
+ // iconColor: AppColors.ratingColorYellow,
+ // labelText: isLoading ? "Rating" : "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}".needTranslation)
+ // .toShimmer2(isShow: isLoading),
],
),
],
@@ -269,7 +312,6 @@ class AppointmentCard extends StatelessWidget {
clinicId: patientAppointmentHistoryResponseModel.clinicID,
onSuccess: (value) async {
if (value) {
- print("Doctor is available");
await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
index 8d0f4e7..b2d74ca 100644
--- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
@@ -42,12 +42,18 @@ class AppointmentDoctorCard extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Image.network(
- patientAppointmentHistoryResponseModel.doctorImageURL!,
- width: 63.w,
- height: 63.h,
- fit: BoxFit.cover,
- ).circle(100.r),
+ Column(
+ children: [
+ Image.network(
+ patientAppointmentHistoryResponseModel.doctorImageURL!,
+ width: 63.w,
+ height: 63.h,
+ fit: BoxFit.cover,
+ ).circle(100.r),
+ SizedBox(height: 12.h),
+ AppCustomChipWidget(icon: AppAssets.rating_icon, iconColor: AppColors.ratingColorYellow, labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
+ ],
+ ),
SizedBox(width: 16.w),
Expanded(
child: Column(
@@ -87,10 +93,6 @@ class AppointmentDoctorCard extends StatelessWidget {
!patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.greyColor : AppColors.successColor,
textColor: !patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? AppColors.textColor : AppColors.whiteColor,
),
- AppCustomChipWidget(
- icon: AppAssets.rating_icon,
- iconColor: AppColors.ratingColorYellow,
- labelText: "Rating: ${patientAppointmentHistoryResponseModel.decimalDoctorRate}"),
],
),
],
diff --git a/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart b/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart
index 9f06a4d..69cdca3 100644
--- a/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart
+++ b/lib/presentation/appointments/widgets/ask_doctor_request_type_select.dart
@@ -79,15 +79,15 @@ class AskDoctorRequestTypeSelect extends StatelessWidget {
onPressed: () {
Navigator.pop(context);
},
- backgroundColor: AppColors.primaryRedColor,
- borderColor: AppColors.primaryRedColor,
- textColor: AppColors.whiteColor,
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
icon: AppAssets.cancel,
- iconColor: AppColors.whiteColor,
+ iconColor: AppColors.primaryRedColor,
borderRadius: 12.r,
- iconSize: 14.h,
- fontSize: 14.f,
- height: 40.h,
+ iconSize: 16.5.h,
+ fontSize: 16.f,
+ height: 56.h,
),
),
SizedBox(width: 8.h),
@@ -120,14 +120,15 @@ class AskDoctorRequestTypeSelect extends StatelessWidget {
LoaderBottomSheet.hideLoader();
});
},
- backgroundColor: AppColors.bgGreenColor,
- borderColor: AppColors.bgGreenColor,
- textColor: Colors.white,
+ backgroundColor: AppColors.bgGreenColor.withValues(alpha: 0.20),
+ borderColor: AppColors.bgGreenColor.withValues(alpha: 0.0),
+ textColor: AppColors.bgGreenColor,
+ iconColor: AppColors.bgGreenColor,
icon: AppAssets.confirm,
- iconSize: 14.h,
+ iconSize: 16.5.h,
borderRadius: 12.r,
- fontSize: 14.f,
- height: 40.h,
+ fontSize: 16.f,
+ height: 56.h,
),
),
],
diff --git a/lib/presentation/authentication/login.dart b/lib/presentation/authentication/login.dart
index 7e5f4a1..0fa995d 100644
--- a/lib/presentation/authentication/login.dart
+++ b/lib/presentation/authentication/login.dart
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.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_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
@@ -154,7 +155,7 @@ class LoginScreenState extends State {
required BuildContext context,
required TextEditingController? phoneNumberController,
required AuthenticationViewModel authViewModel,
- }) {
+ }) async {
AppState appState = getIt();
context.showBottomSheet(
isScrollControlled: true,
diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart
index a66ea4d..8a0f7e7 100644
--- a/lib/presentation/book_appointment/book_appointment_page.dart
+++ b/lib/presentation/book_appointment/book_appointment_page.dart
@@ -2,20 +2,25 @@ import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/size_config.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/models/resp_models/doctors_list_response_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/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/faculity_selection/facility_type_selection_widget.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/region_bottomsheet/region_list_widget.dart' show RegionBottomSheetBody;
+import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_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';
@@ -86,6 +91,111 @@ class _BookAppointmentPageState extends State {
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
getSelectedTabData(bookAppointmentsVM.selectedTabIndex),
+ SizedBox(height: 24.h),
+ "Recent Visits".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
+ SizedBox(height: 16.h),
+ Consumer(builder: (context, myAppointmentsVM, child) {
+ return myAppointmentsVM.isPatientMyDoctorsLoading
+ ? Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Image.network(
+ "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
+ width: 64.w,
+ height: 64.h,
+ fit: BoxFit.cover,
+ ).circle(100).toShimmer2(isShow: true, radius: 50.r),
+ SizedBox(height: 8.h),
+ ("Dr. John Smith Smith Smith")
+ .toString()
+ .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
+ .toShimmer2(isShow: true),
+ ],
+ )
+ : myAppointmentsVM.patientMyDoctorsList.isEmpty
+ ? Container(
+ width: SizeConfig.screenWidth,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 12.r,
+ hasShadow: false,
+ ),
+ child: Utils.getNoDataWidget(
+ context,
+ noDataText: "You don't have any completed visits yet".needTranslation,
+ isSmallWidget: true,
+ width: 62.w,
+ height: 62.h,
+ ),
+ ).paddingSymmetrical(24.w, 0.h)
+ : SizedBox(
+ height: 110.h,
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: myAppointmentsVM.patientMyDoctorsList.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: SizedBox(
+ // width: 80.w,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Image.network(
+ myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
+ width: 64.w,
+ height: 64.h,
+ fit: BoxFit.cover,
+ ).circle(100).toShimmer2(isShow: false, radius: 50.r),
+ SizedBox(height: 8.h),
+ SizedBox(
+ width: 80.w,
+ child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName)
+ .toString()
+ .toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
+ .toShimmer2(isShow: false),
+ ),
+ ],
+ ),
+ ).onPress(() async {
+ bookAppointmentsViewModel.setSelectedDoctor(DoctorsListResponseModel(
+ clinicID: myAppointmentsVM.patientMyDoctorsList[index].clinicID,
+ projectID: myAppointmentsVM.patientMyDoctorsList[index].projectID,
+ doctorID: myAppointmentsVM.patientMyDoctorsList[index].doctorID,
+ ));
+ LoaderBottomSheet.showLoader();
+ await bookAppointmentsViewModel.getDoctorProfile(onSuccess: (dynamic respData) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: DoctorProfilePage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ }),
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h),
+ ),
+ );
+ }),
],
);
}),
diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart
index 1223842..c3f4ad9 100644
--- a/lib/presentation/book_appointment/doctor_profile_page.dart
+++ b/lib/presentation/book_appointment/doctor_profile_page.dart
@@ -48,6 +48,7 @@ class DoctorProfilePage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!,
@@ -59,9 +60,12 @@ class DoctorProfilePage extends StatelessWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}")
- .toString()
- .toText28(isBold: true),
+ SizedBox(
+ width: 220.h,
+ child: ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}")
+ .toString()
+ .toText24(isBold: true),
+ ),
(bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty ? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first : "")
.toString()
.toText18(weight: FontWeight.w500, color: AppColors.primaryRedColor),
@@ -80,18 +84,49 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(height: 12.h),
Wrap(
direction: Axis.horizontal,
- spacing: 3.h,
+ spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
- icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
- labelText: "Rating: ${bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate}".needTranslation,
+ labelText: "Branch: ${bookAppointmentsViewModel.doctorsProfileResponseModel.projectName}".needTranslation,
),
AppCustomChipWidget(
- icon: AppAssets.rating_icon,
iconColor: AppColors.ratingColorYellow,
- labelText: "Reviews: ${bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate}".needTranslation,
+ labelText: "Clinic: ${bookAppointmentsViewModel.doctorsProfileResponseModel.clinicDescription}".needTranslation,
+ ),
+ ],
+ ),
+ SizedBox(height: 24.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Column(
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.doctor_profile_rating_icon,
+ width: 48.w,
+ height: 48.h,
+ fit: BoxFit.contain,
+ ),
+ SizedBox(height: 16.h),
+ "Ratings".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
+ bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate.toString().toText16(isBold: true, color: AppColors.textColor),
+ ],
+ ),
+ SizedBox(width: 36.w),
+ Column(
+ children: [
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.doctor_profile_reviews_icon,
+ width: 48.w,
+ height: 48.h,
+ fit: BoxFit.contain,
+ ),
+ SizedBox(height: 16.h),
+ "Reviews".toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
+ bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate.toString().toText16(isBold: true, color: AppColors.textColor),
+ ],
),
],
),
diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart
index af7d053..b586da1 100644
--- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart
+++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart
@@ -13,13 +13,16 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.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/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 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
@@ -71,37 +74,17 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
children: [
"${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}".toText16(isBold: true),
SizedBox(height: 8.h),
- AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"),
- ],
- ),
- ],
- ),
- ),
- ),
- SizedBox(height: 24.h),
- "Clinic Information".needTranslation.toText16(isBold: true),
- SizedBox(height: 16.h),
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.h,
- hasShadow: false,
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Row(
- children: [
- Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 32.h, height: 32.h, fit: BoxFit.contain),
- SizedBox(width: 8.h),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- (appState.isArabic()
- ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN
- : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)!
- .toText16(isBold: true),
- // SizedBox(height: 8.h),
- // AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old"),
+ Wrap(
+ direction: Axis.horizontal,
+ spacing: 3.h,
+ runSpacing: 4.h,
+ children: [
+ AppCustomChipWidget(labelText: "${appState.getAuthenticatedUser()!.age} Years Old".needTranslation),
+ AppCustomChipWidget(
+ labelText:
+ "${LocaleKeys.clinic.tr()}: ${(appState.isArabic() ? immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceNameN : immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceName)!}"),
+ ],
+ ),
],
),
],
@@ -220,15 +203,81 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
isSaudiCurrency: immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.currency!.toLowerCase() == "sar"),
],
).paddingSymmetrical(24.h, 0.h),
- CustomButton(
- text: LocaleKeys.payNow.tr(context: context),
- onPressed: () async {
- await askVideoCallPermission().then((val) {
- if (val) {
- Navigator.of(context).push(
- CustomPageRoute(
- page: ImmediateLiveCarePaymentPage(),
- ),
+ (immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total == "0.0")
+ ? CustomButton(
+ text: LocaleKeys.confirmLiveCare.tr(context: context),
+ onPressed: () async {
+ await askVideoCallPermission().then((val) async {
+ if (val) {
+ LoaderBottomSheet.showLoader(loadingText: "Confirming LiveCare request, Please wait...".needTranslation);
+
+ await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare("${appState.getAuthenticatedUser()!.patientId}${DateTime.now().millisecondsSinceEpoch}");
+ await immediateLiveCareViewModel.getPatientLiveCareHistory();
+ LoaderBottomSheet.hideLoader();
+ if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: ImmediateLiveCarePendingRequestPage(),
+ ),
+ );
+ } else {
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: "Unknown error occurred...".needTranslation),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ }
+ } else {
+ showCommonBottomSheetWithoutHeight(
+ title: LocaleKeys.notice.tr(context: context),
+ context,
+ child: Utils.getWarningWidget(
+ loadingText:
+ "LiveCare requires Camera, Microphone, Location & Notifications permissions to enable virtual consultation between patient & doctor, Please allow these to proceed."
+ .needTranslation,
+ isShowActionButtons: true,
+ onCancelTap: () {
+ Navigator.pop(context);
+ },
+ onConfirmTap: () async {
+ openAppSettings();
+ }),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ }
+ });
+ },
+ backgroundColor: AppColors.successColor,
+ borderColor: AppColors.successColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50.h,
+ icon: AppAssets.livecare_book_icon,
+ iconColor: AppColors.whiteColor,
+ iconSize: 18.h,
+ ).paddingSymmetrical(24.h, 24.h)
+ : CustomButton(
+ text: LocaleKeys.payNow.tr(context: context),
+ onPressed: () async {
+ await askVideoCallPermission().then((val) {
+ if (val) {
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: ImmediateLiveCarePaymentPage(),
+ ),
);
} else {
showCommonBottomSheetWithoutHeight(
@@ -236,16 +285,17 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
context,
child: Utils.getWarningWidget(
loadingText:
- "LiveCare requires Camera, Microphone & Location permissions to enable virtual consultation between patient & doctor, Please allow these to proceed.".needTranslation,
- isShowActionButtons: true,
- onCancelTap: () {
- Navigator.pop(context);
- },
- onConfirmTap: () async {
- openAppSettings();
- }),
- callBackFunc: () {},
- isFullScreen: false,
+ "LiveCare requires Camera, Microphone, Location & Notifications permissions to enable virtual consultation between patient & doctor, Please allow these to proceed."
+ .needTranslation,
+ isShowActionButtons: true,
+ onCancelTap: () {
+ Navigator.pop(context);
+ },
+ onConfirmTap: () async {
+ openAppSettings();
+ }),
+ callBackFunc: () {},
+ isFullScreen: false,
isCloseButtonVisible: true,
);
}
@@ -272,12 +322,9 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
}
Future askVideoCallPermission() async {
- Map statuses = await [
- Permission.camera,
- Permission.microphone,
- ].request();
+ Map statuses = await [Permission.camera, Permission.microphone, Permission.notification].request();
- if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted) {
+ if (statuses[Permission.camera] == PermissionStatus.granted && statuses[Permission.microphone] == PermissionStatus.granted && statuses[Permission.notification] == PermissionStatus.granted) {
// Camera permission granted
return true;
} else {
diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart
index e9e5c5c..8c8b79c 100644
--- a/lib/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart
+++ b/lib/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart
@@ -16,6 +16,7 @@ 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/chip/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/widgets/countdown_timer.dart';
import 'package:lottie/lottie.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -32,24 +33,24 @@ class _ImmediateLiveCarePendingRequestPageState extends State durationNotifier = ValueNotifier(countdownDuration);
- Timer? timer;
+ // static Duration countdownDuration = Duration(minutes: 1, seconds: 0);
+ // late ValueNotifier durationNotifier = ValueNotifier(immediateLiveCareViewModel.countdownDuration);
+ // Timer? timer;
@override
void initState() {
super.initState();
scheduleMicrotask(() {
- countdownDuration = Duration(minutes: immediateLiveCareViewModel.patientLiveCareHistoryList[0].watingtimeInteger!, seconds: 0);
- durationNotifier = ValueNotifier(countdownDuration);
- startTimer();
+ // countdownDuration = Duration(minutes: immediateLiveCareViewModel.patientLiveCareHistoryList[0].watingtimeInteger!, seconds: 0);
+ // durationNotifier = ValueNotifier(immediateLiveCareViewModel.countdownDuration);
+ // startTimer();
});
}
@override
void dispose() {
- timer?.cancel();
- durationNotifier.dispose();
+ // timer?.cancel();
+ // durationNotifier.dispose();
super.dispose();
}
@@ -85,7 +86,7 @@ class _ImmediateLiveCarePendingRequestPageState extends State(
- valueListenable: durationNotifier,
+ valueListenable: immediateLiveCareVM.durationNotifier,
builder: (context, duration, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -173,133 +174,133 @@ class _ImmediateLiveCarePendingRequestPageState extends State addTime());
- setState(() {});
- }
-
- void addTime() {
- final seconds = durationNotifier.value.inSeconds - 1;
- if (seconds < 0) {
- timer?.cancel();
- // Handle end of timer here
- // showEndMessage();
- } else {
- durationNotifier.value = Duration(seconds: seconds);
- }
- }
-
- Future _onWillPop() async {
- timer?.cancel();
- Navigator.of(context).pop();
- return true;
- }
-
- Widget buildTime(Duration duration) {
- String twoDigits(int n) => n.toString().padLeft(2, '0');
- final hours = twoDigits(duration.inHours);
- final minutes = twoDigits(duration.inMinutes.remainder(60));
- final seconds = twoDigits(duration.inSeconds.remainder(60));
-
- return Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- buildTimeColumn(hours, "Hours".needTranslation),
- buildTimeColumn(minutes, "Mins".needTranslation),
- buildTimeColumn(seconds, "Secs".needTranslation, isLast: true),
- ],
- );
- }
-
- Widget buildTimeColumn(String time, String label, {bool isLast = false}) {
- return Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- Row(
- children: [
- buildDigit(time[0]),
- buildDigit(time[1]),
- if (!isLast) buildTimeSeparator(),
- ],
- ),
- buildLabel(label),
- ],
- );
- }
-
- Widget buildDigit(String digit) {
- return Container(
- padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
- // margin: const EdgeInsets.symmetric(horizontal: 2),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(8),
- ),
- child: ClipRect(
- child: AnimatedSwitcher(
- duration: const Duration(milliseconds: 600),
- switchInCurve: Curves.easeOutExpo,
- switchOutCurve: Curves.easeInExpo,
- transitionBuilder: (Widget child, Animation animation) {
- return Stack(
- children: [
- SlideTransition(
- position: Tween(
- begin: const Offset(0, -1),
- end: const Offset(0, 1),
- ).animate(CurvedAnimation(
- parent: animation,
- curve: Curves.easeOutCubic,
- )),
- child: FadeTransition(
- opacity: animation,
- child: child,
- ),
- ),
- SlideTransition(
- position: Tween(
- begin: const Offset(0, -1),
- end: const Offset(0, 0),
- ).animate(CurvedAnimation(
- parent: animation,
- curve: Curves.bounceIn,
- )),
- child: FadeTransition(
- opacity: animation,
- child: child,
- ),
- ),
- ],
- );
- },
- child: Text(
- digit,
- key: ValueKey(digit),
- style: TextStyle(
- fontWeight: FontWeight.bold,
- color: Colors.black,
- fontSize: 20.f,
- ),
- ),
- ),
- ),
- );
- }
-
- Widget buildLabel(String label) {
- return label.toText14(isBold: true);
- }
-
- Widget buildTimeSeparator() {
- return const Padding(
- padding: EdgeInsets.symmetric(horizontal: 2.0),
- child: Text(
- ":",
- style: TextStyle(
- color: Colors.black,
- fontSize: 20,
- ),
- ),
- );
- }
+ // void startTimer() {
+ // // timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
+ // // setState(() {});
+ // }
+ //
+ // void addTime() {
+ // final seconds = durationNotifier.value.inSeconds - 1;
+ // if (seconds < 0) {
+ // timer?.cancel();
+ // // Handle end of timer here
+ // // showEndMessage();
+ // } else {
+ // durationNotifier.value = Duration(seconds: seconds);
+ // }
+ // }
+ //
+ // Future _onWillPop() async {
+ // timer?.cancel();
+ // Navigator.of(context).pop();
+ // return true;
+ // }
+ //
+ // Widget buildTime(Duration duration) {
+ // String twoDigits(int n) => n.toString().padLeft(2, '0');
+ // final hours = twoDigits(duration.inHours);
+ // final minutes = twoDigits(duration.inMinutes.remainder(60));
+ // final seconds = twoDigits(duration.inSeconds.remainder(60));
+ //
+ // return Row(
+ // mainAxisAlignment: MainAxisAlignment.center,
+ // children: [
+ // buildTimeColumn(hours, "Hours".needTranslation),
+ // buildTimeColumn(minutes, "Mins".needTranslation),
+ // buildTimeColumn(seconds, "Secs".needTranslation, isLast: true),
+ // ],
+ // );
+ // }
+ //
+ // Widget buildTimeColumn(String time, String label, {bool isLast = false}) {
+ // return Column(
+ // mainAxisAlignment: MainAxisAlignment.center,
+ // children: [
+ // Row(
+ // children: [
+ // buildDigit(time[0]),
+ // buildDigit(time[1]),
+ // if (!isLast) buildTimeSeparator(),
+ // ],
+ // ),
+ // buildLabel(label),
+ // ],
+ // );
+ // }
+ //
+ // Widget buildDigit(String digit) {
+ // return Container(
+ // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
+ // // margin: const EdgeInsets.symmetric(horizontal: 2),
+ // decoration: BoxDecoration(
+ // color: Colors.white,
+ // borderRadius: BorderRadius.circular(8),
+ // ),
+ // child: ClipRect(
+ // child: AnimatedSwitcher(
+ // duration: const Duration(milliseconds: 600),
+ // switchInCurve: Curves.easeOutExpo,
+ // switchOutCurve: Curves.easeInExpo,
+ // transitionBuilder: (Widget child, Animation animation) {
+ // return Stack(
+ // children: [
+ // SlideTransition(
+ // position: Tween(
+ // begin: const Offset(0, -1),
+ // end: const Offset(0, 1),
+ // ).animate(CurvedAnimation(
+ // parent: animation,
+ // curve: Curves.easeOutCubic,
+ // )),
+ // child: FadeTransition(
+ // opacity: animation,
+ // child: child,
+ // ),
+ // ),
+ // SlideTransition(
+ // position: Tween(
+ // begin: const Offset(0, -1),
+ // end: const Offset(0, 0),
+ // ).animate(CurvedAnimation(
+ // parent: animation,
+ // curve: Curves.bounceIn,
+ // )),
+ // child: FadeTransition(
+ // opacity: animation,
+ // child: child,
+ // ),
+ // ),
+ // ],
+ // );
+ // },
+ // child: Text(
+ // digit,
+ // key: ValueKey(digit),
+ // style: TextStyle(
+ // fontWeight: FontWeight.bold,
+ // color: Colors.black,
+ // fontSize: 20.f,
+ // ),
+ // ),
+ // ),
+ // ),
+ // );
+ // }
+ //
+ // Widget buildLabel(String label) {
+ // return label.toText14(isBold: true);
+ // }
+ //
+ // Widget buildTimeSeparator() {
+ // return const Padding(
+ // padding: EdgeInsets.symmetric(horizontal: 2.0),
+ // child: Text(
+ // ":",
+ // style: TextStyle(
+ // color: Colors.black,
+ // fontSize: 20,
+ // ),
+ // ),
+ // );
+ // }
}
diff --git a/lib/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart b/lib/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart
index 8181d25..15598f3 100644
--- a/lib/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart
+++ b/lib/presentation/book_appointment/livecare/select_immediate_livecare_clinic_page.dart
@@ -66,35 +66,6 @@ class _SelectImmediateLiveCareClinicPageState extends State(builder: (context, immediateLiveCareVM, child) {
return Column(
children: [
- // SizedBox(height: 16.h),
- // TextInputWidget(
- // labelText: LocaleKeys.search.tr(context: context),
- // hintText: LocaleKeys.clinicName.tr(context: context),
- // controller: searchEditingController,
- // isEnable: true,
- // prefix: null,
- // autoFocus: false,
- // isBorderAllowed: false,
- // keyboardType: TextInputType.text,
- // focusNode: textFocusNode,
- // suffix: searchEditingController.text.isNotEmpty
- // ? GestureDetector(
- // onTap: () {
- // searchEditingController.clear();
- // bookAppointmentsViewModel.filterClinics("");
- // textFocusNode.unfocus();
- // },
- // child: Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown),
- // )
- // : null,
- // onChange: (value) {
- // bookAppointmentsViewModel.filterClinics(value!);
- // },
- // padding: EdgeInsets.symmetric(
- // vertical: ResponsiveExtension(10).h,
- // horizontal: ResponsiveExtension(15).h,
- // ),
- // ),
ListView.separated(
padding: EdgeInsets.only(top: 16.h),
shrinkWrap: true,
diff --git a/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart b/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart
index 7385c6b..3913f10 100644
--- a/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart
+++ b/lib/presentation/book_appointment/livecare/widgets/livecare_clinic_card.dart
@@ -12,6 +12,7 @@ import 'package:hmg_patient_app_new/features/immediate_livecare/models/resp_mode
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_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/widgets/chip/app_custom_chip_widget.dart';
class LiveCareClinicCard extends StatelessWidget {
LiveCareClinicCard({super.key, required this.liveCareClinicListResponseModel, required this.isLoading, required this.immediateLiveCareViewModel});
@@ -34,20 +35,12 @@ class LiveCareClinicCard extends StatelessWidget {
children: [
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Utils.buildSvgWithAssets(icon: AppAssets.generic_clinic_icon, width: 24.h, height: 24.h, fit: BoxFit.contain).toShimmer2(isShow: isLoading),
- Column(
- children: [
- Utils.buildSvgWithAssets(
- icon: AppAssets.livecare_online_icon,
- width: 16.h,
- height: 16.h,
- fit: BoxFit.contain,
- iconColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor)
- .toShimmer2(isShow: isLoading),
- SizedBox(height: 4.h),
- liveCareClinicListResponseModel.isOnline == 1
- ? LocaleKeys.online.tr(context: context).toText10(isBold: true, color: AppColors.successColor).toShimmer2(isShow: isLoading)
- : "Offline".toText10(isBold: true, color: AppColors.primaryRedColor).toShimmer2(isShow: isLoading),
- ],
+ AppCustomChipWidget(
+ icon: AppAssets.livecare_online_icon,
+ iconColor: AppColors.whiteColor,
+ richText: (liveCareClinicListResponseModel.isOnline == 1 ? LocaleKeys.online.tr(context: context) : "Offline".needTranslation).toText10(isBold: true, color: AppColors.whiteColor),
+ backgroundColor: liveCareClinicListResponseModel.isOnline == 1 ? AppColors.successColor : AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
),
]),
SizedBox(height: 8.h),
diff --git a/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart b/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart
index 226ac50..e4d69b1 100644
--- a/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart
+++ b/lib/presentation/book_appointment/livecare/widgets/select_livecare_call_type.dart
@@ -1,10 +1,14 @@
import 'package:flutter/cupertino.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@@ -16,46 +20,30 @@ class SelectLiveCareCallType extends StatelessWidget {
@override
Widget build(BuildContext context) {
//TODO: Replace with actual icons
- return GridView(
- gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 3,
- crossAxisSpacing: 16,
- mainAxisSpacing: 16,
- mainAxisExtent: 130,
- ),
- physics: NeverScrollableScrollPhysics(),
- padding: EdgeInsets.zero,
- shrinkWrap: true,
+ return Column(
children: [
- MedicalFileCard(
- label: "Video Call".needTranslation,
- textColor: AppColors.blackColor,
- backgroundColor: AppColors.whiteColor,
- svgIcon: AppAssets.eye_result_icon,
- isLargeText: true,
- iconSize: 36.h,
+ checkInOptionCard(
+ AppAssets.checkin_location_icon,
+ "Video Call".needTranslation,
+ "View your nearest HMG locations".needTranslation,
).onPress(() {
Navigator.of(context).pop();
immediateLiveCareViewModel.setLiveCareSelectedCallType(1);
}),
- MedicalFileCard(
- label: "Audio Call".needTranslation,
- textColor: AppColors.blackColor,
- backgroundColor: AppColors.whiteColor,
- svgIcon: AppAssets.allergy_info_icon,
- isLargeText: true,
- iconSize: 36.h,
+ SizedBox(height: 16.h),
+ checkInOptionCard(
+ AppAssets.checkin_location_icon,
+ "Audio Call".needTranslation,
+ "Provide your feedback on our services".needTranslation,
).onPress(() {
Navigator.of(context).pop();
immediateLiveCareViewModel.setLiveCareSelectedCallType(2);
}),
- MedicalFileCard(
- label: "Phone Call".needTranslation,
- textColor: AppColors.blackColor,
- backgroundColor: AppColors.whiteColor,
- svgIcon: AppAssets.vaccine_info_icon,
- isLargeText: true,
- iconSize: 36.h,
+ SizedBox(height: 16.h),
+ checkInOptionCard(
+ AppAssets.checkin_location_icon,
+ "Phone Call".needTranslation,
+ "Live chat option with HMG".needTranslation,
).onPress(() {
Navigator.of(context).pop();
immediateLiveCareViewModel.setLiveCareSelectedCallType(3);
@@ -63,4 +51,45 @@ class SelectLiveCareCallType extends StatelessWidget {
],
);
}
+
+ Widget checkInOptionCard(String icon, String title, String subTitle) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.r,
+ hasShadow: false,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.buildSvgWithAssets(icon: icon, width: 40.h, height: 40.h, fit: BoxFit.fill),
+ SizedBox(height: 16.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ title.toText16(isBold: true, color: AppColors.textColor),
+ subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
+ ],
+ ),
+ ),
+ Transform.flip(
+ flipX: getIt.get().isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.blackColor,
+ width: 18.h,
+ height: 13.h,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ).paddingAll(16.h),
+ );
+ }
}
diff --git a/lib/presentation/book_appointment/review_appointment_page.dart b/lib/presentation/book_appointment/review_appointment_page.dart
index 0cb4b2d..415d342 100644
--- a/lib/presentation/book_appointment/review_appointment_page.dart
+++ b/lib/presentation/book_appointment/review_appointment_page.dart
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/loading_utils.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@@ -12,11 +13,15 @@ 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/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart';
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
@@ -113,10 +118,14 @@ class _ReviewAppointmentPageState extends State {
labelText: "${LocaleKeys.branch.tr(context: context)} ${bookAppointmentsViewModel.selectedDoctor.projectName}".needTranslation,
),
AppCustomChipWidget(
- labelText: "${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.selectedAppointmentDate}".needTranslation,
+ labelText:
+ "${LocaleKeys.date.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? DateUtil.formatDateToDate(DateTime.now(), false) : bookAppointmentsViewModel.selectedAppointmentDate}"
+ .needTranslation,
),
AppCustomChipWidget(
- labelText: "${LocaleKeys.time.tr(context: context)}: ${bookAppointmentsViewModel.selectedAppointmentTime}".needTranslation,
+ labelText:
+ "${LocaleKeys.time.tr(context: context)}: ${bookAppointmentsViewModel.isWaitingAppointmentSelected ? "Waiting Appointment".needTranslation : bookAppointmentsViewModel.selectedAppointmentTime}"
+ .needTranslation,
),
],
),
@@ -185,9 +194,13 @@ class _ReviewAppointmentPageState extends State {
hasShadow: true,
),
child: CustomButton(
- text: LocaleKeys.bookAppo.tr(context: context),
+ text: bookAppointmentsViewModel.isWaitingAppointmentSelected ? "Add to waiting list" : LocaleKeys.bookAppo.tr(context: context),
onPressed: () async {
- initiateBookAppointment();
+ if (bookAppointmentsViewModel.isWaitingAppointmentSelected) {
+ getWalkInAppointmentPatientShare();
+ } else {
+ initiateBookAppointment();
+ }
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
@@ -204,6 +217,27 @@ class _ReviewAppointmentPageState extends State {
);
}
+ void getWalkInAppointmentPatientShare() async {
+ LoaderBottomSheet.showLoader(loadingText: "Fetching Appointment Share...".needTranslation);
+ await bookAppointmentsViewModel.getWalkInPatientShareAppointment(onSuccess: (val) {
+ LoaderBottomSheet.hideLoader();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: WaitingAppointmentPaymentPage(),
+ ),
+ );
+ }, onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ }
+
void initiateBookAppointment() async {
LoadingUtils.showFullScreenLoader(barrierDismissible: true, isSuccessDialog: false, loadingText: "Booking your appointment...".needTranslation);
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart
new file mode 100644
index 0000000..f832db6
--- /dev/null
+++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_info.dart
@@ -0,0 +1,118 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.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/common_bottom_sheet.dart';
+import 'package:provider/provider.dart';
+
+class WaitingAppointmentInfo extends StatelessWidget {
+ WaitingAppointmentInfo({super.key});
+
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ bookAppointmentsViewModel = Provider.of(context, listen: false);
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: "Waiting Appointment".needTranslation,
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 24.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.waiting_appointment_icon, width: 48.h, height: 48.h, fit: BoxFit.contain),
+ SizedBox(height: 16.h),
+ "What is Waiting Appointment?".needTranslation.toText16(isBold: true),
+ SizedBox(height: 16.h),
+ "The waiting appointments feature allows you to book an appointment while you are inside the hospital building, and in case there is no available slot in the doctor’s schedule."
+ .needTranslation
+ .toText14(isBold: false),
+ SizedBox(height: 16.h),
+ "The appointment with the doctor is confirmed, but the time of entry is uncertain.".needTranslation.toText14(isBold: false),
+ SizedBox(height: 24.h),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Icon(
+ Icons.warning,
+ size: 20,
+ color: AppColors.warningColorYellow,
+ ),
+ SizedBox(width: 10.w),
+ SizedBox(
+ width: MediaQuery.of(context).size.width * 0.7,
+ child: "Note: You must have to pay within 10 minutes of booking, otherwise your appointment will be cancelled automatically"
+ .needTranslation
+ .toText14(isBold: true, color: AppColors.warningColorYellow),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(24.w, 0),
+ ),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: true,
+ ),
+ child: CustomButton(
+ text: "Continue".needTranslation,
+ onPressed: () async {
+ showCommonBottomSheetWithoutHeight(context,
+ title: LocaleKeys.onlineCheckIn.tr(),
+ child: WaitingAppointmentOnlineCheckinSheet(
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ ),
+ callBackFunc: () {},
+ isFullScreen: false);
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 16,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50.h,
+ icon: AppAssets.calendar,
+ iconColor: AppColors.whiteColor,
+ iconSize: 20.h,
+ ).paddingSymmetrical(24.h, 24.h),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart
new file mode 100644
index 0000000..965844a
--- /dev/null
+++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_online_checkin_sheet.dart
@@ -0,0 +1,161 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/location_util.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/review_appointment_page.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:barcode_scan2/barcode_scan2.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/nfc/nfc_reader_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+
+class WaitingAppointmentOnlineCheckinSheet extends StatelessWidget {
+ WaitingAppointmentOnlineCheckinSheet({super.key, required this.bookAppointmentsViewModel});
+
+ BookAppointmentsViewModel bookAppointmentsViewModel;
+
+ bool _supportsNFC = false;
+
+ late LocationUtils locationUtils;
+ late AppState appState;
+ ProjectDetailListModel projectDetailListModel = ProjectDetailListModel();
+
+ @override
+ Widget build(BuildContext context) {
+ appState = getIt.get();
+ locationUtils = getIt.get();
+ locationUtils.isShowConfirmDialog = true;
+ FlutterNfcKit.nfcAvailability.then((value) {
+ _supportsNFC = (value == NFCAvailability.available);
+ });
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ checkInOptionCard(
+ AppAssets.checkin_location_icon,
+ "Live Location".needTranslation,
+ "Verify your location to be at hospital to check in".needTranslation,
+ ).onPress(() {
+ // locationUtils = LocationUtils(
+ // isShowConfirmDialog: false,
+ // navigationService: myAppointmentsViewModel.navigationService,
+ // appState: myAppointmentsViewModel.appState,
+ // );
+ locationUtils.getCurrentLocation(onSuccess: (value) {
+ projectDetailListModel = Utils.getProjectDetailObj(appState, bookAppointmentsViewModel.waitingAppointmentProjectID);
+ double dist = Utils.distance(value.latitude, value.longitude, double.parse(projectDetailListModel.latitude!), double.parse(projectDetailListModel.longitude!)).ceilToDouble() * 1000;
+ print(dist);
+ if (dist <= projectDetailListModel.geofenceRadius!) {
+ checkScannedNFCAndQRCode(projectDetailListModel.checkInQrCode!, context);
+ } else {
+ showCommonBottomSheetWithoutHeight(context,
+ title: "Error".needTranslation,
+ child: Utils.getErrorWidget(loadingText: "Please ensure you're within the hospital location to perform online check-in.".needTranslation), callBackFunc: () {
+ Navigator.of(context).pop();
+ }, isFullScreen: false);
+ }
+ });
+ }),
+ SizedBox(height: 16.h),
+ checkInOptionCard(
+ AppAssets.checkin_nfc_icon,
+ "NFC (Near Field Communication)".needTranslation,
+ "Scan your phone via NFC board to check in".needTranslation,
+ ).onPress(() {
+ Future.delayed(const Duration(milliseconds: 500), () {
+ showNfcReader(context, onNcfScan: (String nfcId) {
+ Future.delayed(const Duration(milliseconds: 100), () {
+ checkScannedNFCAndQRCode(nfcId, context);
+ });
+ }, onCancel: () {});
+ });
+ }),
+ SizedBox(height: 16.h),
+ checkInOptionCard(
+ AppAssets.checkin_qr_icon,
+ "QR Code".needTranslation,
+ "Scan QR code with your camera to check in".needTranslation,
+ ).onPress(() async {
+ String onlineCheckInQRCode = (await BarcodeScanner.scan().then((value) => value.rawContent));
+ if (onlineCheckInQRCode != "") {
+ checkScannedNFCAndQRCode(onlineCheckInQRCode, context);
+ } else {}
+ }),
+ ],
+ );
+ }
+
+ Widget checkInOptionCard(String icon, String title, String subTitle) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Utils.buildSvgWithAssets(icon: icon, width: 40.h, height: 40.h, fit: BoxFit.fill),
+ SizedBox(height: 16.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ title.toText16(isBold: true, color: AppColors.textColor),
+ subTitle.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor),
+ ],
+ ),
+ ),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.blackColor,
+ width: 18.h,
+ height: 13.h,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ).paddingAll(16.h),
+ );
+ }
+
+ void checkScannedNFCAndQRCode(String scannedCode, BuildContext context) async {
+ LoaderBottomSheet.showLoader(loadingText: "Processing Check-In...".needTranslation);
+ bookAppointmentsViewModel.checkScannedNFCAndQRCode(
+ scannedCode,
+ bookAppointmentsViewModel.waitingAppointmentProjectID,
+ onSuccess: (value) {
+ LoaderBottomSheet.hideLoader();
+ bookAppointmentsViewModel.setIsWaitingAppointmentSelected(true);
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: ReviewAppointmentPage(),
+ ),
+ );
+ },
+ onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(context, title: "Error".needTranslation, child: Utils.getErrorWidget(loadingText: err), callBackFunc: () {
+ // Navigator.of(context).pop();
+ }, isFullScreen: false);
+ },
+ );
+ }
+}
diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart
new file mode 100644
index 0000000..8f9943c
--- /dev/null
+++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart
@@ -0,0 +1,644 @@
+import 'dart:async';
+import 'dart:developer';
+import 'dart:io';
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/app_state.dart';
+import 'package:hmg_patient_app_new/core/cache_consts.dart';
+import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/enums.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
+import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
+import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
+import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
+import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.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/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.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:provider/provider.dart';
+import 'package:smooth_corner/smooth_corner.dart';
+
+class WaitingAppointmentPaymentPage extends StatefulWidget {
+ WaitingAppointmentPaymentPage({super.key});
+
+ // PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
+
+ @override
+ State createState() => _WaitingAppointmentPaymentPageState();
+}
+
+class _WaitingAppointmentPaymentPageState extends State {
+ late BookAppointmentsViewModel bookAppointmentsViewModel;
+ late MyAppointmentsViewModel myAppointmentsViewModel;
+ late PayfortViewModel payfortViewModel;
+ late AppState appState;
+
+ MyInAppBrowser? browser;
+ String selectedPaymentMethod = "";
+
+ String transID = "";
+
+ bool isShowTamara = false;
+ String tamaraPaymentStatus = "";
+ String tamaraOrderID = "";
+
+ @override
+ void initState() {
+ scheduleMicrotask(() {
+ payfortViewModel.initPayfortViewModel();
+ payfortViewModel.setIsApplePayConfigurationLoading(false);
+ // myAppointmentsViewModel.getPatientShareAppointment(
+ // widget.patientAppointmentHistoryResponseModel.projectID,
+ // widget.patientAppointmentHistoryResponseModel.clinicID,
+ // widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onSuccess: (val) {
+ // myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
+ // if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
+ // myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
+ // setState(() {
+ // isShowTamara = true;
+ // });
+ // }
+ // });
+ // }, onError: (err) {
+ // Navigator.of(context).pop();
+ // Navigator.of(context).pop();
+ // });
+ });
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ appState = getIt.get();
+ bookAppointmentsViewModel = Provider.of(context);
+ myAppointmentsViewModel = Provider.of(context);
+ payfortViewModel = Provider.of(context);
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Consumer(builder: (context, bookAppointmentsVM, child) {
+ return Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: "Appointment Payment".needTranslation,
+ child: SingleChildScrollView(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 24.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Image.asset(AppAssets.mada, width: 72.h, height: 25.h),
+ SizedBox(height: 16.h),
+ "Mada".needTranslation.toText16(isBold: true),
+ ],
+ ),
+ SizedBox(width: 8.h),
+ const Spacer(),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.blackColor,
+ width: 18.h,
+ height: 13.h,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(16.h, 16.h),
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ selectedPaymentMethod = "MADA";
+ openPaymentURL("mada");
+ }),
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Image.asset(AppAssets.visa, width: 50.h, height: 50.h),
+ SizedBox(width: 8.h),
+ Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h),
+ ],
+ ),
+ SizedBox(height: 16.h),
+ "Visa or Mastercard".needTranslation.toText16(isBold: true),
+ ],
+ ),
+ SizedBox(width: 8.h),
+ const Spacer(),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.blackColor,
+ width: 18.h,
+ height: 13.h,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(16.h, 16.h),
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ selectedPaymentMethod = "VISA";
+ openPaymentURL("visa");
+ }),
+ SizedBox(height: 16.h),
+ isShowTamara
+ ? Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.h,
+ hasShadow: false,
+ ),
+ child: Row(
+ mainAxisSize: MainAxisSize.max,
+ children: [
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h),
+ SizedBox(height: 16.h),
+ "Tamara".needTranslation.toText16(isBold: true),
+ ],
+ ),
+ SizedBox(width: 8.h),
+ const Spacer(),
+ Transform.flip(
+ flipX: appState.isArabic(),
+ child: Utils.buildSvgWithAssets(
+ icon: AppAssets.forward_arrow_icon_small,
+ iconColor: AppColors.blackColor,
+ width: 18.h,
+ height: 13.h,
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(16.h, 16.h),
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ selectedPaymentMethod = "TAMARA";
+ openPaymentURL("tamara");
+ })
+ : SizedBox.shrink(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: false,
+ ),
+ child: Consumer(builder: (context, payfortVM, child) {
+ //TODO: Need to add loading state & animation for Apple Pay Configuration
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ (bookAppointmentsVM.patientWalkInAppointmentShareResponseModel!.isCash ?? true)
+ ? Container(
+ height: 50.h,
+ decoration: ShapeDecoration(
+ color: AppColors.secondaryLightRedBorderColor,
+ shape: SmoothRectangleBorder(
+ borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
+ smoothness: 1,
+ ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h),
+ CustomButton(
+ text: LocaleKeys.updateInsurance.tr(context: context),
+ onPressed: () {
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: InsuranceHomePage(),
+ ),
+ );
+ },
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.secondaryLightRedBorderColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 10.f,
+ fontWeight: FontWeight.w500,
+ borderRadius: 8,
+ padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
+ height: 30.h,
+ ).paddingSymmetrical(24.h, 0.h),
+ ],
+ ),
+ )
+ : const SizedBox(),
+ SizedBox(height: 24.h),
+ "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 17.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "Amount before tax".needTranslation.toText14(isBold: true),
+ Utils.getPaymentAmountWithSymbol(bookAppointmentsVM.patientWalkInAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13,
+ isSaudiCurrency: true),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor),
+ Utils.getPaymentAmountWithSymbol(
+ bookAppointmentsVM.patientWalkInAppointmentShareResponseModel!.patientTaxAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor),
+ AppColors.greyTextColor,
+ 13,
+ isSaudiCurrency: true),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 17.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ "".needTranslation.toText14(isBold: true),
+ Utils.getPaymentAmountWithSymbol(
+ bookAppointmentsVM.patientWalkInAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17,
+ isSaudiCurrency: true),
+ ],
+ ).paddingSymmetrical(24.h, 0.h),
+ Platform.isIOS
+ ? Utils.buildSvgWithAssets(
+ icon: AppAssets.apple_pay_button,
+ width: 200.h,
+ height: 80.h,
+ fit: BoxFit.contain,
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ // payfortVM.setIsApplePayConfigurationLoading(true);
+ if (Utils.havePrivilege(103)) {
+ startApplePay();
+ } else {
+ openPaymentURL("ApplePay");
+ }
+ })
+ : SizedBox(height: 12.h),
+ SizedBox(height: 12.h),
+ ],
+ );
+ }),
+ ),
+ ],
+ );
+ }),
+ );
+ }
+
+ onBrowserLoadStart(String url) {
+ print("onBrowserLoadStart");
+ print(url);
+
+ if (selectedPaymentMethod == "tamara") {
+ if (Platform.isAndroid) {
+ Uri uri = new Uri.dataFromString(url);
+ tamaraPaymentStatus = uri.queryParameters['status']!;
+ tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!;
+ } else {
+ Uri uri = new Uri.dataFromString(url);
+ tamaraPaymentStatus = uri.queryParameters['paymentStatus']!;
+ tamaraOrderID = uri.queryParameters['orderId']!;
+ }
+ }
+
+ // if(selectedPaymentMethod != "TAMARA") {
+ MyInAppBrowser.successURLS.forEach((element) {
+ if (url.contains(element)) {
+ browser?.close();
+ MyInAppBrowser.isPaymentDone = true;
+ return;
+ }
+ });
+ // }
+
+ // if(selectedPaymentMethod != "TAMARA") {
+ MyInAppBrowser.errorURLS.forEach((element) {
+ if (url.contains(element)) {
+ browser?.close();
+ MyInAppBrowser.isPaymentDone = false;
+ return;
+ }
+ });
+ // }
+ }
+
+ onBrowserExit(bool isPaymentMade) async {
+ checkPaymentStatus();
+ }
+
+ void checkPaymentStatus() async {
+ LoaderBottomSheet.showLoader(loadingText: "Checking payment status, Please wait...".needTranslation);
+ if (selectedPaymentMethod == "TAMARA") {
+ await payfortViewModel.checkTamaraPaymentStatus(
+ transactionID: transID,
+ onSuccess: (apiResponse) async {
+ if (apiResponse.data["status"].toString().toLowerCase() == "success") {
+ tamaraOrderID = apiResponse.data["tamara_order_id"].toString();
+ await payfortViewModel.updateTamaraRequestStatus(responseMessage: "success", status: "14", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
+ await payfortViewModel.markAppointmentAsTamaraPaid(projectID: bookAppointmentsViewModel.selectedDoctor.projectID!, appointmentNo: DateTime.now().millisecondsSinceEpoch);
+ // await myAppointmentsViewModel.addAdvanceNumberRequest(
+ // advanceNumber: "Tamara-Advance-0000",
+ // paymentReference: tamaraOrderID,
+ // appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
+ // onSuccess: (value) async {
+ // if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) {
+ // //TODO: Implement LiveCare Check-In API Call
+ // await myAppointmentsViewModel.insertLiveCareVIDARequest(
+ // clientRequestID: tamaraOrderID,
+ // patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
+ // onSuccess: (apiResponse) {
+ // Future.delayed(Duration(milliseconds: 500), () {
+ // LoaderBottomSheet.hideLoader();
+ // Navigator.pushAndRemoveUntil(
+ // context,
+ // CustomPageRoute(
+ // page: LandingNavigation(),
+ // ),
+ // (r) => false);
+ // });
+ // },
+ // onError: (error) {});
+ // } else {
+ // await myAppointmentsViewModel.generateAppointmentQR(
+ // clinicID: widget.patientAppointmentHistoryResponseModel.clinicID,
+ // projectID: widget.patientAppointmentHistoryResponseModel.projectID,
+ // appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(),
+ // isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!,
+ // onSuccess: (apiResponse) {
+ // Future.delayed(Duration(milliseconds: 500), () {
+ // LoaderBottomSheet.hideLoader();
+ // Navigator.pushAndRemoveUntil(
+ // context,
+ // CustomPageRoute(
+ // page: LandingNavigation(),
+ // ),
+ // (r) => false);
+ // });
+ // });
+ // }
+ // });
+ } else {
+ await payfortViewModel.updateTamaraRequestStatus(responseMessage: "Failed", status: "00", clientRequestID: transID, tamaraOrderID: tamaraOrderID);
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ }
+ },
+ onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ } else {
+ await payfortViewModel.checkPaymentStatus(
+ transactionID: transID,
+ onSuccess: (apiResponse) async {
+ print(apiResponse.data);
+ if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") {
+ LoaderBottomSheet.hideLoader();
+ LoaderBottomSheet.showLoader(loadingText: "Booking Waiting Appointment, Please wait...".needTranslation);
+ await bookAppointmentsViewModel.insertSpecificAppointmentForWalkIn(onSuccess: (val) async {
+ String appointmentNo = val.data['AppointmentNo'].toString();
+ await myAppointmentsViewModel.createAdvancePayment(
+ paymentMethodName: selectedPaymentMethod,
+ projectID: bookAppointmentsViewModel.waitingAppointmentProjectID,
+ clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID!,
+ appointmentNo: appointmentNo,
+ payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!,
+ paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
+ patientID: appState.getAuthenticatedUser()!.patientId.toString(),
+ patientType: appState.getAuthenticatedUser()!.patientType!,
+ onSuccess: (value) async {
+ print(value);
+ await myAppointmentsViewModel.addAdvanceNumberRequest(
+ advanceNumber: Utils.isVidaPlusProject(bookAppointmentsViewModel.waitingAppointmentProjectID)
+ ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString()
+ : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(),
+ paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!,
+ appointmentNo: appointmentNo,
+ onSuccess: (value) async {
+ LoaderBottomSheet.hideLoader();
+ sendCheckInRequest(bookAppointmentsViewModel.waitingAppointmentNFCCode, appointmentNo, context);
+ });
+ });
+ });
+ } else {
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ }
+ });
+ }
+ }
+
+ openPaymentURL(String paymentMethod) {
+ browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context);
+ transID = Utils.getAppointmentTransID(
+ bookAppointmentsViewModel.selectedDoctor.projectID!,
+ bookAppointmentsViewModel.selectedDoctor.clinicID!,
+ bookAppointmentsViewModel.selectedDoctor.doctorID!,
+ );
+
+ //TODO: Need to pass dynamic params to the payment request instead of static values
+ browser?.openPaymentBrowser(
+ bookAppointmentsViewModel.patientWalkInAppointmentShareResponseModel!.patientShareWithTax!,
+ "Appointment check in",
+ transID,
+ bookAppointmentsViewModel.selectedDoctor.projectID.toString(),
+ "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com",
+ selectedPaymentMethod,
+ appState.getAuthenticatedUser()!.patientType.toString(),
+ "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
+ appState.getAuthenticatedUser()!.patientId.toString(),
+ appState.getAuthenticatedUser()!,
+ browser!,
+ false,
+ "2",
+ "",
+ context,
+ "",
+ "",
+ "",
+ "",
+ "3");
+ }
+
+ startApplePay() async {
+ showCommonBottomSheet(context,
+ child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false);
+ transID = Utils.getAppointmentTransID(
+ bookAppointmentsViewModel.selectedDoctor.projectID!,
+ bookAppointmentsViewModel.selectedDoctor.clinicID!,
+ bookAppointmentsViewModel.selectedDoctor.doctorID!,
+ );
+
+ ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest();
+
+ await payfortViewModel.getPayfortConfigurations(
+ serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: bookAppointmentsViewModel.selectedDoctor.projectID!, integrationId: 2);
+
+ applePayInsertRequest.clientRequestID = transID;
+ applePayInsertRequest.clinicID = bookAppointmentsViewModel.selectedDoctor.clinicID!;
+
+ applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
+ applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com";
+ applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString();
+ applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}";
+
+ applePayInsertRequest.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
+ applePayInsertRequest.voipToken = await Utils.getStringFromPrefs(CacheConst.voipToken);
+ applePayInsertRequest.doctorID = bookAppointmentsViewModel.selectedDoctor.doctorID;
+ applePayInsertRequest.projectID = bookAppointmentsViewModel.selectedDoctor.projectID.toString();
+ applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString();
+ applePayInsertRequest.channelID = 3;
+ applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString();
+ applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType;
+ applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa;
+ applePayInsertRequest.appointmentDate = DateUtil.convertDateToString(DateTime.now());
+ applePayInsertRequest.appointmentNo = DateTime.now().millisecondsSinceEpoch;
+ applePayInsertRequest.orderDescription = "Appointment Payment";
+ applePayInsertRequest.liveServiceID = "0";
+ applePayInsertRequest.latitude = "0.0";
+ applePayInsertRequest.longitude = "0.0";
+ applePayInsertRequest.amount = bookAppointmentsViewModel.patientWalkInAppointmentShareResponseModel!.patientShareWithTax!.toString();
+ applePayInsertRequest.isSchedule = "0";
+ applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en';
+ applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2;
+ applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId;
+ applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html";
+ applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html";
+ applePayInsertRequest.paymentOption = "ApplePay";
+
+ applePayInsertRequest.isMobSDK = true;
+ applePayInsertRequest.merchantReference = transID;
+ applePayInsertRequest.merchantIdentifier = payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier;
+ applePayInsertRequest.commandType = "PURCHASE";
+ applePayInsertRequest.signature = payfortViewModel.payfortProjectDetailsRespModel!.signature;
+ applePayInsertRequest.accessCode = payfortViewModel.payfortProjectDetailsRespModel!.accessCode;
+ applePayInsertRequest.shaRequestPhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaRequest;
+ applePayInsertRequest.shaResponsePhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaResponse;
+ applePayInsertRequest.returnURL = "";
+
+ //TODO: Need to pass dynamic params to the Apple Pay instead of static values
+ await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) {
+ payfortViewModel.paymentWithApplePay(
+ customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}",
+ // customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress,
+ customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com",
+ orderDescription: "Appointment Payment",
+ orderAmount: double.parse(bookAppointmentsViewModel.patientWalkInAppointmentShareResponseModel!.patientShareWithTax!.toString()),
+ merchantReference: transID,
+ merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier,
+ applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode,
+ applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest,
+ currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED",
+ onFailed: (failureResult) async {
+ log("failureResult: ${failureResult.message.toString()}");
+ Navigator.of(context).pop();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: failureResult.message.toString()),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ },
+ onSucceeded: (successResult) async {
+ log("successResult: ${successResult.responseMessage.toString()}");
+ selectedPaymentMethod = successResult.paymentOption ?? "VISA";
+ checkPaymentStatus();
+ },
+ // projectId: appo.projectID,
+ // serviceTypeEnum: ServiceTypeEnum.appointmentPayment,
+ );
+ });
+ }
+
+ void sendCheckInRequest(String scannedCode, String appointmentNo, BuildContext context) async {
+ LoaderBottomSheet.showLoader(loadingText: "Processing Check-In...".needTranslation);
+ PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel = PatientAppointmentHistoryResponseModel(
+ appointmentNo: appointmentNo,
+ clinicID: bookAppointmentsViewModel.selectedDoctor.clinicID,
+ projectID: bookAppointmentsViewModel.selectedDoctor.projectID,
+ );
+ await myAppointmentsViewModel.sendCheckInNfcRequest(
+ patientAppointmentHistoryResponseModel: patientAppointmentHistoryResponseModel,
+ scannedCode: scannedCode,
+ checkInType: 2,
+ onSuccess: (apiResponse) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(context, title: "Success".needTranslation, child: Utils.getSuccessWidget(loadingText: apiResponse.data["SuccessMsg"]), callBackFunc: () {
+ Navigator.of(context).pop();
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ }, isFullScreen: false);
+ },
+ onError: (error) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(context, title: "Error".needTranslation, child: Utils.getErrorWidget(loadingText: error), callBackFunc: () {
+ Navigator.of(context).pop();
+ }, isFullScreen: false);
+ },
+ );
+ }
+
+ void insertWalkInAppointment() async {}
+}
diff --git a/lib/presentation/book_appointment/widgets/appointment_calendar.dart b/lib/presentation/book_appointment/widgets/appointment_calendar.dart
index 54ff282..7c4226c 100644
--- a/lib/presentation/book_appointment/widgets/appointment_calendar.dart
+++ b/lib/presentation/book_appointment/widgets/appointment_calendar.dart
@@ -15,6 +15,7 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/book_appointments/models/timeslots.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/review_appointment_page.dart';
+import 'package:hmg_patient_app_new/presentation/book_appointment/waiting_appointment/waiting_appointment_info.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/widgets/buttons/custom_button.dart';
@@ -27,7 +28,7 @@ import 'package:smooth_corner/smooth_corner.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
class AppointmentCalendar extends StatefulWidget {
- const AppointmentCalendar({super.key});
+ AppointmentCalendar({super.key});
@override
State createState() => _AppointmentCalendarState();
@@ -54,7 +55,6 @@ class _AppointmentCalendarState extends State {
String selectedTime = "";
- bool isWaitingAppointmentAvailable = false;
final _selectedDay = DateTime.now();
@override
@@ -176,13 +176,25 @@ class _AppointmentCalendarState extends State {
text: "Select".needTranslation,
onPressed: () async {
if (appState.isAuthenticated) {
- bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime);
- Navigator.of(context).pop();
- Navigator.of(context).push(
- CustomPageRoute(
- page: ReviewAppointmentPage(),
- ),
- );
+ if(selectedTime == "Waiting Appointment".needTranslation){
+ bookAppointmentsViewModel.setWaitingAppointmentProjectID(bookAppointmentsViewModel.selectedDoctor.projectID!);
+ bookAppointmentsViewModel.setWaitingAppointmentDoctor(bookAppointmentsViewModel.selectedDoctor);
+
+ Navigator.of(context).pop();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: WaitingAppointmentInfo(),
+ ),
+ );
+ } else {
+ bookAppointmentsViewModel.setSelectedAppointmentDateTime(selectedDate, selectedTime);
+ Navigator.of(context).pop();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: ReviewAppointmentPage(),
+ ),
+ );
+ }
} else {
showCommonBottomSheetWithoutHeight(
context,
@@ -281,8 +293,8 @@ class _AppointmentCalendarState extends State {
openTimeSlotsPickerForDate(DateTime dateStart, List freeSlots) {
dayEvents.clear();
DateTime dateStartObj = new DateTime(dateStart.year, dateStart.month, dateStart.day, 0, 0, 0, 0, 0);
- if (isWaitingAppointmentAvailable && DateUtils.isSameDay(dateStart, DateTime.now())) {
- dayEvents.add(TimeSlot(isoTime: "Waiting Appointment", start: DateTime.now(), end: DateTime.now(), vidaDate: ""));
+ if (bookAppointmentsViewModel.isWaitingAppointmentAvailable && DateUtils.isSameDay(dateStart, DateTime.now())) {
+ dayEvents.add(TimeSlot(isoTime: "Waiting Appointment".needTranslation, start: DateTime.now(), end: DateTime.now(), vidaDate: ""));
}
freeSlots.forEach((v) {
if (v.start == dateStartObj) dayEvents.add(v);
@@ -321,9 +333,25 @@ class TimeSlotChip extends StatelessWidget {
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
- child: Container(
- padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h),
- decoration: ShapeDecoration(
+ child: label == "Waiting Appointment".needTranslation
+ ? Container(
+ padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h),
+ decoration: ShapeDecoration(
+ color: isSelected ? AppColors.warningColorYellow : AppColors.whiteColor,
+ shape: SmoothRectangleBorder(
+ borderRadius: BorderRadius.circular(8.h),
+ smoothness: 1,
+ side: BorderSide(color: isSelected ? AppColors.warningColorYellow : AppColors.borderOnlyColor.withOpacity(0.2), width: 1),
+ ),
+ ),
+ child: label.toText12(
+ color: isSelected ? AppColors.whiteColor : Colors.black87,
+ fontWeight: FontWeight.w500,
+ ),
+ )
+ : Container(
+ padding: EdgeInsets.symmetric(horizontal: 14.h, vertical: 8.h),
+ decoration: ShapeDecoration(
color: AppColors.whiteColor,
shape: SmoothRectangleBorder(
borderRadius: BorderRadius.circular(8.h),
diff --git a/lib/presentation/book_appointment/widgets/doctor_card.dart b/lib/presentation/book_appointment/widgets/doctor_card.dart
index 4cd147a..7257dbf 100644
--- a/lib/presentation/book_appointment/widgets/doctor_card.dart
+++ b/lib/presentation/book_appointment/widgets/doctor_card.dart
@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
@@ -49,7 +50,7 @@ class DoctorCard extends StatelessWidget {
: doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
width: 63.h,
height: 63.h,
- fit: BoxFit.fill,
+ fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: isLoading),
SizedBox(width: 8.h),
Expanded(
@@ -114,6 +115,13 @@ class DoctorCard extends StatelessWidget {
iconColor: AppColors.ratingColorYellow,
labelText: "Rating: ${isLoading ? 4.78 : doctorsListResponseModel.decimalDoctorRate}".needTranslation,
).toShimmer2(isShow: isLoading),
+ doctorsListResponseModel.nearestFreeSlot != null
+ ? AppCustomChipWidget(
+ labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)).needTranslation,
+ backgroundColor: AppColors.successColor,
+ textColor: AppColors.whiteColor,
+ ).toShimmer2(isShow: isLoading)
+ : SizedBox.shrink(),
],
),
SizedBox(height: 12.h),
diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart
index 31b4598..3d74cfd 100644
--- a/lib/presentation/home/data/landing_page_data.dart
+++ b/lib/presentation/home/data/landing_page_data.dart
@@ -81,7 +81,7 @@ class LandingPageData {
ServiceCardData(
serviceName: "lab_results",
icon: AppAssets.home_lab_result_icon,
- title: "My Lab",
+ title: "Lab",
subtitle: "Results",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
@@ -91,7 +91,7 @@ class LandingPageData {
ServiceCardData(
serviceName: "radiology_results",
icon: AppAssets.home_lab_result_icon,
- title: "My Radiology",
+ title: "Radiology",
subtitle: "Results",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
@@ -101,8 +101,8 @@ class LandingPageData {
ServiceCardData(
serviceName: "prescriptions",
icon: AppAssets.my_prescription_icon,
- title: "My",
- subtitle: "Prescriptions",
+ title: "Prescriptions",
+ subtitle: "Details",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
@@ -112,7 +112,7 @@ class LandingPageData {
serviceName: "insurance_update",
icon: AppAssets.insurance_update_icon,
title: "Insurance",
- subtitle: "Update",
+ subtitle: "Details",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
@@ -131,7 +131,7 @@ class LandingPageData {
ServiceCardData(
serviceName: "sick_leaves",
icon: AppAssets.insurance_update_icon,
- title: "My Sick",
+ title: "Sick",
subtitle: "Leaves",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index 1007547..1b2ce9a 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -9,6 +9,7 @@ import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/int_extensions.dart';
@@ -39,11 +40,14 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/profile_settings.dart';
+import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart';
+import 'package:hmg_patient_app_new/routes/app_routes.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
+import 'package:hmg_patient_app_new/widgets/countdown_timer.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/routes/spring_page_route_builder.dart';
import 'package:provider/provider.dart';
@@ -91,13 +95,13 @@ class _LandingPageState extends State {
if (appState.isAuthenticated) {
habibWalletVM.initHabibWalletProvider();
habibWalletVM.getPatientBalanceAmount();
+ immediateLiveCareViewModel.initImmediateLiveCare();
+ immediateLiveCareViewModel.getPatientLiveCareHistory();
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false);
myAppointmentsViewModel.getPatientMyDoctors();
prescriptionsViewModel.initPrescriptionsViewModel();
insuranceViewModel.initInsuranceProvider();
- immediateLiveCareViewModel.initImmediateLiveCare();
- immediateLiveCareViewModel.getPatientLiveCareHistory();
emergencyServicesViewModel.checkPatientERAdvanceBalance();
}
});
@@ -138,10 +142,19 @@ class _LandingPageState extends State {
text: LocaleKeys.loginOrRegister.tr(context: context),
onPressed: () async {
await authVM.onLoginPressed();
+
+ // Navigator.pushReplacementNamed(
+ // // context,
+ // context,
+ // AppRoutes.zoomCallPage,
+ // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1),
+ // arguments: CallArguments("test123", "123", "Patient", "40", "0", true, 1),
+ // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)),
+ // );
},
- backgroundColor: Color(0xffFEE9EA),
- borderColor: Color(0xffFEE9EA),
- textColor: Color(0xffED1C2B),
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w500,
borderRadius: 12.r,
@@ -161,12 +174,12 @@ class _LandingPageState extends State {
);
}),
Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 18.h, width: 18.h).onPress(() {
- Navigator.of(context).push(
- CustomPageRoute(
- page: MedicalFilePage(),
- // page: LoginScreen(),
- ),
- );
+ // Navigator.of(context).push(
+ // CustomPageRoute(
+ // page: MedicalFilePage(),
+ // // page: LoginScreen(),
+ // ),
+ // );
}),
Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 18.h, width: 18.h).onPress(() {
showCommonBottomSheetWithoutHeight(
@@ -201,8 +214,8 @@ class _LandingPageState extends State {
Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
}),
SizedBox(height: 16.h),
- Consumer(
- builder: (context, myAppointmentsVM, child) {
+ Consumer2(
+ builder: (context, myAppointmentsVM, immediateLiveCareVM, child) {
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@@ -246,27 +259,114 @@ class _LandingPageState extends State {
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
- itemHeight: 200.h,
- pagination: const SwiperPagination(
+ itemHeight: 255.h,
+ pagination: SwiperPagination(
alignment: Alignment.bottomCenter,
- margin: EdgeInsets.only(top: 210 + 8 + 24),
+ margin: EdgeInsets.only(top: 240.h + 8 + 24),
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
),
itemBuilder: (BuildContext context, int index) {
- return Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.r,
- hasShadow: true,
- ),
- child: AppointmentCard(
- patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
- myAppointmentsViewModel: myAppointmentsViewModel,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- isLoading: false,
- isFromHomePage: true,
- ),
- );
+ return (immediateLiveCareVM.patientHasPendingLiveCareRequest && index == 0)
+ ? Column(
+ children: [
+ SizedBox(height: 12.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.r,
+ hasShadow: true,
+ side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Immediate LiveCare Request".needTranslation.toText16(isBold: true),
+ SizedBox(height: 10.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Row(
+ children: [
+ AppCustomChipWidget(
+ labelText: immediateLiveCareVM.patientLiveCareHistoryList[0].stringCallStatus,
+ backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
+ textColor: AppColors.alertColor,
+ ),
+ SizedBox(width: 8.w),
+ AppCustomChipWidget(
+ icon: AppAssets.appointment_calendar_icon,
+ labelText: DateUtil.formatDateToDate(
+ DateUtil.convertStringToDate(immediateLiveCareVM.patientLiveCareHistoryList[0].arrivalTime), false)),
+ ],
+ ),
+ Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
+ // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.cover),
+ ],
+ ),
+ SizedBox(height: 10.h),
+ "Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
+ SizedBox(height: 8.h),
+ "Your turn is after ${immediateLiveCareVM.patientLiveCareHistoryList[0].patCount} patients.".toText14(isBold: true),
+ SizedBox(height: 8.h),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ "Expected waiting time: ".toText12(isBold: true),
+ SizedBox(height: 8.h),
+ ValueListenableBuilder(
+ valueListenable: immediateLiveCareVM.durationNotifier,
+ builder: (context, duration, child) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ buildTime(duration),
+ ],
+ );
+ },
+ ),
+ SizedBox(height: 12.h),
+ ],
+ ),
+ // CustomButton(
+ // text: "View Details".needTranslation,
+ // onPressed: () async {
+ // Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
+ // },
+ // backgroundColor: Color(0xffFEE9EA),
+ // borderColor: Color(0xffFEE9EA),
+ // textColor: Color(0xffED1C2B),
+ // fontSize: 14.f,
+ // fontWeight: FontWeight.w500,
+ // borderRadius: 12.r,
+ // padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
+ // height: 40.h,
+ // ),
+ ],
+ ),
+ ),
+ ).paddingSymmetrical(0.h, 0.h).onPress(() {
+ Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
+ }),
+ SizedBox(height: 12.h),
+ ],
+ )
+ : Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel:
+ myAppointmentsVM.patientAppointmentsHistoryList[immediateLiveCareViewModel.patientHasPendingLiveCareRequest ? --index : index],
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ );
},
)
: Container(
@@ -305,65 +405,6 @@ class _LandingPageState extends State {
},
),
- // Consumer for LiveCare pending request
- Consumer(
- builder: (context, immediateLiveCareVM, child) {
- return immediateLiveCareVM.patientHasPendingLiveCareRequest
- ? Column(
- children: [
- SizedBox(height: 12.h),
- Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 20.r,
- hasShadow: true,
- side: BorderSide(color: AppColors.ratingColorYellow, width: 3.h),
- ),
- width: double.infinity,
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- AppCustomChipWidget(
- labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus,
- backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
- textColor: AppColors.alertColor,
- ),
- Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
- // Lottie.asset(AppAnimations.pending_loading_animation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 40.h, height: 40.h, fit: BoxFit.contain),
- ],
- ),
- SizedBox(height: 8.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- "You have a pending LiveCare request".needTranslation.toText12(isBold: true),
- Utils.buildSvgWithAssets(
- icon: AppAssets.forward_arrow_icon_small,
- iconColor: AppColors.blackColor,
- width: 20.h,
- height: 15.h,
- fit: BoxFit.contain,
- ),
- ],
- ),
- ],
- ),
- ),
- ).paddingSymmetrical(24.h, 0.h).onPress(() {
- Navigator.of(context).push(CustomPageRoute(page: ImmediateLiveCarePendingRequestPage()));
- }),
- SizedBox(height: 12.h),
- ],
- )
- : SizedBox(height: 12.h);
- },
- ),
-
// Consumer for ER Online Check-In pending request
Consumer(
builder: (context, emergencyServicesVM, child) {
@@ -531,7 +572,7 @@ class _LandingPageState extends State {
Navigator.of(context).push(CustomPageRoute(page: ServicesPage()));
}),
],
- ).paddingSymmetrical(24.h, 0.h),
+ ).paddingSymmetrical(24.w, 0.h),
SizedBox(
height: 340.h,
child: ListView.separated(
@@ -578,10 +619,13 @@ class _LandingPageState extends State {
isDone: isDone,
onPressed: () {
// sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true);
- authVM.loginWithFingerPrintFace(() {
+ authVM.loginWithFingerPrintFace(() async {
isDone = true;
cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true);
setState(() {});
+ await Future.delayed(Duration(milliseconds: 2000)).then((value) {
+ if (mounted) Navigator.pop(context);
+ });
});
},
);
diff --git a/lib/presentation/insurance/insurance_approval_details_page.dart b/lib/presentation/insurance/insurance_approval_details_page.dart
index 437c870..150b15c 100644
--- a/lib/presentation/insurance/insurance_approval_details_page.dart
+++ b/lib/presentation/insurance/insurance_approval_details_page.dart
@@ -118,7 +118,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
),
child: Padding(
padding: EdgeInsets.all(16.h),
- child: Column(
+ child: insuranceApprovalResponseModel.apporvalDetails != null ? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"Approval Details".toText16(isBold: true),
@@ -128,7 +128,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
children: [
LocaleKeys.procedureName.tr(context: context).toText14(isBold: true),
Expanded(
- child: insuranceApprovalResponseModel.apporvalDetails!.procedureName!
+ child: (insuranceApprovalResponseModel.apporvalDetails!.procedureName ?? "")
.toText12(fontWeight: FontWeight.w500, color: AppColors.greyTextColor, textAlignment: TextAlign.end, maxLine: 2),
),
],
@@ -150,7 +150,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
],
),
],
- ),
+ ) : SizedBox.shrink(),
),
)
],
diff --git a/lib/presentation/insurance/widgets/insurance_history.dart b/lib/presentation/insurance/widgets/insurance_history.dart
index a5114dd..3e6ee86 100644
--- a/lib/presentation/insurance/widgets/insurance_history.dart
+++ b/lib/presentation/insurance/widgets/insurance_history.dart
@@ -68,7 +68,7 @@ class InsuranceHistory extends StatelessWidget {
Row(
children: [
CustomButton(
- text: insuranceVM.patientInsuranceCardHistoryList[index].statusDescription!,
+ text: insuranceVM.patientInsuranceCardHistoryList[index].statusDescription ?? "",
onPressed: () {},
backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
borderColor: AppColors.primaryRedColor.withOpacity(0.0),
diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart
index 4ffd979..81c507f 100644
--- a/lib/presentation/lab/lab_orders_page.dart
+++ b/lib/presentation/lab/lab_orders_page.dart
@@ -1 +1 @@
-import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.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/transitions/fade_page.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import '../../widgets/appbar/collapsing_list_view.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
@override
void initState() {
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: SingleChildScrollView(
padding: EdgeInsets.all(24.h),
physics: NeverScrollableScrollPhysics(),
child: Consumer(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
activeIndex == 0
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading
? 5
: model.patientLabOrders.isNotEmpty
? model.patientLabOrders.length
: 1,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)
: model.patientLabOrders.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabResultItemView(
onTap: () {
model.currentlySelectedPatientOrder =
model.patientLabOrders[
index];
labProvider.getPatientLabResultByHospital(model.patientLabOrders[
index]);
labProvider
.getPatientSpecialResult(
model.patientLabOrders[
index]);
Navigator.push(
context,
CustomPageRoute(
page: LabResultByClinic(labOrder: model.patientLabOrders[index]),
));
},
labOrder: model.patientLabOrders[index],
index: index,
isExpanded: isExpanded),
),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation);
},
)
: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading
? 5
: model.uniqueTests.toList().isNotEmpty
? model.uniqueTests.toList().length
: 1,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)
: model.uniqueTests.toList().isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: _appState,
onTap: () {
if (model.uniqueTests.toList()[index].model != null) {
rangeViewModel.flush();
model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!,
(_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!));
}
},
tests: model.uniqueTests.toList()[index],
index: index,
isExpanded: isExpanded)),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation);
},
)
],
);
},
),
),
));
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
+import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_order_by_test.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/chip/custom_chip_widget.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/transitions/fade_page.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import '../../widgets/appbar/collapsing_list_view.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
@override
void initState() {
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.labResults.tr(),
search: () async {
final lavVM = Provider.of(context, listen: false);
if (lavVM.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: lavVM.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
lavVM.filterLabReports(value);
}
}
},
child: SingleChildScrollView(
padding: EdgeInsets.all(24.h),
physics: NeverScrollableScrollPhysics(),
child: Consumer(
builder: (context, model, child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "By Visit".needTranslation),
CustomTabBarModel(null, "By Test".needTranslation),
// CustomTabBarModel(null, "Completed".needTranslation),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? CustomChipWidget(
chipText: selectedFilterText!,
chipType: ChipTypeEnum.alert,
isSelected: true,
)
: SizedBox(),
activeIndex == 0
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading
? 5
: model.patientLabOrders.isNotEmpty
? model.patientLabOrders.length
: 1,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)
: model.patientLabOrders.isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabResultItemView(
onTap: () {
model.currentlySelectedPatientOrder =
model.patientLabOrders[
index];
labProvider.getPatientLabResultByHospital(model.patientLabOrders[
index]);
labProvider
.getPatientSpecialResult(
model.patientLabOrders[
index]);
Navigator.push(
context,
CustomPageRoute(
page: LabResultByClinic(labOrder: model.patientLabOrders[index]),
));
},
labOrder: model.patientLabOrders[index],
index: index,
isExpanded: isExpanded),
),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation);
},
)
: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: model.isLabOrdersLoading
? 5
: model.uniqueTests.toList().isNotEmpty
? model.uniqueTests.toList().length
: 1,
itemBuilder: (context, index) {
final isExpanded = expandedIndex == index;
return model.isLabOrdersLoading
? LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)
: model.uniqueTests.toList().isNotEmpty
? AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: _appState,
onTap: () {
if (model.uniqueTests.toList()[index].model != null) {
rangeViewModel.flush();
model.getPatientLabResult(model.uniqueTests.toList()[index].model!, model.uniqueTests.toList()[index].description!,
(_appState.isArabic() ? model.uniqueTests.toList()[index].testDescriptionAr! : model.uniqueTests.toList()[index].testDescriptionEn!), "");
}
},
tests: model.uniqueTests.toList()[index],
index: index,
isExpanded: isExpanded)),
),
)
: Utils.getNoDataWidget(context, noDataText: "You don't have any lab results yet.".needTranslation);
},
)
],
);
},
),
),
));
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
diff --git a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart
index 8893ffd..3f05443 100644
--- a/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart
+++ b/lib/presentation/lab/lab_result_via_clinic/LabResultList.dart
@@ -26,7 +26,7 @@ class LabResultList extends StatelessWidget {
shrinkWrap: true,itemCount: list.length,itemBuilder: (____, index) {
var labItem = list[index];
return LabOrderResultItem(onTap: () {
- model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.packageShortDescription!);
+ model.getPatientLabResult(model.currentlySelectedPatientOrder!, labItem.description ?? "", labItem.packageShortDescription!, labItem.uOM ?? "");
},
tests: labItem,
index: index,
diff --git a/lib/presentation/lab/lab_results/lab_result_details.dart b/lib/presentation/lab/lab_results/lab_result_details.dart
index 2330d0d..67eae63 100644
--- a/lib/presentation/lab/lab_results/lab_result_details.dart
+++ b/lib/presentation/lab/lab_results/lab_result_details.dart
@@ -59,115 +59,72 @@ class LabResultDetails extends StatelessWidget {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Text(
- recentLabResult.testCode ?? "",
- style: TextStyle(
- fontSize: 32,
- fontFamily: 'Poppins',
- fontWeight: FontWeight.w600,
- color: AppColors.textColor,
- letterSpacing: -2),
- ),
- Text(
- "Result of ${recentLabResult.verifiedOn ?? ""}".needTranslation,
- style: TextStyle(
- fontSize: 12,
- fontFamily: 'Poppins',
- fontWeight: FontWeight.w500,
- color: AppColors.greyTextColor,
- ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ (recentLabResult.testCode ?? "").toText28(isBold: true, color: AppColors.textColor, letterSpacing: -2),
+ Visibility(
+ visible: recentLabResult.checkIfGraphShouldBeDisplayed() == true,
+ child: Column(
+ spacing: 6.h,
+ children: [
+ context
+ .read()
+ .getSeverityText(
+ recentLabResult.calculatedResultFlag ?? "",
+ )
+ .tr()
+ .toText10(weight: FontWeight.w500, color: AppColors.greyTextColor),
+ Utils.buildSvgWithAssets(
+ icon: AppAssets.lab_result_indicator,
+ width: 21,
+ height: 23,
+ iconColor: context.read().getColor(
+ recentLabResult.calculatedResultFlag ?? "",
+ )),
+ ],
+ ),
+ ),
+ ],
),
+ SizedBox(height: 4.h),
+ ("Result of ${recentLabResult.verifiedOn ?? ""}".needTranslation).toText11(weight: FontWeight.w500, color: AppColors.greyTextColor),
],
),
- //todo change the text color according to the provided test values
Row(
crossAxisAlignment: CrossAxisAlignment.center,
-
children: [
Expanded(
child: Row(
-
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
- child: Text(
- "${recentLabResult.resultValue} ${recentLabResult.uOM}" ??
- "",
- style: TextStyle(
- fontSize: 24.f,
- fontWeight: FontWeight.w600,
- fontFamily: 'Poppins',
+ child: ("${recentLabResult.resultValue} ${recentLabResult.uOM}" ?? "").toText28(
+ isBold: true,
color: context.read().getColor(
recentLabResult.calculatedResultFlag ?? "",
- ),
- letterSpacing: -2,
- ),
- overflow: TextOverflow.ellipsis, // prevent overflow
- maxLines: 1,
- softWrap: false,
- ),
+ ),
+ letterSpacing: -2),
),
SizedBox(width: 4.h,),
- Expanded(
- flex: 2,
- child: Visibility(
- visible: recentLabResult.referanceRange != null,
- child: Text(
- "(Reference range ${recentLabResult.referanceRange})".needTranslation,
- style: TextStyle(
- fontSize: 12.f,
- fontWeight: FontWeight.w500,
- fontFamily: 'Poppins',
- color: AppColors.greyTextColor,
- ),
- // overflow: TextOverflow.ellipsis,
- // maxLines: 2,
- softWrap: true,
- ),
- ),
- ),
- ],
- ),
- ),
- Visibility(
- visible:
- recentLabResult.checkIfGraphShouldBeDisplayed() == true,
- child: Column(
- spacing: 6.h,
- children: [
- context
- .read()
- .getSeverityText(
- recentLabResult.calculatedResultFlag ?? "",
- )
- .tr()
- .toText10(
- weight: FontWeight.w500,
- color: AppColors.greyTextColor),
- Utils.buildSvgWithAssets(
- icon: AppAssets.lab_result_indicator,
- width: 21,
- height: 23,
- iconColor: context.read().getColor(
- recentLabResult.calculatedResultFlag ?? "",
- )),
],
),
),
- // SizedBox(
- // width: 21,
- // child: Utils.buildSvgWithAssets(
- // icon: AppAssets.lab_result_indicator,
- // width: 21,
- // height: 23,
- // iconColor: context.read().getColor(
- // recentLabResult.calculatedResultFlag ?? "",
- // ),
- // ),
- // ),
],
- )
-
+ ),
+ Visibility(
+ visible: recentLabResult.referanceRange != null,
+ child: Text(
+ "Reference range: \n${recentLabResult.referanceRange!.trim()}".needTranslation,
+ style: TextStyle(
+ fontSize: 12.f,
+ fontWeight: FontWeight.w500,
+ fontFamily: 'Poppins',
+ color: AppColors.greyTextColor,
+ ),
+ softWrap: true,
+ ),
+ ),
],
));
diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart
index 883ca37..7b5341c 100644
--- a/lib/presentation/medical_file/medical_file_page.dart
+++ b/lib/presentation/medical_file/medical_file_page.dart
@@ -26,6 +26,7 @@ import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_mo
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_doctors_page.dart';
+import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/doctor_profile_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart';
@@ -84,7 +85,6 @@ class _MedicalFilePageState extends State {
appState = getIt.get();
scheduleMicrotask(() {
if (appState.isAuthenticated) {
- labViewModel.initLabProvider();
insuranceViewModel.initInsuranceProvider();
medicalFileViewModel.setIsPatientSickLeaveListLoading(true);
medicalFileViewModel.getPatientSickLeaveList();
@@ -225,9 +225,9 @@ class _MedicalFilePageState extends State {
),
AppCustomChipWidget(
icon: AppAssets.blood_icon,
- labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}",
+ labelText: "Blood: ${appState.getUserBloodGroup.isEmpty ? "N/A" : appState.getUserBloodGroup.isEmpty}".needTranslation,
iconColor: AppColors.primaryRedColor,
- labelPadding: EdgeInsetsDirectional.only(end: 8.w),
+ labelPadding: EdgeInsetsDirectional.only(end: 4.w, start: 0.w),
),
Consumer(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
@@ -390,6 +390,7 @@ class _MedicalFilePageState extends State {
: ListView.separated(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
+ padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemCount: myAppointmentsVM.patientAppointmentsHistoryList.length,
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
@@ -407,15 +408,42 @@ class _MedicalFilePageState extends State {
onRescheduleTap: () {
openDoctorScheduleCalendar(myAppointmentsVM.patientAppointmentsHistoryList[index]);
},
- onAskDoctorTap: () {},
+ onAskDoctorTap: () async {
+ LoaderBottomSheet.showLoader(loadingText: "Checking doctor availability...".needTranslation);
+ await myAppointmentsViewModel.isDoctorAvailable(
+ projectID: myAppointmentsVM.patientAppointmentsHistoryList[index].projectID,
+ doctorId: myAppointmentsVM.patientAppointmentsHistoryList[index].doctorID,
+ clinicId: myAppointmentsVM.patientAppointmentsHistoryList[index].clinicID,
+ onSuccess: (value) async {
+ if (value) {
+ await myAppointmentsViewModel.getAskDoctorRequestTypes(onSuccess: (val) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ title: LocaleKeys.askDoctor.tr(context: context),
+ child: AskDoctorRequestTypeSelect(
+ askDoctorRequestTypeList: myAppointmentsViewModel.askDoctorRequestTypeList,
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList[index],
+ ),
+ callBackFunc: () {},
+ isFullScreen: false,
+ isCloseButtonVisible: true,
+ );
+ });
+ } else {
+ print("Doctor is not available");
+ }
+ });
+ },
)),
),
),
);
},
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 12.h),
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w),
),
- ).paddingSymmetrical(24.w, 0);
+ );
}),
SizedBox(height: 24.h),
"Lab & Radiology".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
@@ -430,6 +458,7 @@ class _MedicalFilePageState extends State {
labOrderTests: labViewModel.isLabOrdersLoading ? [] : labViewModel.labOrderTests,
isLoading: labViewModel.isLabOrdersLoading,
).onPress(() {
+ labViewModel.initLabProvider();
Navigator.of(context).push(
CustomPageRoute(
page: LabOrdersPage(),
@@ -544,9 +573,9 @@ class _MedicalFilePageState extends State {
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
),
- SizedBox(height: 24.h),
+ SizedBox(height: 8.h),
const Divider(color: AppColors.dividerColor),
- SizedBox(height: 24.h),
+ SizedBox(height: 8.h),
Row(
children: [
Expanded(
@@ -667,11 +696,12 @@ class _MedicalFilePageState extends State {
),
).paddingSymmetrical(24.w, 0.h)
: SizedBox(
- height: 100.h,
+ height: 110.h,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: myAppointmentsVM.patientMyDoctorsList.length,
shrinkWrap: true,
+ padding: EdgeInsets.only(left: 24.w, right: 24.w),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
@@ -691,7 +721,8 @@ class _MedicalFilePageState extends State {
fit: BoxFit.cover,
).circle(100).toShimmer2(isShow: false, radius: 50.r),
SizedBox(height: 8.h),
- Expanded(
+ SizedBox(
+ width: 80.w,
child: (myAppointmentsVM.patientMyDoctorsList[index].doctorName)
.toString()
.toText12(fontWeight: FontWeight.w500, isCenter: true, maxLine: 2)
@@ -730,7 +761,7 @@ class _MedicalFilePageState extends State {
},
separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 8.h),
),
- ).paddingSymmetrical(24.w, 0);
+ );
}),
SizedBox(height: 24.h),
"Others".needTranslation.toText18(isBold: true).paddingSymmetrical(24.w, 0.h),
diff --git a/lib/presentation/medical_file/widgets/lab_rad_card.dart b/lib/presentation/medical_file/widgets/lab_rad_card.dart
index 1a56baf..696b332 100644
--- a/lib/presentation/medical_file/widgets/lab_rad_card.dart
+++ b/lib/presentation/medical_file/widgets/lab_rad_card.dart
@@ -43,29 +43,29 @@ class LabRadCard extends StatelessWidget {
Flexible(child: labelText.toText14(isBold: true).toShimmer2(isShow: false, radius: 6.r, height: 32.h)),
],
),
- SizedBox(height: 16.h),
- labOrderTests.isNotEmpty
- ? ListView.separated(
- scrollDirection: Axis.vertical,
- padding: EdgeInsets.zero,
- physics: NeverScrollableScrollPhysics(),
- shrinkWrap: true,
- itemBuilder: (cxt, index) {
- return labOrderTests[index]
- .toText12(isBold: true, maxLine: 1)
- .toShimmer2(isShow: false, radius: 6.r, height: 24.h, width: 120.w)
- .toShimmer2(isShow: isLoading);
- },
- separatorBuilder: (cxt, index) => SizedBox(height: 8.h),
- itemCount: 3,
- )
- : "You don't have any records yet".needTranslation.toText13(
- color: AppColors.greyTextColor, isCenter: true),
+ // SizedBox(height: 16.h),
+ // labOrderTests.isNotEmpty
+ // ? ListView.separated(
+ // scrollDirection: Axis.vertical,
+ // padding: EdgeInsets.zero,
+ // physics: NeverScrollableScrollPhysics(),
+ // shrinkWrap: true,
+ // itemBuilder: (cxt, index) {
+ // return labOrderTests[index]
+ // .toText12(isBold: true, maxLine: 1)
+ // .toShimmer2(isShow: false, radius: 6.r, height: 24.h, width: 120.w)
+ // .toShimmer2(isShow: isLoading);
+ // },
+ // separatorBuilder: (cxt, index) => SizedBox(height: 8.h),
+ // itemCount: 3,
+ // )
+ // : "You don't have any records yet".needTranslation.toText13(
+ // color: AppColors.greyTextColor, isCenter: true),
SizedBox(height: 16.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- SizedBox.shrink(),
+ "View All".toText12(isBold: true),
Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
diff --git a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart
index 3924761..1b09dd5 100644
--- a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart
+++ b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart
@@ -53,7 +53,7 @@ class MedicalFileAppointmentCard extends StatelessWidget {
).toShimmer2(isShow: myAppointmentsViewModel.isMyAppointmentsLoading),
SizedBox(height: 16.h),
Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: false),
width: 200.w,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -167,7 +167,9 @@ class MedicalFileAppointmentCard extends StatelessWidget {
return DateTime.now().difference(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate)).inDays <= 15
? CustomButton(
text: LocaleKeys.askDoctor.tr(context: context),
- onPressed: () {},
+ onPressed: () {
+ onAskDoctorTap();
+ },
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
diff --git a/lib/presentation/medical_file/widgets/medical_file_card.dart b/lib/presentation/medical_file/widgets/medical_file_card.dart
index 8c38363..6343e2a 100644
--- a/lib/presentation/medical_file/widgets/medical_file_card.dart
+++ b/lib/presentation/medical_file/widgets/medical_file_card.dart
@@ -29,7 +29,7 @@ class MedicalFileCard extends StatelessWidget {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: backgroundColor,
borderRadius: 12.r,
- hasShadow: true
+ hasShadow: false
),
padding: EdgeInsets.all(12.w),
child: Column(
diff --git a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart
new file mode 100644
index 0000000..0bedbe2
--- /dev/null
+++ b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart
@@ -0,0 +1,174 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_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/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/buttons/custom_button.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:provider/provider.dart';
+
+class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget {
+ PrescriptionDeliveryOrderSummaryPage({super.key});
+
+ late PrescriptionsViewModel prescriptionsViewModel;
+
+ @override
+ Widget build(BuildContext context) {
+ prescriptionsViewModel = Provider.of(context, listen: false);
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: Column(
+ children: [
+ Expanded(
+ child: CollapsingListView(
+ title: LocaleKeys.orderSummary.tr(context: context),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.r,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.orderDetail.tr(context: context).toText16(isBold: true),
+ SizedBox(height: 16.h),
+ ...List.generate(
+ prescriptionsViewModel.prescriptionDetailsList.length,
+ (index) => Container(
+ margin: EdgeInsets.all(0.0),
+ child: Row(
+ children: [
+ ClipRRect(
+ borderRadius: BorderRadius.all(
+ Radius.circular(5.r),
+ ),
+ child: Image.network(
+ prescriptionsViewModel.prescriptionDetailsList[index].imageSRCUrl!,
+ fit: BoxFit.cover,
+ width: 60.w,
+ height: 70.h,
+ ),
+ ),
+ Expanded(
+ child: Padding(
+ padding: EdgeInsets.all(8.h),
+ child: Center(
+ child: prescriptionsViewModel.prescriptionDetailsList[index].itemDescription!.trim().toText12(),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 20.r,
+ hasShadow: true,
+ ),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.deliveryLocation.tr(context: context).toText16(isBold: true),
+ SizedBox(height: 16.h),
+ ClipRRect(
+ clipBehavior: Clip.hardEdge,
+ borderRadius: BorderRadius.circular(20.r),
+ child: Image.network(
+ "https://maps.googleapis.com/maps/api/staticmap?center=${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat},${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng}&zoom=15&size=350x165&maptype=roadmap&markers=color:red%7C${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat},${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng",
+ fit: BoxFit.contain,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ).paddingSymmetrical(24.w, 0),
+ ),
+ ),
+ Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.h,
+ hasShadow: true,
+ ),
+ child: CustomButton(
+ text: LocaleKeys.submit.tr(context: context),
+ onPressed: () async {
+ LoaderBottomSheet.showLoader(loadingText: "Submitting your request...");
+ await prescriptionsViewModel.submitPrescriptionDeliveryRequest(
+ latitude: prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat.toString(),
+ longitude: prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng.toString(),
+ appointmentNo: prescriptionsViewModel.prescriptionDetailsList.first.appointmentNo.toString(),
+ dischargeID: "0",
+ projectID: prescriptionsViewModel.prescriptionDetailsList.first.projectID.toString(),
+ onSuccess: (val) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getSuccessWidget(loadingText: "Request sent successfully.".needTranslation),
+ callBackFunc: () {
+ Navigator.of(context).pop();
+ },
+ title: "",
+ isCloseButtonVisible: true,
+ isDismissible: false,
+ isFullScreen: false,
+ );
+ },
+ onError: (err) {
+ LoaderBottomSheet.hideLoader();
+ showCommonBottomSheetWithoutHeight(
+ context,
+ child: Utils.getErrorWidget(loadingText: err),
+ callBackFunc: () {
+ Navigator.of(context).pop();
+ },
+ title: "",
+ isCloseButtonVisible: true,
+ isDismissible: false,
+ isFullScreen: false,
+ );
+ });
+ },
+ backgroundColor: AppColors.successColor,
+ borderColor: AppColors.successColor.withOpacity(0.01),
+ textColor: AppColors.whiteColor,
+ fontSize: 16.f,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12.r,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 50.h,
+ icon: AppAssets.prescription_refill_icon,
+ iconColor: AppColors.whiteColor,
+ iconSize: 20.h,
+ ).paddingSymmetrical(24.h, 24.h),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart b/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart
new file mode 100644
index 0000000..9f2b2a2
--- /dev/null
+++ b/lib/presentation/prescriptions/prescription_delivery_orders_list_page.dart
@@ -0,0 +1,111 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
+import 'package:hmg_patient_app_new/core/app_assets.dart';
+import 'package:hmg_patient_app_new/core/utils/date_util.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
+import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
+import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
+import 'package:hmg_patient_app_new/presentation/emergency_services/history/widget/RequestStatus.dart';
+import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
+import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
+import 'package:provider/provider.dart';
+
+class PrescriptionDeliveryOrdersListPage extends StatelessWidget {
+ const PrescriptionDeliveryOrdersListPage({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: AppColors.bgScaffoldColor,
+ body: CollapsingListView(
+ title: LocaleKeys.orders.tr(context: context),
+ child: SingleChildScrollView(
+ child: Consumer(builder: (context, model, child) {
+ return Column(
+ children: [
+ ListView.builder(
+ itemCount: model.isPrescriptionsDeliveryOrdersLoading
+ ? 4
+ : model.prescriptionsOrderList.isNotEmpty
+ ? model.prescriptionsOrderList.length
+ : 1,
+ physics: NeverScrollableScrollPhysics(),
+ shrinkWrap: true,
+ padding: const EdgeInsets.only(left: 0, right: 8),
+ itemBuilder: (context, index) {
+ return model.isPrescriptionsDeliveryOrdersLoading
+ ? LabResultItemView(
+ onTap: () {},
+ labOrder: null,
+ index: index,
+ isLoading: true,
+ )
+ : model.prescriptionsOrderList.isNotEmpty
+ ? AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 500),
+ child: SlideAnimation(
+ verticalOffset: 100.0,
+ child: FadeInAnimation(
+ child: AnimatedContainer(
+ duration: Duration(milliseconds: 300),
+ curve: Curves.easeInOut,
+ margin: EdgeInsets.symmetric(vertical: 8.h),
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
+ child: InkWell(
+ onTap: () {
+ },
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ spacing: 8.h,
+ children: [
+ RequestStatus(status: model.prescriptionsOrderList[index].statusId ?? 0),
+ "Req ID: ${model.prescriptionsOrderList[index].iD}".toText16(color: AppColors.textColor, weight: FontWeight.w600),
+ Row(
+ spacing: 4.w,
+ children: [
+ chip(Utils.getDayMonthYearDateFormatted(DateTime.tryParse(model.prescriptionsOrderList[index].created!)), AppAssets.calendar,
+ AppColors.blackBgColor),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ : Utils.getNoDataWidget(context, noDataText: "You don't have any prescription orders yet.".needTranslation);
+ },
+ ).paddingSymmetrical(24.h, 0.h),
+ ],
+ );
+ }),
+ ),
+ ),
+ );
+ }
+
+ chip(String title, String iconString, Color iconColor) {
+ return AppCustomChipWidget(
+ labelText: title,
+ icon: iconString,
+ iconColor: iconColor,
+ iconSize: 12.h,
+ );
+ }
+}
diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart
index 58c5206..3d631ce 100644
--- a/lib/presentation/prescriptions/prescriptions_list_page.dart
+++ b/lib/presentation/prescriptions/prescriptions_list_page.dart
@@ -6,6 +6,7 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
+import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@@ -14,10 +15,13 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
+import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_delivery_orders_list_page.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
+import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
+import 'package:hmg_patient_app_new/widgets/map/map_utility_screen.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:hmg_patient_app_new/widgets/shimmer/common_shimmer_widget.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
@@ -51,6 +55,14 @@ class _PrescriptionsListPageState extends State {
backgroundColor: AppColors.bgScaffoldColor,
body: CollapsingListView(
title: LocaleKeys.prescriptions.tr(context: context),
+ requests: () {
+ prescriptionsViewModel.getPrescriptionOrdersList();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: PrescriptionDeliveryOrdersListPage(),
+ ),
+ );
+ },
child: SingleChildScrollView(
child: Consumer(builder: (context, model, child) {
return Column(
@@ -237,16 +249,26 @@ class _PrescriptionsListPageState extends State {
text: prescription.isHomeMedicineDeliverySupported!
? LocaleKeys.resendOrder.tr(context: context)
: LocaleKeys.prescriptionDeliveryError.tr(context: context),
- onPressed: () {},
- backgroundColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color,
- borderColor: AppColors.successColor.withOpacity(0.01),
- textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
- fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12,
- fontWeight: FontWeight.w500,
- borderRadius: 12,
- padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
- height: 40.h,
- icon: AppAssets.prescription_refill_icon,
+ onPressed: () async {
+ if (prescription.isHomeMedicineDeliverySupported!) {
+ LoaderBottomSheet.showLoader(loadingText: "Fetching prescription details...".needTranslation);
+ await prescriptionsViewModel.getPrescriptionDetails(prescriptionsViewModel.patientPrescriptionOrders[index],
+ onSuccess: (val) {
+ LoaderBottomSheet.hideLoader();
+ prescriptionsViewModel.initiatePrescriptionDelivery();
+ });
+ }
+ },
+ backgroundColor:
+ prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor.withOpacity(0.15) : AppColors.greyF7Color,
+ borderColor: AppColors.successColor.withOpacity(0.01),
+ textColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
+ fontSize: prescription.isHomeMedicineDeliverySupported! ? 14 : 12,
+ fontWeight: FontWeight.w500,
+ borderRadius: 12,
+ padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
+ height: 40.h,
+ icon: AppAssets.prescription_refill_icon,
iconColor: prescription.isHomeMedicineDeliverySupported! ? AppColors.successColor : AppColors.textColor.withOpacity(0.35),
iconSize: 14.h,
),
diff --git a/lib/presentation/tele_consultation/zoom/call_screen.dart b/lib/presentation/tele_consultation/zoom/call_screen.dart
new file mode 100644
index 0000000..21ad42f
--- /dev/null
+++ b/lib/presentation/tele_consultation/zoom/call_screen.dart
@@ -0,0 +1,1840 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_ios_voip_kit_karmm/flutter_ios_voip_kit.dart';
+import 'package:flutter_zoom_videosdk/flutter_zoom_view.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk_camera_device.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk_cmd_channel.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk_event_listener.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk_live_transcription_message_info.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk_share_action.dart';
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk_user.dart';
+import 'package:get_it/get_it.dart';
+import 'package:hmg_patient_app_new/core/app_export.dart';
+import 'package:hmg_patient_app_new/core/cache_consts.dart';
+import 'package:hmg_patient_app_new/core/utils/jwt.dart';
+import 'package:hmg_patient_app_new/core/utils/utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
+import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/video_view.dart';
+import 'package:hmg_patient_app_new/services/cache_service.dart';
+import 'package:hmg_patient_app_new/theme/colors.dart';
+import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
+import 'package:image_picker/image_picker.dart';
+
+class CallScreen extends StatefulHookWidget {
+ const CallScreen({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _CallScreenState();
+}
+
+class _CallScreenState extends State {
+ static TextEditingController changeNameController = TextEditingController();
+ double opacityLevel = 1.0;
+
+ final voIPKit = FlutterIOSVoIPKit.instance;
+
+ bool isScreenBeingShared = false;
+
+ void _changeOpacity() {
+ setState(() => opacityLevel = opacityLevel == 0 ? 1.0 : 0.0);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ var zoom = ZoomVideoSdk();
+ var eventListener = ZoomVideoSdkEventListener();
+ var isInSession = useState(false);
+ var sessionName = useState('');
+ var sessionPassword = useState('');
+ var users = useState([]);
+ var fullScreenUser = useState(null);
+ var sharingUser = useState(null);
+ var videoInfo = useState('');
+ var isSharing = useState(false);
+ var isMuted = useState(true);
+ var isVideoOn = useState(false);
+ var isSpeakerOn = useState(false);
+ var isRenameModalVisible = useState(false);
+ var isRecordingStarted = useState(false);
+ var isMicOriginalOn = useState(false);
+ var isMounted = useIsMounted();
+ var audioStatusFlag = useState(false);
+ var videoStatusFlag = useState(false);
+ var userNameFlag = useState(false);
+ var userShareStatusFlag = useState(false);
+ var isReceiveSpokenLanguageContentEnabled = useState(false);
+ var isVideoMirrored = useState(false);
+ var isOriginalAspectRatio = useState(false);
+ var isPiPView = useState(false);
+ var isSharedCamera = useState(false);
+ CameraShareView cameraShareView = const CameraShareView(creationParams: {});
+
+ //hide status bar
+ SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack);
+ var circleButtonSize = 65.0;
+ Color backgroundColor = const Color(0xFF232323);
+ Color buttonBackgroundColor = const Color.fromRGBO(0, 0, 0, 0.6);
+ Color chatTextColor = const Color(0xFFAAAAAA);
+ Widget changeNamePopup;
+ final args = ModalRoute.of(context)!.settings.arguments as CallArguments;
+
+ useEffect(() {
+ Future.microtask(() async {
+ var token = generateJwt(args.sessionName, args.role);
+
+ Utils.removeFromPrefs(CacheConst.isAppOpenedFromCall);
+
+ try {
+ Map SDKaudioOptions = {"connect": true, "mute": false, "autoAdjustSpeakerVolume": false};
+ Map SDKvideoOptions = {
+ "localVideoOn": true,
+ };
+ JoinSessionConfig joinSession = JoinSessionConfig(
+ sessionName: args.sessionName,
+ sessionPassword: args.sessionPwd,
+ token: token,
+ userName: args.displayName,
+ audioOptions: SDKaudioOptions,
+ videoOptions: SDKvideoOptions,
+ sessionIdleTimeoutMins: int.parse(args.sessionIdleTimeoutMins),
+ );
+ await zoom.joinSession(joinSession);
+ } catch (e) {
+ const AlertDialog(
+ title: Text("Error"),
+ content: Text("Failed to join the session"),
+ );
+ Future.delayed(const Duration(milliseconds: 1000)).asStream().listen((event) {
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ });
+ }
+ });
+ return null;
+ }, []);
+
+ useEffect(() {
+ final sessionJoinListener = eventListener.addListener(EventType.onSessionJoin, (data) async {
+ data = data as Map;
+ isInSession.value = true;
+ zoom.session.getSessionName().then((value) => sessionName.value = value!);
+ sessionPassword.value = await zoom.session.getSessionPassword();
+ debugPrint("sessionPhonePasscode: ${await zoom.session.getSessionPhonePasscode()}");
+ ZoomVideoSdkUser mySelf = ZoomVideoSdkUser.fromJson(jsonDecode(data['sessionUser']));
+ List? remoteUsers = await zoom.session.getRemoteUsers();
+ var muted = await mySelf.audioStatus?.isMuted();
+ var videoOn = await mySelf.videoStatus?.isOn();
+ var speakerOn = await zoom.audioHelper.getSpeakerStatus();
+ fullScreenUser.value = mySelf;
+ remoteUsers?.insert(0, mySelf);
+ isMuted.value = muted!;
+ isSpeakerOn.value = speakerOn;
+ isVideoOn.value = videoOn!;
+ users.value = remoteUsers!;
+ isReceiveSpokenLanguageContentEnabled.value = await zoom.liveTranscriptionHelper.isReceiveSpokenLanguageContentEnabled();
+ });
+
+ final sessionLeaveListener = eventListener.addListener(EventType.onSessionLeave, (data) async {
+ data = data as Map;
+ debugPrint("onSessionLeave: ${data['reason']}");
+ isInSession.value = false;
+ users.value = [];
+ fullScreenUser.value = null;
+ // Navigator.popAndPushNamed(
+ // context,
+ // "Join",
+ // arguments: JoinArguments(args.isJoin, sessionName.value, sessionPassword.value, args.displayName, args.sessionIdleTimeoutMins, args.role),
+ // );
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ });
+
+ final sessionNeedPasswordListener = eventListener.addListener(EventType.onSessionNeedPassword, (data) async {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text('Session Need Password'),
+ content: const Text('Password is required'),
+ actions: [
+ TextButton(
+ onPressed: () async => {
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false),
+ await zoom.leaveSession(false),
+ },
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ });
+
+ final sessionPasswordWrongListener = eventListener.addListener(EventType.onSessionPasswordWrong, (data) async {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text('Session Password Incorrect'),
+ content: const Text('Password is wrong'),
+ actions: [
+ TextButton(
+ onPressed: () async => {
+ Navigator.popAndPushNamed(context, 'Join', arguments: JoinArguments(args.isJoin, args.sessionName, "", args.displayName, args.sessionIdleTimeoutMins, args.role)),
+ await zoom.leaveSession(false),
+ },
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ });
+
+ final userVideoStatusChangedListener = eventListener.addListener(EventType.onUserVideoStatusChanged, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ var userListJson = jsonDecode(data['changedUsers']) as List;
+ List userList = userListJson.map((userJson) => ZoomVideoSdkUser.fromJson(userJson)).toList();
+ for (var user in userList) {
+ {
+ if (user.userId == mySelf?.userId) {
+ mySelf?.videoStatus?.isOn().then((on) => isVideoOn.value = on);
+ }
+ }
+ }
+ videoStatusFlag.value = !videoStatusFlag.value;
+ });
+
+ final userAudioStatusChangedListener = eventListener.addListener(EventType.onUserAudioStatusChanged, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ var userListJson = jsonDecode(data['changedUsers']) as List;
+ List userList = userListJson.map((userJson) => ZoomVideoSdkUser.fromJson(userJson)).toList();
+ for (var user in userList) {
+ {
+ if (user.userId == mySelf?.userId) {
+ mySelf?.audioStatus?.isMuted().then((muted) => isMuted.value = muted);
+ }
+ }
+ }
+ audioStatusFlag.value = !audioStatusFlag.value;
+ });
+
+ final userShareStatusChangeListener = eventListener.addListener(EventType.onUserShareStatusChanged, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ ZoomVideoSdkUser shareUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['user'].toString()));
+ ZoomVideoSdkShareAction? shareAction = ZoomVideoSdkShareAction.fromJson(jsonDecode(data['shareAction']));
+
+ if (shareAction.shareStatus == ShareStatus.Start || shareAction.shareStatus == ShareStatus.Resume) {
+ sharingUser.value = shareUser;
+ fullScreenUser.value = shareUser;
+ isSharing.value = (shareUser.userId == mySelf?.userId);
+
+ setState(() {
+ isScreenBeingShared = true;
+ });
+ Future.delayed(const Duration(seconds: 2), () {
+ setState(() {
+ isScreenBeingShared = false;
+ });
+ });
+ } else {
+ sharingUser.value = null;
+ isSharing.value = false;
+ isSharedCamera.value = false;
+ fullScreenUser.value = mySelf;
+ isScreenBeingShared = false;
+ setState(() {
+ isScreenBeingShared = true;
+ });
+ Future.delayed(const Duration(seconds: 2), () {
+ setState(() {
+ isScreenBeingShared = false;
+ });
+ });
+ }
+ userShareStatusFlag.value = !userShareStatusFlag.value;
+ });
+
+ final shareContentChangedListener = eventListener.addListener(EventType.onShareContentChanged, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ ZoomVideoSdkUser shareUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['user'].toString()));
+ ZoomVideoSdkShareAction? shareAction = ZoomVideoSdkShareAction.fromJson(jsonDecode(data['shareAction']));
+ if (shareAction.shareType == ShareType.Camera) {
+ debugPrint("Camera share started");
+ isSharedCamera.value = (shareUser.userId == mySelf?.userId);
+ }
+ });
+
+ final userJoinListener = eventListener.addListener(EventType.onUserJoin, (data) async {
+ if (!isMounted()) return;
+ data = data as Map;
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ var userListJson = jsonDecode(data['remoteUsers']) as List;
+ List remoteUserList = userListJson.map((userJson) => ZoomVideoSdkUser.fromJson(userJson)).toList();
+ remoteUserList.insert(0, mySelf!);
+ users.value = remoteUserList;
+ });
+
+ final userLeaveListener = eventListener.addListener(EventType.onUserLeave, (data) async {
+ if (!isMounted()) return;
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ data = data as Map;
+ List? remoteUserList = await zoom.session.getRemoteUsers();
+ var leftUserListJson = jsonDecode(data['leftUsers']) as List;
+ List leftUserLis = leftUserListJson.map((userJson) => ZoomVideoSdkUser.fromJson(userJson)).toList();
+ if (fullScreenUser.value != null) {
+ for (var user in leftUserLis) {
+ {
+ if (fullScreenUser.value?.userId == user.userId) {
+ fullScreenUser.value = mySelf;
+ }
+ }
+ }
+ } else {
+ fullScreenUser.value = mySelf;
+ }
+ remoteUserList?.add(mySelf!);
+ users.value = remoteUserList!;
+ });
+
+ final userNameChangedListener = eventListener.addListener(EventType.onUserNameChanged, (data) async {
+ if (!isMounted()) return;
+ data = data as Map;
+ ZoomVideoSdkUser? changedUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['changedUser']));
+ int index;
+ for (var user in users.value) {
+ if (user.userId == changedUser.userId) {
+ index = users.value.indexOf(user);
+ users.value[index] = changedUser;
+ }
+ }
+ userNameFlag.value = !userNameFlag.value;
+ });
+
+ final commandReceived = eventListener.addListener(EventType.onCommandReceived, (data) async {
+ data = data as Map;
+ debugPrint("sender: ${ZoomVideoSdkUser.fromJson(jsonDecode(data['sender']))}, command: ${data['command']}");
+ });
+
+ final liveStreamStatusChangeListener = eventListener.addListener(EventType.onLiveStreamStatusChanged, (data) async {
+ data = data as Map;
+ debugPrint("onLiveStreamStatusChanged: status: ${data['status']}");
+ });
+
+ final liveTranscriptionStatusChangeListener = eventListener.addListener(EventType.onLiveTranscriptionStatus, (data) async {
+ data = data as Map;
+ debugPrint("onLiveTranscriptionStatus: status: ${data['status']}");
+ });
+
+ final cloudRecordingStatusListener = eventListener.addListener(EventType.onCloudRecordingStatus, (data) async {
+ data = data as Map;
+ debugPrint("onCloudRecordingStatus: status: ${data['status']}");
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ if (data['status'] == RecordingStatus.Start) {
+ if (mySelf != null && !mySelf.isHost!) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ content: const Text('The session is being recorded.'),
+ actions: [
+ TextButton(
+ onPressed: () async {
+ await zoom.acceptRecordingConsent();
+ if (context.mounted) {
+ Navigator.pop(context);
+ }
+ ;
+ },
+ child: const Text('accept'),
+ ),
+ TextButton(
+ onPressed: () async {
+ String currentConsentType = await zoom.getRecordingConsentType();
+ if (currentConsentType == ConsentType.ConsentType_Individual) {
+ await zoom.declineRecordingConsent();
+ Navigator.pop(context);
+ } else {
+ await zoom.declineRecordingConsent();
+ zoom.leaveSession(false);
+ if (!context.mounted) return;
+ Navigator.popAndPushNamed(
+ context,
+ "Join",
+ arguments: JoinArguments(args.isJoin, sessionName.value, sessionPassword.value, args.displayName, args.sessionIdleTimeoutMins, args.role),
+ );
+ }
+ },
+ child: const Text('decline'),
+ ),
+ ],
+ ),
+ );
+ }
+ isRecordingStarted.value = true;
+ } else {
+ isRecordingStarted.value = false;
+ }
+ });
+
+ final liveTranscriptionMsgInfoReceivedListener = eventListener.addListener(EventType.onLiveTranscriptionMsgInfoReceived, (data) async {
+ data = data as Map;
+ ZoomVideoSdkLiveTranscriptionMessageInfo? messageInfo = ZoomVideoSdkLiveTranscriptionMessageInfo.fromJson(jsonDecode(data['messageInfo']));
+ debugPrint("onLiveTranscriptionMsgInfoReceived: content: ${messageInfo.messageContent}");
+ });
+
+ final inviteByPhoneStatusListener = eventListener.addListener(EventType.onInviteByPhoneStatus, (data) async {
+ data = data as Map;
+ debugPrint("onInviteByPhoneStatus: status: ${data['status']}, reason: ${data['reason']}");
+ });
+
+ final multiCameraStreamStatusChangedListener = eventListener.addListener(EventType.onMultiCameraStreamStatusChanged, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? changedUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['changedUser']));
+ var status = data['status'];
+ for (var user in users.value) {
+ {
+ if (changedUser.userId == user.userId) {
+ if (status == MultiCameraStreamStatus.Joined) {
+ user.hasMultiCamera = true;
+ } else if (status == MultiCameraStreamStatus.Left) {
+ user.hasMultiCamera = false;
+ }
+ }
+ }
+ }
+ });
+
+ final requireSystemPermission = eventListener.addListener(EventType.onRequireSystemPermission, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? changedUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['changedUser']));
+ var permissionType = data['permissionType'];
+ switch (permissionType) {
+ case SystemPermissionType.Camera:
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text("Can't Access Camera"),
+ content: const Text("please turn on the toggle in system settings to grant permission"),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context, 'OK'),
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ break;
+ case SystemPermissionType.Microphone:
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text("Can't Access Microphone"),
+ content: const Text("please turn on the toggle in system settings to grant permission"),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context, 'OK'),
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ break;
+ }
+ });
+
+ final networkStatusChangeListener = eventListener.addListener(EventType.onUserVideoNetworkStatusChanged, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? networkUser = ZoomVideoSdkUser.fromJson(jsonDecode(data['user']));
+
+ if (data['status'] == NetworkStatus.Bad) {
+ debugPrint("onUserVideoNetworkStatusChanged: status: ${data['status']}, user: ${networkUser.userName}");
+ }
+ });
+
+ final eventErrorListener = eventListener.addListener(EventType.onError, (data) async {
+ data = data as Map;
+ String errorType = data['errorType'];
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text("Error"),
+ content: Text(errorType),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context, 'OK'),
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ if (errorType == Errors.SessionJoinFailed || errorType == Errors.SessionDisconnecting) {
+ Timer(
+ const Duration(milliseconds: 1000),
+ () => {
+ Navigator.popAndPushNamed(
+ context,
+ "Join",
+ arguments: JoinArguments(args.isJoin, sessionName.value, sessionPassword.value, args.displayName, args.sessionIdleTimeoutMins, args.role),
+ ),
+ });
+ }
+ });
+
+ final userRecordingConsentListener = eventListener.addListener(EventType.onUserRecordingConsent, (data) async {
+ data = data as Map;
+ ZoomVideoSdkUser? user = ZoomVideoSdkUser.fromJson(jsonDecode(data['user']));
+ debugPrint('userRecordingConsentListener: user= ${user.userName}');
+ });
+
+ final callCRCDeviceStatusListener = eventListener.addListener(EventType.onCallCRCDeviceStatusChanged, (data) async {
+ data = data as Map;
+ debugPrint('onCallCRCDeviceStatusChanged: status = ${data['status']}');
+ });
+
+ final originalLanguageMsgReceivedListener = eventListener.addListener(EventType.onOriginalLanguageMsgReceived, (data) async {
+ data = data as Map;
+ ZoomVideoSdkLiveTranscriptionMessageInfo? messageInfo = ZoomVideoSdkLiveTranscriptionMessageInfo.fromJson(jsonDecode(data['messageInfo']));
+ debugPrint("onOriginalLanguageMsgReceived: content: ${messageInfo.messageContent}");
+ });
+
+ final chatPrivilegeChangedListener = eventListener.addListener(EventType.onChatPrivilegeChanged, (data) async {
+ data = data as Map;
+ String type = data['privilege'];
+ debugPrint('chatPrivilegeChangedListener: type= $type');
+ });
+
+ final testMicStatusListener = eventListener.addListener(EventType.onTestMicStatusChanged, (data) async {
+ data = data as Map;
+ String status = data['status'];
+ debugPrint('testMicStatusListener: status= $status');
+ });
+
+ final micSpeakerVolumeChangedListener = eventListener.addListener(EventType.onMicSpeakerVolumeChanged, (data) async {
+ data = data as Map;
+ int type = data['micVolume'];
+ debugPrint('onMicSpeakerVolumeChanged: micVolume= $type, speakerVolume');
+ });
+
+ final cameraControlRequestResultListener = eventListener.addListener(EventType.onCameraControlRequestResult, (data) async {
+ data = data as Map;
+ bool approved = data['approved'];
+ debugPrint('onCameraControlRequestResult: approved= $approved');
+ });
+
+ final callOutUserJoinListener = eventListener.addListener(EventType.onCalloutJoinSuccess, (data) async {
+ data = data as Map;
+ String phoneNumber = data['phoneNumber'];
+ ZoomVideoSdkUser? user = ZoomVideoSdkUser.fromJson(jsonDecode(data['user']));
+ debugPrint('onCalloutJoinSuccess: phoneNumber= $phoneNumber, user= ${user.userName}');
+ });
+
+ return () => {
+ sessionJoinListener.cancel(),
+ sessionLeaveListener.cancel(),
+ sessionPasswordWrongListener.cancel(),
+ sessionNeedPasswordListener.cancel(),
+ userVideoStatusChangedListener.cancel(),
+ userAudioStatusChangedListener.cancel(),
+ userJoinListener.cancel(),
+ userLeaveListener.cancel(),
+ userNameChangedListener.cancel(),
+ userShareStatusChangeListener.cancel(),
+ liveStreamStatusChangeListener.cancel(),
+ cloudRecordingStatusListener.cancel(),
+ inviteByPhoneStatusListener.cancel(),
+ eventErrorListener.cancel(),
+ commandReceived.cancel(),
+ liveTranscriptionStatusChangeListener.cancel(),
+ liveTranscriptionMsgInfoReceivedListener.cancel(),
+ multiCameraStreamStatusChangedListener.cancel(),
+ requireSystemPermission.cancel(),
+ userRecordingConsentListener.cancel(),
+ networkStatusChangeListener.cancel(),
+ callCRCDeviceStatusListener.cancel(),
+ originalLanguageMsgReceivedListener.cancel(),
+ chatPrivilegeChangedListener.cancel(),
+ testMicStatusListener.cancel(),
+ micSpeakerVolumeChangedListener.cancel(),
+ cameraControlRequestResultListener.cancel(),
+ callOutUserJoinListener.cancel(),
+ shareContentChangedListener.cancel(),
+ };
+ }, [zoom, users.value, isMounted]);
+
+ void selectVirtualBackgroundItem() async {
+ final ImagePicker picker = ImagePicker();
+ // Pick an image.
+ final XFile? image = await picker.pickImage(source: ImageSource.gallery);
+ await zoom.virtualBackgroundHelper.addVirtualBackgroundItem(image!.path);
+ }
+
+ void onPressAudio() async {
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ if (mySelf != null) {
+ final audioStatus = mySelf.audioStatus;
+ if (audioStatus != null) {
+ var muted = await audioStatus.isMuted();
+ if (muted) {
+ await zoom.audioHelper.unMuteAudio(mySelf.userId);
+ } else {
+ await zoom.audioHelper.muteAudio(mySelf.userId);
+ }
+ }
+ }
+ }
+
+ void onPressVideo() async {
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ if (mySelf != null) {
+ final videoStatus = mySelf.videoStatus;
+ if (videoStatus != null) {
+ var videoOn = await videoStatus.isOn();
+ if (videoOn) {
+ await zoom.videoHelper.stopVideo();
+ } else {
+ await zoom.videoHelper.startVideo();
+ }
+ }
+ }
+ }
+
+ void onPressShare() async {
+ var isOtherSharing = await zoom.shareHelper.isOtherSharing();
+ var isShareLocked = await zoom.shareHelper.isShareLocked();
+ String? shareCameraViewResult = Errors.InternalError;
+
+ if (isOtherSharing) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text("Error"),
+ content: const Text('Other is sharing'),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context, 'OK'),
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ } else if (isShareLocked) {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) => AlertDialog(
+ title: const Text("Error"),
+ content: const Text('Share is locked by host'),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context, 'OK'),
+ child: const Text('OK'),
+ ),
+ ],
+ ),
+ );
+ } else if (isSharing.value) {
+ zoom.shareHelper.stopShare();
+ } else {
+ List options = [
+ ListTile(
+ title: Text(
+ 'Share Device Screen',
+ style: TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ zoom.shareHelper.shareScreen(),
+ Navigator.of(context).pop(),
+ },
+ ),
+ ListTile(
+ title: Text(
+ 'Share Camera',
+ style: TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ shareCameraViewResult = await zoom.shareHelper.startShareCamera(cameraShareView),
+ debugPrint('start camera: $shareCameraViewResult'),
+ Navigator.of(context).pop(),
+ },
+ ),
+ ];
+ showDialog(
+ context: context,
+ builder: (context) {
+ return Dialog(
+ elevation: 0.0,
+ insetPadding: const EdgeInsets.symmetric(horizontal: 40),
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
+ child: SizedBox(
+ height: options.length * 60,
+ child: Scrollbar(
+ child: ListView(
+ shrinkWrap: true,
+ scrollDirection: Axis.vertical,
+ children: ListTile.divideTiles(
+ context: context,
+ tiles: options,
+ ).toList(),
+ ),
+ ),
+ ),
+ );
+ });
+ }
+ }
+
+ void onSelectedUserVolume(ZoomVideoSdkUser user) async {
+ var isShareAudio = user.isSharing;
+ bool canSetVolume = await user.canSetUserVolume(user.userId, isShareAudio);
+ num userVolume;
+
+ List options = [
+ ListTile(
+ title: Text(
+ 'Adjust Volume',
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ color: Colors.black,
+ ),
+ ),
+ ),
+ ListTile(
+ title: Text(
+ 'Current volume',
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ debugPrint('user volume'),
+ userVolume = await user.getUserVolume(user.userId, isShareAudio),
+ debugPrint('user ${user.userName}\'s volume is ${userVolume!}'),
+ },
+ ),
+ ];
+ if (canSetVolume) {
+ options.add(
+ ListTile(
+ title: Text(
+ 'Volume up',
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ userVolume = await user.getUserVolume(user.userId, isShareAudio),
+ if (userVolume < 10)
+ {
+ await user.setUserVolume(user.userId, userVolume + 1, isShareAudio),
+ }
+ else
+ {
+ debugPrint("Cannot volume up."),
+ }
+ },
+ ),
+ );
+ options.add(
+ ListTile(
+ title: Text(
+ 'Volume down',
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ userVolume = await user.getUserVolume(user.userId, isShareAudio),
+ if (userVolume > 0)
+ {
+ await user.setUserVolume(user.userId, userVolume - 1, isShareAudio),
+ }
+ else
+ {
+ debugPrint("Cannot volume down."),
+ }
+ },
+ ),
+ );
+ }
+ showDialog(
+ context: context,
+ builder: (context) {
+ return Dialog(
+ elevation: 0.0,
+ insetPadding: const EdgeInsets.symmetric(horizontal: 40),
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
+ child: SizedBox(
+ height: options.length * 58,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ ListView(
+ shrinkWrap: true,
+ children: ListTile.divideTiles(
+ context: context,
+ tiles: options,
+ ).toList(),
+ ),
+ ],
+ ),
+ ));
+ });
+ }
+
+ void onPressCameraList() async {
+ List options = [];
+ List cameraList = await zoom.videoHelper.getCameraList();
+ for (var camera in cameraList) {
+ options.add(
+ ListTile(
+ title: Text(
+ camera.deviceName,
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ await zoom.videoHelper.switchCamera(camera.deviceId),
+ Navigator.of(context).pop(),
+ },
+ ),
+ );
+ }
+ options.add(
+ ListTile(
+ title: Text(
+ "Cancel",
+ style: const TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.normal,
+ color: Colors.black,
+ ),
+ ),
+ onTap: () async => {
+ Navigator.of(context).pop(),
+ },
+ ),
+ );
+ showDialog(
+ context: context,
+ builder: (context) {
+ return Dialog(
+ elevation: 0.0,
+ insetPadding: const EdgeInsets.symmetric(horizontal: 40),
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
+ child: SizedBox(
+ height: options.length * 60,
+ child: Scrollbar(
+ child: ListView(
+ shrinkWrap: true,
+ scrollDirection: Axis.vertical,
+ children: ListTile.divideTiles(
+ context: context,
+ tiles: options,
+ ).toList(),
+ ),
+ ),
+ ),
+ );
+ });
+ }
+
+ // Future onPressMore() async {
+ // ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ // bool isShareLocked = await zoom.shareHelper.isShareLocked();
+ // bool canSwitchSpeaker = await zoom.audioHelper.canSwitchSpeaker();
+ // bool canStartRecording = (await zoom.recordingHelper.canStartRecording()) == Errors.Success;
+ // debugPrint("canStartRecording ${await zoom.recordingHelper.canStartRecording()}");
+ // var startLiveTranscription = (await zoom.liveTranscriptionHelper.getLiveTranscriptionStatus()) == LiveTranscriptionStatus.Start;
+ // bool canStartLiveTranscription = await zoom.liveTranscriptionHelper.canStartLiveTranscription();
+ // bool isHost = (mySelf != null) ? (await mySelf.getIsHost()) : false;
+ // isOriginalAspectRatio.value = await zoom.videoHelper.isOriginalAspectRatioEnabled();
+ // bool canCallOutToCRC = await zoom.CRCHelper.isCRCEnabled();
+ // bool supportVB = await zoom.virtualBackgroundHelper.isSupportVirtualBackground();
+ // List? shareActionList = await mySelf?.getShareActionList();
+ //
+ // List options = [
+ // ListTile(
+ // title: Text(
+ // 'More',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 18,
+ // fontWeight: FontWeight.w600,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // ),
+ // ListTile(
+ // title: Text(
+ // 'Get Chat Privilege',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint("Chat Privilege = ${await zoom.chatHelper.getChatPrivilege()}"),
+ // Navigator.of(context).pop(),
+ // },
+ // ),
+ // ListTile(
+ // title: Text(
+ // 'Get Session Dial-in Number infos',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint("session number = ${await zoom.session.getSessionNumber()}"),
+ // Navigator.of(context).pop(),
+ // },
+ // ),
+ // ListTile(
+ // title: Text(
+ // 'Get Camera List',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint("camera list= ${await zoom.videoHelper.getCameraList()}"),
+ // onPressCameraList(),
+ // Navigator.of(context).pop(),
+ // },
+ // ),
+ // ListTile(
+ // title: Text(
+ // '${isMicOriginalOn.value ? 'Disable' : 'Enable'} Original Sound',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint("${isMicOriginalOn.value}"),
+ // await zoom.audioSettingHelper.enableMicOriginalInput(!isMicOriginalOn.value),
+ // isMicOriginalOn.value = await zoom.audioSettingHelper.isMicOriginalInputEnable(),
+ // debugPrint("Original sound ${isMicOriginalOn.value ? 'Enabled' : 'Disabled'}"),
+ // Navigator.of(context).pop(),
+ // },
+ // )
+ // ];
+ //
+ // if (supportVB) {
+ // options.add(
+ // ListTile(
+ // title: Text(
+ // 'Add Virtual Background',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // selectVirtualBackgroundItem(),
+ // Navigator.of(context).pop(),
+ // },
+ // ),
+ // );
+ // }
+ //
+ // if (canCallOutToCRC) {
+ // options.add(ListTile(
+ // title: Text(
+ // 'Call-out to CRC devices',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint('CRC result = ${await zoom.CRCHelper.callCRCDevice("bjn.vc", ZoomVideoSdkCRCProtocolType.SIP)}'),
+ // Navigator.of(context).pop(),
+ // },
+ // ));
+ // options.add(ListTile(
+ // title: Text(
+ // 'Cancel call-out to CRC devices',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint('cancel result= ${await zoom.CRCHelper.cancelCallCRCDevice()}'),
+ // Navigator.of(context).pop(),
+ // },
+ // ));
+ // }
+ //
+ // if (canSwitchSpeaker) {
+ // options.add(ListTile(
+ // title: Text(
+ // 'Turn ${isSpeakerOn.value ? 'off' : 'on'} Speaker',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // await zoom.audioHelper.setSpeaker(!isSpeakerOn.value),
+ // isSpeakerOn.value = await zoom.audioHelper.getSpeakerStatus(),
+ // debugPrint('Turned ${isSpeakerOn.value ? 'on' : 'off'} Speaker'),
+ // Navigator.of(context).pop(),
+ // },
+ // ));
+ // }
+ //
+ // if (isHost) {
+ // options.add(ListTile(
+ // title: Text(
+ // '${isShareLocked ? 'Unlock' : 'Lock'} Share',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // debugPrint("isShareLocked = ${await zoom.shareHelper.lockShare(!isShareLocked)}"),
+ // Navigator.of(context).pop(),
+ // }));
+ // options.add(ListTile(
+ // title: Text(
+ // 'Change Name',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () => {
+ // isRenameModalVisible.value = true,
+ // Navigator.of(context).pop(),
+ // },
+ // ));
+ // }
+ //
+ // if (canStartLiveTranscription) {
+ // options.add(ListTile(
+ // title: Text(
+ // "${startLiveTranscription ? 'Stop' : 'Start'} Live Transcription",
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // if (startLiveTranscription)
+ // {
+ // debugPrint('stopLiveTranscription= ${await zoom.liveTranscriptionHelper.stopLiveTranscription()}'),
+ // }
+ // else
+ // {
+ // debugPrint('startLiveTranscription= ${await zoom.liveTranscriptionHelper.startLiveTranscription()}'),
+ // },
+ // Navigator.of(context).pop(),
+ // },
+ // ));
+ // options.add(ListTile(
+ // title: Text(
+ // '${isReceiveSpokenLanguageContentEnabled.value ? 'Disable' : 'Enable'} receiving original caption',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // await zoom.liveTranscriptionHelper.enableReceiveSpokenLanguageContent(!isReceiveSpokenLanguageContentEnabled.value),
+ // isReceiveSpokenLanguageContentEnabled.value = await zoom.liveTranscriptionHelper.isReceiveSpokenLanguageContentEnabled(),
+ // debugPrint("isReceiveSpokenLanguageContentEnabled = ${isReceiveSpokenLanguageContentEnabled.value}"),
+ // Navigator.of(context).pop(),
+ // }));
+ // }
+ //
+ // if (canStartRecording) {
+ // options.add(ListTile(
+ // title: Text(
+ // '${isRecordingStarted.value ? 'Stop' : 'Start'} Recording',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // if (!isRecordingStarted.value)
+ // {
+ // debugPrint('isRecordingStarted = ${await zoom.recordingHelper.startCloudRecording()}'),
+ // }
+ // else
+ // {
+ // debugPrint('isRecordingStarted = ${await zoom.recordingHelper.stopCloudRecording()}'),
+ // },
+ // Navigator.of(context).pop(),
+ // }));
+ // }
+ //
+ // if (Platform.isAndroid) {
+ // bool isFlashlightSupported = await zoom.videoHelper.isSupportFlashlight();
+ // bool isFlashlightOn = await zoom.videoHelper.isFlashlightOn();
+ // if (isFlashlightSupported) {
+ // options.add(ListTile(
+ // title: Text(
+ // '${isFlashlightOn ? 'Turn Off' : 'Turn On'} Flashlight',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // if (!isFlashlightOn)
+ // {
+ // await zoom.videoHelper.turnOnOrOffFlashlight(true),
+ // }
+ // else
+ // {
+ // await zoom.videoHelper.turnOnOrOffFlashlight(false),
+ // },
+ // Navigator.of(context).pop(),
+ // }));
+ // }
+ // }
+ //
+ // if (Platform.isIOS) {
+ // options.add(ListTile(
+ // title: Text(
+ // '${isPiPView.value ? 'Disable' : 'Enable'} picture in picture view',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // isPiPView.value = !isPiPView.value,
+ // Navigator.of(context).pop(),
+ // }));
+ // }
+ //
+ // if (isVideoOn.value) {
+ // options.add(ListTile(
+ // title: Text(
+ // 'Mirror the video',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // await zoom.videoHelper.mirrorMyVideo(!isVideoMirrored.value),
+ // isVideoMirrored.value = await zoom.videoHelper.isMyVideoMirrored(),
+ // Navigator.of(context).pop(),
+ // }));
+ // options.add(ListTile(
+ // title: Text(
+ // '${isOriginalAspectRatio.value ? 'Enable' : 'Disable'} original aspect ratio',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.normal,
+ // color: Colors.black,
+ // ),
+ // ),
+ // ),
+ // onTap: () async => {
+ // await zoom.videoHelper.enableOriginalAspectRatio(!isOriginalAspectRatio.value),
+ // isOriginalAspectRatio.value = await zoom.videoHelper.isOriginalAspectRatioEnabled(),
+ // debugPrint("isOriginalAspectRatio= ${isOriginalAspectRatio.value}"),
+ // Navigator.of(context).pop(),
+ // }));
+ // }
+ //
+ // showDialog(
+ // context: context,
+ // builder: (context) {
+ // return Dialog(
+ // elevation: 0.0,
+ // insetPadding: const EdgeInsets.symmetric(horizontal: 40),
+ // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
+ // child: SizedBox(
+ // height: 500,
+ // child: Scrollbar(
+ // child: ListView(
+ // shrinkWrap: true,
+ // scrollDirection: Axis.vertical,
+ // children: ListTile.divideTiles(
+ // context: context,
+ // tiles: options,
+ // ).toList(),
+ // ),
+ // ),
+ // ),
+ // );
+ // });
+ // }
+
+ void onLeaveSession(bool isEndSession) async {
+ await zoom.leaveSession(isEndSession);
+ Navigator.pushAndRemoveUntil(
+ context,
+ CustomPageRoute(
+ page: LandingNavigation(),
+ ),
+ (r) => false);
+ }
+
+ void showLeaveOptions() async {
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ bool isHost = await mySelf!.getIsHost();
+
+ Widget endSession;
+ Widget leaveSession;
+ Widget cancel = TextButton(
+ child: const Text('Cancel'),
+ onPressed: () {
+ Navigator.pop(context); //close Dialog
+ },
+ );
+
+ switch (defaultTargetPlatform) {
+ case TargetPlatform.android:
+ endSession = TextButton(
+ child: const Text('End Session'),
+ onPressed: () => onLeaveSession(true),
+ );
+ leaveSession = TextButton(
+ child: const Text('Leave Session'),
+ onPressed: () => onLeaveSession(false),
+ );
+ break;
+ default:
+ endSession = CupertinoActionSheetAction(
+ isDestructiveAction: true,
+ child: const Text('End Session'),
+ onPressed: () => onLeaveSession(true),
+ );
+ leaveSession = CupertinoActionSheetAction(
+ child: const Text('Leave Session'),
+ onPressed: () => onLeaveSession(false),
+ );
+ break;
+ }
+
+ List options = [
+ leaveSession,
+ cancel,
+ ];
+
+ if (Platform.isAndroid) {
+ if (isHost) {
+ options.removeAt(1);
+ options.insert(0, endSession);
+ }
+ showDialog(
+ context: context,
+ builder: (context) {
+ return AlertDialog(
+ content: const Text("Do you want to leave this session?"),
+ shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2.0))),
+ actions: options,
+ );
+ });
+ } else {
+ options.removeAt(1);
+ if (isHost) {
+ options.insert(1, endSession);
+ }
+ showCupertinoModalPopup(
+ context: context,
+ builder: (context) => CupertinoActionSheet(
+ message: const Text('Are you sure that you want to leave the session?'),
+ actions: options,
+ cancelButton: cancel,
+ ),
+ );
+ }
+ }
+
+ final chatMessageController = TextEditingController();
+
+ void sendChatMessage(String message) async {
+ await zoom.chatHelper.sendChatToAll(message);
+ ZoomVideoSdkUser? self = await zoom.session.getMySelf();
+ ZoomVideoSdkCmdChannel cmdChannel = zoom.cmdChannel;
+ for (var user in users.value) {
+ if (user.userId != self?.userId) {
+ await zoom.cmdChannel.sendCommand(user.userId, message);
+ }
+ }
+ chatMessageController.clear();
+ // send the chat as a command
+ }
+
+ void onSelectedUser(ZoomVideoSdkUser user) async {
+ setState(() {
+ fullScreenUser.value = user;
+ });
+ }
+
+ changeNamePopup = Center(
+ child: Stack(
+ children: [
+ Visibility(
+ visible: isRenameModalVisible.value,
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ alignment: Alignment.bottomLeft,
+ decoration: const BoxDecoration(
+ borderRadius: BorderRadius.all(Radius.circular(10)),
+ color: Colors.white,
+ ),
+ width: MediaQuery.of(context).size.width - 130,
+ height: MediaQuery.of(context).size.height * 0.2,
+ child: Center(
+ child: (Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(top: 20, left: 20),
+ child: Text(
+ 'Change Name',
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 10, left: 20),
+ child: SizedBox(
+ width: MediaQuery.of(context).size.width - 230,
+ child: TextField(
+ onEditingComplete: () {},
+ autofocus: true,
+ cursorColor: Colors.black,
+ controller: changeNameController,
+ decoration: InputDecoration(
+ isDense: true,
+ hintText: 'New name',
+ hintStyle: TextStyle(
+ fontSize: 14.0,
+ color: chatTextColor,
+ ),
+ ),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(top: 15),
+ child: Row(
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 40),
+ child: InkWell(
+ child: Text(
+ 'Apply',
+ style: const TextStyle(
+ fontSize: 16,
+ ),
+ ),
+ onTap: () async {
+ if (fullScreenUser.value != null) {
+ ZoomVideoSdkUser? mySelf = await zoom.session.getMySelf();
+ await zoom.userHelper.changeName((mySelf?.userId)!, changeNameController.text);
+ changeNameController.clear();
+ }
+ isRenameModalVisible.value = false;
+ },
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(left: 40),
+ child: InkWell(
+ child: Text(
+ 'Cancel',
+ style: const TextStyle(
+ fontSize: 16,
+ ),
+ ),
+ onTap: () async {
+ isRenameModalVisible.value = false;
+ },
+ ),
+ )
+ ],
+ ),
+ )
+ ],
+ )),
+ ),
+ ),
+ ],
+ )),
+ ],
+ ),
+ );
+
+ Widget fullScreenView;
+ Widget smallView;
+ Widget cameraView;
+
+ if (isInSession.value && fullScreenUser.value != null && users.value.isNotEmpty) {
+ fullScreenView = AnimatedOpacity(
+ opacity: opacityLevel,
+ duration: const Duration(seconds: 3),
+ child: VideoView(
+ user: users.value.length > 1 ? users.value[1] : users.value[0],
+ hasMultiCamera: false,
+ isPiPView: isPiPView.value,
+ sharing: sharingUser.value == null ? false : (sharingUser.value?.userId == fullScreenUser.value?.userId),
+ preview: false,
+ focused: false,
+ multiCameraIndex: "0",
+ videoAspect: VideoAspect.Original,
+ fullScreen: true,
+ resolution: VideoResolution.Resolution360,
+ ),
+ );
+
+ smallView = Container(
+ height: 110,
+ margin: const EdgeInsets.only(left: 20, right: 20),
+ alignment: Alignment.center,
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: users.value.length,
+ itemBuilder: (BuildContext context, int index) {
+ return InkWell(
+ onTap: () async {
+ onSelectedUser(users.value[index]);
+ },
+ onDoubleTap: () async {
+ onSelectedUserVolume(users.value[index]);
+ },
+ child: Center(
+ child: VideoView(
+ user: users.value[index],
+ hasMultiCamera: false,
+ isPiPView: false,
+ sharing: false,
+ preview: false,
+ focused: false,
+ multiCameraIndex: "0",
+ videoAspect: VideoAspect.Original,
+ fullScreen: false,
+ resolution: VideoResolution.Resolution180,
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (BuildContext context, int index) => const Divider(),
+ ),
+ );
+ } else {
+ fullScreenView = Container(
+ color: Colors.black,
+ child: const Center(
+ child: Text(
+ "Connecting...",
+ style: TextStyle(
+ fontSize: 20,
+ color: Colors.white,
+ ),
+ ),
+ ));
+ smallView = Container(
+ height: 110,
+ color: Colors.transparent,
+ );
+ }
+
+ cameraView = Offstage(
+ offstage: !isSharedCamera.value,
+ child: AnimatedOpacity(
+ opacity: isSharedCamera.value ? 1.0 : 0.0,
+ duration: const Duration(milliseconds: 300),
+ child: cameraShareView,
+ ),
+ );
+
+ _changeOpacity;
+ return Scaffold(
+ resizeToAvoidBottomInset: false,
+ backgroundColor: backgroundColor,
+ body: Stack(
+ children: [
+ // isScreenBeingShared ? fullScreenView : SizedBox.shrink(),
+ isScreenBeingShared ? SizedBox.shrink() : fullScreenView,
+ cameraView,
+ Container(
+ padding: const EdgeInsets.only(top: 35),
+ child: Stack(
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Container(
+ height: 80,
+ width: 180,
+ margin: const EdgeInsets.only(top: 16, left: 8),
+ padding: const EdgeInsets.all(8),
+ alignment: Alignment.topLeft,
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.all(Radius.circular(8.0)),
+ // color: buttonBackgroundColor,
+ ),
+ // child: InkWell(
+ // onTap: () async {
+ // showDialog(
+ // context: context,
+ // builder: (context) {
+ // return Dialog(
+ // elevation: 0.0,
+ // insetPadding: const EdgeInsets.symmetric(horizontal: 40),
+ // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
+ // child: FractionallySizedBox(
+ // heightFactor: 0.2,
+ // widthFactor: 0.3,
+ // child: Column(
+ // crossAxisAlignment: CrossAxisAlignment.stretch,
+ // children: [
+ // ListView(
+ // shrinkWrap: true,
+ // children: ListTile.divideTiles(
+ // context: context,
+ // tiles: [
+ // ListTile(
+ // title: Text(
+ // 'Session Information',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 18,
+ // fontWeight: FontWeight.w600,
+ // ),
+ // ),
+ // ),
+ // ),
+ // ListTile(
+ // title: Text(
+ // 'Session Name',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // ),
+ // ),
+ // ),
+ // subtitle: Text(
+ // sessionName.value,
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 12,
+ // ),
+ // ),
+ // ),
+ // ),
+ // ListTile(
+ // title: Text(
+ // 'Session Password',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // ),
+ // ),
+ // ),
+ // subtitle: Text(
+ // sessionPassword.value,
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 12,
+ // ),
+ // ),
+ // ),
+ // ),
+ // ListTile(
+ // title: Text(
+ // 'Participants',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // ),
+ // ),
+ // ),
+ // subtitle: Text(
+ // '${users.value.length}',
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 12,
+ // ),
+ // ),
+ // ),
+ // ),
+ // ],
+ // ).toList(),
+ // ),
+ // ],
+ // ),
+ // ));
+ // });
+ // },
+ // child: Stack(
+ // children: [
+ // Column(
+ // children: [
+ // const Padding(padding: EdgeInsets.symmetric(vertical: 4)),
+ // Align(
+ // alignment: Alignment.centerLeft,
+ // child: Text(
+ // sessionName.value,
+ // overflow: TextOverflow.ellipsis,
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.w600,
+ // color: Colors.white,
+ // ),
+ // ),
+ // ),
+ // ),
+ // const Padding(padding: EdgeInsets.symmetric(vertical: 5)),
+ // Align(
+ // alignment: Alignment.centerLeft,
+ // child: Text(
+ // "Participants: ${users.value.length}",
+ // style: GoogleFonts.lato(
+ // textStyle: const TextStyle(
+ // fontSize: 14,
+ // fontWeight: FontWeight.w600,
+ // color: Colors.white,
+ // ),
+ // ),
+ // ),
+ // )
+ // ],
+ // ),
+ // Container(
+ // alignment: Alignment.centerRight,
+ // child: Image.asset(
+ // "assets/icons/unlocked@2x.png",
+ // height: 22,
+ // )),
+ // ],
+ // ),
+ // ),
+ ),
+ TextButton(
+ onPressed: (showLeaveOptions),
+ child: Container(
+ alignment: Alignment.topRight,
+ margin: const EdgeInsets.only(top: 16, right: 8),
+ padding: const EdgeInsets.only(top: 5, bottom: 5, left: 16, right: 16),
+ height: 28,
+ decoration: BoxDecoration(
+ borderRadius: const BorderRadius.all(Radius.circular(20.0)),
+ color: buttonBackgroundColor,
+ ),
+ child: const Text(
+ "LEAVE",
+ style: TextStyle(
+ fontSize: 14,
+ fontWeight: FontWeight.bold,
+ color: Color(0xFFE02828),
+ ),
+ ),
+ )),
+ ],
+ ),
+ Align(
+ alignment: Alignment.centerRight,
+ child: FractionallySizedBox(
+ widthFactor: 0.2,
+ heightFactor: 0.6,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ IconButton(
+ onPressed: onPressAudio,
+ icon: isMuted.value ? Image.asset("assets/images/png/zoom/unmute@2x.png") : Image.asset("assets/images/png/zoom/mute@2x.png"),
+ iconSize: circleButtonSize,
+ tooltip: isMuted.value == true ? "Unmute" : "Mute",
+ ),
+ IconButton(
+ onPressed: onPressShare,
+ icon: isSharing.value ? Image.asset("assets/images/png/zoom/share-off@2x.png") : Image.asset("assets/images/png/zoom/share-on@2x.png"),
+ iconSize: circleButtonSize,
+ ),
+ IconButton(
+ onPressed: onPressVideo,
+ iconSize: circleButtonSize,
+ icon: isVideoOn.value ? Image.asset("assets/images/png/zoom/video-off@2x.png") : Image.asset("assets/images/png/zoom/video-on@2x.png"),
+ ),
+ Column(
+ children: [
+ IconButton(
+ onPressed: () async {
+ onPressCameraList();
+ },
+ icon: Icon(Icons.cameraswitch, color: Colors.white),
+ color: Colors.white,
+ iconSize: 40.h,
+ ),
+ "Switch Camera".toText12(color: AppColors.whiteColor, isCenter: true, isBold: true)
+ ],
+ ),
+ ],
+ ),
+ )),
+ Container(
+ margin: const EdgeInsets.only(left: 16, right: 16, bottom: 40, top: 10),
+ alignment: Alignment.bottomCenter,
+ child: SizedBox(
+ height: MediaQuery.of(context).viewInsets.bottom == 0 ? 65 : MediaQuery.of(context).viewInsets.bottom + 18,
+ child: TextField(
+ maxLines: 1,
+ textAlign: TextAlign.left,
+ style: TextStyle(color: chatTextColor),
+ cursorColor: chatTextColor,
+ textAlignVertical: TextAlignVertical.center,
+ controller: chatMessageController,
+ decoration: InputDecoration(
+ contentPadding: const EdgeInsets.only(left: 16, top: 10, bottom: 10, right: 16),
+ enabledBorder: OutlineInputBorder(
+ borderSide: BorderSide(width: 1, color: chatTextColor), //<-- SEE HERE
+ ),
+ hintText: 'Type your message here...',
+ hintStyle: TextStyle(
+ fontSize: 14.0,
+ color: chatTextColor,
+ ),
+ ),
+ onSubmitted: (String str) {
+ sendChatMessage(str);
+ },
+ ),
+ ),
+ ),
+ Container(
+ alignment: Alignment.bottomLeft,
+ margin: const EdgeInsets.only(bottom: 120),
+ child: smallView,
+ ),
+ // CommentList(zoom: zoom, eventListener: eventListener),
+ changeNamePopup,
+ ],
+ )),
+ ],
+ ));
+ }
+}
+
+class CallArguments {
+ final bool isJoin;
+ final String sessionName;
+ final String sessionPwd;
+ final String displayName;
+ final String sessionIdleTimeoutMins;
+ final String role;
+ final int callType;
+
+ CallArguments(this.sessionName, this.sessionPwd, this.displayName, this.sessionIdleTimeoutMins, this.role, this.isJoin, this.callType);
+}
+
+class JoinArguments {
+ final bool isJoin;
+ final String sessionName;
+ final String sessionPwd;
+ final String displayName;
+ final String sessionTimeout;
+ final String roleType;
+
+ JoinArguments(this.isJoin, this.sessionName, this.sessionPwd, this.displayName, this.sessionTimeout, this.roleType);
+}
diff --git a/lib/presentation/tele_consultation/zoom/video_view.dart b/lib/presentation/tele_consultation/zoom/video_view.dart
new file mode 100644
index 0000000..4c1e049
--- /dev/null
+++ b/lib/presentation/tele_consultation/zoom/video_view.dart
@@ -0,0 +1,258 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_zoom_videosdk/flutter_zoom_view.dart' as FlutterZoomView;
+import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart';
+
+const SHOW_TALKING_ICON_DURATION = 2000;
+
+class VideoView extends FlutterZoomView.ZoomView {
+ const VideoView({
+ super.key,
+ required super.user,
+ required super.sharing,
+ required super.preview,
+ required super.focused,
+ required super.hasMultiCamera,
+ required super.multiCameraIndex,
+ required super.videoAspect,
+ required super.fullScreen,
+ required super.resolution,
+ required super.isPiPView,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ var isVideoOn = useState(false);
+ var isTalking = useState(false);
+ var isMuted = useState(false);
+ var isMounted = useIsMounted();
+ var zoom = ZoomVideoSdk();
+ var isSharing = useState(false);
+ user?.audioStatus?.isMuted().then((muted) => isMuted.value = muted);
+
+ useEffect(() {
+ updateVideoStatus() {
+ if (user == null) return;
+ Future.microtask(() async {
+ if (isMounted()) {
+ isVideoOn.value = (await user!.videoStatus!.isOn());
+ isSharing.value = sharing;
+ }
+ });
+ }
+
+ resetAudioStatus() {
+ isTalking.value = false;
+ isMuted.value = false;
+ }
+
+ updateAudioStatus() async {
+ if (!isMounted()) return;
+ var talking = await user?.audioStatus?.isTalking();
+ var muted = await user?.audioStatus?.isMuted();
+ isMuted.value = muted!;
+ isTalking.value = talking!;
+ if (talking) {
+ Timer(
+ const Duration(milliseconds: SHOW_TALKING_ICON_DURATION),
+ () => {
+ if (isMounted())
+ {
+ isTalking.value = false,
+ }
+ });
+ }
+ }
+
+ updateVideoStatus();
+ return null;
+ }, [zoom, user]);
+
+ ImageIcon audioStatusIcon;
+ if (isTalking.value) {
+ audioStatusIcon = const ImageIcon(
+ AssetImage("assets/images/png/zoom/talking@2x.png"),
+ );
+ } else if (isMuted.value) {
+ audioStatusIcon = const ImageIcon(
+ AssetImage("assets/images/png/zoom/muted@2x.png"),
+ );
+ }
+ // Pass parameters to the platform side.
+ final Map creationParams = {};
+ creationParams.putIfAbsent("userId", () => user?.userId);
+ creationParams.putIfAbsent("sharing", () => sharing);
+ creationParams.putIfAbsent("preview", () => preview);
+ creationParams.putIfAbsent("focused", () => focused);
+ creationParams.putIfAbsent("hasMultiCamera", () => hasMultiCamera);
+ if (videoAspect.isEmpty) {
+ creationParams.putIfAbsent("videoAspect", () => VideoAspect.PanAndScan);
+ } else {
+ creationParams.putIfAbsent("videoAspect", () => videoAspect);
+ }
+ creationParams.putIfAbsent("fullScreen", () => fullScreen);
+ if (resolution.isNotEmpty) {
+ creationParams.putIfAbsent("videoAspect", () => videoAspect);
+ }
+
+ if (fullScreen) {
+ if (sharing) {
+ return Container(
+ height: MediaQuery.of(context).size.height,
+ width: MediaQuery.of(context).size.width,
+ alignment: Alignment.center,
+ child: FlutterZoomView.View(creationParams: creationParams),
+ );
+ } else if (isVideoOn.value) {
+ return Container(
+ height: MediaQuery.of(context).size.height,
+ width: MediaQuery.of(context).size.width,
+ alignment: Alignment.center,
+ child: FlutterZoomView.View(creationParams: creationParams),
+ );
+ } else {
+ return Container(
+ margin: const EdgeInsets.symmetric(vertical: 0),
+ child: Container(
+ alignment: Alignment.center,
+ child: const Image(
+ image: AssetImage("assets/images/png/zoom/default-avatar.png"),
+ )),
+ );
+ }
+ } else {
+ if (isVideoOn.value || sharing) {
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 8),
+ height: 110,
+ width: 110,
+ child: Stack(
+ children: [
+ Container(
+ height: 110,
+ width: 110,
+ decoration: BoxDecoration(
+ color: const Color(0xff232323),
+ border: Border.all(
+ color: const Color(0xff666666),
+ width: 1,
+ ),
+ borderRadius: const BorderRadius.all(Radius.circular(8)),
+ ),
+ alignment: Alignment.center,
+ child: FlutterZoomView.View(creationParams: creationParams),
+ ),
+ Container(
+ height: 110,
+ width: 110,
+ decoration: const BoxDecoration(
+ borderRadius: BorderRadius.all(Radius.circular(8)),
+ ),
+ ),
+ Container(
+ alignment: Alignment.bottomCenter,
+ child: Container(
+ height: 20,
+ width: 110,
+ decoration: const BoxDecoration(
+ color: Colors.black26,
+ borderRadius: BorderRadius.all(Radius.circular(8)),
+ ),
+ ),
+ ),
+ Container(
+ alignment: Alignment.bottomLeft,
+ margin: const EdgeInsets.only(left: 5),
+ child: Text(
+ user!.userName,
+ textAlign: TextAlign.left,
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 12,
+ ),
+ ),
+ ),
+ Container(
+ alignment: Alignment.bottomRight,
+ margin: const EdgeInsets.only(right: 5, bottom: 5),
+ // height: 110,
+ // width: 110,
+ child: Image(
+ height: 12,
+ width: 12,
+ image: isMuted.value ? const AssetImage("assets/images/png/zoom/muted@2x.png") : const AssetImage("assets/images/png/zoom/talking@2x.png"),
+ fit: BoxFit.cover,
+ ),
+ )
+ ],
+ ),
+ );
+ } else {
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 8),
+ height: 110,
+ width: 110,
+ child: Stack(
+ children: [
+ Container(
+ height: 110,
+ width: 110,
+ decoration: BoxDecoration(
+ color: const Color(0xff232323),
+ border: Border.all(
+ color: const Color(0xff666666),
+ width: 1,
+ ),
+ borderRadius: const BorderRadius.all(Radius.circular(8)),
+ ),
+ alignment: Alignment.center,
+ child: Container(
+ alignment: Alignment.center,
+ child: const Image(
+ height: 60,
+ width: 60,
+ image: AssetImage("assets/images/png/zoom/default-avatar.png"),
+ )),
+ ),
+ Align(
+ alignment: Alignment.bottomCenter,
+ child: Container(
+ height: 20,
+ width: 110,
+ decoration: const BoxDecoration(
+ color: Colors.black26,
+ borderRadius: BorderRadius.all(Radius.circular(8)),
+ ),
+ ),
+ ),
+ Container(
+ alignment: Alignment.bottomLeft,
+ margin: const EdgeInsets.only(left: 5),
+ child: Text(
+ user!.userName,
+ textAlign: TextAlign.left,
+ style: const TextStyle(
+ color: Colors.white,
+ fontSize: 12,
+ ),
+ ),
+ ),
+ Container(
+ alignment: Alignment.bottomRight,
+ margin: const EdgeInsets.only(right: 5, bottom: 5),
+ child: Image(
+ height: 12,
+ width: 12,
+ image: isMuted.value ? const AssetImage("assets/images/png/zoom/muted@2x.png") : const AssetImage("assets/images/png/zoom/talking@2x.png"),
+ fit: BoxFit.cover,
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+ }
+ }
+}
diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart
index b37ebfa..ed5c0db 100644
--- a/lib/routes/app_routes.dart
+++ b/lib/routes/app_routes.dart
@@ -7,6 +7,7 @@ import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart'
import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart';
import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_procedures_page.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
+import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart';
import 'package:hmg_patient_app_new/splashPage.dart';
class AppRoutes {
@@ -19,6 +20,7 @@ class AppRoutes {
static const String eReferralPage = '/erReferralPage';
static const String comprehensiveCheckupPage = '/comprehensiveCheckupPage';
static const String homeHealthCarePage = '/homeHealthCarePage';
+ static const String zoomCallPage = '/zoomCallPage';
static Map get routes => {
initialRoute: (context) => SplashPage(),
@@ -29,6 +31,7 @@ class AppRoutes {
medicalFilePage: (context) => MedicalFilePage(),
eReferralPage: (context) => NewReferralPage(),
comprehensiveCheckupPage: (context) => ComprehensiveCheckupPage(),
- homeHealthCarePage: (context) => HhcProceduresPage()
+ homeHealthCarePage: (context) => HhcProceduresPage(),
+ zoomCallPage: (context) => CallScreen()
};
}
diff --git a/lib/splashPage.dart b/lib/splashPage.dart
index ba7cefd..b5b752b 100644
--- a/lib/splashPage.dart
+++ b/lib/splashPage.dart
@@ -4,8 +4,11 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_callkit_incoming/entities/call_event.dart';
+import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart';
+import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart';
import 'package:hmg_patient_app_new/presentation/onboarding/onboarding_screen.dart';
import 'package:hmg_patient_app_new/presentation/onboarding/splash_animation_screen.dart';
@@ -19,6 +22,9 @@ import 'package:hmg_patient_app_new/features/authentication/authentication_view_
// import 'package:hmg_patient_app_new/presentation/authantication/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/tele_consultation/zoom/call_screen.dart';
+import 'package:hmg_patient_app_new/services/cache_service.dart';
+import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
import 'package:lottie/lottie.dart';
@@ -38,6 +44,7 @@ class _SplashScreenState extends State {
late AuthenticationViewModel authVm;
Future initializeStuff() async {
+ listenerEvent();
Timer(
Duration(milliseconds: 500),
() async {
@@ -47,22 +54,155 @@ class _SplashScreenState extends State {
);
await authVm.getServicePrivilege();
Timer(Duration(seconds: 2, milliseconds: 500), () async {
+ bool isAppOpenedFromCall = await GetIt.instance().getBool(key: CacheConst.isAppOpenedFromCall) ?? false;
+
LocalNotification.init(onNotificationClick: (payload) {});
- if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) {
- Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen())));
+ if (isAppOpenedFromCall) {
+ navigateToTeleConsult();
} else {
- Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation())));
+ if (await Utils.getBoolFromPrefs(CacheConst.firstLaunch)) {
+ // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen())));
+ Navigator.of(context).pushReplacement(FadePage(page: OnboardingScreen()));
+ } else {
+ // Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation())));
+ Navigator.of(context).pushReplacement(FadePage(page: LandingNavigation()));
+ }
}
});
var zoom = ZoomVideoSdk();
InitConfig initConfig = InitConfig(
domain: "zoom.us",
- enableLog: true,
+ enableLog: false,
);
zoom.initSdk(initConfig);
}
+ navigateToTeleConsult() async {
+ String roomID = await Utils.getStringFromPrefs(CacheConst.zoomRoomID);
+ print('RoomID: $roomID');
+
+ // GetIt.instance().remove(key: CacheConst.isAppOpenedFromCall);
+ Utils.removeFromPrefs(CacheConst.isAppOpenedFromCall);
+
+ Navigator.of(GetIt.instance().navigatorKey.currentContext!).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation())));
+ Navigator.pushReplacementNamed(
+ // context,
+ GetIt.instance().navigatorKey.currentContext!,
+ AppRoutes.zoomCallPage,
+ // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1),
+ arguments: CallArguments(roomID, "123", "Patient", "40", "0", true, 1),
+ // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)),
+ );
+ }
+
+ Future listenerEvent() async {
+ print('Call Canceled : ------->');
+
+ try {
+ FlutterCallkitIncoming.onEvent.listen((event) async {
+ switch (event!.event) {
+ case Event.actionCallIncoming:
+ print('actionCallIncoming Event Received: ------->');
+ break;
+ case Event.actionCallStart:
+ print('actionCallStart Event Received: ------->');
+ break;
+ case Event.actionCallAccept:
+ print('actionCallAccept Event Received: ------->');
+ FlutterCallkitIncoming.endAllCalls();
+ try {
+ navigateToTeleConsult();
+ // String roomID = await Utils.getStringFromPrefs(SharedPrefsConsts.ZOOM_ROOM_ID);
+ // String roomPass = await Utils.getStringFromPrefs(SharedPrefsConsts.ZOOM_ROOM_PASS);
+
+ // print('RoomID: $roomID');
+ // print('RoomPass: $roomPass');
+
+ // Future.delayed(const Duration(milliseconds: 3000), () async {
+ // // try {
+ // // String teleConsultURL = "https://teleconsult.meena-health.com/api/videocall/doVideoCall?roomid=";
+ // // await Utils.getStringFromPrefs(SharedPrefsConsts.PATIENT_MRN).then((value) {
+ // // teleConsultURL = teleConsultURL + value;
+ // // print("TeleConsult URL: $teleConsultURL");
+ // // Uri uri = Uri.parse(teleConsultURL);
+ // // launchUrl(uri, mode: LaunchMode.externalApplication, webOnlyWindowName: "TeleConsult");
+ // // });
+ //
+ // // var cameraStatus = await Permission.camera.request();
+ // // var micStatus = await Permission.microphone.request();
+ // // var permissionStatus = await [Permission.microphone, Permission.camera, Permission.notification].request();
+ // // if (permissionStatus[1]?.isDenied == true) {
+ // // await Permission.camera.request();
+ // // }
+ // // if (permissionStatus[0]?.isDenied == true) {
+ // // await Permission.microphone.request();
+ // // }
+ //
+ // Navigator.pushNamed(
+ // // context,
+ // navigatorKey.currentContext!,
+ // AppRoutes.zoomCallPage,
+ // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1),
+ // arguments: CallArguments(roomID, roomPass, "Patient", "40", "0", true, 1),
+ // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)),
+ // );
+ // // } catch (err) {
+ // // print(err);
+ // // }
+ // });
+ // String teleConsultURL = "https://teleconsult.meena-health.com/api/videocall/doVideoCall?roomid=";
+ // await Utils.getStringFromPrefs(SharedPrefsConsts.PATIENT_MRN).then((value) {
+ // teleConsultURL = teleConsultURL + value;
+ // print("TeleConsult URL: $teleConsultURL");
+ // Uri uri = Uri.parse(teleConsultURL);
+ // launchUrl(uri, mode: LaunchMode.inAppBrowserView, webOnlyWindowName: "Test");
+ // });
+ // navigatorKey.currentState!.pushNamed(AppRoutes.incomingCallScreenRoute);
+ // await Navigator.pushNamed(context, AppRoutes.incomingCallScreenRoute).then((value) {});
+ } catch (e) {
+ print("ERRORRRR!!! ${e.toString()}");
+ // await AppSharedPreferences().setObject('call_data', data);
+ // var data1 = await AppSharedPreferences().getObject('call_data');
+ }
+ break;
+ case Event.actionCallDecline:
+ print('actionCallConnected Event Received: ------->');
+ await FlutterCallkitIncoming.endAllCalls();
+ GetIt.instance().remove(key: CacheConst.isAppOpenedFromCall);
+ break;
+ case Event.actionCallEnded:
+ break;
+ case Event.actionCallTimeout:
+ break;
+ case Event.actionCallCallback:
+ break;
+ case Event.actionCallToggleHold:
+ break;
+ case Event.actionCallToggleMute:
+ break;
+ case Event.actionCallToggleDmtf:
+ break;
+ case Event.actionCallToggleGroup:
+ break;
+ case Event.actionCallToggleAudioSession:
+ break;
+ case Event.actionDidUpdateDevicePushTokenVoip:
+ break;
+ case Event.actionCallCustom:
+ break;
+ case Event.actionCallConnected:
+ print('actionCallConnected Event Received: ------->');
+ await FlutterCallkitIncoming.endAllCalls();
+ break;
+ }
+ });
+ } on Exception catch (e) {
+ print(e);
+ print("ERRORRRR!!! ${e.toString()}");
+ }
+ }
+
/// load the Privilege from service
Future loadPrivilege() async {
// ProjectViewModel projectProvider = Provider.of(context, listen: false);
diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart
index 8fa5f99..734fe0f 100644
--- a/lib/widgets/appbar/collapsing_list_view.dart
+++ b/lib/widgets/appbar/collapsing_list_view.dart
@@ -110,7 +110,7 @@ class CollapsingListView extends StatelessWidget {
letterSpacing: -0.5),
).expanded,
if (logout != null) 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 (report != null) actionButton(context, t, title: "Feedback".needTranslation, icon: AppAssets.report_icon).onPress(report!),
if (history != null) actionButton(context, t, title: "History".needTranslation, icon: AppAssets.insurance_history_icon).onPress(history!),
if (instructions != null) actionButton(context, t, title: "Instructions".needTranslation, icon: AppAssets.requests).onPress(instructions!),
if (requests != null) actionButton(context, t, title: "Requests".needTranslation, icon: AppAssets.insurance_history_icon).onPress(requests!),
@@ -162,7 +162,7 @@ class CollapsingListView extends StatelessWidget {
style: context.dynamicTextStyle(
color: AppColors.primaryRedColor,
letterSpacing: -0.4,
- fontSize: (14 - (2 * (1 - t))).f,
+ fontSize: (12 - (2 * (1 - t))).f,
fontWeight: FontWeight.lerp(
FontWeight.w300,
FontWeight.w500,
diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart
index 6904edc..8be16b7 100644
--- a/lib/widgets/chip/app_custom_chip_widget.dart
+++ b/lib/widgets/chip/app_custom_chip_widget.dart
@@ -22,6 +22,7 @@ class AppCustomChipWidget extends StatelessWidget {
this.deleteIconColor = AppColors.textColor,
this.deleteIconHasColor = false,
this.padding = EdgeInsets.zero,
+ this.isIconPNG = false,
this.onChipTap,
this.labelPadding,
this.onDeleteTap,
@@ -42,6 +43,7 @@ class AppCustomChipWidget extends StatelessWidget {
final OutlinedBorder? shape;
final EdgeInsets? padding;
final EdgeInsetsDirectional? labelPadding;
+ final bool isIconPNG;
final void Function()? onChipTap;
final void Function()? onDeleteTap;
@@ -66,8 +68,10 @@ class AppCustomChipWidget extends StatelessWidget {
child: icon.isNotEmpty
? Chip(
avatar: icon.isNotEmpty
- ? Utils.buildSvgWithAssets(
- icon: icon,
+ ? isIconPNG
+ ? Image.asset(icon, width: iconS, height: iconS)
+ : Utils.buildSvgWithAssets(
+ icon: icon,
width: iconS,
height: iconS,
iconColor: iconHasColor ? iconColor : null,
diff --git a/lib/widgets/countdown_timer.dart b/lib/widgets/countdown_timer.dart
new file mode 100644
index 0000000..165a833
--- /dev/null
+++ b/lib/widgets/countdown_timer.dart
@@ -0,0 +1,111 @@
+import 'package:flutter/material.dart';
+import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
+import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
+
+Widget buildTime(Duration duration, {bool isHomePage = false}) {
+ String twoDigits(int n) => n.toString().padLeft(2, '0');
+ final hours = twoDigits(duration.inHours);
+ final minutes = twoDigits(duration.inMinutes.remainder(60));
+ final seconds = twoDigits(duration.inSeconds.remainder(60));
+
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ buildTimeColumn(hours, "Hours".needTranslation),
+ buildTimeColumn(minutes, "Mins".needTranslation),
+ buildTimeColumn(seconds, "Secs".needTranslation, isLast: true),
+ ],
+ );
+}
+
+Widget buildTimeColumn(String time, String label, {bool isLast = false}) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Row(
+ children: [
+ buildDigit(time[0]),
+ buildDigit(time[1]),
+ if (!isLast) buildTimeSeparator(),
+ ],
+ ),
+ buildLabel(label),
+ ],
+ );
+}
+
+Widget buildDigit(String digit) {
+ return Container(
+ padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
+ // margin: const EdgeInsets.symmetric(horizontal: 2),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: ClipRect(
+ child: AnimatedSwitcher(
+ duration: const Duration(milliseconds: 600),
+ switchInCurve: Curves.easeOutExpo,
+ switchOutCurve: Curves.easeInExpo,
+ transitionBuilder: (Widget child, Animation animation) {
+ return Stack(
+ children: [
+ SlideTransition(
+ position: Tween(
+ begin: const Offset(0, -1),
+ end: const Offset(0, 1),
+ ).animate(CurvedAnimation(
+ parent: animation,
+ curve: Curves.easeOutCubic,
+ )),
+ child: FadeTransition(
+ opacity: animation,
+ child: child,
+ ),
+ ),
+ SlideTransition(
+ position: Tween