fix toggle

pull/152/head
Fatimah.Alshammari 4 months ago
parent 27ad4e0764
commit 77242825bb

@ -5,36 +5,24 @@ import 'package:hmg_patient_app_new/features/active_prescriptions/active_prescri
import 'package:hmg_patient_app_new/services/error_handler_service.dart'; import 'package:hmg_patient_app_new/services/error_handler_service.dart';
class ActivePrescriptionsViewModel extends ChangeNotifier { class ActivePrescriptionsViewModel extends ChangeNotifier {
bool isActivePrescriptionsDetailsLoading = false;
late ActivePrescriptionsRepo activePrescriptionsRepo; late ActivePrescriptionsRepo activePrescriptionsRepo;
late ErrorHandlerService errorHandlerService; late ErrorHandlerService errorHandlerService;
List<ActivePrescriptionsResponseModel> activePrescriptionsDetailsList = [];
ActivePrescriptionsViewModel({ ActivePrescriptionsViewModel({
required this.activePrescriptionsRepo, required this.activePrescriptionsRepo,
required this.errorHandlerService, required this.errorHandlerService,
}); });
List<ActivePrescriptionsResponseModel> activePrescriptionsDetailsList = [];
initActivePrescriptionsViewModel() {
getActiveMedications();
notifyListeners();
}
setPrescriptionsDetailsLoading() {
isActivePrescriptionsDetailsLoading = true;
notifyListeners();
}
// Get medications list
Future<void> getActiveMedications({ Future<void> getActiveMedications({
Function(dynamic)? onSuccess, Function(dynamic)? onSuccess,
Function(String)? onError, Function(String)? onError,
}) async { }) async {
final result = await activePrescriptionsRepo.getActivePrescriptionsDetails(); final result =
await activePrescriptionsRepo.getActivePrescriptionsDetails();
result.fold( result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure), (failure) async =>
await errorHandlerService.handleError(failure: failure),
(apiResponse) { (apiResponse) {
if (apiResponse.messageStatus == 1) { if (apiResponse.messageStatus == 1) {
activePrescriptionsDetailsList = apiResponse.data ?? []; activePrescriptionsDetailsList = apiResponse.data ?? [];
@ -56,140 +44,58 @@ class ActivePrescriptionsViewModel extends ChangeNotifier {
return DateTime.tryParse(date) ?? DateTime.now(); return DateTime.tryParse(date) ?? DateTime.now();
} }
// Extract numeric value ( "3 / week" 3)
int extractNumberFromFrequency(String? frequency) {
if (frequency == null) return 1;
final m = RegExp(r'(\d+)').firstMatch(frequency);
if (m != null) return int.tryParse(m.group(1)!) ?? 1;
return 1;
}
// Generate medication days based on frequency text
List<DateTime> generateMedicationDays(ActivePrescriptionsResponseModel med) { List<DateTime> generateMedicationDays(ActivePrescriptionsResponseModel med) {
final start = parseDate(med.startDate); final start = parseDate(med.startDate);
final duration = med.days ?? 0; final duration = med.days ?? 0;
final frequency = (med.frequency ?? "").toLowerCase().trim(); if (duration <= 0) return [];
final f = (med.frequency ?? "").toLowerCase().trim();
int intervalDays = 1;
List<DateTime> result = []; if (f.contains("every six hours") ||
if (duration <= 0) return result; f.contains("every 6 hours") ||
f.contains("every four hours") ||
// Every N hours ( "Every Six Hours", "Every 8 hours") f.contains("every 4 hours") ||
if (frequency.contains("hour")) { f.contains("every eight hours") ||
final match = RegExp(r'every\s+(\d+)').firstMatch(frequency); f.contains("every 8 hours") ||
int intervalHours = 0; f.contains("every 12 hours") ||
f.contains("every twelve hours") ||
if (match != null) { f.contains("every 24 hours") ||
intervalHours = int.tryParse(match.group(1)!) ?? 0; f.contains("3 times a day") ||
} else { f.contains("once a day")) {
// handle text numbers like "Every six hours" intervalDays = 1;
final textNum = {
"one": 1,
"two": 2,
"three": 3,
"four": 4,
"five": 5,
"six": 6,
"seven": 7,
"eight": 8,
"nine": 9,
"ten": 10,
"twelve": 12,
};
for (var key in textNum.keys) {
if (frequency.contains(key)) {
intervalHours = textNum[key]!;
break;
}
}
}
if (intervalHours > 0) {
for (int day = 0; day < duration; day++) {
final dayStart = start.add(Duration(days: day));
for (int hour = 0; hour < 24; hour += intervalHours) {
result.add(DateTime(dayStart.year, dayStart.month, dayStart.day, hour));
}
}
return result;
} }
else if (f.contains("once a week")) {
intervalDays = 7;
} }
else if (f.contains("every 3 days")) {
// Daily (every day) intervalDays = 3;
if (frequency.contains("day") &&
!frequency.contains("every other") &&
!frequency.contains("every ")) {
for (int i = 0; i < duration; i++) {
result.add(start.add(Duration(days: i)));
} }
else if (f.contains("every other day")) {
intervalDays = 2;
} }
// Every other day List<DateTime> result = [];
else if (frequency.contains("every other day")) { for (int offset = 0; offset < duration; offset += intervalDays) {
for (int i = 0; i < duration; i += 2) { result.add(start.add(Duration(days: offset)));
result.add(start.add(Duration(days: i)));
}
}
// Every N days e.g. "Every 3 days", "Every 5 days"
else if (frequency.contains("every") && frequency.contains("day")) {
final match = RegExp(r'every\s+(\d+)').firstMatch(frequency);
final interval = match != null ? int.tryParse(match.group(1)!) ?? 1 : 1;
for (int i = 0; i < duration; i += interval) {
result.add(start.add(Duration(days: i)));
}
}
// Once or twice a week
else if (frequency.contains("once a week")) {
for (int i = 0; i < duration; i += 7) {
result.add(start.add(Duration(days: i)));
}
} else if (frequency.contains("twice a week")) {
for (int i = 0; i < duration; i += 3) {
result.add(start.add(Duration(days: i)));
}
}
// Numeric frequency like "3 / week", "2 / week"
else if (frequency.contains("week")) {
int timesPerWeek = extractNumberFromFrequency(frequency);
double interval = 7 / timesPerWeek;
double dayPointer = 0;
for (int i = 0; i < duration; i++) {
if (i >= dayPointer.floor()) {
result.add(start.add(Duration(days: i)));
dayPointer += interval;
}
}
}
else {
result.add(start);
}
final unique = <String, DateTime>{};
for (final d in result) {
unique["${d.year}-${d.month}-${d.day}"] = d;
} }
return unique.values.toList()..sort((a, b) => a.compareTo(b)); return result;
} }
bool sameYMD(DateTime a, DateTime b) => bool sameYMD(DateTime a, DateTime b) =>
a.year == b.year && a.month == b.month && a.day == b.day; a.year == b.year && a.month == b.month && a.day == b.day;
// Filter medications for selected day List<ActivePrescriptionsResponseModel> getMedsForSelectedDay(
List<ActivePrescriptionsResponseModel> getMedsForSelectedDay(DateTime selectedDate) { DateTime selectedDate) {
final target = DateTime(selectedDate.year, selectedDate.month, selectedDate.day); final clean = DateTime(selectedDate.year, selectedDate.month, selectedDate.day);
return activePrescriptionsDetailsList.where((med) { return activePrescriptionsDetailsList.where((med) {
final days = generateMedicationDays(med); final days = generateMedicationDays(med);
return days.any((d) => sameYMD(d, target)); return days.any((d) => sameYMD(d, clean));
}).toList(); }).toList();
} }
} }

@ -35,7 +35,7 @@ class ActivePrescriptionsResponseModel {
int? scaleOffset; int? scaleOffset;
String? startDate; String? startDate;
// Added for reminder feature // Added for reminder feature
List<String?> selectedDoseTimes = []; List<String?> selectedDoseTimes = [];
bool isReminderOn = false; // toggle status bool isReminderOn = false; // toggle status

@ -18,6 +18,7 @@ import '../../widgets/buttons/custom_button.dart';
import '../../widgets/chip/app_custom_chip_widget.dart'; // for date formatting import '../../widgets/chip/app_custom_chip_widget.dart'; // for date formatting
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../widgets/loader/bottomsheet_loader.dart'; import '../../widgets/loader/bottomsheet_loader.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ActiveMedicationPage extends StatefulWidget { class ActiveMedicationPage extends StatefulWidget {
@ -32,42 +33,97 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
late DateTime selectedDate; late DateTime selectedDate;
List<ActivePrescriptionsResponseModel> selectedDayMeds = []; List<ActivePrescriptionsResponseModel> selectedDayMeds = [];
ActivePrescriptionsViewModel? activePreVM; ActivePrescriptionsViewModel? activePreVM;
Map<int, bool> medReminderStatus = {};
Map<String, bool> medReminderStatus = {};
String _buildMedKey(ActivePrescriptionsResponseModel med) {
return "${med.itemId}_${med.startDate}_${med.days}_${med.frequency}";
}
int _getDosesCount(ActivePrescriptionsResponseModel med) {
return med.frequencyNumber ?? 1;
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
currentDate = DateTime.now(); currentDate = DateTime.now();
selectedDate = currentDate; selectedDate = currentDate;
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
activePreVM = Provider.of<ActivePrescriptionsViewModel>(context, listen: false); activePreVM =
Provider.of<ActivePrescriptionsViewModel>(context, listen: false);
LoaderBottomSheet.showLoader(); LoaderBottomSheet.showLoader();
await activePreVM!.getActiveMedications( await activePreVM!.getActiveMedications(
onSuccess: (_) { onSuccess: (_) async {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
final todayMeds = activePreVM!.getMedsForSelectedDay(selectedDate);
setState(() { final todayMeds =
selectedDayMeds = todayMeds; activePreVM!.getMedsForSelectedDay(selectedDate);
setState(() => selectedDayMeds = todayMeds);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await loadSavedReminders();
}); });
}, },
onError: (_) { onError: (_) {
LoaderBottomSheet.hideLoader(); LoaderBottomSheet.hideLoader();
}, },
); );
activePreVM!.addListener(() { activePreVM!.addListener(() {
if (!mounted) return; if (!mounted) return;
final medsForDay = activePreVM!.getMedsForSelectedDay(selectedDate); final medsForDay =
activePreVM!.getMedsForSelectedDay(selectedDate);
setState(() => selectedDayMeds = medsForDay); setState(() => selectedDayMeds = medsForDay);
}); });
}); });
} }
Future<void> loadSavedReminders() async {
final prefs = await SharedPreferences.getInstance();
for (final med in activePreVM!.activePrescriptionsDetailsList) {
final medKey = _buildMedKey(med);
final doses = _getDosesCount(med);
med.selectedDoseTimes =
List<String?>.filled(doses, null, growable: false);
for (int i = 0; i < doses; i++) {
final saved = prefs.getString("doseTime_${medKey}_$i");
if (saved != null) {
med.selectedDoseTimes[i] = saved;
}
}
final reminderOn =
prefs.getBool("reminderStatus_$medKey") ?? false;
med.isReminderOn = reminderOn;
medReminderStatus[medKey] = reminderOn;
}
setState(() {});
}
Future<void> saveReminderStatus(String medKey, bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool("reminderStatus_$medKey", value);
}
Future<void> saveDoseTime(
String medKey, int doseIndex, String time) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString("doseTime_${medKey}_$doseIndex", time);
}
List<DateTime> getUpcomingDays() =>
List.generate(7, (index) => currentDate.add(Duration(days: index)));
List<DateTime> getUpcomingDays() => List.generate(7, (index) => currentDate.add(Duration(days: index)));
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final days = getUpcomingDays(); final days = getUpcomingDays();
final dateText = "${selectedDate.day}${getSuffix(selectedDate.day)} ${DateFormat.MMMM().format(selectedDate)}";
return Scaffold( return Scaffold(
backgroundColor: AppColors.scaffoldBgColor, backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar( appBar: CustomAppBar(
@ -115,7 +171,7 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
child: Transform.translate( child: Transform.translate(
offset: const Offset(0, -4), offset: const Offset(0, -4),
child: Text( child: Text(
getSuffix(selectedDate.day), _getSuffix(selectedDate.day),
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
color: AppColors.textColor, color: AppColors.textColor,
@ -150,10 +206,22 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
itemCount: selectedDayMeds.length, itemCount: selectedDayMeds.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final med = selectedDayMeds[index]; final med = selectedDayMeds[index];
final doses = med.doseDailyQuantity ?? 1; final doses = _getDosesCount(med);
med.selectedDoseTimes ??= List.filled(doses, null); if (med.selectedDoseTimes.length != doses) {
final old = med.selectedDoseTimes;
med.selectedDoseTimes =
List<String?>.filled(doses, null,
growable: false);
for (int i = 0;
i < old.length && i < doses;
i++) {
med.selectedDoseTimes[i] = old[i];
}
}
return Container( return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 24.r, borderRadius: 24.r,
hasShadow: true, hasShadow: true,
@ -163,12 +231,21 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
children: [ children: [
_buildMedHeader(med), _buildMedHeader(med),
Row( Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment:
CrossAxisAlignment.center,
children: [ children: [
// Utils.buildSvgWithAssets(
// icon: AppAssets.,
// height: 18.h,
// width: 18.h,
// iconColor:
// AppColors.lightGreyTextColor,
// ),
Icon( Icon(
Icons.info_outline, Icons.info_outline,
color: AppColors.lightGreyTextColor, color: AppColors
size: 20, .lightGreyTextColor,
size: 18,
), ),
SizedBox(width: 6.h), SizedBox(width: 6.h),
Expanded( Expanded(
@ -176,18 +253,25 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
text: TextSpan( text: TextSpan(
children: [ children: [
TextSpan( TextSpan(
text: "Remarks: ".needTranslation, text: "Remarks: "
.needTranslation,
style: TextStyle( style: TextStyle(
color: AppColors.textColor, color:
fontWeight: FontWeight.w600, AppColors.textColor,
fontWeight:
FontWeight.w600,
fontSize: 10, fontSize: 10,
), ),
), ),
TextSpan( TextSpan(
text: "some remarks about the prescription will be here".needTranslation, text:
"some remarks about the prescription will be here"
.needTranslation,
style: TextStyle( style: TextStyle(
color: AppColors.lightGreyTextColor, color: AppColors
fontWeight: FontWeight.normal, .lightGreyTextColor,
fontWeight:
FontWeight.normal,
fontSize: 10, fontSize: 10,
), ),
), ),
@ -197,10 +281,10 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
), ),
], ],
).paddingOnly(left: 16, right: 16), ).paddingOnly(left: 16, right: 16),
const Divider(color: AppColors.greyColor), const Divider(
// Reminder Section color: AppColors.greyColor),
GestureDetector( GestureDetector(
onTap: () => showDoseDialog(med, index), onTap: () => showDoseDialog(med),
child: Row( child: Row(
children: [ children: [
Container( Container(
@ -209,13 +293,16 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.greyColor, color: AppColors.greyColor,
borderRadius: BorderRadius.circular(10), borderRadius:
BorderRadius.circular(10),
), ),
child: Utils.buildSvgWithAssets( child:
Utils.buildSvgWithAssets(
icon: AppAssets.bell, icon: AppAssets.bell,
height: 24.h, height: 24.h,
width: 24.h, width: 24.h,
iconColor: AppColors.greyTextColor, iconColor:
AppColors.greyTextColor,
), ),
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
@ -224,32 +311,43 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
crossAxisAlignment: crossAxisAlignment:
CrossAxisAlignment.start, CrossAxisAlignment.start,
children: [ children: [
Text("Set Reminder".needTranslation, Text(
"Set Reminder"
.needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 14.f, fontSize: 14.f,
fontWeight: FontWeight.w600, fontWeight:
color: AppColors.textColor)), FontWeight.w600,
Text("Notify me before the consumption time".needTranslation, color: AppColors
.textColor)),
Text(
"Notify me before the consumption time"
.needTranslation,
style: TextStyle( style: TextStyle(
fontSize: 12.f, fontSize: 12.f,
color: AppColors.textColorLight, color: AppColors
.textColorLight,
)), )),
], ],
), ),
), ),
_buildToggle(index) _buildToggle(med),
], ],
).paddingAll(16), ).paddingAll(16),
), ),
const Divider(color: AppColors.greyColor), const Divider(
color: AppColors.greyColor),
_buildButtons(), _buildButtons(),
], ],
), ),
); );
}, },
) )
: Utils.getNoDataWidget(context, : Utils.getNoDataWidget(
noDataText: "No medications today".needTranslation), context,
noDataText:
"No medications today".needTranslation,
),
), ),
), ),
], ],
@ -258,7 +356,8 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
} }
// medicine card // medicine card
Widget _buildMedHeader(ActivePrescriptionsResponseModel med) => Padding( Widget _buildMedHeader(ActivePrescriptionsResponseModel med) =>
Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -269,14 +368,16 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
child: Container( child: Container(
width: 59.h, width: 59.h,
height: 59.h, height: 59.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.spacerLineColor, color: AppColors.spacerLineColor,
borderRadius: 30.r, borderRadius: 30.r,
hasShadow: false, hasShadow: false,
), ),
child: Utils.buildImgWithNetwork( child: Utils.buildImgWithNetwork(
url: med.productImageString ?? "" ).circle(52.h) url: med.productImageString ?? "",
), iconColor: Colors.transparent)
.circle(52.h)),
), ),
SizedBox(width: 12.h), SizedBox(width: 12.h),
Expanded( Expanded(
@ -294,10 +395,19 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
spacing: 4, spacing: 4,
runSpacing: 4, runSpacing: 4,
children: [ children: [
AppCustomChipWidget(labelText: "Route: ${med.route}".needTranslation), AppCustomChipWidget(
AppCustomChipWidget(labelText: "Frequency: ${med.frequency}".needTranslation), labelText:
AppCustomChipWidget(labelText: "Daily Dose: ${med.doseDailyQuantity}".needTranslation), "Route: ${med.route}".needTranslation),
AppCustomChipWidget(labelText: "Duration: ${med.days}".needTranslation), AppCustomChipWidget(
labelText:
"Frequency: ${med.frequency}".needTranslation),
AppCustomChipWidget(
labelText:
"Daily Dose: ${med.doseDailyQuantity}"
.needTranslation),
AppCustomChipWidget(
labelText:
"Duration: ${med.days}".needTranslation),
], ],
), ),
], ],
@ -320,21 +430,25 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
SizedBox(width: 12.h), SizedBox(width: 12.h),
Expanded( Expanded(
child: CustomButton( child: CustomButton(
text: "Read Instructions".needTranslation, fontSize: 13.f, onPressed: () {})), text: "Read Instructions".needTranslation,
fontSize: 13.f,
onPressed: () {})),
]), ]),
); );
Widget _buildToggle(int index) { Widget _buildToggle(ActivePrescriptionsResponseModel med) {
final value = medReminderStatus[index] ?? false; final medKey = _buildMedKey(med);
final value = medReminderStatus[medKey] ?? false;
return GestureDetector( return GestureDetector(
onTap: () async { onTap: () async {
await showDoseDialog(selectedDayMeds[index], index); await showDoseDialog(med);
setState(() { final hasTime =
if ((selectedDayMeds[index].selectedDoseTimes ?? []).any((t) => t != null)) { (med.selectedDoseTimes).any((t) => t != null);
medReminderStatus[index] = true; medReminderStatus[medKey] = hasTime;
} await saveReminderStatus(medKey, hasTime);
});
setState(() {});
}, },
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
@ -342,11 +456,14 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
height: 28.h, height: 28.h,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
color: value ? AppColors.lightGreenColor : AppColors.greyColor.withOpacity(0.3), color: value
? AppColors.lightGreenColor
: AppColors.greyColor.withOpacity(0.3),
), ),
child: AnimatedAlign( child: AnimatedAlign(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
alignment: value ? Alignment.centerRight : Alignment.centerLeft, alignment:
value ? Alignment.centerRight : Alignment.centerLeft,
child: Padding( child: Padding(
padding: const EdgeInsets.all(3), padding: const EdgeInsets.all(3),
child: Container( child: Container(
@ -354,7 +471,9 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
height: 22.h, height: 22.h,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: value ? AppColors.textGreenColor : AppColors.greyTextColor, color: value
? AppColors.textGreenColor
: AppColors.greyTextColor,
), ),
), ),
), ),
@ -363,10 +482,16 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
); );
} }
Future<void> showDoseDialog(ActivePrescriptionsResponseModel med, int medIndex) {
final doses = med.frequencyNumber ?? 1; Future<void> showDoseDialog(ActivePrescriptionsResponseModel med) {
final doses = _getDosesCount(med);
if (med.selectedDoseTimes.length != doses) { if (med.selectedDoseTimes.length != doses) {
med.selectedDoseTimes = List.generate(doses, (_) => null); final old = med.selectedDoseTimes;
med.selectedDoseTimes =
List<String?>.filled(doses, null, growable: false);
for (int i = 0; i < old.length && i < doses; i++) {
med.selectedDoseTimes[i] = old[i];
}
} }
return showModalBottomSheet( return showModalBottomSheet(
@ -378,17 +503,20 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
height: 520.h, height: 520.h,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bottomSheetBgColor, color: AppColors.bottomSheetBgColor,
customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), customBorder: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
hasShadow: true, hasShadow: true,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Text( Text(
"Reminders".needTranslation, "Reminders".needTranslation,
@ -400,7 +528,13 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
), ),
GestureDetector( GestureDetector(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(context),
child: Icon(Icons.close, color:AppColors.blackBgColor), child: Utils.buildSvgWithAssets(
icon: AppAssets.close_bottom_sheet_icon,
height: 24.h,
width: 24.h,
iconColor:
AppColors.blackBgColor,
),
), ),
], ],
), ),
@ -415,45 +549,67 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
AppColors.labelColorYellow, AppColors.labelColorYellow,
AppColors.purpleBg AppColors.purpleBg
][doseIndex % 4]; ][doseIndex % 4];
final doseLabel =
final doseLabel = "${doseIndex + 1}${getSuffix(doseIndex + 1)}"; "${doseIndex + 1}${_getSuffix(doseIndex + 1)}";
final time = med.selectedDoseTimes[doseIndex] ?? "Not set yet"; final time =
med.selectedDoseTimes[doseIndex] ??
"Not set yet";
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
showTimePickerSheet(med, medIndex, doseIndex); showTimePickerSheet(med, doseIndex);
}, },
child: Container( child: Container(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder()
.toSmoothCornerDecoration(
color: AppColors.whiteColor, color: AppColors.whiteColor,
borderRadius: 16.r, borderRadius: 16.r,
hasShadow: false, hasShadow: false,
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 14),
vertical: 6, horizontal: 14),
decoration: BoxDecoration( decoration: BoxDecoration(
color: badgeColor, color: badgeColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Text( child: RichText(
doseLabel, text: TextSpan(
children: [
TextSpan(
text: "${doseIndex + 1}",
style: TextStyle( style: TextStyle(
color: AppColors.whiteColor, color: AppColors.whiteColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16.f, fontSize: 16.f,
), ),
), ),
WidgetSpan(
child: Transform.translate(
offset: const Offset(0, -4),
child: Text(
_getSuffix(doseIndex + 1),
style: TextStyle(
color: AppColors.whiteColor,
fontSize: 10.f,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
), ),
SizedBox(height: 8.h), SizedBox(height: 8.h),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded( Expanded(
child: Text( child: Text(
@ -465,8 +621,13 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
), ),
), ),
), ),
Icon(Icons.arrow_forward_outlined, Utils.buildSvgWithAssets(
size: 24.w, color: AppColors.textColor), icon: AppAssets.arrow_forward,
height: 24.h,
width: 24.h,
iconColor:
AppColors.textColor,
),
], ],
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
@ -494,7 +655,7 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
void showTimePickerSheet( void showTimePickerSheet(
ActivePrescriptionsResponseModel med, int medIndex, int doseIndex) { ActivePrescriptionsResponseModel med, int doseIndex) {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -502,20 +663,24 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
builder: (_) => Container( builder: (_) => Container(
width: double.infinity, width: double.infinity,
height: 460.h, height: 460.h,
decoration: BoxDecoration( decoration: const BoxDecoration(
color: AppColors.bottomSheetBgColor, color: AppColors.bottomSheetBgColor,
borderRadius: borderRadius: BorderRadius.only(
BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
), ),
child: ReminderTimerDialog( child: ReminderTimerDialog(
med: med, med: med,
frequencyNumber: med.doseDailyQuantity ?? 1, frequencyNumber: _getDosesCount(med),
doseIndex: doseIndex, doseIndex: doseIndex,
onTimeSelected: (String time) { onTimeSelected: (String time) async {
setState(() { final medKey = _buildMedKey(med);
med.selectedDoseTimes[doseIndex] = time; med.selectedDoseTimes[doseIndex] = time;
medReminderStatus[medIndex] = true; await saveDoseTime(medKey, doseIndex, time);
}); medReminderStatus[medKey] = true;
await saveReminderStatus(medKey, true);
setState(() {});
}, },
), ),
), ),
@ -529,7 +694,8 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
final vm = final vm =
Provider.of<ActivePrescriptionsViewModel>(context, listen: false); Provider.of<ActivePrescriptionsViewModel>(context,
listen: false);
setState(() { setState(() {
selectedDate = date; selectedDate = date;
selectedDayMeds = vm.getMedsForSelectedDay(date); selectedDayMeds = vm.getMedsForSelectedDay(date);
@ -547,7 +713,8 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
color: isSelected color: isSelected
? AppColors.primaryRedBorderColor ? AppColors.primaryRedBorderColor
: AppColors.spacerLineColor, : AppColors.spacerLineColor,
width: 1), width: 1,
),
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
@ -577,14 +744,14 @@ class _ActiveMedicationPageState extends State<ActiveMedicationPage> {
); );
} }
String getSuffix(int day) { String _getSuffix(int day) {
if (day == 1 || day == 21 || day == 31) return "st"; if (day == 1 || day == 21 || day == 31) return "st";
if (day == 2 || day == 22) return "nd"; if (day == 2 || day == 22) return "nd";
if (day == 3 || day == 23) return "rd"; if (day == 3 || day == 23) return "rd";
return "th"; return "th";
} }
}
}
class ReminderTimerDialog extends StatefulWidget { class ReminderTimerDialog extends StatefulWidget {
@ -617,32 +784,94 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
["06:00 PM", "07:00 PM", "08:00 PM", "09:00 PM"], // Evening ["06:00 PM", "07:00 PM", "08:00 PM", "09:00 PM"], // Evening
]; ];
String _getSuffix(int number) {
if (number == 1 || number == 21 || number == 31) return "st";
if (number == 2 || number == 22) return "nd";
if (number == 3 || number == 23) return "rd";
return "th";
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final int bucket = widget.doseIndex.clamp(0, 2); final int bucket = widget.doseIndex.clamp(0, 2);
final List<String> times = presetTimes[bucket]; final List<String> times = presetTimes[bucket];
return Padding( return Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Container( child: Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration( decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bottomSheetBgColor, color: AppColors.bottomSheetBgColor,
customBorder: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), customBorder: const BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24),
),
hasShadow: true, hasShadow: true,
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Row(
"Time for ${widget.doseIndex + 1} dose".needTranslation, mainAxisAlignment: MainAxisAlignment.spaceBetween,
style: TextStyle(fontSize: 18.f, fontWeight: FontWeight.bold), children: [
RichText(
text: TextSpan(
children: [
TextSpan(
text: "Time for ",
style: TextStyle(
fontSize: 18.f,
fontWeight: FontWeight.bold,
color: AppColors.textColor,
),
),
TextSpan(
text: "${widget.doseIndex + 1}",
style: TextStyle(
fontSize: 18.f,
fontWeight: FontWeight.bold,
color: AppColors.textColor,
),
),
WidgetSpan(
child: Transform.translate(
offset: const Offset(0, -6),
child: Text(
_getSuffix(widget.doseIndex + 1),
style: TextStyle(
fontSize: 12.f,
fontWeight: FontWeight.bold,
color: AppColors.textColor,
),
),
),
),
TextSpan(
text: " reminder",
style: TextStyle(
fontSize: 18.f,
fontWeight: FontWeight.bold,
color: AppColors.textColor,
),
),
],
),
),
GestureDetector(
onTap: () => Navigator.pop(context),
child:Utils.buildSvgWithAssets(
icon: AppAssets.close_bottom_sheet_icon,
height: 24.h,
width: 24.h,
iconColor:
AppColors.blackBgColor,
),),
],
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
// Preset times
Wrap( Wrap(
spacing: 8, spacing: 8,
runSpacing: 8, runSpacing: 8,
alignment: WrapAlignment.start,
children: times.map((t) { children: times.map((t) {
bool selected = _selectedTime == t; bool selected = _selectedTime == t;
return AppCustomChipWidget( return AppCustomChipWidget(
@ -660,7 +889,7 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
), ),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 14), padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14),
onChipTap: () { onChipTap: () {
setState(() { setState(() {
_selectedTime = t; _selectedTime = t;
@ -675,9 +904,7 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
SizedBox(height: 25.h), SizedBox(height: 25.h),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() => showPicker = !showPicker);
showPicker = !showPicker;
});
}, },
child: Center( child: Center(
child: Column( child: Column(
@ -687,7 +914,7 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
style: TextStyle( style: TextStyle(
fontSize: 48.f, fontSize: 48.f,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.textColor color: AppColors.textColor,
), ),
), ),
Text( Text(
@ -703,7 +930,6 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
), ),
), ),
SizedBox(height: 15.h), SizedBox(height: 15.h),
// Time picker
if (showPicker) if (showPicker)
SizedBox( SizedBox(
height: 100.h, height: 100.h,
@ -717,15 +943,11 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
selectedTime.hour, selectedTime.hour,
selectedTime.minute, selectedTime.minute,
), ),
onDateTimeChanged: (DateTime newTime) { onDateTimeChanged: (newTime) {
setState(() { setState(() {
_selectedTime = null; _selectedTime = null;
selectedTime = TimeOfDay( selectedTime = TimeOfDay(hour: newTime.hour, minute: newTime.minute);
hour: newTime.hour, bigTimeText = selectedTime.format(context).split(" ")[0];
minute: newTime.minute,
);
bigTimeText =
selectedTime.format(context).split(" ")[0];
}); });
}, },
), ),
@ -745,8 +967,7 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
), ),
), ),
onPressed: () async { onPressed: () async {
final selectedFormattedTime = final selectedFormattedTime = selectedTime.format(context);
selectedTime.format(context);
widget.onTimeSelected(selectedFormattedTime); widget.onTimeSelected(selectedFormattedTime);
try { try {
final parts = selectedFormattedTime.split(":"); final parts = selectedFormattedTime.split(":");
@ -756,7 +977,6 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
if (isPM && hour != 12) hour += 12; if (isPM && hour != 12) hour += 12;
if (!isPM && hour == 12) hour = 0; if (!isPM && hour == 12) hour = 0;
int totalMinutes = hour * 60 + minute; int totalMinutes = hour * 60 + minute;
// Call setCalender()
await setCalender( await setCalender(
context, context,
eventId: widget.med.itemId.toString(), eventId: widget.med.itemId.toString(),
@ -768,14 +988,11 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
route: widget.med.route ?? "", route: widget.med.route ?? "",
); );
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(content: Text("Reminder added to calendar ✅".needTranslation)),
content: Text("Reminder added to calendar ✅".needTranslation)),
); );
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(content: Text("Error while setting calendar: $e".needTranslation)),
content:
Text("Error while setting calendar: $e".needTranslation)),
); );
} }
Navigator.pop(context); Navigator.pop(context);
@ -797,7 +1014,6 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
); );
} }
TimeOfDay _parseTime(String t) { TimeOfDay _parseTime(String t) {
try { try {
int hour = int.parse(t.split(":")[0]); int hour = int.parse(t.split(":")[0]);
@ -806,7 +1022,7 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {
if (pm && hour != 12) hour += 12; if (pm && hour != 12) hour += 12;
if (!pm && hour == 12) hour = 0; if (!pm && hour == 12) hour = 0;
return TimeOfDay(hour: hour, minute: minute); return TimeOfDay(hour: hour, minute: minute);
} catch (e) { } catch (_) {
return TimeOfDay.now(); return TimeOfDay.now();
} }
} }
@ -815,3 +1031,5 @@ class _ReminderTimerDialogState extends State<ReminderTimerDialog> {

Loading…
Cancel
Save