Incoming call updates

pull/107/head
haroon amjad 3 months ago
parent 5a1c58e6b2
commit 36bf63dcde

@ -30,9 +30,6 @@
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"
tools:node="remove" />
<uses-permission
android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
tools:node="remove" />
@ -91,6 +88,12 @@
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-feature
android:name="android.hardware.location.network"
android:required="false" />
@ -115,6 +118,7 @@
</queries>
<application
android:foregroundServiceType="mediaPlayback|connectedDevice|dataSync"
android:name=".Application"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_local"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

@ -0,0 +1,22 @@
export const icons = {
chatSend: require('./chat-send.png'),
defaultAvatar: require('./default-avatar.png'),
locked: require('./locked.png'),
more: require('./more.png'),
mute: require('./mute.png'),
muted: require('./muted.png'),
shareOn: require('./share-on.png'),
shareOff: require('./share-off.png'),
speakerOn: require('./speaker-on.png'),
speakerOff: require('./speaker-off.png'),
switchCamera: require('./switch-camera.png'),
hamburger: require('./hamburger.png'),
questionBalloon: require('./question-ballon.png'),
talking: require('./talking.png'),
unmute: require('./unmute.png'),
unlocked: require('./unlocked.png'),
videoOn: require('./video-on.png'),
videoOff: require('./video-off.png'),
};
export type IconTypes = keyof typeof icons;

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

@ -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"));

@ -710,7 +710,7 @@ var GET_PRESCRIPTION_INSTRUCTIONS_PDF = 'Services/ChatBot_Service.svc/REST/Chatb
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -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=';

@ -175,6 +175,8 @@ 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';
//bottom navigation//

@ -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";
}

@ -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 '';
}
}

@ -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<dynamic> backgroundMessageHandler(dynamic message) async {
callPage(String sessionID, String token) async {}
_incomingCall(Map<String, dynamic> data) async {
print('the value of the _incomingCall remote message is $data');
// 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<CacheService>().saveString(key: CacheConst.zoomRoomID, value: roomID);
// GetIt.instance<CacheService>().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: <String, dynamic>{'userId': '1a2b3c4d'},
headers: <String, dynamic>{'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<NavigationService>().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));

@ -807,7 +807,11 @@ class Utils {
static Future<bool> checkConnection({bool bypassConnectionCheck = false}) async {
if (bypassConnectionCheck) return true;
List<ConnectivityResult> 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;

@ -0,0 +1,4 @@
const Map configs = {
'ZOOM_SDK_KEY': 'b9T74nhfTg-ioP9urm970A',
'ZOOM_SDK_SECRET': 'KOzmjBNXQ1f4IPHpnngfL29uZvJMufSy2Fk8',
};

@ -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),
);

@ -349,8 +349,8 @@ class BookAppointmentsViewModel extends ChangeNotifier {
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) {

@ -151,18 +151,15 @@ class ImmediateLiveCareViewModel extends ChangeNotifier {
if (patientLiveCareHistoryList.isNotEmpty) {
if (patientLiveCareHistoryList[0].callStatus! < 4) {
countdownDuration = Duration(seconds: 0);
durationNotifier.dispose();
patientHasPendingLiveCareRequest = true;
countdownDuration = Duration(minutes: patientLiveCareHistoryList.first.watingtimeInteger!, seconds: 0);
durationNotifier = ValueNotifier<Duration>(countdownDuration);
startTimer();
} else {
patientHasPendingLiveCareRequest = false;
timer.cancel();
}
} else {
patientHasPendingLiveCareRequest = false;
timer.cancel();
}
notifyListeners();
if (onSuccess != null) {

@ -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!;

@ -77,14 +77,14 @@ class LabViewModel extends ChangeNotifier {
required this.navigationService});
initLabProvider() {
if (isLabNeedToLoad) {
// if (isLabNeedToLoad) {
patientLabOrders.clear();
filteredLabOrders.clear();
labOrderTests.clear();
isLabOrdersLoading = true;
isLabResultsLoading = true;
getPatientLabOrders();
}
// }
notifyListeners();
}
@ -94,7 +94,7 @@ class LabViewModel extends ChangeNotifier {
}
Future<void> getPatientLabOrders({Function(dynamic)? onSuccess, Function(String)? onError}) async {
if (!isLabNeedToLoad) return;
// if (!isLabNeedToLoad) return;
isLabOrdersLoading = true;
patientLabOrders.clear();
@ -289,7 +289,7 @@ class LabViewModel extends ChangeNotifier {
);
}
Future<void> getPatientLabResult(PatientLabOrdersResponseModel laborder, String procedureName, String testDescription) async {
Future<void> 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];

@ -124,19 +124,47 @@ class AppointmentCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
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,
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(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),
@ -163,6 +191,12 @@ class AppointmentCard extends StatelessWidget {
? 'Cardiology'
: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)}",
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
isIconPNG: true,
icon: getIt.get<AppState>().getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg,
iconSize: 18.h,
labelText: isLoading ? 'Cardiology' : "Patient: ${getIt.get<AppState>().getAuthenticatedUser()!.firstName!}",
).toShimmer2(isShow: isLoading),
// if (!isFromMedicalReport)
// AppCustomChipWidget(
// icon: AppAssets.appointment_time_icon,
@ -278,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(

@ -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,
),
),
],

@ -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<LoginScreen> {
required BuildContext context,
required TextEditingController? phoneNumberController,
required AuthenticationViewModel authViewModel,
}) {
}) async {
AppState appState = getIt<AppState>();
context.showBottomSheet(
isScrollControlled: true,

@ -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<BookAppointmentPage> {
).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<MyAppointmentsViewModel>(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),
),
);
}),
],
);
}),

