diff --git a/assets/images/svg/insurance.svg b/assets/images/svg/insurance.svg new file mode 100644 index 0000000..c8d3c38 --- /dev/null +++ b/assets/images/svg/insurance.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/more.svg b/assets/images/svg/more.svg new file mode 100644 index 0000000..4dbe5fd --- /dev/null +++ b/assets/images/svg/more.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/requests.svg b/assets/images/svg/requests.svg new file mode 100644 index 0000000..7d90098 --- /dev/null +++ b/assets/images/svg/requests.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 7f1b6d1..ac3cfe6 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -72,6 +72,9 @@ class AppAssets { static const String cancel_circle_icon = '$svgBasePath/cancel_circle.svg'; static const String update_insurance_card_icon = '$svgBasePath/update_insurance_card.svg'; static const String close_bottom_sheet_icon = '$svgBasePath/close_bottom_sheet_icon.svg'; + static const String insurance = '$svgBasePath/insurance.svg'; + static const String requests = '$svgBasePath/requests.svg'; + static const String more = '$svgBasePath/more.svg'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; diff --git a/lib/core/utils/calendar_utils.dart b/lib/core/utils/calendar_utils.dart index 8786f7f..54b550f 100644 --- a/lib/core/utils/calendar_utils.dart +++ b/lib/core/utils/calendar_utils.dart @@ -4,7 +4,12 @@ import 'dart:io'; import 'dart:ui'; 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:jiffy/jiffy.dart'; import 'package:manage_calendar_events/manage_calendar_events.dart' as ios; +import 'package:permission_handler/permission_handler.dart'; import 'package:timezone/data/latest.dart' as tzl; final DeviceCalendarPlugin deviceCalendarPlugin = DeviceCalendarPlugin(); @@ -44,6 +49,94 @@ class CalendarUtils { // return _completer!.future; // } + Future> requestPermissions() async { + var permissionResults = [Permission.calendarFullAccess].request(); + 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); + // } + // } + // } + // } + + // 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); + // }, + // ), + // ); + // }, + // ); + // } + static Future getInstance() async { tzl.initializeTimeZones(); if (_completer != null) { diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 352415b..a2bb6d1 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -13,6 +13,8 @@ extension CapExtension on String { String get allInCaps => this.toUpperCase(); + String get needTranslation => this; + String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; } @@ -167,6 +169,8 @@ extension EmailValidator on String { bool isCenter = false, int? maxlines, TextAlign? textAlign, + FontWeight? weight, + double? letterSpacing = -0.4, }) => Text( this, @@ -175,8 +179,8 @@ extension EmailValidator on String { style: TextStyle( color: color ?? AppColors.blackColor, fontSize: 16.fSize, - letterSpacing: -0.4, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal, + letterSpacing: letterSpacing, + fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null, ), ); @@ -228,14 +232,11 @@ extension EmailValidator on String { style: TextStyle(height: height ?? 23 / 26, color: color ?? AppColors.blackColor, fontSize: 26.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), ); - Widget toText28({Color? color, bool isBold = false, double? height, bool isCenter = false}) => Text( - this, - textAlign: isCenter ? TextAlign.center : null, - style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), - ); - - + this, + textAlign: isCenter ? TextAlign.center : null, + style: TextStyle(height: height ?? 23 / 28, color: color ?? AppColors.blackColor, fontSize: 28.fSize, letterSpacing: -1, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), + ); Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( this, diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index db21b9e..e863dab 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.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/custom_tab_bar.dart' show CustomTabBar; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 4bb5cdb..ac652cb 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/presentation/insurance/widgets/patient_insur import 'package:hmg_patient_app_new/presentation/medical_file/widgets/medical_file_card.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/custom_tab_bar.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -168,6 +169,18 @@ class MedicalFilePage extends StatelessWidget { ), ), SizedBox(height: 16.h), + CustomTabBar( + activeTextColor: Color(0xffED1C2B), + activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1), + tabs: [ + CustomTabBarModel(AppAssets.myFilesBottom, "General".needTranslation), + CustomTabBarModel(AppAssets.insurance, "Insurance".needTranslation), + CustomTabBarModel(AppAssets.requests, "Requests".needTranslation), + CustomTabBarModel(AppAssets.more, "More".needTranslation), + ], + onTabChange: (index) {}, + ), + SizedBox(height: 16.h), //Insurance Tab Data Consumer(builder: (context, insuranceVM, child) { return insuranceVM.isInsuranceLoading diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index 65dc550..1f8e0a2 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -313,6 +313,7 @@ class _PrescriptionDetailPageState extends State { SizedBox(width: 12.h), Switch( activeColor: AppColors.successColor, + activeTrackColor: AppColors.successColor.withValues(alpha: .15), value: prescriptionVM.prescriptionDetailsList[index].hasReminder!, onChanged: (newValue) { setState(() { diff --git a/lib/services/permission_service.dart b/lib/services/permission_service.dart new file mode 100644 index 0000000..6287234 --- /dev/null +++ b/lib/services/permission_service.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +import 'package:permission_handler/permission_handler.dart'; + +// import 'package:vibration/vibration.dart'; +import 'package:geolocator/geolocator.dart' as geo; + +class PermissionService { + // final LocalStorage storage = new LocalStorage("permission"); + geo.LocationPermission? locationPermission; + + // AppGlobal appGlobal = new AppGlobal(); + + // setVibrationPermission(flag) async { + // storage.setItem('isVibration', flag); + // } + // + // isVibrationEnabled() { + // return (storage.getItem('isVibration') == null) || + // (storage.getItem('isVibration')) == false + // ? false + // : true; + // } + + // vibrate(callback, context) async { + // if (callback == null) return null; + // if (isVibrationEnabled() == true) { + // // if (await Vibration.hasVibrator() !=null) { + // // Vibration.vibrate(duration: 100); + // // callback(); + // // } + // } else { + // callback(); + // } + // } + + // setTheme(flag) async { + // storage.setItem('isTheme', flag); + // } + // + // isThemeEnabled() { + // return storage.getItem('isTheme'); + // } + + cameraPermission() async { + Map statuses = await [ + Permission.camera, + ].request(); + } + + static isCameraEnabled() async { + return await Permission.camera.isGranted; + } + + static isExternalStorageEnabled() async { + return await Permission.storage.isGranted; + } + + static isHealthDataPermissionEnabled() async { + return await Permission.sensors.isGranted; + } + + static isMicrophonePermissionEnabled() async { + return await Permission.microphone.isGranted; + } + + static isCalendarPermissionEnabled() async { + return await Permission.calendarFullAccess.isGranted; + } + + setCameraLocationPermission(context) async { + Navigator.pop(context); + openAppSettings(); + } + + static isLocationEnabled() async { + var permission = await geo.Geolocator.checkPermission(); + if (permission == geo.LocationPermission.denied) { + return false; + } else { + return true; + } + } + + openSettings() async { + openAppSettings(); + } + + openAccessbility() { + // OpenSettings.openAppSetting(); + } +} diff --git a/lib/widgets/custom_tab_bar.dart b/lib/widgets/custom_tab_bar.dart new file mode 100644 index 0000000..b6c8ef3 --- /dev/null +++ b/lib/widgets/custom_tab_bar.dart @@ -0,0 +1,113 @@ +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/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; + +class CustomTabBarModel { + String? image; + String title; + + CustomTabBarModel(this.image, this.title); +} + +class CustomTabBar extends StatefulWidget { + final int initialIndex = 0; + final List tabs; + final Color activeTextColor; + final Color activeBackgroundColor; + final Color inActiveTextColor; + final Color inActiveBackgroundColor; + final Function(int)? onTabChange; + + CustomTabBar( + {Key? key, + required this.tabs, + this.activeTextColor = const Color(0xff2E3039), + this.inActiveTextColor = const Color(0xff898A8D), + this.activeBackgroundColor = const Color(0x142E3039), + this.inActiveBackgroundColor = Colors.white, + this.onTabChange}) + : super(key: key); + + @override + _CustomTabBarState createState() { + return _CustomTabBarState(); + } +} + +class _CustomTabBarState extends State { + int selectedIndex = 0; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + void callBackCurrentIndex() { + if (widget.onTabChange != null) widget.onTabChange!(selectedIndex); + } + + @override + Widget build(BuildContext context) { + late Widget parentWidget; + + if (widget.tabs.length > 3) { + parentWidget = ListView.separated( + scrollDirection: Axis.horizontal, + padding: EdgeInsets.zero, + physics: const BouncingScrollPhysics(), + itemBuilder: (cxt, index) => myTab(widget.tabs[index], index), + separatorBuilder: (cxt, index) => 4.width, + itemCount: widget.tabs.length, + ); + } else { + parentWidget = Row( + spacing: 4, + children: [for (int i = 0; i < widget.tabs.length; i++) myTab(widget.tabs[i], i).expanded], + ); + } + + return Container( + height: 62.h, + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(11), + ), + child: parentWidget); + } + + Widget myTab(CustomTabBarModel tabBar, int currentIndex) { + bool isSelected = selectedIndex == currentIndex; + return Container( + height: 54.h, + padding: EdgeInsets.only(top: 4, bottom: 4, left: 16, right: 16), + alignment: Alignment.center, + decoration: BoxDecoration( + color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor, + borderRadius: BorderRadius.circular(7), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 4, + children: [ + if (tabBar.image != null) Utils.buildSvgWithAssets(icon: tabBar.image!, height: 18, width: 18, iconColor: isSelected ? widget.activeTextColor : widget.inActiveTextColor), + tabBar.title + .toText16(weight: isSelected ? FontWeight.w600 : FontWeight.w500, color: isSelected ? widget.activeTextColor : widget.inActiveTextColor, letterSpacing: isSelected ? -0.3 : -0.1), + ], + )).onPress(() { + setState(() { + selectedIndex = currentIndex; + }); + callBackCurrentIndex(); + }); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 1263e33..0f02a05 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -64,6 +64,7 @@ dependencies: equatable: ^2.0.7 google_api_availability: ^5.0.1 firebase_analytics: ^11.5.1 + jiffy: ^6.4.3 web: any flutter_staggered_animations: ^1.1.1