From cb4601050c187dbf6e3b03729ba9a28d7c30794d Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Mon, 8 Sep 2025 17:20:21 +0300 Subject: [PATCH] prescription reminder bottom sheet added. --- assets/images/svg/bell.svg | 4 + lib/core/app_assets.dart | 1 + lib/core/utils/calendar_utils.dart | 175 ++++++++++-------- lib/extensions/string_extensions.dart | 4 +- .../prescription_detail_page.dart | 11 +- .../prescription_reminder_view.dart | 116 ++++++++++++ lib/theme/colors.dart | 1 + lib/widgets/common_bottom_sheet.dart | 49 ++++- 8 files changed, 275 insertions(+), 86 deletions(-) create mode 100644 assets/images/svg/bell.svg create mode 100644 lib/presentation/prescriptions/prescription_reminder_view.dart diff --git a/assets/images/svg/bell.svg b/assets/images/svg/bell.svg new file mode 100644 index 0000000..82be912 --- /dev/null +++ b/assets/images/svg/bell.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index ac3cfe6..c70a309 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -13,6 +13,7 @@ class AppAssets { static const String email = '$svgBasePath/email.svg'; static const String globe = '$svgBasePath/globe.svg'; static const String cancel = '$svgBasePath/cancel.svg'; + static const String bell = '$svgBasePath/bell.svg'; static const String login1 = '$svgBasePath/login1.svg'; static const String tamara = '$svgBasePath/tamara.svg'; static const String confirm = '$svgBasePath/confirm.svg'; diff --git a/lib/core/utils/calendar_utils.dart b/lib/core/utils/calendar_utils.dart index 54b550f..00a5958 100644 --- a/lib/core/utils/calendar_utils.dart +++ b/lib/core/utils/calendar_utils.dart @@ -7,6 +7,7 @@ import 'package:device_calendar/device_calendar.dart'; import 'package:flutter/widgets.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/services/permission_service.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:jiffy/jiffy.dart'; import 'package:manage_calendar_events/manage_calendar_events.dart' as ios; import 'package:permission_handler/permission_handler.dart'; @@ -54,88 +55,100 @@ class CalendarUtils { return permissionResults; } - // showReminderDialog(BuildContext context, DateTime dateTime, String doctorName, String eventId, String appoDateFormatted, String appoTimeFormatted, - // {required Function() onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool isMultiAllowed = false}) async { - // if (Platform.isAndroid) { - // if (await PermissionService.isCalendarPermissionEnabled()) { - // _showReminderDialog(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted, - // onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed); - // } else { - // // Utils.showPermissionConsentDialog(context, TranslationBase.of(context).calendarPermission, () async { - // // if (await Permission.calendarFullAccess.request().isGranted) { - // // _showReminderDialog(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted, - // // onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed); - // // } - // // }); - // } - // } else { - // if (await Permission.calendarWriteOnly.request().isGranted) { - // if (await Permission.calendarFullAccess.request().isGranted) { - // _showReminderDialog(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted, - // onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed); - // } - // } - // } - // } + showReminderDialog(BuildContext context, DateTime dateTime, String doctorName, String eventId, String appoDateFormatted, String appoTimeFormatted, + {required Function() onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool isMultiAllowed = false}) async { + if (Platform.isAndroid) { + if (await PermissionService.isCalendarPermissionEnabled()) { + _showReminderBottomSheet(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted, + onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed); + } else { + // Utils.showPermissionConsentDialog(context, TranslationBase.of(context).calendarPermission, () async { + // if (await Permission.calendarFullAccess.request().isGranted) { + // _showReminderDialog(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted, + // onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed); + // } + // }); + } + } else { + if (await Permission.calendarWriteOnly.request().isGranted) { + if (await Permission.calendarFullAccess.request().isGranted) { + _showReminderBottomSheet(context, dateTime, doctorName, eventId, appoDateFormatted, appoTimeFormatted, + onSuccess: onSuccess, title: title, description: description, onMultiDateSuccess: onMultiDateSuccess, isMultiAllowed: isMultiAllowed); + } + } + } + } - // Future _showReminderDialog(BuildContext providedContext, DateTime dateTime, String doctorName, String eventId, String appoDateFormatted, String appoTimeFormatted, - // {required Function onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool? isMultiAllowed}) async { - // return showDialog( - // context: providedContext, - // barrierDismissible: true, // user must tap button! - // builder: (BuildContext context) { - // return Dialog( - // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)), - // insetPadding: EdgeInsets.all(21), - // child: ReminderDialog( - // onClick: (int i) async { - // String text = ""; - // if (i == 0) { - // // Before 30 mints - // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(minutes: 30).dateTime; - // text = "30 minutes"; - // // dateTime.add(new Duration(minutes: -30)); - // } else if (i == 1) { - // // Before 1 hour - // // dateTime.add(new Duration(minutes: -60)); - // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(hours: 1).dateTime; - // text = "1 hours"; - // } else if (i == 2) { - // // Before 1 hour and 30 mints - // // dateTime.add(new Duration(minutes: -90)); - // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(hours: 1, minutes: 30).dateTime; - // text = "1 hours 30 minutes"; - // } else if (i == 3) { - // // Before 2 hours - // // dateTime.add(new Duration(minutes: -120)); - // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(hours: 2).dateTime; - // text = "2 hours"; - // } - // if (!isMultiAllowed!) { - // if (onMultiDateSuccess == null) { - // CalendarUtils calendarUtils = await CalendarUtils.getInstance(); - // await calendarUtils.createOrUpdateEvent( - // title: title ?? "TranslationBase.of(providedContext).reminderTitle".needTranslation + " " + doctorName, - // description: description ?? "At " + appoDateFormatted + " " + appoTimeFormatted, - // scheduleDateTime: dateTime, - // eventId: eventId, location: ''); - // onSuccess(); - // } - // } else { - // onMultiDateSuccess!(i); - // } - // // await _analytics.logEvent( - // // name: name.trim().toLowerCase(), - // // parameters: safeParameters, - // // ); - // // todo @sikander discuss analytics for reminder - // // locator().appointment.appointment_reminder_time(reminde_before: text); - // }, - // ), - // ); - // }, - // ); - // } + Future _showReminderBottomSheet(BuildContext providedContext, DateTime dateTime, String doctorName, String eventId, String appoDateFormatted, String appoTimeFormatted, + {required Function onSuccess, String? title, String? description, Function(int)? onMultiDateSuccess, bool? isMultiAllowed}) async { + showCommonBottomSheet( + providedContext, + child: Column(children: [ + + ],), + callBackFunc: () {}, + title: "", + isCloseButtonVisible: false, + isFullScreen: false, height: null, + ); + + // return showDialog( + // context: providedContext, + // barrierDismissible: true, // user must tap button! + // builder: (BuildContext context) { + // return Dialog( + // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0.0)), + // insetPadding: EdgeInsets.all(21), + // child: ReminderDialog( + // onClick: (int i) async { + // String text = ""; + // if (i == 0) { + // // Before 30 mints + // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(minutes: 30).dateTime; + // text = "30 minutes"; + // // dateTime.add(new Duration(minutes: -30)); + // } else if (i == 1) { + // // Before 1 hour + // // dateTime.add(new Duration(minutes: -60)); + // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(hours: 1).dateTime; + // text = "1 hours"; + // } else if (i == 2) { + // // Before 1 hour and 30 mints + // // dateTime.add(new Duration(minutes: -90)); + // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(hours: 1, minutes: 30).dateTime; + // text = "1 hours 30 minutes"; + // } else if (i == 3) { + // // Before 2 hours + // // dateTime.add(new Duration(minutes: -120)); + // dateTime = Jiffy.parseFromDateTime(dateTime).subtract(hours: 2).dateTime; + // text = "2 hours"; + // } + // if (!isMultiAllowed!) { + // if (onMultiDateSuccess == null) { + // CalendarUtils calendarUtils = await CalendarUtils.getInstance(); + // await calendarUtils.createOrUpdateEvent( + // title: title ?? "TranslationBase.of(providedContext).reminderTitle".needTranslation + " " + doctorName, + // description: description ?? "At " + appoDateFormatted + " " + appoTimeFormatted, + // scheduleDateTime: dateTime, + // eventId: eventId, + // location: ''); + // onSuccess(); + // } + // } else { + // onMultiDateSuccess!(i); + // } + // // await _analytics.logEvent( + // // name: name.trim().toLowerCase(), + // // parameters: safeParameters, + // // ); + // // todo @sikander discuss analytics for reminder + // // locator().appointment.appointment_reminder_time(reminde_before: text); + // }, + // ), + // ); + // }, + // ); + } static Future getInstance() async { tzl.initializeTimeZones(); diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 0f04824..3dfe75f 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -203,9 +203,9 @@ extension EmailValidator on String { style: TextStyle(fontSize: 19.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4), ); - Widget toText20({Color? color, bool isBold = false}) => Text( + Widget toText20({Color? color,FontWeight? weight, bool isBold = false}) => Text( this, - style: TextStyle(fontSize: 20.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? AppColors.blackColor, letterSpacing: -0.4), + style: TextStyle(fontSize: 20.fSize, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), color: color ?? AppColors.blackColor, letterSpacing: -0.4), ); Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index 1f8e0a2..fbc44ba 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -12,8 +12,10 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/prescriptions/models/resp_models/patient_prescriptions_response_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_reminder_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -309,8 +311,7 @@ class _PrescriptionDetailPageState extends State { LocaleKeys.setReminder.tr(context: context).toText13(isBold: true), "Notify me before the consumption time".toText10(color: AppColors.textColorLight), ], - ), - SizedBox(width: 12.h), + ).expanded, Switch( activeColor: AppColors.successColor, activeTrackColor: AppColors.successColor.withValues(alpha: .15), @@ -319,6 +320,12 @@ class _PrescriptionDetailPageState extends State { setState(() { prescriptionVM.setPrescriptionItemReminder(newValue, prescriptionVM.prescriptionDetailsList[index]); }); + showCommonBottomSheetWithoutHeight(context, title: "Set the timer of reminder".needTranslation, child: PrescriptionReminderView( + setReminder: (int value) { + DateTime startDate = DateTime.now(); + DateTime endDate = DateTime(startDate.year, startDate.month, startDate.day + prescriptionVM.prescriptionDetailsList[index].days!.toInt()); + }, + ), callBackFunc: () {}, isFullScreen: false); }, ), ], diff --git a/lib/presentation/prescriptions/prescription_reminder_view.dart b/lib/presentation/prescriptions/prescription_reminder_view.dart new file mode 100644 index 0000000..69758df --- /dev/null +++ b/lib/presentation/prescriptions/prescription_reminder_view.dart @@ -0,0 +1,116 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.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/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class PrescriptionReminderView extends StatefulWidget { + Function(int) setReminder; + + PrescriptionReminderView({Key? key, required this.setReminder}) : super(key: key); + + @override + _PrescriptionReminderViewState createState() { + return _PrescriptionReminderViewState(); + } +} + +class _PrescriptionReminderViewState extends State { + final List _options = [15, 30, 60, 90]; + int _selectedOption = 0; // Nullable to represent no selection initially + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + spacing: 16.h, + children: [ + Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24), + child: ListView.builder( + itemCount: _options.length, + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.only(top: 8, bottom: 8), + shrinkWrap: true, + itemBuilder: (context, index) { + return Theme( + data: Theme.of(context).copyWith( + listTileTheme: ListTileThemeData(horizontalTitleGap: 4), + ), + child: RadioListTile( + title: Text( + "${_options[index]} minutes before".needTranslation, + style: TextStyle( + fontSize: 16.h, + fontWeight: FontWeight.w500, + ), + ), + value: index, + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return AppColors.primaryRedColor; + } + return Color(0xffEEEEEE); + }), + contentPadding: EdgeInsets.only(left: 12.h, right: 12.h), + groupValue: _selectedOption, + onChanged: (int? newValue) { + setState(() { + _selectedOption = newValue!; + }); + }, + ), + ); + }, + ), + ), + Row( + spacing: 16.h, + children: [ + Expanded( + child: CustomButton( + text: LocaleKeys.cancel.tr(), + onPressed: () { + Navigator.of(context).pop(); + }, + backgroundColor: AppColors.secondaryLightRedColor, + borderColor: AppColors.secondaryLightRedColor, + textColor: AppColors.primaryRedColor, + icon: AppAssets.cancel, + iconColor: AppColors.primaryRedColor, + ), + ), + Expanded( + child: CustomButton( + text: LocaleKeys.setReminder.tr(), + onPressed: () { + Navigator.of(context).pop(); + widget.setReminder(_selectedOption); + }, + backgroundColor: AppColors.bgGreenColor, + borderColor: AppColors.bgGreenColor, + textColor: Colors.white, + icon: AppAssets.bell, + ), + ), + ], + ), + ], + ); + } +} diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index 5cb9ced..5fb0df4 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -12,6 +12,7 @@ class AppColors { static const lightGray = Color(0xFFF4F5F7); static const lightPurple = Color(0xFFB7A3E6); static const scaffoldBgColor = Color(0xFFF8F8F8); + static const bottomSheetBgColor = Color(0xFFF8F8FA); static const lightGreyEFColor = Color(0xffeaeaff); static const greyF7Color = Color(0xffF7F7F7); static const lightGrayColor = Color(0xff808080); diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index f6039eb..95f8fc5 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -7,7 +7,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; void showCommonBottomSheet(BuildContext context, - {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true, bool isFullScreen = true}) { + {required Widget child, required VoidCallback callBackFunc, String? title, required double? height, bool isCloseButtonVisible = true, bool isFullScreen = true}) { showModalBottomSheet( sheetAnimationStyle: AnimationStyle( duration: Duration(milliseconds: 500), // Custom animation duration @@ -86,3 +86,50 @@ class ButtonSheetContent extends StatelessWidget { ); } } + +void showCommonBottomSheetWithoutHeight( + BuildContext context, { + required Widget child, + required VoidCallback callBackFunc, + String title = "", + bool isCloseButtonVisible = true, + bool isFullScreen = true, +}) { + showModalBottomSheet( + sheetAnimationStyle: AnimationStyle( + duration: Duration(milliseconds: 500), // Custom animation duration + reverseDuration: Duration(milliseconds: 300), // Custom reverse animation duration + ), + context: context, + isScrollControlled: true, + showDragHandle: false, + backgroundColor: AppColors.bottomSheetBgColor, + builder: (BuildContext context) { + return SafeArea( + top: false, + left: false, + right: false, + child: Container( + padding: EdgeInsets.only(left: 24, top: 24, right: 24, bottom: 12), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.bottomSheetBgColor, borderRadius: 24.h), + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 16.h, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (title.isNotEmpty) title.toText20(weight: FontWeight.w600).expanded, + Utils.buildSvgWithAssets(icon: AppAssets.close_bottom_sheet_icon, iconColor: Color(0xff2B353E)).onPress(() { + Navigator.of(context).pop(); + }), + ], + ), + child, + ], + )), + ); + }).then((value) { + callBackFunc(); + }); +}