Compare commits

..

10 Commits

@ -21,7 +21,7 @@
"mySchedule": "My Schedule",
"logout": "Logout",
"respirationRate": "Respiration Rate",
"bookAppo": "New Appointment",
"bookAppo": "Book Appointment",
"searchBy": "Search By:",
"clinic": "Clinic",
"byClinic": "By Clinic",

@ -86,7 +86,7 @@
<key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access to enable virtual consultation between patient &amp; doctor</string>
<key>NSMotionUsageDescription</key>
<string>This app requires motion detection access to function properly.</string>
<string>This app requires access to motion detection to count your daily steps.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to select image as document &amp; upload it.</string>
<key>NSPhotoLibraryAddUsageDescription</key>

@ -210,8 +210,8 @@ class ApiClientImp implements ApiClient {
body['TokenID'] = "@dm!n";
}
body['TokenID'] = "@dm!n";
// body['PatientID'] = 1307867;
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 3310954;
// body['PatientID'] = 53320;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;

@ -27,7 +27,7 @@ class RequestUtils {
}) {
bool fileNo = false;
if (nationId.isNotEmpty) {
final numericRegex = RegExp(r'^[0-9]+$');
final numericRegex = RegExp(r'^[0-9]+$');
fileNo = nationId.length < 10 && numericRegex.hasMatch(nationId);
//fileNo = nationId.length < 10 && nationId.isNumericOnly() ;
if (fileNo) {
@ -40,14 +40,23 @@ class RequestUtils {
if (zipCode == "0") {
request.patientMobileNumberOthers = phoneNumber;
} else {
request.patientMobileNumber = int.parse(phoneNumber);
// Remove any non-numeric characters before parsing
final numericPhone = phoneNumber.replaceAll(RegExp(r'[^0-9]'), '');
final parsedPhone = int.tryParse(numericPhone);
if (parsedPhone != null) {
request.patientMobileNumber = parsedPhone;
} else {
// If parsing fails, use as string in Others field
request.patientMobileNumberOthers = phoneNumber;
}
}
}
request.oTPSendType = otpTypeEnum.toInt(); // could map OTPTypeEnum if needed
request.zipCode = zipCode; // or countryCode if defined elsewhere
if (isForRegister) {
request.patientIdentificationID = int.parse(nationId);
final parsedNationId = int.tryParse(nationId.replaceAll(RegExp(r'[^0-9]'), ''));
request.patientIdentificationID = parsedNationId ?? 0;
request.searchType = 1;
request.isHijri = calenderType.toInt;
request.patientID = patientId;
@ -56,7 +65,7 @@ class RequestUtils {
request.isDentalAllowedBackend = false;
} else {
if (fileNo) {
request.patientID = patientId ?? int.parse(nationId);
request.patientID = patientId ?? (int.tryParse(nationId.replaceAll(RegExp(r'[^0-9]'), '')) ?? 0);
request.patientIdentificationID = request.nationalID;
request.searchType = 2;
} else {
@ -71,15 +80,15 @@ class RequestUtils {
static dynamic getCommonRequestWelcome(
{required String phoneNumber,
required OTPTypeEnum otpTypeEnum,
required String? deviceToken,
required bool patientOutSA,
required String? loginTokenID,
RegistrationDataModelPayload? registeredData,
int? patientId,
required String nationIdText,
required String countryCode,
required int loginType}) {
required OTPTypeEnum otpTypeEnum,
required String? deviceToken,
required bool patientOutSA,
required String? loginTokenID,
RegistrationDataModelPayload? registeredData,
int? patientId,
required String nationIdText,
required String countryCode,
required int loginType}) {
bool fileNo = false;
if (nationIdText.isNotEmpty) {
final numericRegex = RegExp(r'^[0-9]+$');
@ -91,8 +100,17 @@ class RequestUtils {
request.patientMobileNumberOthers = phoneNumber;
request.mobileNo = phoneNumber;
} else {
request.patientMobileNumber = int.parse(phoneNumber);
request.mobileNo = '0$phoneNumber';
// Remove any non-numeric characters before parsing
final numericPhone = phoneNumber.replaceAll(RegExp(r'[^0-9]'), '');
final parsedPhone = int.tryParse(numericPhone);
if (parsedPhone != null) {
request.patientMobileNumber = parsedPhone;
request.mobileNo = '0$numericPhone';
} else {
// If parsing fails, use as string in Others field
request.patientMobileNumberOthers = phoneNumber;
request.mobileNo = phoneNumber;
}
}
request.deviceToken = deviceToken;
request.projectOutSA = patientOutSA;
@ -106,8 +124,8 @@ class RequestUtils {
request.searchType = registeredData.searchType != null
? registeredData.searchType
: fileNo
? 1
: 2;
? 1
: 2;
request.patientID = registeredData.patientId ?? 0;
request.patientIdentificationID = request.nationalID = (registeredData.patientIdentificationId ?? 0);
request.dob = registeredData.dob;
@ -115,7 +133,8 @@ class RequestUtils {
log("nationIdText: ${nationIdText}");
} else {
if (fileNo) {
request.patientID = patientId ?? int.parse(nationIdText);
final numericNationId = nationIdText.replaceAll(RegExp(r'[^0-9]'), '');
request.patientID = patientId ?? (int.tryParse(numericNationId) ?? 0);
request.patientIdentificationID = request.nationalID = '0';
request.searchType = 2;
//TODO: Issue HEre is Not Login
@ -155,8 +174,17 @@ class RequestUtils {
request.patientMobileNumberOthers = mobileNumber;
request.mobileNo = mobileNumber;
} else {
request.patientMobileNumber = int.parse(mobileNumber);
request.mobileNo = '0$mobileNumber';
// Remove any non-numeric characters before parsing
final numericMobile = mobileNumber.replaceAll(RegExp(r'[^0-9]'), '');
final parsedMobile = int.tryParse(numericMobile);
if (parsedMobile != null) {
request.patientMobileNumber = parsedMobile;
request.mobileNo = '0$numericMobile';
} else {
// If parsing fails, use as string in Others field
request.patientMobileNumberOthers = mobileNumber;
request.mobileNo = mobileNumber;
}
}
}
request.projectOutSA = patientOutSA;
@ -240,16 +268,16 @@ class RequestUtils {
"Patientobject": {
"TempValue": true,
"PatientIdentificationType": (isDubai
? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1)
: appState.getNHICUserData.idNumber!.substring(0, 1)) ==
"1"
? appState.getUserRegistrationPayload.patientIdentificationId?.toString().substring(0, 1)
: appState.getNHICUserData.idNumber!.substring(0, 1)) ==
"1"
? 1
: 2,
"PatientIdentificationNo":
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"MobileNumber": appState.getUserRegistrationPayload.patientMobileNumber ?? 0,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"FirstNameN": isDubai ? "..." : appState.getNHICUserData.firstNameAr,
@ -266,31 +294,31 @@ class RequestUtils {
"DateofBirthN": date,
"EmailAddress": emailAddress,
"SourceType": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
appState.getUserRegistrationPayload.zipCode == '+966')
? "1"
: "2",
"PreferredLanguage": appState.getLanguageCode() == "ar" ? (isDubai ? "1" : 1) : (isDubai ? "2" : 2),
"Marital": isDubai
? (maritalStatus == MaritalStatusTypeEnum.single
? '0'
: maritalStatus == MaritalStatusTypeEnum.married
? '1'
: '2')
? '0'
: maritalStatus == MaritalStatusTypeEnum.married
? '1'
: '2')
: (appState.getNHICUserData.maritalStatusCode == 'U'
? '0'
: appState.getNHICUserData.maritalStatusCode == 'M'
? '1'
: '2'),
? '0'
: appState.getNHICUserData.maritalStatusCode == 'M'
? '1'
: '2'),
},
"PatientIdentificationID":
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
isDubai ? appState.getUserRegistrationPayload.patientIdentificationId.toString() : appState.getNHICUserData.idNumber.toString(),
"PatientMobileNumber": appState.getUserRegistrationPayload.patientMobileNumber.toString()[0] == '0'
? appState.getUserRegistrationPayload.patientMobileNumber
: '0${appState.getUserRegistrationPayload.patientMobileNumber}',
"DOB": dob,
"IsHijri": appState.getUserRegistrationPayload.isHijri,
"PatientOutSA": (appState.getUserRegistrationPayload.zipCode == CountryEnum.saudiArabia.countryCode ||
appState.getUserRegistrationPayload.zipCode == '+966')
appState.getUserRegistrationPayload.zipCode == '+966')
? 0
: 1,
"isDentalAllowedBackend": appState.getUserRegistrationPayload.isDentalAllowedBackend,
@ -314,7 +342,9 @@ class RequestUtils {
request.sharedPatientId = 0;
request.sharedPatientIdentificationId = nationalIDorFile;
} else if (loginType == 2) {
request.sharedPatientId = int.parse(nationalIDorFile);
// Remove any non-numeric characters before parsing
final numericId = nationalIDorFile.replaceAll(RegExp(r'[^0-9]'), '');
request.sharedPatientId = int.tryParse(numericId) ?? 0;
request.sharedPatientIdentificationId = '';
}
request.searchType = loginType;
@ -325,4 +355,4 @@ class RequestUtils {
request.isDentalAllowedBackend = false;
return request;
}
}
}

@ -676,7 +676,6 @@ class BookAppointmentsViewModel extends ChangeNotifier {
(failure) async {
isDoctorsListLoading = false;
if (onError != null) onError(LocaleKeys.noDoctorFound.tr());
notifyListeners();
},
(apiResponse) {
@ -691,7 +690,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
setIsNearestAppointmentSelected(isNearest);
// setIsNearestAppointmentSelected(isNearest);
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -78,7 +78,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
List<PatientAppointmentHistoryResponseModel> patientUpcomingAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientArrivedAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientAllArrivedAppointmentsHistoryList = [];
// List<PatientAppointmentHistoryResponseModel> patientAllArrivedAppointmentsHistoryList = [];
List<PatientAppointmentHistoryResponseModel> patientMyDoctorsList = [];
@ -174,7 +174,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
patientArrivedAppointmentsHistoryList.clear();
patientAllArrivedAppointmentsHistoryList.clear();
// patientAllArrivedAppointmentsHistoryList.clear();
patientEyeMeasurementsAppointmentsHistoryList.clear();
isMyAppointmentsLoading = true;
isTimeLineAppointmentsLoading = true;
@ -290,7 +290,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientAppointmentsByHospital.clear();
patientAppointmentsViewList.clear();
patientAllArrivedAppointmentsHistoryList.clear();
// patientAllArrivedAppointmentsHistoryList.clear();
filteredAppointmentList.clear();
patientAppointmentsHistoryList.clear();
patientUpcomingAppointmentsHistoryList.clear();
@ -329,7 +329,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
isMyAppointmentsLoading = false;
isAppointmentDataToBeLoaded = false;
if (!isForTimeLine) {
patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
// patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
isArrivedAppointmentDataLoaded = true;
isFullArrivedAppointmentsLoaded = true;
}
@ -363,11 +363,25 @@ class MyAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
changeTabToArrived() {
if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
selectedTabIndex = 1;
updateListWRTTab(1);
notifyListeners();
}
}
/// Loads full arrived appointments list if not already loaded
/// This is called when navigating to My Appointments page from landing page
Future<void> loadFullArrivedAppointmentsIfNeeded({Function(dynamic)? onSuccess, Function(String)? onError}) async {
// Skip if already loaded
if (isFullArrivedAppointmentsLoaded) {
// Auto-switch to Arrived tab if no upcoming appointments (even when data is already loaded)
if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
selectedTabIndex = 1;
updateListWRTTab(1);
notifyListeners();
}
return;
}
@ -383,7 +397,7 @@ class MyAppointmentsViewModel extends ChangeNotifier {
// Handle error
} else if (apiResponse.messageStatus == 1) {
patientArrivedAppointmentsHistoryList = apiResponse.data!;
patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
// patientAllArrivedAppointmentsHistoryList = apiResponse.data!;
isArrivedAppointmentDataLoaded = true;
isFullArrivedAppointmentsLoaded = true;
@ -394,8 +408,14 @@ class MyAppointmentsViewModel extends ChangeNotifier {
isMyAppointmentsLoading = false;
// Update filtered list based on current tab
updateListWRTTab(selectedTabIndex);
// Auto-switch to Arrived tab if no upcoming appointments
if (patientUpcomingAppointmentsHistoryList.isEmpty && patientArrivedAppointmentsHistoryList.isNotEmpty && selectedTabIndex == 0) {
selectedTabIndex = 1;
updateListWRTTab(1);
} else {
// Update filtered list based on current tab
updateListWRTTab(selectedTabIndex);
}
notifyListeners();
if (onSuccess != null) {
@ -414,9 +434,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
availableFilters.add(AppointmentListingFilters.LIVECARE);
}
if (filteredAppointmentList.any((element) => element.isLiveCareAppointment == false)) {
availableFilters.add(AppointmentListingFilters.WALKIN);
}
// if (filteredAppointmentList.any((element) => element.isLiveCareAppointment == false)) {
// availableFilters.add(AppointmentListingFilters.WALKIN);
// }
if (filteredAppointmentList.any((element) => AppointmentType.isArrived(element) == true)) {
availableFilters.add(AppointmentListingFilters.ARRIVED);
@ -853,15 +873,15 @@ class MyAppointmentsViewModel extends ChangeNotifier {
selectedFilter = [];
// if(previouslySelectedTab == selectedTabIndex ) return;
switch (index) {
// case 0:
// filteredAppointmentList.clear();
// filteredAppointmentList.addAll(patientAppointmentsHistoryList);
// break;
case 0:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientAppointmentsHistoryList);
break;
case 1:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientUpcomingAppointmentsHistoryList);
break;
case 2:
case 1:
filteredAppointmentList.clear();
filteredAppointmentList.addAll(patientArrivedAppointmentsHistoryList);
break;
@ -887,11 +907,12 @@ class MyAppointmentsViewModel extends ChangeNotifier {
this.end = end;
isDateFilterSelected = true;
List<PatientAppointmentHistoryResponseModel> sourceList = [];
if (selectedTabIndex == 0) {
sourceList = patientAppointmentsHistoryList;
} else if (selectedTabIndex == 1) {
// if (selectedTabIndex == 0) {
// sourceList = patientAppointmentsHistoryList;
// } else
if (selectedTabIndex == 0) {
sourceList = patientUpcomingAppointmentsHistoryList;
} else if (selectedTabIndex == 2) {
} else if (selectedTabIndex == 1) {
sourceList = patientArrivedAppointmentsHistoryList;
}
// if (isDateFilterSelected) sourceList = filteredAppointmentList;

@ -191,7 +191,9 @@ class PrescriptionsViewModel extends ChangeNotifier {
result.fold(
// (failure) async => await errorHandlerService.handleError(failure: failure),
(failure) async {
onError!(failure.message);
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) async {
if (apiResponse.messageStatus == 2) {

@ -70,9 +70,7 @@ class ProfilePictureViewModel extends ChangeNotifier {
try {
_cachedImageBytes = base64Decode(imageData);
_cachedImageDataHash = '${imageData.length}_${imageData.hashCode}';
print('✅ Cached existing profile image');
} catch (e) {
print('❌ Error caching existing image: $e');
_cachedImageBytes = null;
_cachedImageDataHash = null;
}
@ -85,7 +83,6 @@ class ProfilePictureViewModel extends ChangeNotifier {
final currentPatientId = _appState.getAuthenticatedUser()?.patientId;
if (currentPatientId != null && currentPatientId != _currentPatientId) {
print('🔄 User switched detected: $_currentPatientId -> $currentPatientId');
_handleUserSwitch(currentPatientId);
return true;
}
@ -97,8 +94,6 @@ class ProfilePictureViewModel extends ChangeNotifier {
final oldPatientId = _currentPatientId;
_currentPatientId = newPatientId;
print('🧹 Clearing cache for old user: $oldPatientId');
// Clear AppState cache
_appState.clearProfileImageCache();
@ -113,17 +108,14 @@ class ProfilePictureViewModel extends ChangeNotifier {
notifyListeners();
// Load new user's profile image
print('📥 Loading profile image for new user: $newPatientId');
_profileSettingsViewModel.getProfileImage(
patientID: newPatientId,
forceRefresh: true,
onSuccess: (data) {
print('✅ Profile image loaded successfully for user: $newPatientId');
_tryCacheExistingImage();
notifyListeners();
},
onError: (error) {
print('❌ Error loading profile image: $error');
notifyListeners();
},
);
@ -133,22 +125,18 @@ class ProfilePictureViewModel extends ChangeNotifier {
void loadProfileImage({bool forceRefresh = false}) {
// Check if profile image is already loaded in AppState (skip if forcing refresh)
if (!forceRefresh && _appState.getProfileImageData != null && _appState.getProfileImageData!.isNotEmpty) {
print('✅ Profile image already cached in AppState');
return;
}
final patientID = _appState.getAuthenticatedUser()?.patientId;
if (patientID == null) {
print('⚠️ Cannot load profile image - no authenticated user');
return;
}
print('📥 Loading profile image for patient: $patientID (forceRefresh: $forceRefresh)');
_profileSettingsViewModel.getProfileImage(
patientID: patientID,
forceRefresh: forceRefresh,
onSuccess: (data) {
print('✅ Profile image loaded successfully');
_tryCacheExistingImage();
notifyListeners();
},
@ -188,10 +176,6 @@ class ProfilePictureViewModel extends ChangeNotifier {
false, // Don't show files option, only camera and gallery
(base64String, file) async {
try {
print('=== Starting image processing ===');
print('File path: ${file.path}');
print('File exists: ${await file.exists()}');
print('Original file size: ${await file.length() / 1024} KB');
// Compress and resize the image
print('Calling compressAndResizeImage...');
@ -266,14 +250,10 @@ class ProfilePictureViewModel extends ChangeNotifier {
onError('No authenticated user found');
return;
}
print('📤 Uploading profile image for patient: $patientID');
_profileSettingsViewModel.uploadProfileImage(
patientID: patientID,
imageData: base64String,
onSuccess: (data) async {
print('✅ Profile image uploaded successfully');
// Clear old cache first to ensure fresh data
_cachedImageBytes = null;
_cachedImageDataHash = null;
@ -287,9 +267,13 @@ class ProfilePictureViewModel extends ChangeNotifier {
// Increment version to trigger targeted rebuild (no full screen refresh)
_profileImageVersion.value++;
print('🔄 Profile image version updated to ${_profileImageVersion.value} (targeted rebuild)');
onSuccess(data);
final String successMessage = data is String
? data
: (data is Map && data['message'] != null
? data['message'].toString()
: data?.toString() ?? 'Success');
onSuccess(successMessage);
},
onError: (error) {
print('❌ Error uploading profile image: $error');
@ -308,7 +292,6 @@ class ProfilePictureViewModel extends ChangeNotifier {
try {
_cachedImageBytes = base64Decode(imageData!);
_cachedImageDataHash = currentHash;
print('🔄 Updated cached image bytes');
} catch (e) {
print('❌ Error decoding profile image: $e');
_cachedImageBytes = null;
@ -326,7 +309,6 @@ class ProfilePictureViewModel extends ChangeNotifier {
_cachedImageDataHash = null;
_selectedImage = null;
notifyListeners();
print('🧹 Cleared all profile picture cache');
}
/// Check if we should show shimmer loading

@ -69,12 +69,18 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
widget.patientAppointmentHistoryResponseModel.clinicID,
widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onSuccess: (val) {
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null) {
// if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null && myAppointmentsViewModel.patientAppointmentShareResponseModel != null) {
if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null &&
myAppointmentsViewModel.patientAppointmentShareResponseModel?.patientShareWithTax != null &&
myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel?.minLimit?.amount != null &&
myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel?.maxLimit?.amount != null) {
if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) {
setState(() {
isShowTamara = true;
});
if (mounted) {
setState(() {
isShowTamara = true;
});
}
}
}
});

@ -47,8 +47,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
myAppointmentsViewModel.initAppointmentsViewModel();
myAppointmentsViewModel.getPatientAppointments(true, false, isForTimeLine: false);
}
// Load full arrived appointments if not already loaded
myAppointmentsViewModel.loadFullArrivedAppointmentsIfNeeded();
myAppointmentsViewModel.changeTabToArrived();
});
super.initState();
}
@ -71,9 +70,9 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)),
// CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)),
CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)),
CustomTabBarModel(null, LocaleKeys.completed.tr(context: context)),
CustomTabBarModel(null, LocaleKeys.arrived.tr(context: context)),
],
onTabChange: (index) {
setState(() {
@ -82,6 +81,10 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
myAppointmentsViewModel.onTabChange(index);
myAppointmentsViewModel.updateListWRTTab(index);
context.read<DateRangeSelectorRangeViewModel>().flush();
if(index == 1) {
myAppointmentsViewModel.loadFullArrivedAppointmentsIfNeeded();
}
},
).paddingSymmetrical(24.h, 0.h),
// Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
@ -152,7 +155,7 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
? myAppointmentsVM.patientAppointmentsViewList.length
: 1,
itemBuilder: (context, index) {
final isExpanded = myAppointmentsVM.selectedTabIndex == 1 ? true : expandedIndex == index;
final isExpanded = myAppointmentsVM.selectedTabIndex == 0 ? true : expandedIndex == index;
return myAppointmentsVM.isMyAppointmentsLoading
? Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),

@ -1021,6 +1021,7 @@ class _SelectClinicPageState extends State<SelectClinicPage> {
void onClinicSelected(GetClinicsListResponseModel clinic) {
bookAppointmentsViewModel.setSelectedClinic(clinic);
bookAppointmentsViewModel.setIsDoctorsListLoading(true);
searchEditingController.text = "";
if (clinic.isLiveCareClinicAndOnline ?? false) {
Navigator.of(context).push(
CustomPageRoute(

@ -48,7 +48,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
Clarity.setCurrentScreenName('Select Doctor Page');
_scrollController = ScrollController();
scheduleMicrotask(() {
bookAppointmentsViewModel.setIsNearestAppointmentSelected(true);
bookAppointmentsViewModel.setIsNearestAppointmentSelected(false);
if (bookAppointmentsViewModel.isLiveCareSchedule) {
bookAppointmentsViewModel.getLiveCareDoctorsList();
} else {
@ -57,8 +57,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
} else if (bookAppointmentsViewModel.isGetDocForHealthCal) {
bookAppointmentsViewModel.getDoctorsListByHealthCal();
} else {
bookAppointmentsViewModel.setIsNearestAppointmentSelected(true);
bookAppointmentsViewModel.getDoctorsList(isNearest: true);
bookAppointmentsViewModel.getDoctorsList(isNearest: false);
}
}
});

@ -144,8 +144,9 @@ class DoctorCard extends StatelessWidget {
spacing: 3.h,
runSpacing: 4.h,
children: [
bookAppointmentsViewModel.isNearestAppointmentSelected
? doctorsListResponseModel.nearestFreeSlot != null
// bookAppointmentsViewModel.isNearestAppointmentSelected
// ?
doctorsListResponseModel.nearestFreeSlot != null
? AppCustomChipWidget(
labelText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot)),
// richText: (isLoading ? "Cardiologist" : DateUtil.getDateStringForNearestSlot(doctorsListResponseModel.nearestFreeSlot))
@ -155,7 +156,8 @@ class DoctorCard extends StatelessWidget {
textColor: AppColors.successColor,
).toShimmer2(isShow: isLoading)
: SizedBox.shrink()
: SizedBox.shrink(),
// : SizedBox.shrink()
,
AppCustomChipWidget(
labelText: "${isLoading ? "Cardiologist" : doctorsListResponseModel.clinicName}",
).toShimmer2(isShow: isLoading),

@ -120,12 +120,23 @@ class _LandingPageState extends State<LandingPage> {
authVM = context.read<AuthenticationViewModel>();
habibWalletVM = context.read<HabibWalletViewModel>();
appointmentRatingViewModel = context.read<AppointmentRatingViewModel>();
appState = getIt.get<AppState>();
authVM.savePushTokenToAppState();
if (mounted) {
authVM.checkLastLoginStatus(() {
showQuickLogin(context);
});
final user = appState.getAuthenticatedUser();
final hasUserQuickLoginData =
user != null &&
(user.mobileNumber?.isNotEmpty ?? false) &&
(user.patientIdentificationNo?.isNotEmpty ?? false);
if (hasUserQuickLoginData) {
authVM.checkLastLoginStatus(() {
if (mounted) {
showQuickLogin(context);
}
});
}
}
_scrollController.addListener(() {
@ -172,12 +183,19 @@ class _LandingPageState extends State<LandingPage> {
appointmentNo,
projectID,
onSuccess: ((response) {
// Only open dialog if details are loaded AND widget is mounted
if (!mounted || appointmentRatingViewModel.appointmentDetails == null) return;
appointmentRatingViewModel.setClinicOrDoctor(false);
appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context));
appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context));
appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr());
appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr());
openLastRating();
appState.setRatedVisible(true);
}),
onError: (String errorMessage) {
// Silently mark as rated to prevent retry loops
if (mounted) appState.setRatedVisible(true);
},
);
}
},
@ -1458,6 +1476,7 @@ class _LandingPageState extends State<LandingPage> {
}
openLastRating() {
if (!mounted) return;
showCommonBottomSheetWithoutHeight(
context,
titleWidget: Selector<AppointmentRatingViewModel, String?>(

@ -74,7 +74,7 @@ class _PreferredLanguageWidgetState extends State<PreferredLanguageWidget> {
callBackFunc: () async {
Navigator.of(GetIt.instance<NavigationService>().navigatorKey.currentContext!).pop();
profileSettingsViewModel.getProfileSettings();
}, isFullScreen: false, isAutoDismiss: true);
}, isFullScreen: false, isAutoDismiss: true, isCloseButtonVisible: false);
},
onError: (error) {
LoaderBottomSheet.hideLoader();

@ -134,7 +134,7 @@ class _UpdateEmailDialogState extends State<UpdateEmailDialog> {
callBackFunc: () async {
Navigator.of(getIt<NavigationService>().navigatorKey.currentContext!).pop();
profileSettingsViewModel!.getProfileSettings();
}, isFullScreen: false, isAutoDismiss: true);
}, isFullScreen: false, isAutoDismiss: true, isCloseButtonVisible: false);
},
onError: (error) {
LoaderBottomSheet.hideLoader();

@ -29,11 +29,19 @@ class OrganSelectorPage extends StatefulWidget {
State<OrganSelectorPage> createState() => _OrganSelectorPageState();
}
class _OrganSelectorPageState extends State<OrganSelectorPage> {
class _OrganSelectorPageState extends State<OrganSelectorPage> with SingleTickerProviderStateMixin {
static const double _expandedBodyZoomFactor = 1.08;
static const double _expandedSheetHeightFactor = 0.3;
static const bool _enableTutorialOverlay = false;
static const bool _enableSwipeToFlip = false;
late final AppState _appState;
late final DialogService dialogService;
late final CacheService cacheService;
late final AnimationController _introZoomHintController;
late final Animation<double> _introZoomHintAnimation;
bool _showTutorial = false;
bool _hasPlayedIntroZoomHint = false;
@override
void initState() {
@ -41,10 +49,52 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
_appState = getIt.get<AppState>();
dialogService = getIt<DialogService>();
cacheService = getIt<CacheService>();
_introZoomHintController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
)..addListener(() {
if (mounted) setState(() {});
});
_introZoomHintAnimation = TweenSequence<double>([
TweenSequenceItem(
tween: Tween<double>(begin: 1.0, end: 1.25),
weight: 45,
),
TweenSequenceItem(
tween: Tween<double>(begin: 1.25, end: 1.0),
weight: 55,
),
]).animate(CurvedAnimation(
parent: _introZoomHintController,
curve: Curves.easeInOut,
));
_checkAndShowTutorial();
_playIntroZoomHint();
}
@override
void dispose() {
_introZoomHintController.dispose();
super.dispose();
}
Future<void> _playIntroZoomHint() async {
if (_hasPlayedIntroZoomHint) return;
_hasPlayedIntroZoomHint = true;
await Future.delayed(const Duration(milliseconds: 700));
if (!mounted) return;
_introZoomHintController.forward(from: 0);
}
Future<void> _checkAndShowTutorial() async {
if (!_enableTutorialOverlay) {
_showTutorial = false;
return;
}
// final hasSeenTutorial = cacheService.getBool(key: CacheConst.organSelectorTutorialShown) ?? false;
// if (!hasSeenTutorial) {
// Show tutorial after a short delay to ensure the screen is fully built
@ -118,7 +168,11 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
Expanded(
child: Stack(
children: [
_buildBodyViewer(viewModel),
SafeArea(
top: false,
bottom: false,
child: _buildBodyViewer(viewModel),
),
_buildViewToggleButtons(viewModel),
// _buildViewZoomButtons(viewModel),
_buildBottomSheet(viewModel),
@ -131,7 +185,7 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
),
),
// Tutorial overlay
if (_showTutorial)
if (_enableTutorialOverlay && _showTutorial)
PinchZoomTutorialOverlay(
onComplete: _onTutorialComplete,
),
@ -152,7 +206,13 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
height: 24.h,
),
padding: EdgeInsetsDirectional.only(start: 12, end: 12),
onPressed: () => Navigator.pop(context),
onPressed: () {
// Clear selected organs and sheet status when going back
final viewModel = context.read<SymptomsCheckerViewModel>();
viewModel.clearAllSelections();
viewModel.setBottomSheetExpanded(false);
Navigator.pop(context);
},
highlightColor: Colors.transparent,
),
),
@ -175,45 +235,82 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
Widget _buildBodyViewer(SymptomsCheckerViewModel viewModel) {
return GestureDetector(
onHorizontalDragEnd: (details) {
// Swipe left or right to toggle view
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200 || details.primaryVelocity! > 200) {
viewModel.toggleView();
}
}
},
child: Padding(
padding: EdgeInsets.fromLTRB(16.h, 16.h, 16.h, 60.h),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 600),
transitionBuilder: (child, animation) => _build3DFlipTransition(child, animation),
switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut,
child: Builder(
key: ValueKey<BodyView>(viewModel.currentView),
builder: (context) {
// Detect female gender from viewModel; allow Arabic value as fallback
final bool isFemale =
(viewModel.selectedGender != null && (viewModel.selectedGender!.toLowerCase() == 'female' || viewModel.selectedGender == 'أنثى'));
final String bodyAsset = viewModel.currentView == BodyView.front
? (isFemale ? AppAssets.fullBodyFrontFemale : AppAssets.fullBodyFrontMale)
: (isFemale ? AppAssets.fullBodyBackFemale : AppAssets.fullBodyBackMale);
return InteractiveBodyWidget(
bodyImageAsset: bodyAsset,
organs: viewModel.currentOrgans,
selectedOrganIds: viewModel.selectedOrganIds,
onOrganTap: viewModel.toggleOrganSelection,
isBodyHidden: viewModel.isBodyHidden,
tooltipOrganId: viewModel.tooltipOrganId,
isArabic: _appState.isArabic(),
zoomScale: viewModel.currentZoomScale,
);
onHorizontalDragEnd: !_enableSwipeToFlip
? null
: (details) {
// Swipe left or right to toggle view
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200 || details.primaryVelocity! > 200) {
viewModel.toggleView();
}
}
},
),
),
child: LayoutBuilder(
builder: (context, constraints) {
final bool isExpanded = viewModel.isBottomSheetExpanded;
final double screenHeight = MediaQuery.of(context).size.height;
final double extraBottomInset = isExpanded ? screenHeight * _expandedSheetHeightFactor : 0;
final bool isUserZoomedIn = viewModel.currentZoomScale > 1.01;
Widget buildInteractiveBody({required bool useExpandedZoom}) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 600),
transitionBuilder: (child, animation) => _build3DFlipTransition(child, animation),
switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut,
child: Builder(
key: ValueKey<BodyView>(viewModel.currentView),
builder: (context) {
final bool isFemale = (viewModel.selectedGender != null && (viewModel.selectedGender!.toLowerCase() == 'female' || viewModel.selectedGender == 'أنثى'));
final double effectiveZoomScale = viewModel.currentZoomScale * _introZoomHintAnimation.value;
final String bodyAsset = viewModel.currentView == BodyView.front
? (isFemale ? AppAssets.fullBodyFrontFemale : AppAssets.fullBodyFrontMale)
: (isFemale ? AppAssets.fullBodyBackFemale : AppAssets.fullBodyBackMale);
final body = InteractiveBodyWidget(
bodyImageAsset: bodyAsset,
organs: viewModel.currentOrgans,
selectedOrganIds: viewModel.selectedOrganIds,
onOrganTap: viewModel.toggleOrganSelection,
isBodyHidden: viewModel.isBodyHidden,
tooltipOrganId: viewModel.tooltipOrganId,
isArabic: _appState.isArabic(),
zoomScale: effectiveZoomScale,
);
return body;
},
),
);
}
if (!isExpanded) {
return Padding(
padding: EdgeInsets.fromLTRB(16.h, 16.h, 16.h, 60.h),
child: buildInteractiveBody(useExpandedZoom: false),
);
}
return SingleChildScrollView(
physics: isExpanded && !isUserZoomedIn ? const ClampingScrollPhysics() : const NeverScrollableScrollPhysics(),
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight + extraBottomInset,
),
child: Padding(
padding: EdgeInsets.only(
bottom: extraBottomInset,
top: 24.h,
),
child: Padding(
padding: EdgeInsets.fromLTRB(16.h, 16.h, 16.h, 60.h),
child: buildInteractiveBody(useExpandedZoom: true),
),
),
),
);
},
),
);
}
@ -408,37 +505,73 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> {
}
Widget _buildExpandCollapseButton(SymptomsCheckerViewModel viewModel) {
final organCount = viewModel.selectedOrgans.length;
final showBadge = !viewModel.isBottomSheetExpanded && organCount > 0;
return PositionedDirectional(
end: 24.w,
top: -24.h,
child: GestureDetector(
onTap: viewModel.toggleBottomSheet,
behavior: HitTestBehavior.opaque,
child: Container(
child: SizedBox(
width: 70.w,
height: 70.h,
alignment: Alignment.center,
child: Container(
width: 48.w,
height: 48.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 11.r,
),
child: Center(
child: Transform.flip(
flipX: _appState.isArabic(),
child: AnimatedRotation(
duration: const Duration(milliseconds: 300),
turns: viewModel.isBottomSheetExpanded ? 0.25 : -0.25,
child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight,
width: 25.w,
height: 25.h,
child: Stack(
clipBehavior: Clip.none,
children: [
Align(
alignment: Alignment.center,
child: Container(
width: 48.w,
height: 48.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 11.r,
),
child: Center(
child: Transform.flip(
flipX: _appState.isArabic(),
child: AnimatedRotation(
duration: const Duration(milliseconds: 300),
turns: viewModel.isBottomSheetExpanded ? 0.25 : -0.25,
child: Utils.buildSvgWithAssets(
icon: AppAssets.arrowRight,
width: 25.w,
height: 25.h,
),
),
),
),
),
),
),
if (showBadge)
PositionedDirectional(
top: 8.h,
end: 8.w,
child: Container(
width: 22.w,
height: 22.h,
alignment: Alignment.center,
decoration: BoxDecoration(
color: AppColors.primaryRedColor,
shape: BoxShape.circle,
border: Border.all(
color: AppColors.whiteColor,
width: 1.5,
),
),
child: Text(
'$organCount',
style: TextStyle(
color: AppColors.whiteColor,
fontSize: 10.f,
fontWeight: FontWeight.w700,
),
),
),
),
],
),
),
),

@ -1,3 +1,4 @@
import 'dart:developer';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
@ -7,7 +8,6 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/symptoms_checker/models/organ_model.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/widgets/organ_dot_widget.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart';
import 'package:vector_math/vector_math_64.dart' show Vector3;
class InteractiveBodyWidget extends StatefulWidget {
final String bodyImageAsset;
@ -63,9 +63,12 @@ class _InteractiveBodyWidgetState extends State<InteractiveBodyWidget> {
@override
void dispose() {
_transformationController.dispose();
currentZoom = 0.0;
super.dispose();
}
double currentZoom = 0.0;
Future<void> _loadImageAspectRatio() async {
final ByteData data = await rootBundle.load(widget.bodyImageAsset);
final ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
@ -77,17 +80,16 @@ class _InteractiveBodyWidgetState extends State<InteractiveBodyWidget> {
_imageAspectRatio = image.width / image.height;
});
}
_transformationController.addListener(() {
currentZoom = _transformationController.value.getMaxScaleOnAxis();
setState(() {});
});
}
void _updateZoom(double scale) {
// Get current translation
final currentTransform = _transformationController.value;
final currentTranslation = currentTransform.getTranslation();
// Create new transformation with updated scale while preserving translation
final newTransform = Matrix4.identity()
..setTranslation(currentTranslation)
..scaleByVector3(Vector3(scale, scale, 1.0));
// Keep programmatic zoom centered to avoid drifting the body off-screen.
final newTransform = Matrix4.identity()..scaleByDouble(scale, scale, 1.0, 1.0);
_transformationController.value = newTransform;
}
@ -103,8 +105,12 @@ class _InteractiveBodyWidgetState extends State<InteractiveBodyWidget> {
return Center(
child: InteractiveViewer(
transformationController: _transformationController,
minScale: 0.5,
maxScale: 4.0,
alignment: Alignment.center,
panEnabled: true,
scaleEnabled: true,
boundaryMargin: currentZoom > 1.2 ? EdgeInsets.all(200.h) : EdgeInsets.zero,
minScale: 1.0,
maxScale: 9.0,
clipBehavior: Clip.none,
child: AspectRatio(
aspectRatio: _imageAspectRatio!,
@ -135,7 +141,7 @@ class _InteractiveBodyWidgetState extends State<InteractiveBodyWidget> {
// Organ dots
...widget.organs.map((organ) {
final isSelected = widget.selectedOrganIds.contains(organ.id);
final dotSize = 16.0;
final dotSize = 18.0;
final leftPos = (organ.position.x * imageConstraints.maxWidth) - (dotSize / 2);
final topPos = (organ.position.y * imageConstraints.maxHeight) - (dotSize / 2);

@ -23,7 +23,8 @@ abstract class DialogService {
Future<void> showExceptionBottomSheet({required String message, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showCommonBottomSheetWithoutH(
{String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed});
Future<void> showSuccessBottomSheetWithoutH({String? label, required String message, required Function() onOkPressed, Function()? onCancelPressed});
@ -119,21 +120,15 @@ class DialogServiceImp implements DialogService {
}
@override
Future<void> showCommonBottomSheetWithoutH({String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) async {
Future<void> showCommonBottomSheetWithoutH(
{String? label, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) async {
final context = navigationService.navigatorKey.currentContext;
if (context == null) return;
showCommonBottomSheetWithoutHeight(
context,
title: label ?? "",
child: exceptionBottomSheetWidget(
context: context,
message: message,
okLabel: okLabel,
cancelLabel: cancelLabel,
onOkPressed: onOkPressed,
onCancelPressed: onCancelPressed,
isConfirmButton: isConfirmButton
),
context: context, message: message, okLabel: okLabel, cancelLabel: cancelLabel, onOkPressed: onOkPressed, onCancelPressed: onCancelPressed, isConfirmButton: isConfirmButton),
callBackFunc: () {},
);
}
@ -240,7 +235,8 @@ class DialogServiceImp implements DialogService {
}
}
Widget exceptionBottomSheetWidget({required BuildContext context, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) {
Widget exceptionBottomSheetWidget(
{required BuildContext context, required String message, String? okLabel, String? cancelLabel, bool isConfirmButton = false, required Function() onOkPressed, Function()? onCancelPressed}) {
return Column(
children: [
(message).toText16(isBold: false, color: AppColors.textColor),

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
@ -15,10 +16,7 @@ class AppTheme {
visualDensity: VisualDensity.adaptivePlatformDensity,
brightness: Brightness.light,
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder()
},
builders: {TargetPlatform.android: ZoomPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder()},
),
hintColor: Colors.grey[400],
disabledColor: Colors.grey[300],
@ -51,10 +49,7 @@ class AppTheme {
visualDensity: VisualDensity.adaptivePlatformDensity,
brightness: Brightness.dark,
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder()
},
builders: {TargetPlatform.android: ZoomPageTransitionsBuilder(), TargetPlatform.iOS: CupertinoPageTransitionsBuilder()},
),
hintColor: Colors.grey[600],
disabledColor: Colors.grey[700],
@ -63,8 +58,7 @@ class AppTheme {
scaffoldBackgroundColor: AppColors.dark.scaffoldBgColor,
highlightColor: Colors.grey[800]!.withOpacity(0.4),
splashColor: Colors.transparent,
bottomSheetTheme: BottomSheetThemeData(
backgroundColor: Colors.black.withOpacity(0)),
bottomSheetTheme: BottomSheetThemeData(backgroundColor: Colors.black.withOpacity(0)),
floatingActionButtonTheme: const FloatingActionButtonThemeData(highlightElevation: 2, disabledElevation: 0, elevation: 2),
appBarTheme: AppBarTheme(
color: AppColors.dark.scaffoldBgColor,

@ -257,8 +257,7 @@ void showCommonBottomSheetWithoutHeight(
duration: Duration(milliseconds: 500),
reverseDuration: Duration(milliseconds: 300),
),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width //MediaQuery.of(context).size.width, // Full width
),
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width),
context: context,
isScrollControlled: true,
showDragHandle: false,

Loading…
Cancel
Save