diff --git a/lib/features/book_appointments/book_appointments_view_model.dart b/lib/features/book_appointments/book_appointments_view_model.dart index 16dd70c..ae74ffb 100644 --- a/lib/features/book_appointments/book_appointments_view_model.dart +++ b/lib/features/book_appointments/book_appointments_view_model.dart @@ -66,6 +66,11 @@ class BookAppointmentsViewModel extends ChangeNotifier { List doctorsList = []; List filteredDoctorList = []; + // Grouped doctors lists + List> doctorsListByClinic = []; + List> doctorsListByHospital = []; + List> doctorsListGrouped = []; + List liveCareDoctorsList = []; List patientDentalPlanEstimationList = []; @@ -151,14 +156,28 @@ class BookAppointmentsViewModel extends ChangeNotifier { setIsSortByClinic(bool value) { isSortByClinic = value; - if (isSortByClinic) { - doctorsList.sort((a, b) => a.clinicName!.compareTo(b.clinicName!)); - } else { - doctorsList.sort((a, b) => a.projectName!.compareTo(b.projectName!)); - } + doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital; notifyListeners(); } + // Group doctors by clinic and hospital + void _groupDoctorsList() { + final clinicMap = >{}; + final hospitalMap = >{}; + + for (var doctor in doctorsList) { + final clinicKey = (doctor.clinicName ?? 'Unknown').trim(); + clinicMap.putIfAbsent(clinicKey, () => []).add(doctor); + + final hospitalKey = (doctor.projectName ?? 'Unknown').trim(); + hospitalMap.putIfAbsent(hospitalKey, () => []).add(doctor); + } + + doctorsListByClinic = clinicMap.values.toList(); + doctorsListByHospital = hospitalMap.values.toList(); + doctorsListGrouped = isSortByClinic ? doctorsListByClinic : doctorsListByHospital; + } + BookAppointmentsViewModel( {required this.bookAppointmentsRepo, required this.errorHandlerService, @@ -419,6 +438,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { initializeFilteredList(); clearSearchFilters(); getFiltersFromDoctorList(); + _groupDoctorsList(); notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -450,6 +470,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { initializeFilteredList(); clearSearchFilters(); getFiltersFromDoctorList(); + _groupDoctorsList(); notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); @@ -1117,6 +1138,7 @@ class BookAppointmentsViewModel extends ChangeNotifier { // initializeFilteredList(); // clearSearchFilters(); // getFiltersFromDoctorList(); + _groupDoctorsList(); notifyListeners(); if (onSuccess != null) { onSuccess(apiResponse); diff --git a/lib/presentation/book_appointment/select_doctor_page.dart b/lib/presentation/book_appointment/select_doctor_page.dart index 6045d79..d77e152 100644 --- a/lib/presentation/book_appointment/select_doctor_page.dart +++ b/lib/presentation/book_appointment/select_doctor_page.dart @@ -40,8 +40,14 @@ class _SelectDoctorPageState extends State { late AppState appState; late BookAppointmentsViewModel bookAppointmentsViewModel; + // Scroll controller to control page scrolling when a group expands + late ScrollController _scrollController; + // Map of keys for each item to allow scrolling to them + final Map _itemKeys = {}; + @override void initState() { + _scrollController = ScrollController(); scheduleMicrotask(() { bookAppointmentsViewModel.setIsNearestAppointmentSelected(false); if (bookAppointmentsViewModel.isLiveCareSchedule) { @@ -59,6 +65,12 @@ class _SelectDoctorPageState extends State { super.initState(); } + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { bookAppointmentsViewModel = Provider.of(context, listen: false); @@ -68,6 +80,7 @@ class _SelectDoctorPageState extends State { body: CollapsingListView( title: "Choose Doctor".needTranslation, child: SingleChildScrollView( + controller: _scrollController, child: Padding( padding: EdgeInsets.symmetric(horizontal: 24.h), child: Consumer(builder: (context, bookAppointmentsVM, child) { @@ -174,9 +187,7 @@ class _SelectDoctorPageState extends State { physics: NeverScrollableScrollPhysics(), itemCount: bookAppointmentsVM.isDoctorsListLoading ? 5 - : (bookAppointmentsVM.isLiveCareSchedule - ? (bookAppointmentsVM.liveCareDoctorsList.isNotEmpty ? bookAppointmentsVM.liveCareDoctorsList.length : 1) - : (bookAppointmentsVM.doctorsList.isNotEmpty ? bookAppointmentsVM.doctorsList.length : 1)), + : (bookAppointmentsVM.doctorsListGrouped.isNotEmpty ? bookAppointmentsVM.doctorsListGrouped.length : 1), itemBuilder: (context, index) { final isExpanded = expandedIndex == index; return bookAppointmentsVM.isDoctorsListLoading @@ -185,7 +196,7 @@ class _SelectDoctorPageState extends State { isLoading: true, bookAppointmentsViewModel: bookAppointmentsViewModel, ) - : checkIsDoctorsListEmpty() + : bookAppointmentsVM.doctorsListGrouped.isEmpty ? Utils.getNoDataWidget(context, noDataText: "No Doctor found for selected criteria...".needTranslation) : AnimationConfiguration.staggeredList( position: index, @@ -194,6 +205,7 @@ class _SelectDoctorPageState extends State { verticalOffset: 100.0, child: FadeInAnimation( child: AnimatedContainer( + key: _itemKeys.putIfAbsent(index, () => GlobalKey()), duration: Duration(milliseconds: 300), curve: Curves.easeInOut, decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), @@ -202,34 +214,97 @@ class _SelectDoctorPageState extends State { setState(() { expandedIndex = isExpanded ? null : index; }); + // After rebuild, ensure the expanded item is visible + WidgetsBinding.instance.addPostFrameCallback((_) { + final key = _itemKeys[index]; + if (key != null && key.currentContext != null && expandedIndex == index) { + Scrollable.ensureVisible( + key.currentContext!, + duration: Duration(milliseconds: 350), + curve: Curves.easeInOut, + alignment: 0.1, + ); + } + }); }, - child: DoctorCard( - doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index], - isLoading: false, - bookAppointmentsViewModel: bookAppointmentsViewModel, - ).onPress(() async { - bookAppointmentsVM - .setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]); - // bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel()); - LoaderBottomSheet.showLoader(); - await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) { - LoaderBottomSheet.hideLoader(); - Navigator.of(context).push( - CustomPageRoute( - page: DoctorProfilePage(), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header row with count badge and expand/collapse icon + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CustomButton( + text: "${bookAppointmentsVM.doctorsListGrouped[index].length} ${'doctors'.needTranslation}", + onPressed: () {}, + backgroundColor: AppColors.greyColor, + borderColor: AppColors.greyColor, + textColor: AppColors.blackColor, + fontSize: 10, + fontWeight: FontWeight.w500, + borderRadius: 8, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 30.h, + ), + Icon(isExpanded ? Icons.expand_less : Icons.expand_more), + ], ), - ); - }, onError: (err) { - LoaderBottomSheet.hideLoader(); - showCommonBottomSheetWithoutHeight( - context, - child: Utils.getErrorWidget(loadingText: err), - callBackFunc: () {}, - isFullScreen: false, - isCloseButtonVisible: true, - ); - }); - }), + SizedBox(height: 8.h), + // Clinic/Hospital name as group title + Text( + bookAppointmentsVM.isSortByClinic + ? (bookAppointmentsVM.doctorsListGrouped[index].first.clinicName ?? 'Unknown') + : (bookAppointmentsVM.doctorsListGrouped[index].first.projectName ?? 'Unknown'), + style: TextStyle(fontSize: 16.h, fontWeight: FontWeight.w600), + overflow: TextOverflow.ellipsis, + ), + // Expanded content - list of doctors in this group + AnimatedSwitcher( + duration: Duration(milliseconds: 400), + child: isExpanded + ? Container( + key: ValueKey(index), + padding: EdgeInsets.only(top: 12.h), + child: Column( + children: bookAppointmentsVM.doctorsListGrouped[index].map((doctor) { + return Container( + margin: EdgeInsets.only(bottom: 12.h), + child: DoctorCard( + doctorsListResponseModel: doctor, + isLoading: false, + bookAppointmentsViewModel: bookAppointmentsViewModel, + ).onPress(() async { + bookAppointmentsVM.setSelectedDoctor(doctor); + LoaderBottomSheet.showLoader(); + await bookAppointmentsVM.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, + ); + }); + }), + ); + }).toList(), + ), + ) + : SizedBox.shrink(), + ), + ], + ), + ), ), ), ), diff --git a/lib/presentation/health_calculators/health_calculator_result.dart b/lib/presentation/health_calculators/health_calculator_result.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart b/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart index 4a78549..c085ac8 100644 --- a/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart +++ b/lib/presentation/health_calculators_and_converts/health_calculator_view_model.dart @@ -49,28 +49,41 @@ class HealthCalcualtorViewModel extends ChangeNotifier { String? get mmolValue => _mmolValue; + // Store the original entered value and which field it was entered in + String? _originalValue; + String _originalUnit = 'mg/dL'; // which field the original value was entered in + bool _isSwapped = false; // toggle state for switch + String _activeUnit = 'mg/dL'; // current source unit String get activeUnit => _activeUnit; - // ================== BLOOD CHOLESTEROL ================== String? _cholMgdlValue; String? _cholMmolValue; + String? _cholOriginalValue; + String _cholOriginalUnit = 'mg/dL'; + bool _cholIsSwapped = false; String _cholActiveUnit = 'mg/dL'; String? get cholMgdlValue => _cholMgdlValue; + String? get cholMmolValue => _cholMmolValue; + String get cholActiveUnit => _cholActiveUnit; // ================== TRIGLYCERIDES ================== String? _triMgdlValue; String? _triMmolValue; + String? _triOriginalValue; + String _triOriginalUnit = 'mg/dL'; + bool _triIsSwapped = false; String _triActiveUnit = 'mg/dL'; String? get triMgdlValue => _triMgdlValue; + String? get triMmolValue => _triMmolValue; - String get triActiveUnit => _triActiveUnit; + String get triActiveUnit => _triActiveUnit; // Generic helpers @@ -726,13 +739,10 @@ class HealthCalcualtorViewModel extends ChangeNotifier { final DateTime firstTrimesterStart = lmp; final DateTime firstTrimesterEnd = lmp.add(const Duration(days: 83)); - final DateTime secondTrimesterStart = - firstTrimesterEnd.add(const Duration(days: 1)); - final DateTime secondTrimesterEnd = - lmp.add(const Duration(days: 195)); + final DateTime secondTrimesterStart = firstTrimesterEnd.add(const Duration(days: 1)); + final DateTime secondTrimesterEnd = lmp.add(const Duration(days: 195)); - final DateTime thirdTrimesterStart = - secondTrimesterEnd.add(const Duration(days: 1)); + final DateTime thirdTrimesterStart = secondTrimesterEnd.add(const Duration(days: 1)); final DateTime thirdTrimesterEnd = dueDate; deliveryResult = { @@ -741,7 +751,7 @@ class HealthCalcualtorViewModel extends ChangeNotifier { // Raw DateTime (useful for logic/UI) 'lmpDateTime': lmp, 'dueDateTime': dueDate, - 'dueDateDay' : _formatDateWithDayName(dueDate), + 'dueDateDay': _formatDateWithDayName(dueDate), // Trimester info 'firstTrimester': { 'start': _formatDate(firstTrimesterStart), @@ -769,7 +779,6 @@ class HealthCalcualtorViewModel extends ChangeNotifier { notifyListeners(); } - // Blood sugar conversions void calculateBloodSugar({required String valueText, required String unit}) { if (valueText.trim().isEmpty) { @@ -838,8 +847,6 @@ class HealthCalcualtorViewModel extends ChangeNotifier { 'dietType': dietType }; - - void onBloodSugarChanged(String value, String fromUnit) { _activeUnit = fromUnit; @@ -855,47 +862,105 @@ class HealthCalcualtorViewModel extends ChangeNotifier { if (fromUnit == 'mg/dL') { _mgdlValue = value; - _mmolValue = (parsed / 18.0182).toStringAsFixed(1); + _mmolValue = (parsed / 18.0182).toStringAsFixed(3); } else { _mmolValue = value; - _mgdlValue = (parsed * 18.0182).toStringAsFixed(0); + _mgdlValue = (parsed * 18.0182).toStringAsFixed(3); } notifyListeners(); } - void switchBloodSugarValues() { - if (_activeUnit == 'mg/dL') { - final mmol = double.tryParse(_mmolValue ?? ''); - if (mmol == null) return; + void onBloodSugarMgdlChanged(String value) { + _mgdlValue = value; + _originalValue = value; + _originalUnit = 'mg/dL'; + _isSwapped = false; + _activeUnit = 'mg/dL'; + + if (value.isEmpty) { + _mmolValue = null; + _originalValue = null; + notifyListeners(); + return; + } - _activeUnit = 'mmol/L'; + final parsed = double.tryParse(value); + if (parsed == null) return; - // mmol stays as source - _mmolValue = mmol.toStringAsFixed(1); - _mgdlValue = (mmol * 18.0182).toStringAsFixed(0); - } else { - final mgdl = double.tryParse(_mgdlValue ?? ''); - if (mgdl == null) return; + // Convert mg/dL to mmol/L + _mmolValue = (parsed / 18.0182).toStringAsFixed(3); + notifyListeners(); + } - _activeUnit = 'mg/dL'; + void onBloodSugarMmolChanged(String value) { + _mmolValue = value; + _originalValue = value; + _originalUnit = 'mmol/L'; + _isSwapped = false; + _activeUnit = 'mmol/L'; - // mg/dL stays as source - _mgdlValue = mgdl.toStringAsFixed(0); - _mmolValue = (mgdl / 18.0182).toStringAsFixed(1); + if (value.isEmpty) { + _mgdlValue = null; + _originalValue = null; + notifyListeners(); + return; } + final parsed = double.tryParse(value); + if (parsed == null) return; + + // Convert mmol/L to mg/dL + _mgdlValue = (parsed * 18.0182).toStringAsFixed(3); notifyListeners(); } + + void switchBloodSugarValues() { + // Toggle between two states using the original entered value + if (_originalValue == null || _originalValue!.isEmpty) return; + + final originalParsed = double.tryParse(_originalValue!); + if (originalParsed == null) return; + + _isSwapped = !_isSwapped; + + if (_originalUnit == 'mg/dL') { + if (_isSwapped) { + // Original was in mg/dL, now treat it as mmol/L + _mmolValue = _originalValue; // Keep original format + _mgdlValue = (originalParsed * 18.0182).toStringAsFixed(3); + } else { + // Back to original: value in mg/dL + _mgdlValue = _originalValue; // Keep original format + _mmolValue = (originalParsed / 18.0182).toStringAsFixed(3); + } + } else { + // Original was in mmol/L + if (_isSwapped) { + // Now treat it as mg/dL + _mgdlValue = _originalValue; // Keep original format + _mmolValue = (originalParsed / 18.0182).toStringAsFixed(3); + } else { + // Back to original: value in mmol/L + _mmolValue = _originalValue; // Keep original format + _mgdlValue = (originalParsed * 18.0182).toStringAsFixed(3); + } + } + + notifyListeners(); + } + // --- NEW: Method to clear the values --- void clearBloodSugar() { _mgdlValue = null; _mmolValue = null; + _originalValue = null; + _originalUnit = 'mg/dL'; + _isSwapped = false; + _activeUnit = 'mg/dL'; notifyListeners(); } - - void onBloodCholesterolChanged(String value, String fromUnit) { _cholActiveUnit = fromUnit; @@ -911,47 +976,104 @@ class HealthCalcualtorViewModel extends ChangeNotifier { if (fromUnit == 'mg/dL') { _cholMgdlValue = value; - _cholMmolValue = (parsed / 38.67).toStringAsFixed(2); + _cholMmolValue = (parsed / 38.67).toStringAsFixed(3); } else { _cholMmolValue = value; - _cholMgdlValue = (parsed * 38.67).toStringAsFixed(0); + _cholMgdlValue = (parsed * 38.67).toStringAsFixed(3); } notifyListeners(); } - void switchBloodCholesterolValues() { - if (_cholActiveUnit == 'mg/dL') { - final mmol = double.tryParse(_cholMmolValue ?? ''); - if (mmol == null) return; + void onCholesterolMgdlChanged(String value) { + _cholMgdlValue = value; + _cholOriginalValue = value; + _cholOriginalUnit = 'mg/dL'; + _cholIsSwapped = false; + _cholActiveUnit = 'mg/dL'; + + if (value.isEmpty) { + _cholMmolValue = null; + _cholOriginalValue = null; + notifyListeners(); + return; + } - _cholActiveUnit = 'mmol/L'; + final parsed = double.tryParse(value); + if (parsed == null) return; - _cholMmolValue = mmol.toStringAsFixed(2); - _cholMgdlValue = (mmol * 38.67).toStringAsFixed(0); - } else { - final mgdl = double.tryParse(_cholMgdlValue ?? ''); - if (mgdl == null) return; + // Convert mg/dL to mmol/L + _cholMmolValue = (parsed / 38.67).toStringAsFixed(3); + notifyListeners(); + } - _cholActiveUnit = 'mg/dL'; + void onCholesterolMmolChanged(String value) { + _cholMmolValue = value; + _cholOriginalValue = value; + _cholOriginalUnit = 'mmol/L'; + _cholIsSwapped = false; + _cholActiveUnit = 'mmol/L'; - _cholMgdlValue = mgdl.toStringAsFixed(0); - _cholMmolValue = (mgdl / 38.67).toStringAsFixed(2); + if (value.isEmpty) { + _cholMgdlValue = null; + _cholOriginalValue = null; + notifyListeners(); + return; } + final parsed = double.tryParse(value); + if (parsed == null) return; + + // Convert mmol/L to mg/dL + _cholMgdlValue = (parsed * 38.67).toStringAsFixed(3); notifyListeners(); } + void switchBloodCholesterolValues() { + // Toggle between two states using the original entered value + if (_cholOriginalValue == null || _cholOriginalValue!.isEmpty) return; + + final originalParsed = double.tryParse(_cholOriginalValue!); + if (originalParsed == null) return; + + _cholIsSwapped = !_cholIsSwapped; + + if (_cholOriginalUnit == 'mg/dL') { + if (_cholIsSwapped) { + // Original was in mg/dL, now treat it as mmol/L + _cholMmolValue = _cholOriginalValue; // Keep original format + _cholMgdlValue = (originalParsed * 38.67).toStringAsFixed(3); + } else { + // Back to original: value in mg/dL + _cholMgdlValue = _cholOriginalValue; // Keep original format + _cholMmolValue = (originalParsed / 38.67).toStringAsFixed(3); + } + } else { + // Original was in mmol/L + if (_cholIsSwapped) { + // Now treat it as mg/dL + _cholMgdlValue = _cholOriginalValue; // Keep original format + _cholMmolValue = (originalParsed / 38.67).toStringAsFixed(3); + } else { + // Back to original: value in mmol/L + _cholMmolValue = _cholOriginalValue; // Keep original format + _cholMgdlValue = (originalParsed * 38.67).toStringAsFixed(3); + } + } + + notifyListeners(); + } void clearBloodCholesterol() { _cholMgdlValue = null; _cholMmolValue = null; + _cholOriginalValue = null; + _cholOriginalUnit = 'mg/dL'; + _cholIsSwapped = false; _cholActiveUnit = 'mg/dL'; notifyListeners(); } - - void onTriglyceridesChanged(String value, String fromUnit) { _triActiveUnit = fromUnit; @@ -967,30 +1089,89 @@ class HealthCalcualtorViewModel extends ChangeNotifier { if (fromUnit == 'mg/dL') { _triMgdlValue = value; - _triMmolValue = (parsed / 88.57).toStringAsFixed(2); + _triMmolValue = (parsed / 88.57).toStringAsFixed(3); } else { _triMmolValue = value; - _triMgdlValue = (parsed * 88.57).toStringAsFixed(0); + _triMgdlValue = (parsed * 88.57).toStringAsFixed(3); } notifyListeners(); } + void onTriglyceridesMgdlChanged(String value) { + _triMgdlValue = value; + _triOriginalValue = value; + _triOriginalUnit = 'mg/dL'; + _triIsSwapped = false; + _triActiveUnit = 'mg/dL'; + + if (value.isEmpty) { + _triMmolValue = null; + _triOriginalValue = null; + notifyListeners(); + return; + } + + final parsed = double.tryParse(value); + if (parsed == null) return; + + // Convert mg/dL to mmol/L + _triMmolValue = (parsed / 88.57).toStringAsFixed(3); + notifyListeners(); + } + + void onTriglyceridesMmolChanged(String value) { + _triMmolValue = value; + _triOriginalValue = value; + _triOriginalUnit = 'mmol/L'; + _triIsSwapped = false; + _triActiveUnit = 'mmol/L'; + + if (value.isEmpty) { + _triMgdlValue = null; + _triOriginalValue = null; + notifyListeners(); + return; + } + + final parsed = double.tryParse(value); + if (parsed == null) return; + + // Convert mmol/L to mg/dL + _triMgdlValue = (parsed * 88.57).toStringAsFixed(3); + notifyListeners(); + } + void switchTriglyceridesValues() { - if (_triActiveUnit == 'mg/dL') { - final mmol = double.tryParse(_triMmolValue ?? ''); - if (mmol == null) return; + // Toggle between two states using the original entered value + if (_triOriginalValue == null || _triOriginalValue!.isEmpty) return; - _triActiveUnit = 'mmol/L'; - _triMmolValue = mmol.toStringAsFixed(2); - _triMgdlValue = (mmol * 88.57).toStringAsFixed(0); - } else { - final mgdl = double.tryParse(_triMgdlValue ?? ''); - if (mgdl == null) return; + final originalParsed = double.tryParse(_triOriginalValue!); + if (originalParsed == null) return; + + _triIsSwapped = !_triIsSwapped; - _triActiveUnit = 'mg/dL'; - _triMgdlValue = mgdl.toStringAsFixed(0); - _triMmolValue = (mgdl / 88.57).toStringAsFixed(2); + if (_triOriginalUnit == 'mg/dL') { + if (_triIsSwapped) { + // Original was in mg/dL, now treat it as mmol/L + _triMmolValue = _triOriginalValue; // Keep original format + _triMgdlValue = (originalParsed * 88.57).toStringAsFixed(3); + } else { + // Back to original: value in mg/dL + _triMgdlValue = _triOriginalValue; // Keep original format + _triMmolValue = (originalParsed / 88.57).toStringAsFixed(3); + } + } else { + // Original was in mmol/L + if (_triIsSwapped) { + // Now treat it as mg/dL + _triMgdlValue = _triOriginalValue; // Keep original format + _triMmolValue = (originalParsed / 88.57).toStringAsFixed(3); + } else { + // Back to original: value in mmol/L + _triMmolValue = _triOriginalValue; // Keep original format + _triMgdlValue = (originalParsed * 88.57).toStringAsFixed(3); + } } notifyListeners(); @@ -999,10 +1180,10 @@ class HealthCalcualtorViewModel extends ChangeNotifier { void clearTriglycerides() { _triMgdlValue = null; _triMmolValue = null; + _triOriginalValue = null; + _triOriginalUnit = 'mg/dL'; + _triIsSwapped = false; _triActiveUnit = 'mg/dL'; notifyListeners(); } - - - } diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart index 161f318..e3bc7ff 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/blood_cholesterol.dart @@ -52,14 +52,15 @@ class _BloodCholesterolWidgetState extends State { final mgdlText = provider.cholMgdlValue ?? ''; final mmolText = provider.cholMmolValue ?? ''; - if (_mgdlController.text != mgdlText) { + // Only update if focus is not on this field (to avoid cursor jumping) + if (!_mgdlFocus.hasFocus && _mgdlController.text != mgdlText) { _mgdlController.text = mgdlText; _mgdlController.selection = TextSelection.fromPosition( TextPosition(offset: _mgdlController.text.length), ); } - if (_mmolController.text != mmolText) { + if (!_mmolFocus.hasFocus && _mmolController.text != mmolText) { _mmolController.text = mmolText; _mmolController.selection = TextSelection.fromPosition( TextPosition(offset: _mmolController.text.length), @@ -81,7 +82,7 @@ class _BloodCholesterolWidgetState extends State { focusNode: _mgdlFocus, onChanged: (value) { if (_isProgrammaticChange) return; - provider.onBloodCholesterolChanged(value, 'mg/dL'); + provider.onCholesterolMgdlChanged(value); }, ).paddingOnly(top: 16.h), Row( @@ -96,24 +97,21 @@ class _BloodCholesterolWidgetState extends State { width: 40.h, height: 40.h, ).onPress(() { + // Unfocus both fields before switching + _mgdlFocus.unfocus(); + _mmolFocus.unfocus(); provider.switchBloodCholesterolValues(); - if (provider.cholActiveUnit == 'mg/dL') { - _mgdlFocus.requestFocus(); - } else { - _mmolFocus.requestFocus(); - } }), ], ), _buildInputField( label: "MMOL/L", - hint: "6.7", + hint: "3.1", controller: _mmolController, focusNode: _mmolFocus, onChanged: (value) { if (_isProgrammaticChange) return; - - provider.onBloodCholesterolChanged(value, 'mmol/L'); + provider.onCholesterolMmolChanged(value); }, ).paddingOnly(bottom: 16.h), const Divider(height: 1, color: Color(0xFFEEEEEE)), diff --git a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart index 86b4c1e..cf7cc61 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/blood_sugar.dart @@ -51,14 +51,16 @@ class _BloodSugarWidgetState extends State { _isProgrammaticChange = true; final mgdlText = provider.mgdlValue ?? ''; final mmolText = provider.mmolValue ?? ''; - if (_mgdlController.text != mgdlText) { + + // Only update if focus is not on this field (to avoid cursor jumping) + if (!_mgdlFocus.hasFocus && _mgdlController.text != mgdlText) { _mgdlController.text = mgdlText; _mgdlController.selection = TextSelection.fromPosition( TextPosition(offset: _mgdlController.text.length), ); } - if (_mmolController.text != mmolText) { + if (!_mmolFocus.hasFocus && _mmolController.text != mmolText) { _mmolController.text = mmolText; _mmolController.selection = TextSelection.fromPosition( TextPosition(offset: _mmolController.text.length), @@ -84,7 +86,7 @@ class _BloodSugarWidgetState extends State { focusNode: _mgdlFocus, onChanged: (value) { if (_isProgrammaticChange) return; - provider.onBloodSugarChanged(value, 'mg/dL'); + provider.onBloodSugarMgdlChanged(value); }, ).paddingOnly(top: 16.h), Row( @@ -99,12 +101,10 @@ class _BloodSugarWidgetState extends State { width: 40.h, height: 40.h, ).onPress(() { + // Unfocus both fields before switching + _mgdlFocus.unfocus(); + _mmolFocus.unfocus(); provider.switchBloodSugarValues(); - if (provider.activeUnit == 'mg/dL') { - _mgdlFocus.requestFocus(); - } else { - _mmolFocus.requestFocus(); - } }), ], ), @@ -115,8 +115,7 @@ class _BloodSugarWidgetState extends State { focusNode: _mmolFocus, onChanged: (value) { if (_isProgrammaticChange) return; - - provider.onBloodSugarChanged(value, 'mmol/L'); + provider.onBloodSugarMmolChanged(value); }, ).paddingOnly(bottom: 16.h), const Divider(height: 1, color: Color(0xFFEEEEEE)), diff --git a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart index 17ad1f9..1c8c99e 100644 --- a/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart +++ b/lib/presentation/health_calculators_and_converts/widgets/triglycerides.dart @@ -53,14 +53,15 @@ class _TriglyceridesWidgetState extends State { final mgdlText = provider.triMgdlValue ?? ''; final mmolText = provider.triMmolValue ?? ''; - if (_mgdlController.text != mgdlText) { + // Only update if focus is not on this field (to avoid cursor jumping) + if (!_mgdlFocus.hasFocus && _mgdlController.text != mgdlText) { _mgdlController.text = mgdlText; _mgdlController.selection = TextSelection.fromPosition( TextPosition(offset: _mgdlController.text.length), ); } - if (_mmolController.text != mmolText) { + if (!_mmolFocus.hasFocus && _mmolController.text != mmolText) { _mmolController.text = mmolText; _mmolController.selection = TextSelection.fromPosition( TextPosition(offset: _mmolController.text.length), @@ -85,7 +86,7 @@ class _TriglyceridesWidgetState extends State { focusNode: _mgdlFocus, onChanged: (value) { if (_isProgrammaticChange) return; - provider.onTriglyceridesChanged(value, 'mg/dL'); + provider.onTriglyceridesMgdlChanged(value); }, ).paddingOnly(top: 16.h), @@ -101,13 +102,10 @@ class _TriglyceridesWidgetState extends State { width: 40.h, height: 40.h, ).onPress(() { + // Unfocus both fields before switching + _mgdlFocus.unfocus(); + _mmolFocus.unfocus(); provider.switchTriglyceridesValues(); - - if (provider.triActiveUnit == 'mg/dL') { - _mgdlFocus.requestFocus(); - } else { - _mmolFocus.requestFocus(); - } }), ], ), @@ -119,7 +117,7 @@ class _TriglyceridesWidgetState extends State { focusNode: _mmolFocus, onChanged: (value) { if (_isProgrammaticChange) return; - provider.onTriglyceridesChanged(value, 'mmol/L'); + provider.onTriglyceridesMmolChanged(value); }, ).paddingOnly(bottom: 16.h), @@ -170,7 +168,6 @@ class _TriglyceridesWidgetState extends State { child: TextField( controller: controller, focusNode: focusNode, - keyboardType: const TextInputType.numberWithOptions(decimal: true), onChanged: onChanged, @@ -185,6 +182,7 @@ class _TriglyceridesWidgetState extends State { style: TextStyle( fontSize: 32.f, fontWeight: FontWeight.bold, + color: Colors.black87, ), ), ),