@ -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),
],
),
],
),

@ -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<bool> askVideoCallPermission() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.camera,
Permission.microphone,
].request();
Map<Permission, PermissionStatus> 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 {

@ -66,35 +66,6 @@ class _SelectImmediateLiveCareClinicPageState extends State<SelectImmediateLiveC
child: Consumer<ImmediateLiveCareViewModel>(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,

@ -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),

@ -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<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),
);
}
}

@ -40,6 +40,8 @@ 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';
@ -93,13 +95,13 @@ class _LandingPageState extends State<LandingPage> {
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();
}
});
@ -140,10 +142,19 @@ class _LandingPageState extends State<LandingPage> {
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,
@ -203,8 +214,8 @@ class _LandingPageState extends State<LandingPage> {
Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
}),
SizedBox(height: 16.h),
Consumer<MyAppointmentsViewModel>(
builder: (context, myAppointmentsVM, child) {
Consumer2<MyAppointmentsViewModel, ImmediateLiveCareViewModel>(
builder: (context, myAppointmentsVM, immediateLiveCareVM, child) {
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
@ -248,14 +259,14 @@ class _LandingPageState extends State<LandingPage> {
indicatorLayout: PageIndicatorLayout.COLOR,
axisDirection: AxisDirection.right,
controller: _controller,
itemHeight: 270.h,
itemHeight: 255.h,
pagination: SwiperPagination(
alignment: Alignment.bottomCenter,
margin: EdgeInsets.only(top: 250.h + 8 + 24),
margin: EdgeInsets.only(top: 240.h + 8 + 24),
builder: DotSwiperPaginationBuilder(color: Color(0xffD9D9D9), activeColor: AppColors.blackBgColor),
),
itemBuilder: (BuildContext context, int index) {
return (immediateLiveCareViewModel.patientHasPendingLiveCareRequest && index == 0)
return (immediateLiveCareVM.patientHasPendingLiveCareRequest && index == 0)
? Column(
children: [
SizedBox(height: 12.h),
@ -279,7 +290,7 @@ class _LandingPageState extends State<LandingPage> {
Row(
children: [
AppCustomChipWidget(
labelText: immediateLiveCareViewModel.patientLiveCareHistoryList[0].stringCallStatus,
labelText: immediateLiveCareVM.patientLiveCareHistoryList[0].stringCallStatus,
backgroundColor: AppColors.warningColorYellow.withValues(alpha: 0.20),
textColor: AppColors.alertColor,
),
@ -287,7 +298,7 @@ class _LandingPageState extends State<LandingPage> {
AppCustomChipWidget(
icon: AppAssets.appointment_calendar_icon,
labelText: DateUtil.formatDateToDate(
DateUtil.convertStringToDate(immediateLiveCareViewModel.patientLiveCareHistoryList[0].arrivalTime), false)),
DateUtil.convertStringToDate(immediateLiveCareVM.patientLiveCareHistoryList[0].arrivalTime), false)),
],
),
Utils.buildSvgWithAssets(icon: AppAssets.waiting_icon, width: 24.h, height: 24.h),
@ -297,7 +308,7 @@ class _LandingPageState extends State<LandingPage> {
SizedBox(height: 10.h),
"Hala ${appState.getAuthenticatedUser()!.firstName}!!!".needTranslation.toText16(isBold: true),
SizedBox(height: 8.h),
"Your turn is after ${immediateLiveCareViewModel.patientLiveCareHistoryList[0].patCount} patients.".toText14(isBold: true),
"Your turn is after ${immediateLiveCareVM.patientLiveCareHistoryList[0].patCount} patients.".toText14(isBold: true),
SizedBox(height: 8.h),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -305,7 +316,7 @@ class _LandingPageState extends State<LandingPage> {
"Expected waiting time: ".toText12(isBold: true),
SizedBox(height: 8.h),
ValueListenableBuilder<Duration>(
valueListenable: immediateLiveCareViewModel.durationNotifier,
valueListenable: immediateLiveCareVM.durationNotifier,
builder: (context, duration, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
@ -561,7 +572,7 @@ class _LandingPageState extends State<LandingPage> {
Navigator.of(context).push(CustomPageRoute(page: ServicesPage()));
}),
],
).paddingSymmetrical(24.h, 0.h),
).paddingSymmetrical(24.w, 0.h),
SizedBox(
height: 340.h,
child: ListView.separated(
@ -613,7 +624,7 @@ class _LandingPageState extends State<LandingPage> {
cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true);
setState(() {});
await Future.delayed(Duration(milliseconds: 3000)).then((value) {
Navigator.pop(context);
if (mounted) Navigator.pop(context);
});
});
},

File diff suppressed because one or more lines are too long

@ -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,

@ -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<LabViewModel>()
.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<LabViewModel>().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<LabViewModel>().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<LabViewModel>()
.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<LabViewModel>().getColor(
recentLabResult.calculatedResultFlag ?? "",
)),
],
),
),
// SizedBox(
// width: 21,
// child: Utils.buildSvgWithAssets(
// icon: AppAssets.lab_result_indicator,
// width: 21,
// height: 23,
// iconColor: context.read<LabViewModel>().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,
),
),
],
));

