Health calculators & converters

pull/133/head
aamir-csol 2 weeks ago
parent 54f8391066
commit 3601e39a29

@ -64,6 +64,11 @@ class BookAppointmentsViewModel extends ChangeNotifier {
List<DoctorsListResponseModel> doctorsList = [];
List<DoctorsListResponseModel> filteredDoctorList = [];
// Grouped doctors lists
List<List<DoctorsListResponseModel>> doctorsListByClinic = [];
List<List<DoctorsListResponseModel>> doctorsListByHospital = [];
List<List<DoctorsListResponseModel>> doctorsListGrouped = [];
List<DoctorsListResponseModel> liveCareDoctorsList = [];
List<PatientDentalPlanEstimationResponseModel> patientDentalPlanEstimationList = [];
@ -149,14 +154,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 = <String, List<DoctorsListResponseModel>>{};
final hospitalMap = <String, List<DoctorsListResponseModel>>{};
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,
@ -404,6 +423,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@ -435,6 +455,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
initializeFilteredList();
clearSearchFilters();
getFiltersFromDoctorList();
_groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
@ -1102,6 +1123,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
// initializeFilteredList();
// clearSearchFilters();
// getFiltersFromDoctorList();
_groupDoctorsList();
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);

@ -40,8 +40,14 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
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<int, GlobalKey> _itemKeys = {};
@override
void initState() {
_scrollController = ScrollController();
scheduleMicrotask(() {
if (bookAppointmentsViewModel.isLiveCareSchedule) {
bookAppointmentsViewModel.getLiveCareDoctorsList();
@ -58,6 +64,12 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
super.initState();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
bookAppointmentsViewModel = Provider.of<BookAppointmentsViewModel>(context, listen: false);
@ -67,6 +79,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
body: CollapsingListView(
title: "Choose Doctor".needTranslation,
child: SingleChildScrollView(
controller: _scrollController,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.h),
child: Consumer<BookAppointmentsViewModel>(builder: (context, bookAppointmentsVM, child) {
@ -149,9 +162,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
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
@ -160,7 +171,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
isLoading: true,
bookAppointmentsViewModel: bookAppointmentsViewModel,
)
: checkIsDoctorsListEmpty()
: bookAppointmentsVM.doctorsListGrouped.isEmpty
? Utils.getNoDataWidget(context, noDataText: "No Doctor found for selected criteria...".needTranslation)
: AnimationConfiguration.staggeredList(
position: index,
@ -169,6 +180,7 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
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),
@ -177,15 +189,69 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
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: 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),
],
),
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<int>(index),
padding: EdgeInsets.only(top: 12.h),
child: Column(
children: bookAppointmentsVM.doctorsListGrouped[index].map<Widget>((doctor) {
return Container(
margin: EdgeInsets.only(bottom: 12.h),
child: DoctorCard(
doctorsListResponseModel: bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index],
doctorsListResponseModel: doctor,
isLoading: false,
bookAppointmentsViewModel: bookAppointmentsViewModel,
).onPress(() async {
bookAppointmentsVM
.setSelectedDoctor(bookAppointmentsVM.isLiveCareSchedule ? bookAppointmentsVM.liveCareDoctorsList[index] : bookAppointmentsVM.doctorsList[index]);
// bookAppointmentsVM.setSelectedDoctor(DoctorsListResponseModel());
bookAppointmentsVM.setSelectedDoctor(doctor);
LoaderBottomSheet.showLoader();
await bookAppointmentsVM.getDoctorProfile(onSuccess: (dynamic respData) {
LoaderBottomSheet.hideLoader();
@ -205,6 +271,15 @@ class _SelectDoctorPageState extends State<SelectDoctorPage> {
);
});
}),
);
}).toList(),
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
),

@ -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 = {
@ -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;
}
final parsed = double.tryParse(value);
if (parsed == null) return;
// Convert mg/dL to mmol/L
_mmolValue = (parsed / 18.0182).toStringAsFixed(3);
notifyListeners();
}
void onBloodSugarMmolChanged(String value) {
_mmolValue = value;
_originalValue = value;
_originalUnit = 'mmol/L';
_isSwapped = false;
_activeUnit = 'mmol/L';
// mmol stays as source
_mmolValue = mmol.toStringAsFixed(1);
_mgdlValue = (mmol * 18.0182).toStringAsFixed(0);
} else {
final mgdl = double.tryParse(_mgdlValue ?? '');
if (mgdl == null) return;
if (value.isEmpty) {
_mgdlValue = null;
_originalValue = null;
notifyListeners();
return;
}
_activeUnit = 'mg/dL';
final parsed = double.tryParse(value);
if (parsed == null) return;
// mg/dL stays as source
_mgdlValue = mgdl.toStringAsFixed(0);
_mmolValue = (mgdl / 18.0182).toStringAsFixed(1);
// 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';
_cholActiveUnit = 'mmol/L';
if (value.isEmpty) {
_cholMmolValue = null;
_cholOriginalValue = null;
notifyListeners();
return;
}
_cholMmolValue = mmol.toStringAsFixed(2);
_cholMgdlValue = (mmol * 38.67).toStringAsFixed(0);
} else {
final mgdl = double.tryParse(_cholMgdlValue ?? '');
if (mgdl == null) return;
final parsed = double.tryParse(value);
if (parsed == null) return;
_cholActiveUnit = 'mg/dL';
// Convert mg/dL to mmol/L
_cholMmolValue = (parsed / 38.67).toStringAsFixed(3);
notifyListeners();
}
_cholMgdlValue = mgdl.toStringAsFixed(0);
_cholMmolValue = (mgdl / 38.67).toStringAsFixed(2);
void onCholesterolMmolChanged(String value) {
_cholMmolValue = value;
_cholOriginalValue = value;
_cholOriginalUnit = 'mmol/L';
_cholIsSwapped = false;
_cholActiveUnit = 'mmol/L';
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 switchTriglyceridesValues() {
if (_triActiveUnit == 'mg/dL') {
final mmol = double.tryParse(_triMmolValue ?? '');
if (mmol == null) return;
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';
_triMmolValue = mmol.toStringAsFixed(2);
_triMgdlValue = (mmol * 88.57).toStringAsFixed(0);
} else {
final mgdl = double.tryParse(_triMgdlValue ?? '');
if (mgdl == null) return;
_triActiveUnit = 'mg/dL';
_triMgdlValue = mgdl.toStringAsFixed(0);
_triMmolValue = (mgdl / 88.57).toStringAsFixed(2);
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() {
// Toggle between two states using the original entered value
if (_triOriginalValue == null || _triOriginalValue!.isEmpty) return;
final originalParsed = double.tryParse(_triOriginalValue!);
if (originalParsed == null) return;
_triIsSwapped = !_triIsSwapped;
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();
}
}

@ -52,14 +52,15 @@ class _BloodCholesterolWidgetState extends State<BloodCholesterolWidget> {
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<BloodCholesterolWidget> {
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<BloodCholesterolWidget> {
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)),

@ -51,14 +51,16 @@ class _BloodSugarWidgetState extends State<BloodSugarWidget> {
_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<BloodSugarWidget> {
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<BloodSugarWidget> {
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<BloodSugarWidget> {
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)),

@ -53,14 +53,15 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
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<TriglyceridesWidget> {
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<TriglyceridesWidget> {
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<TriglyceridesWidget> {
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<TriglyceridesWidget> {
child: TextField(
controller: controller,
focusNode: focusNode,
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
onChanged: onChanged,
@ -185,6 +182,7 @@ class _TriglyceridesWidgetState extends State<TriglyceridesWidget> {
style: TextStyle(
fontSize: 32.f,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
),

Loading…
Cancel
Save