diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f5b408d8..762c964d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,8 +42,8 @@ tools:node="remove" /> - - + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index c25d80ef..8db00a5f 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -1289,7 +1289,7 @@ "quickLinks": "روابط سريعة", "viewMedicalFileLandingPage": "عرض الملف الطبي", "immediateLiveCareRequest": "طلب لايف كير فوري", - "yourTurnIsAfterPatients": "دورك بعد {count} مريض. يرجى الانتظار حتى يحين دورك، وسيناديك الطبيب قريباً.", + "yourTurnIsAfterPatients": "أمامك {count} مريض. سيقوم الطبيب بالتواصل معكً.", "dontHaveHHCOrders": "ليس لديك أي أوامر رعاية صحية منزلية حتى الآن.", "hhcOrders": "أوامر الرعاية الصحية المنزلية", "requestedServices": "الخدمات المطلوبة", @@ -1825,5 +1825,6 @@ "rechargePageKey": "اختر المستفيد", "selectFamilyFile": "اختيار ", "ancillaryOrdersListNew": "طلبات غير مكتملة", - "medicalReportNew": "التقارير الطبية" + "medicalReportNew": "التقارير الطبية", + "livecarePendingReuqestNew": "انتظار الطبيب" } diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 3388e54f..362d86e4 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -1279,7 +1279,7 @@ "quickLinks": "Quick Links", "viewMedicalFileLandingPage": "View medical file", "immediateLiveCareRequest": "Immediate LiveCare Request", - "yourTurnIsAfterPatients": "Your turn is after {count} patients. Please wait for your turn, The doctor will call you shortly.", + "yourTurnIsAfterPatients": "There are {count} patients ahead of you. The doctor will call you shortly.", "dontHaveHHCOrders": "You don't have any Home Health Care orders yet.", "hhcOrders": "HHC Orders", "requestedServices": "Requested Services", @@ -1815,7 +1815,8 @@ "rechargePageKey": "Recharge Wallet", "selectFamilyFile": "Select", "ancillaryOrdersListNew": "Ancillary Orders List", - "medicalReportNew": "Medical Report" + "medicalReportNew": "Medical Report", + "livecarePendingReuqestNew": "Live Care Pending Request" } diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index ff39fa70..ae4eb152 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -4,7 +4,7 @@ import 'package:hmg_patient_app_new/core/enums.dart'; 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 diff --git a/lib/features/immediate_livecare/immediate_livecare_view_model.dart b/lib/features/immediate_livecare/immediate_livecare_view_model.dart index e61292d6..8a30f154 100644 --- a/lib/features/immediate_livecare/immediate_livecare_view_model.dart +++ b/lib/features/immediate_livecare/immediate_livecare_view_model.dart @@ -53,8 +53,8 @@ class ImmediateLiveCareViewModel extends ChangeNotifier { late AppState _appState; Duration countdownDuration = Duration(minutes: 1, seconds: 0); - late ValueNotifier durationNotifier; - late Timer timer; + ValueNotifier durationNotifier = ValueNotifier(Duration(minutes: 1, seconds: 0)); + Timer? timer; bool isShowNotEligibleDialog = false; String notEligibleErrorMsg = ""; @@ -264,16 +264,20 @@ 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(); + durationNotifier.value = countdownDuration; + // Start timer if not already running + if (timer == null || !timer!.isActive) { + startTimer(); + } } else { patientHasPendingLiveCareRequest = false; + stopTimer(); // Stop timer if call status >= 4 } } else { patientHasPendingLiveCareRequest = false; + stopTimer(); // Stop timer if no history } notifyListeners(); if (onSuccess != null) { @@ -312,18 +316,35 @@ class ImmediateLiveCareViewModel extends ChangeNotifier { } void startTimer() { - timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime()); - notifyListeners(); + // Only start timer if it's not already active + if (timer == null || !timer!.isActive) { + timer = Timer.periodic(const Duration(seconds: 1), (_) { + addTime(); + }); + } } void addTime() { - final seconds = durationNotifier.value.inSeconds - 1; - if (seconds < 0) { + final currentSeconds = durationNotifier.value.inSeconds; + + if (currentSeconds <= 0) { + // Timer has reached zero, stop it timer?.cancel(); - // Handle end of timer here - // showEndMessage(); + durationNotifier.value = Duration.zero; } else { - durationNotifier.value = Duration(seconds: seconds); + // Decrease by 1 second and continue + durationNotifier.value = Duration(seconds: currentSeconds - 1); } } + + void stopTimer() { + timer?.cancel(); + } + + @override + void dispose() { + timer?.cancel(); + durationNotifier.dispose(); + super.dispose(); + } } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 01b27328..05b0bd20 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1818,5 +1818,6 @@ abstract class LocaleKeys { static const selectFamilyFile = 'selectFamilyFile'; static const ancillaryOrdersListNew = 'ancillaryOrdersListNew'; static const medicalReportNew = 'medicalReportNew'; + static const livecarePendingReuqestNew = 'livecarePendingReuqestNew'; } 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 b3801113..4bb1a0fd 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -239,8 +239,8 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.currency ?? "ريال").toLowerCase() == "ريال")), ], ).paddingSymmetrical(24.h, 0.h), - (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0.0") - // (true) + // (immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0" || immediateLiveCareVM.liveCareImmediateAppointmentFeesList.total == "0.0") + (true) ? CustomButton( text: LocaleKeys.confirmLiveCare.tr(context: context), onPressed: () async { 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 23c0c07b..0d42f7e5 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 @@ -1,5 +1,4 @@ -import 'dart:async'; - +import 'dart:ui' as ui; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; @@ -33,24 +32,19 @@ class _ImmediateLiveCarePendingRequestPageState extends State durationNotifier = ValueNotifier(immediateLiveCareViewModel.countdownDuration); - // Timer? timer; - @override void initState() { super.initState(); - scheduleMicrotask(() { - // countdownDuration = Duration(minutes: immediateLiveCareViewModel.patientLiveCareHistoryList[0].watingtimeInteger!, seconds: 0); - // durationNotifier = ValueNotifier(immediateLiveCareViewModel.countdownDuration); - // startTimer(); - }); } @override void dispose() { - // timer?.cancel(); - // durationNotifier.dispose(); + // Cancel the timer when leaving the page + try { + immediateLiveCareViewModel.stopTimer(); + } catch (e) { + // Handle case where viewModel might not be initialized + } super.dispose(); } @@ -65,7 +59,7 @@ 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/profile_settings/widgets/profile_picture_widget.dart b/lib/presentation/profile_settings/widgets/profile_picture_widget.dart index e7abcb12..6b30c0fc 100644 --- a/lib/presentation/profile_settings/widgets/profile_picture_widget.dart +++ b/lib/presentation/profile_settings/widgets/profile_picture_widget.dart @@ -135,13 +135,13 @@ class _ProfilePictureWidgetState extends State { print('✅ Profile image loaded successfully for user: $currentPatientId'); if (mounted) { _tryCacheExistingImage(); - setState(() {}); // Force rebuild to show new data + setState(() {}); // Force rebuild to show new data } }, onError: (error) { print('❌ Error loading profile image: $error'); if (mounted) { - setState(() {}); // Force rebuild to show default avatar + setState(() {}); // Force rebuild to show default avatar } }, ); @@ -365,16 +365,16 @@ class _ProfilePictureWidgetState extends State { try { print('=== Checking gallery permission ==='); - // Determine which permission to check based on platform and Android version - Permission galleryPermission; - - if (Platform.isIOS) { - galleryPermission = Permission.photos; - } else { - // Android: use photos permission which handles API level differences automatically - galleryPermission = Permission.photos; + // For Android 13+ (API 33+), the Android Photo Picker handles permissions internally + // No need to request READ_MEDIA_IMAGES or READ_EXTERNAL_STORAGE permissions + if (Platform.isAndroid) { + print('✅ Android Photo Picker will handle permissions internally'); + return true; } + // iOS: Check photos permission + Permission galleryPermission = Permission.photos; + // First check current status PermissionStatus currentStatus = await galleryPermission.status; print('Current gallery permission status: $currentStatus'); @@ -520,9 +520,7 @@ class _ProfilePictureWidgetState extends State { // Use cached decoded bytes — update cache if source data changed final String? imageData = GetIt.instance().getProfileImageData; - final String? currentHash = (imageData != null && imageData.isNotEmpty) - ? '${imageData.length}_${imageData.hashCode}' - : null; + final String? currentHash = (imageData != null && imageData.isNotEmpty) ? '${imageData.length}_${imageData.hashCode}' : null; // Re-decode only if the underlying data actually changed if (currentHash != null && currentHash != _cachedImageDataHash) { @@ -574,9 +572,7 @@ class _ProfilePictureWidgetState extends State { builder: (context, profileVm, _) { // If we already have cached bytes, show the image even while "loading" // to prevent the shimmer→image blink on page open - final bool showShimmer = profileVm.isProfileImageLoading && - _cachedImageBytes == null && - _selectedImage == null; + final bool showShimmer = profileVm.isProfileImageLoading && _cachedImageBytes == null && _selectedImage == null; return Center( child: Stack( @@ -644,4 +640,3 @@ class _ProfilePictureWidgetState extends State { ); } } - diff --git a/lib/widgets/countdown_timer.dart b/lib/widgets/countdown_timer.dart index f809d9f5..e51f9291 100644 --- a/lib/widgets/countdown_timer.dart +++ b/lib/widgets/countdown_timer.dart @@ -23,93 +23,42 @@ Widget buildTime(Duration duration, {bool isHomePage = false}) { Widget buildTimeColumn(String time, String label, {bool isLast = false}) { return Column( - children: [ Row( - children: [ Column( children: [ Row( children: [ buildDigit(time[0]), + SizedBox(width: 2.w), buildDigit(time[1]), ], ), + SizedBox(height: 4.h), buildLabel(label), ], ), if (!isLast) ...[ - SizedBox(width: 8.w,), + SizedBox(width: 8.w), buildTimeSeparator(), - SizedBox(width: 8.w,), + SizedBox(width: 8.w), ], ], ), - ], ); } Widget buildDigit(String digit) { - return - // Container( - // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - // // margin: const EdgeInsets.symmetric(horizontal: 2), - // decoration: BoxDecoration( - // color: AppColors.whiteColor, - // 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: 27.f, - ), - ), - ), - ); - // ); + return Text( + digit, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black, + fontSize: 27.f, + ), + ); } Widget buildLabel(String label) { @@ -118,12 +67,13 @@ Widget buildLabel(String label) { Widget buildTimeSeparator() { return Padding( - padding: EdgeInsets.symmetric(horizontal: 2.0), + padding: EdgeInsets.only(bottom: 16.h), child: Text( ":", style: TextStyle( color: Colors.black, fontSize: 28.f, + fontWeight: FontWeight.bold, ), ), ); diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index f7665a56..3fa7d061 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -10,8 +10,11 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:image_picker_android/image_picker_android.dart'; +import 'package:image_picker_platform_interface/image_picker_platform_interface.dart'; final ImagePicker picker = ImagePicker(); +final ImagePickerPlatform _pickerPlatform = ImagePickerPlatform.instance; class ImageOptions { static void showImageOptionsNew( @@ -214,25 +217,47 @@ class ImageOptions { } void galleryImageAndroid(Function(String, File) image) async { - XFile? pickedFile = await picker.pickImage(source: ImageSource.gallery, imageQuality: 20); - if (pickedFile == null) return; + // Enable Android Photo Picker if available + if (_pickerPlatform is ImagePickerAndroid) { + (_pickerPlatform as ImagePickerAndroid).useAndroidPhotoPicker = true; + } + + try { + final XFile? pickedFile = await _pickerPlatform.getImageFromSource(source: ImageSource.gallery, options: const ImagePickerOptions(imageQuality: 20)); + + if (pickedFile == null) return; - File _image = File(pickedFile.path); - String fileName = _image.path; - var bytes = File(fileName).readAsBytesSync(); - String base64Encode = base64.encode(bytes); - image(base64Encode, _image); + File _image = File(pickedFile.path); + String fileName = _image.path; + var bytes = File(fileName).readAsBytesSync(); + String base64Encode = base64.encode(bytes); + image(base64Encode, _image); + } catch (e) { + print('Error picking image: $e'); + } } void cameraImageAndroid(Function(String, File) image) async { - XFile? pickedFile = await picker.pickImage(source: ImageSource.camera, imageQuality: 20); - if (pickedFile == null) return; + if (_pickerPlatform is ImagePickerAndroid) { + (_pickerPlatform as ImagePickerAndroid).useAndroidPhotoPicker = true; + } - File _image = File(pickedFile.path); - String fileName = _image.path; - var bytes = File(fileName).readAsBytesSync(); - String base64Encode = base64.encode(bytes); - image(base64Encode, _image); + try { + final XFile? pickedFile = await _pickerPlatform.getImageFromSource( + source: ImageSource.camera, + options: const ImagePickerOptions(imageQuality: 20), + ); + + if (pickedFile == null) return; + + File _image = File(pickedFile.path); + String fileName = _image.path; + var bytes = File(fileName).readAsBytesSync(); + String base64Encode = base64.encode(bytes); + image(base64Encode, _image); + } catch (e) { + print('Error taking picture: $e'); + } } class _BottomSheet extends StatelessWidget { diff --git a/pubspec.yaml b/pubspec.yaml index 14ad3913..a27fb251 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -90,6 +90,8 @@ dependencies: flutter_callkit_incoming: ^3.0.0 in_app_update: ^4.2.5 # device_preview: ^1.3.1 + image_picker_android: ^0.8.13+16 + image_picker_platform_interface: ^2.10.0 location: ^8.0.1 gms_check: ^1.0.4