pull/359/head
haroon amjad 6 days ago
parent 0043e2917d
commit 0dea7414d9

@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts { class ApiConsts {
static const maxSmallScreen = 660; static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -911,8 +911,6 @@ var AUTO_GENERATE_INVOICE_TAMARA = 'Services/PayFort_Serv.svc/REST/Tamara_Getinf
var GET_ONESIGNAL_VOIP_TOKEN = 'https://onesignal.com/api/v1/players'; var GET_ONESIGNAL_VOIP_TOKEN = 'https://onesignal.com/api/v1/players';
var CANCEL_PHARMA_LIVECARE_REQUEST = 'https://vcallapi.hmg.com/api/PharmaLiveCare/SendPaymentStatus';
var INSERT_FREE_SLOTS_LOGS = 'Services/Doctors.svc/Rest/InsertDoctorFreeSlotsLogs'; var INSERT_FREE_SLOTS_LOGS = 'Services/Doctors.svc/Rest/InsertDoctorFreeSlotsLogs';
var GET_NATIONALITY = 'Services/Lists.svc/REST/GetNationality'; var GET_NATIONALITY = 'Services/Lists.svc/REST/GetNationality';

@ -1200,8 +1200,8 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel,
onSuccess: (apiResponse) { onSuccess: (apiResponse) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); // myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
myAppointmentsViewModel.getPatientAppointments(true, false); // myAppointmentsViewModel.getPatientAppointments(true, false);
showCommonBottomSheet(context, showCommonBottomSheet(context,
child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) { child: Utils.getSuccessWidget(loadingText: LocaleKeys.appointmentConfirmedSuccessfully.tr(context: context)), callBackFunc: (str) {
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true); myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart';
@ -9,7 +11,6 @@ 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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'dart:ui' as ui;
class CustomCountryDropdown extends StatefulWidget { class CustomCountryDropdown extends StatefulWidget {
final List<CountryEnum> countryList; final List<CountryEnum> countryList;
@ -35,7 +36,9 @@ class CustomCountryDropdown extends StatefulWidget {
class CustomCountryDropdownState extends State<CustomCountryDropdown> { class CustomCountryDropdownState extends State<CustomCountryDropdown> {
CountryEnum? selectedCountry; CountryEnum? selectedCountry;
late OverlayEntry _overlayEntry; OverlayEntry? _overlayEntry;
Timer? _showDropdownTimer;
Timer? _refocusTimer;
bool _isDropdownOpen = false; bool _isDropdownOpen = false;
FocusNode textFocusNode = FocusNode(); FocusNode textFocusNode = FocusNode();
@ -55,6 +58,9 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
@override @override
void dispose() { void dispose() {
_showDropdownTimer?.cancel();
_refocusTimer?.cancel();
_removeOverlayEntry();
textFocusNode.dispose(); textFocusNode.dispose();
super.dispose(); super.dispose();
} }
@ -74,9 +80,18 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
}, },
child: Row( child: Row(
children: [ children: [
Utils.buildSvgWithAssets(icon: selectedCountry != null ? selectedCountry!.iconPath : AppAssets.ksa, width: 40.h, height: 40.h, applyThemeColor: false), Utils.buildSvgWithAssets(
icon: selectedCountry != null
? selectedCountry!.iconPath
: AppAssets.ksa,
width: 40.h,
height: 40.h,
applyThemeColor: false),
SizedBox(width: 8.h), SizedBox(width: 8.h),
Utils.buildSvgWithAssets(icon: _isDropdownOpen ? AppAssets.dropdow_icon : AppAssets.dropdow_icon), Utils.buildSvgWithAssets(
icon: _isDropdownOpen
? AppAssets.dropdow_icon
: AppAssets.dropdow_icon),
], ],
), ),
), ),
@ -94,7 +109,11 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
children: [ children: [
Text( Text(
LocaleKeys.phoneNumber.tr(), LocaleKeys.phoneNumber.tr(),
style: TextStyle(fontSize: 12.f, height: 1.5, fontWeight: FontWeight.w600, letterSpacing: -1), style: TextStyle(
fontSize: 12.f,
height: 1.5,
fontWeight: FontWeight.w600,
letterSpacing: -1),
), ),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@ -102,9 +121,15 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
if (selectedCountry != CountryEnum.others) if (selectedCountry != CountryEnum.others)
Text( Text(
selectedCountry!.countryCode, selectedCountry!.countryCode,
style: TextStyle(fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5, fontFamily: "Poppins"), style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w600,
letterSpacing: -0.4,
height: 1.5,
fontFamily: "Poppins"),
), ),
if (selectedCountry != CountryEnum.others) SizedBox(width: 4.h), if (selectedCountry != CountryEnum.others)
SizedBox(width: 4.h),
if (widget.isEnableTextField) if (widget.isEnableTextField)
SizedBox( SizedBox(
height: 20.h, height: 20.h,
@ -113,14 +138,19 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: TextField( child: TextField(
focusNode: textFocusNode, focusNode: textFocusNode,
style: TextStyle(fontSize: 12.f, fontWeight: FontWeight.w600, letterSpacing: -0.4, height: 1.5, fontFamily: "Poppins"), style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.w600,
letterSpacing: -0.4,
height: 1.5,
fontFamily: "Poppins"),
decoration: InputDecoration( decoration: InputDecoration(
hintText: selectedCountry == CountryEnum.others ? "001*******" : "", hintText: selectedCountry == CountryEnum.others
? "001*******"
: "",
isDense: true, isDense: true,
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
), ),
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
onChanged: widget.onPhoneNumberChanged, onChanged: widget.onPhoneNumberChanged,
@ -136,21 +166,31 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
Text( Text(
selectedCountry != null selectedCountry != null
? appState.getLanguageCode() == "ar" ? appState.getLanguageCode() == "ar"
? selectedCountry!.nameArabic ? selectedCountry!.nameArabic
: selectedCountry!.displayName : selectedCountry!.displayName
: LocaleKeys.selectCountry.tr(), : LocaleKeys.selectCountry.tr(),
style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2), style: TextStyle(
fontSize: 14.f,
height: 21 / 14,
fontWeight: FontWeight.w600,
letterSpacing: -0.2),
), ),
], ],
); );
} }
void _openDropdown() { void _openDropdown() {
if (!mounted || _isDropdownOpen || _overlayEntry != null) return;
_showDropdownTimer?.cancel();
if (textFocusNode.hasFocus) { if (textFocusNode.hasFocus) {
textFocusNode.unfocus(); textFocusNode.unfocus();
// Wait for keyboard to close before calculating position // Wait for keyboard to close before calculating position
Future.delayed(Duration(milliseconds: 300), () { _showDropdownTimer = Timer(const Duration(milliseconds: 300), () {
_showDropdown(); _showDropdownTimer = null;
if (mounted) {
_showDropdown();
}
}); });
} else { } else {
_showDropdown(); _showDropdown();
@ -158,8 +198,18 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
} }
void _showDropdown() { void _showDropdown() {
if (!mounted || _isDropdownOpen || _overlayEntry != null) return;
AppState appState = getIt.get<AppState>(); AppState appState = getIt.get<AppState>();
RenderBox renderBox = context.findRenderObject() as RenderBox; final renderObject = context.findRenderObject();
final overlay = Overlay.maybeOf(context);
if (renderObject is! RenderBox ||
!renderObject.attached ||
overlay == null) {
return;
}
final renderBox = renderObject;
Offset offset = renderBox.localToGlobal(Offset.zero); Offset offset = renderBox.localToGlobal(Offset.zero);
bool isRtl = appState.getLanguageCode() == "ar"; bool isRtl = appState.getLanguageCode() == "ar";
double leftPosition; double leftPosition;
@ -169,7 +219,7 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
leftPosition = offset.dx; leftPosition = offset.dx;
} }
_overlayEntry = OverlayEntry( final overlayEntry = OverlayEntry(
builder: (context) => Stack( builder: (context) => Stack(
children: [ children: [
Positioned.fill( Positioned.fill(
@ -185,32 +235,41 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
width: !widget.isFromBottomSheet ? renderBox.size.width : 60.h, width: !widget.isFromBottomSheet ? renderBox.size.width : 60.h,
child: Material( child: Material(
child: Container( child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white, borderRadius: 12),
child: Column( child: Column(
children: widget.countryList children: widget.countryList
.map( .map(
(country) => GestureDetector( (country) => GestureDetector(
onTap: () { onTap: () => _selectCountry(country),
setState(() { child: Container(
selectedCountry = country; padding: EdgeInsets.symmetric(
}); vertical: 8.h, horizontal: 8.h),
widget.onCountryChange?.call(country); decoration: RoundedRectangleBorder()
_closeDropdown(); .toSmoothCornerDecoration(borderRadius: 16.h),
}, child: Row(
child: Container( children: [
padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h), Utils.buildSvgWithAssets(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 16.h), icon: country.iconPath,
child: Row( width: 38.h,
children: [ height: 38.h,
Utils.buildSvgWithAssets(icon: country.iconPath, width: 38.h, height: 38.h, applyThemeColor: false), applyThemeColor: false),
if (!widget.isFromBottomSheet) SizedBox(width: 12.h), if (!widget.isFromBottomSheet)
if (!widget.isFromBottomSheet) SizedBox(width: 12.h),
Text(appState.getLanguageCode() == "ar" ? country.nameArabic : country.displayName, if (!widget.isFromBottomSheet)
style: TextStyle(fontSize: 14.f, height: 21 / 14, fontWeight: FontWeight.w600, letterSpacing: -0.2)), Text(
], appState.getLanguageCode() == "ar"
), ? country.nameArabic
)), : country.displayName,
) style: TextStyle(
fontSize: 14.f,
height: 21 / 14,
fontWeight: FontWeight.w600,
letterSpacing: -0.2)),
],
),
)),
)
.toList(), .toList(),
), ),
), ),
@ -220,7 +279,8 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
), ),
); );
Overlay.of(context)?.insert(_overlayEntry); overlay.insert(overlayEntry);
_overlayEntry = overlayEntry;
setState(() { setState(() {
_isDropdownOpen = true; _isDropdownOpen = true;
}); });
@ -291,14 +351,57 @@ class CustomCountryDropdownState extends State<CustomCountryDropdown> {
// }); // });
// } // }
void _closeDropdown() { void _selectCountry(CountryEnum country) {
_overlayEntry.remove(); if (!mounted) {
_removeOverlayEntry();
return;
}
_removeOverlayEntry();
setState(() { setState(() {
selectedCountry = country;
_isDropdownOpen = false; _isDropdownOpen = false;
}); });
// Notify the parent only after this widget has finished updating and
// removing its overlay. The callback may synchronously close the parent.
widget.onCountryChange?.call(country);
_scheduleTextFieldRefocus();
}
void _closeDropdown() {
_showDropdownTimer?.cancel();
_showDropdownTimer = null;
_removeOverlayEntry();
if (!mounted) {
_isDropdownOpen = false;
return;
}
if (_isDropdownOpen) {
setState(() {
_isDropdownOpen = false;
});
}
_scheduleTextFieldRefocus();
}
void _removeOverlayEntry() {
final overlayEntry = _overlayEntry;
_overlayEntry = null;
if (overlayEntry == null) return;
overlayEntry.remove();
overlayEntry.dispose();
}
void _scheduleTextFieldRefocus() {
_refocusTimer?.cancel();
if (widget.isEnableTextField && widget.isFromBottomSheet) { if (widget.isEnableTextField && widget.isFromBottomSheet) {
Future.delayed(Duration(milliseconds: 100), () { _refocusTimer = Timer(const Duration(milliseconds: 100), () {
_refocusTimer = null;
if (mounted && textFocusNode.canRequestFocus) { if (mounted && textFocusNode.canRequestFocus) {
FocusScope.of(context).requestFocus(textFocusNode); FocusScope.of(context).requestFocus(textFocusNode);
} }

Loading…
Cancel
Save