@ -225,9 +225,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
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<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return AppCustomChipWidget(
@ -390,6 +390,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
: 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(
@ -440,9 +441,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
);
},
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),
@ -695,11 +696,12 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
),
).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,
@ -719,7 +721,8 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
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)
@ -758,7 +761,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
},
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),

@ -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,

@ -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(

File diff suppressed because it is too large Load Diff

@ -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<void>.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<String, dynamic> creationParams = <String, dynamic>{};
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,
),
),
],
),
);
}
}
}
}

@ -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<String, WidgetBuilder> 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()
};
}

@ -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<SplashPage> {
late AuthenticationViewModel authVm;
Future<void> initializeStuff() async {
listenerEvent();
Timer(
Duration(milliseconds: 500),
() async {
@ -47,22 +54,155 @@ class _SplashScreenState extends State<SplashPage> {
);
await authVm.getServicePrivilege();
Timer(Duration(seconds: 2, milliseconds: 500), () async {
bool isAppOpenedFromCall = await GetIt.instance<CacheService>().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<CacheService>().remove(key: CacheConst.isAppOpenedFromCall);
Utils.removeFromPrefs(CacheConst.isAppOpenedFromCall);
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: LandingNavigation())));
Navigator.pushReplacementNamed(
// context,
GetIt.instance<NavigationService>().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<void> 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<CacheService>().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<ProjectViewModel>(context, listen: false);

@ -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,

@ -59,7 +59,8 @@ dependencies:
geolocator: ^14.0.2
dropdown_search: ^6.0.2
google_maps_flutter: ^2.13.1
flutter_zoom_videosdk: ^2.1.10
flutter_zoom_videosdk: 2.1.10
dart_jsonwebtoken: ^3.2.0
dartz: ^0.10.1
equatable: ^2.0.7
google_api_availability: ^5.0.1
@ -79,6 +80,7 @@ dependencies:
path_provider: ^2.0.8
open_filex: ^4.7.0
flutter_swiper_view: ^1.1.8
flutter_callkit_incoming: ^3.0.0
location: ^8.0.1
gms_check: ^1.0.4
@ -108,6 +110,7 @@ flutter:
- assets/images/png/body_parts/female/
- assets/images/svg/
- assets/images/png/
- assets/images/png/zoom/
- assets/animations/
- assets/animations/lottie/

Loading…
Cancel
Save