initial commit
						commit
						b6766654e0
					
				| @ -0,0 +1,45 @@ | |||||||
|  | # Miscellaneous | ||||||
|  | *.class | ||||||
|  | *.log | ||||||
|  | *.pyc | ||||||
|  | *.swp | ||||||
|  | .DS_Store | ||||||
|  | .atom/ | ||||||
|  | .build/ | ||||||
|  | .buildlog/ | ||||||
|  | .history | ||||||
|  | .svn/ | ||||||
|  | .swiftpm/ | ||||||
|  | migrate_working_dir/ | ||||||
|  | 
 | ||||||
|  | # IntelliJ related | ||||||
|  | *.iml | ||||||
|  | *.ipr | ||||||
|  | *.iws | ||||||
|  | .idea/ | ||||||
|  | 
 | ||||||
|  | # The .vscode folder contains launch configuration and tasks you configure in | ||||||
|  | # VS Code which you may wish to be included in version control, so this line | ||||||
|  | # is commented out by default. | ||||||
|  | #.vscode/ | ||||||
|  | 
 | ||||||
|  | # Flutter/Dart/Pub related | ||||||
|  | **/doc/api/ | ||||||
|  | **/ios/Flutter/.last_build_id | ||||||
|  | .dart_tool/ | ||||||
|  | .flutter-plugins | ||||||
|  | .flutter-plugins-dependencies | ||||||
|  | .pub-cache/ | ||||||
|  | .pub/ | ||||||
|  | /build/ | ||||||
|  | 
 | ||||||
|  | # Symbolication related | ||||||
|  | app.*.symbols | ||||||
|  | 
 | ||||||
|  | # Obfuscation related | ||||||
|  | app.*.map.json | ||||||
|  | 
 | ||||||
|  | # Android Studio will place build artifacts here | ||||||
|  | /android/app/debug | ||||||
|  | /android/app/profile | ||||||
|  | /android/app/release | ||||||
| @ -0,0 +1,33 @@ | |||||||
|  | # This file tracks properties of this Flutter project. | ||||||
|  | # Used by Flutter tool to assess capabilities and perform upgrades etc. | ||||||
|  | # | ||||||
|  | # This file should be version controlled and should not be manually edited. | ||||||
|  | 
 | ||||||
|  | version: | ||||||
|  |   revision: "d7b523b356d15fb81e7d340bbe52b47f93937323" | ||||||
|  |   channel: "stable" | ||||||
|  | 
 | ||||||
|  | project_type: app | ||||||
|  | 
 | ||||||
|  | # Tracks metadata for the flutter migrate command | ||||||
|  | migration: | ||||||
|  |   platforms: | ||||||
|  |     - platform: root | ||||||
|  |       create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 | ||||||
|  |       base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 | ||||||
|  |     - platform: android | ||||||
|  |       create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 | ||||||
|  |       base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 | ||||||
|  |     - platform: ios | ||||||
|  |       create_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 | ||||||
|  |       base_revision: d7b523b356d15fb81e7d340bbe52b47f93937323 | ||||||
|  | 
 | ||||||
|  |   # User provided section | ||||||
|  | 
 | ||||||
|  |   # List of Local paths (relative to this file) that should be | ||||||
|  |   # ignored by the migrate tool. | ||||||
|  |   # | ||||||
|  |   # Files that are not part of the templates will be ignored by default. | ||||||
|  |   unmanaged_files: | ||||||
|  |     - 'lib/main.dart' | ||||||
|  |     - 'ios/Runner.xcodeproj/project.pbxproj' | ||||||
| @ -0,0 +1,16 @@ | |||||||
|  | # hmg_patient_app_new | ||||||
|  | 
 | ||||||
|  | New HMG Patient App | ||||||
|  | 
 | ||||||
|  | ## Getting Started | ||||||
|  | 
 | ||||||
|  | This project is a starting point for a Flutter application. | ||||||
|  | 
 | ||||||
|  | A few resources to get you started if this is your first Flutter project: | ||||||
|  | 
 | ||||||
|  | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) | ||||||
|  | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) | ||||||
|  | 
 | ||||||
|  | For help getting started with Flutter development, view the | ||||||
|  | [online documentation](https://docs.flutter.dev/), which offers tutorials, | ||||||
|  | samples, guidance on mobile development, and a full API reference. | ||||||
| @ -0,0 +1,28 @@ | |||||||
|  | # This file configures the analyzer, which statically analyzes Dart code to | ||||||
|  | # check for errors, warnings, and lints. | ||||||
|  | # | ||||||
|  | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled | ||||||
|  | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be | ||||||
|  | # invoked from the command line by running `flutter analyze`. | ||||||
|  | 
 | ||||||
|  | # The following line activates a set of recommended lints for Flutter apps, | ||||||
|  | # packages, and plugins designed to encourage good coding practices. | ||||||
|  | include: package:flutter_lints/flutter.yaml | ||||||
|  | 
 | ||||||
|  | linter: | ||||||
|  |   # The lint rules applied to this project can be customized in the | ||||||
|  |   # section below to disable rules from the `package:flutter_lints/flutter.yaml` | ||||||
|  |   # included above or to enable additional rules. A list of all available lints | ||||||
|  |   # and their documentation is published at https://dart.dev/lints. | ||||||
|  |   # | ||||||
|  |   # Instead of disabling a lint rule for the entire project in the | ||||||
|  |   # section below, it can also be suppressed for a single line of code | ||||||
|  |   # or a specific dart file by using the `// ignore: name_of_lint` and | ||||||
|  |   # `// ignore_for_file: name_of_lint` syntax on the line or in the file | ||||||
|  |   # producing the lint. | ||||||
|  |   rules: | ||||||
|  |     # avoid_print: false  # Uncomment to disable the `avoid_print` rule | ||||||
|  |     # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule | ||||||
|  | 
 | ||||||
|  | # Additional information about this file can be found at | ||||||
|  | # https://dart.dev/guides/language/analysis-options | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | { | ||||||
|  |   "english": "English", | ||||||
|  |   "arabic": "عربي", | ||||||
|  |   "login": "تسجيل الدخول", | ||||||
|  |   "noDataAvailable": "لا تتوافر بيانات", | ||||||
|  |   "ok": "موافق", | ||||||
|  |   "confirm": "تأكيد", | ||||||
|  |   "loadingText": "جاري التحميل، الرجاء الانتظار..." | ||||||
|  | } | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | { | ||||||
|  |   "english": "English", | ||||||
|  |   "arabic": "عربي", | ||||||
|  |   "login": "Login", | ||||||
|  |   "noDataAvailable": "No Data Available", | ||||||
|  |   "confirm": "Confirm", | ||||||
|  |   "ok": "OK", | ||||||
|  |   "loadingText": "Loading, please wait..." | ||||||
|  | } | ||||||
| @ -0,0 +1,4 @@ | |||||||
|  | storePassword=HmGsa123 | ||||||
|  | keyPassword=HmGsa123 | ||||||
|  | keyAlias=hmg | ||||||
|  | storeFile=key | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | export '../routes/app_routes.dart'; | ||||||
|  | export 'utils/size_utils.dart'; | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/main.dart'; | ||||||
|  | 
 | ||||||
|  | import 'consts.dart'; | ||||||
|  | 
 | ||||||
|  | class AppState { | ||||||
|  |   static final AppState _instance = AppState._internal(); | ||||||
|  | 
 | ||||||
|  |   AppState._internal(); | ||||||
|  | 
 | ||||||
|  |   factory AppState() => _instance; | ||||||
|  | 
 | ||||||
|  |   //Tokens | ||||||
|  | 
 | ||||||
|  |   bool isAuthenticated = false; | ||||||
|  | 
 | ||||||
|  |   set setIsAuthenticated(v) => isAuthenticated = v; | ||||||
|  | 
 | ||||||
|  |   set setAppAuthToken(v) => appAuthToken = v; | ||||||
|  | 
 | ||||||
|  |   String appAuthToken = ""; | ||||||
|  | 
 | ||||||
|  |   set setUserLat(v) => userLat = v; | ||||||
|  | 
 | ||||||
|  |   set setUserLong(v) => userLong = v; | ||||||
|  | 
 | ||||||
|  |   double userLat = 0.0; | ||||||
|  |   double userLong = 0.0; | ||||||
|  | 
 | ||||||
|  |   bool isArabic() => EasyLocalization.of(navigatorKey.currentContext!)?.locale.languageCode == "ar"; | ||||||
|  | 
 | ||||||
|  |   int getLanguageID(context) => EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; | ||||||
|  | 
 | ||||||
|  | // bool isUserLoggedIn() => | ||||||
|  | } | ||||||
| @ -0,0 +1,163 @@ | |||||||
|  | class ApiConsts { | ||||||
|  |   static const maxSmallScreen = 660; | ||||||
|  | 
 | ||||||
|  |   static bool isDevelopment = true; | ||||||
|  | 
 | ||||||
|  |   // static String baseUrlBackend = 'http://ec2-13-51-36-142.eu-north-1.compute.amazonaws.com:8082/api/'; // Backend API URL UAT | ||||||
|  |   // static String baseUrlBackend = 'http://207.127.99.20:82/api/'; // Backend API URL PROD | ||||||
|  |   static String baseUrlBackend = 'https://api-mob.meena-health.com/api/'; // Backend API URL PROD | ||||||
|  | 
 | ||||||
|  |   static String checkIfUserExists = 'UserRegistrationHIS/phone'; | ||||||
|  |   static String checkIfUserExistsMultiple = 'UserRegistrationHIS/phone/multiple'; | ||||||
|  |   static String checkIfUserExistsByMRN = 'UserRegistrationHIS/mrn/phone'; | ||||||
|  |   static String checkIfUserLastLoggedIn = 'UserRegistrationHIS/device-token'; | ||||||
|  |   static String updateUserLastLogIn = 'UserRegistrationHIS/save-device-token'; | ||||||
|  |   static String getStories = 'Stories'; | ||||||
|  |   static String saveAppointmentDetailsInBackend = 'UserAppointment'; | ||||||
|  |   static String getPatientAppointmentHistory = 'UserAppointment/my-appointments'; | ||||||
|  |   static String updateAppointmentDetailsInBackend = 'UserAppointment/update/'; | ||||||
|  |   static String submitDoctorReview = 'DoctorReview/submit-review'; | ||||||
|  |   static String getUserDataFromYakeenByIqama = 'Yakeen/by-iqama'; | ||||||
|  |   static String getUserDataFromYakeenByNin = 'Yakeen/by-nin'; | ||||||
|  |   static String sendVerificationCode = 'UserLogin/otp-request'; | ||||||
|  |   static String checkVerificationCode = 'UserLogin/otp-verify'; | ||||||
|  |   static String registerNewPatient = 'UserRegistrationHIS/save/patient'; | ||||||
|  |   static String getPatientVitalSignsHistory = 'Vitals/my-vitals'; | ||||||
|  |   static String getPatientLabReports = 'LabsOrder/my-orders'; | ||||||
|  |   static String getPatientRadReports = 'RadiologyOrder/my-orders'; | ||||||
|  |   static String getPatientPrescriptionsReports = 'Prescription/my-prescriptions'; | ||||||
|  |   static String updatePatientStoryViewedAPI = 'Stories/view'; | ||||||
|  |   static String getClinicsList = 'Clinics'; | ||||||
|  |   static String getDoctorsList = 'Doctors'; | ||||||
|  |   static String checkAppVersion = 'AppVersion/check-version'; | ||||||
|  |   static String getPatientLabOrdersAndResults = 'LabsOrder/my-order-result'; | ||||||
|  |   static String updatePatientProfile = 'Users/profile-update'; | ||||||
|  |   static String getPatientVitalSignsFromHIS = 'getpatientvital'; | ||||||
|  |   static String getDoctorReviewsByDoctorID = 'DoctorReview/'; | ||||||
|  | 
 | ||||||
|  |   // static String baseUrl = 'http://158.101.230.106:5016/phi/'; // HIS API URL UAT | ||||||
|  |   static String baseUrl = 'http://158.101.232.32:5016/phi/'; // HIS API URL PROD | ||||||
|  | 
 | ||||||
|  |   //Doctor Search | ||||||
|  |   static String doctorSearch = 'doctor/search'; | ||||||
|  | 
 | ||||||
|  |   //Get Patient Data | ||||||
|  |   static String getPatientData = 'patientdata/getpatientsdata'; | ||||||
|  | 
 | ||||||
|  |   //Get Free Slots | ||||||
|  |   static String getFreeSlots = 'doctor/timeslot'; | ||||||
|  | 
 | ||||||
|  |   //Create Appointment | ||||||
|  |   static String createAppointment = 'patient/appointment/create'; | ||||||
|  | 
 | ||||||
|  |   //Cancel Appointment | ||||||
|  |   static String cancelAppointment = 'patient/appointment/create'; | ||||||
|  | 
 | ||||||
|  |   //Create Deposit | ||||||
|  |   static String createDeposit = 'patient/PatientDeposits'; | ||||||
|  | 
 | ||||||
|  |   //Update Patient Photo | ||||||
|  |   static String updatePatientPhoto = 'patientphoto'; | ||||||
|  | 
 | ||||||
|  |   // get Patient Insurance Details | ||||||
|  |   static String getPatientInsuranceDetails = "http://158.101.232.32:5021/getpolicydetails"; | ||||||
|  | 
 | ||||||
|  |   // get Patient Insurance Details | ||||||
|  |   static String getDoctorSessionsByAppointments = "http://158.101.232.32:5016/phi/doctor/getdoctorsessionbyappointment"; // Prod | ||||||
|  | 
 | ||||||
|  |   // static String getDoctorSessionsByAppointments = "http://158.101.230.106:5016/phi/doctor/getdoctorsessionbyappointment"; // UAT | ||||||
|  | 
 | ||||||
|  |   //get Organization List | ||||||
|  |   static String getOrganizationsList = 'organisationsearch'; | ||||||
|  | 
 | ||||||
|  |   //get Departments List | ||||||
|  |   static String getDepartmentsList = 'getdepartmentsearch'; | ||||||
|  | 
 | ||||||
|  |   static String VERSION_ID = "1.6"; | ||||||
|  | 
 | ||||||
|  |   // Nabed APIs | ||||||
|  |   static String nabedBaseUrl = "https://portal.nabed.net/api/"; // Live | ||||||
|  |   static String nabedAuthenticationURL = "auth/jwt-token"; | ||||||
|  |   static String nabedGetPatientData = "educate/external/content/icd?page="; | ||||||
|  | 
 | ||||||
|  |   // SANED URLs | ||||||
|  |   static String sanedLoginPassword = "Aa123456@"; | ||||||
|  |   static String sanedSource = "Meena Mobile App"; | ||||||
|  | 
 | ||||||
|  |   static String baseUrlSaned = 'https://staging.sanedhealth.com/'; // SANED API URL STAGING | ||||||
|  |   // static String baseUrlSaned = 'https://hc.sanedhealth.com/'; // SANED API URL PRODUCTION | ||||||
|  |   static String sanedAPIVersion = 'mn_hc/'; | ||||||
|  | 
 | ||||||
|  |   // static String sanedWebSocketURL = 'wss://locationapi.sanedhealth.com/ws?apikey=st_zuhDm9xSyI'; // UAT SANED WEBSOCKET URL | ||||||
|  |   static String sanedWebSocketURL = 'wss://locationapiprod.sanedhealth.com/ws?apikey=st_A1xVs5qA7z'; // PRODUCTION WEBSOCKET URL | ||||||
|  | 
 | ||||||
|  |   static String getSanedAuthToken = 'api/external/login'; | ||||||
|  |   static String getSanedProductCategories = 'get_product_categories'; | ||||||
|  |   static String getProductPackages = 'get/product/packages'; | ||||||
|  |   static String getPackageDetails = 'get/package/details'; | ||||||
|  |   static String checkPatientByNationalitySaned = 'check_patient_by_nationality'; | ||||||
|  |   static String signupPatient = 'api/signup'; | ||||||
|  |   static String getPatientCartDetails = 'shop/cart'; | ||||||
|  |   static String addPackageToCart = 'shop/cart/update'; | ||||||
|  |   static String updateCartItems = 'shop/cart/update_json'; | ||||||
|  |   static String getPatientLocationList = 'get_patient_location_list'; | ||||||
|  |   static String updatePatientLocationList = 'update_patient_location'; | ||||||
|  |   static String getAvailableSlots = 'get/category/slot'; | ||||||
|  |   static String createHHCAppointment = 'api/create/appointment'; | ||||||
|  |   static String cancelHHCAppointment = 'cancel_appointment'; | ||||||
|  |   static String getBlackOutDates = 'get/blackout/dates'; | ||||||
|  | 
 | ||||||
|  |   // static String createConfirmHHCAppointment = 'api/create/appointment'; | ||||||
|  |   static String getPatientHHCAppointmentHistory = 'get_patient_medical_history/appointment'; | ||||||
|  | 
 | ||||||
|  |   static setBackendURLs() { | ||||||
|  |     if (isDevelopment) { | ||||||
|  |       baseUrlSaned = 'https://staging.sanedhealth.com/'; | ||||||
|  |       sanedWebSocketURL = "wss://locationapi.sanedhealth.com/ws?apikey=st_zuhDm9xSyI"; | ||||||
|  |       baseUrlBackend = "http://ec2-13-51-36-142.eu-north-1.compute.amazonaws.com:8082/api/"; | ||||||
|  |       baseUrl = "http://158.101.230.106:5016/phi/"; | ||||||
|  |       getDoctorSessionsByAppointments = "http://158.101.230.106:5016/phi/doctor/getdoctorsessionbyappointment"; | ||||||
|  |     } else { | ||||||
|  |       baseUrlSaned = 'https://hc.sanedhealth.com/'; | ||||||
|  |       sanedWebSocketURL = "wss://locationapiprod.sanedhealth.com/ws?apikey=st_A1xVs5qA7z"; | ||||||
|  |       baseUrlBackend = "https://api-mob.meena-health.com/api/"; | ||||||
|  |       baseUrl = "http://158.101.232.32:5016/phi/"; | ||||||
|  |       getDoctorSessionsByAppointments = "http://158.101.232.32:5016/phi/doctor/getdoctorsessionbyappointment"; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | // Login | ||||||
|  | // static String baseUrlLogin = 'https://keycloak.dev.evxtest.monster/realms/'; | ||||||
|  | // static String getLoginToken = 'my_city_uat/protocol/openid-connect/token'; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class SharedPrefsConsts { | ||||||
|  |   static String isRememberMe = "remember_me"; | ||||||
|  |   static String username = "doctorId"; | ||||||
|  |   static String password = "password"; | ||||||
|  |   static String logInTokenID = "logInTokenID"; | ||||||
|  |   static String vidaAuthTokenID = "vidaAuthTokenID"; | ||||||
|  |   static String vidaRefreshTokenID = "vidaRefreshTokenID"; | ||||||
|  |   static String authenticationTokenID = "authenticationTokenID"; | ||||||
|  |   static String projectID = "projectID"; | ||||||
|  |   static String clinicId = "clinicId"; | ||||||
|  |   static String lastLoginDate = "lastLoginDate"; | ||||||
|  |   static String lastLoginTime = "lastLoginTime"; | ||||||
|  |   static String memberModel = "memberModel"; | ||||||
|  | 
 | ||||||
|  |   static String isShowOnboarding = "is_show_onboarding"; | ||||||
|  |   static String appAuthToken = "app_auth_token"; | ||||||
|  |   static String appUserID = "app_user_id"; | ||||||
|  |   static String loggedInUserObj = "logged_in_user_obj"; | ||||||
|  | 
 | ||||||
|  |   static String PUSH_TOKEN = "push_token"; | ||||||
|  |   static String APNS_TOKEN = "apns_token"; | ||||||
|  |   static String VOIP_TOKEN = "voip_token"; | ||||||
|  |   static String PATIENT_MRN = "patient_mrn"; | ||||||
|  | 
 | ||||||
|  |   static String loggedInUserID = "logged_in_user_id"; | ||||||
|  |   static String loggedInUserPassword = "logged_in_user_password"; | ||||||
|  | 
 | ||||||
|  |   static String user_lat = 'user-lat'; | ||||||
|  |   static String user_long = 'user-long'; | ||||||
|  | } | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:injector/injector.dart'; | ||||||
|  | 
 | ||||||
|  | class AppDependencies { | ||||||
|  |   static void addDependencies() { | ||||||
|  |     Injector injector = Injector.appInstance; | ||||||
|  |     injector.registerSingleton<AppState>(() => AppState()); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | // enum APPSTATUS { | ||||||
|  | //   loading, | ||||||
|  | //   unAuthenticated, | ||||||
|  | //   authenticated, | ||||||
|  | //   unverified, | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | enum AuthMethodTypes { | ||||||
|  |   sms, | ||||||
|  |   whatsApp, | ||||||
|  |   fingerPrint, | ||||||
|  |   faceID, | ||||||
|  |   moreOptions, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum ViewState { | ||||||
|  |   hide, | ||||||
|  |   idle, | ||||||
|  |   busy, | ||||||
|  |   error, | ||||||
|  |   busyLocal, | ||||||
|  |   errorLocal, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum LoginType { | ||||||
|  |   FROM_LOGIN, | ||||||
|  |   SILENT_LOGIN, | ||||||
|  |   SILENT_WITH_OTP, | ||||||
|  | } | ||||||
| @ -0,0 +1,174 @@ | |||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/consts.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:geolocator/geolocator.dart'; | ||||||
|  | import 'package:google_maps_flutter/google_maps_flutter.dart'; | ||||||
|  | 
 | ||||||
|  | // import 'package:huawei_location/huawei_location.dart'; | ||||||
|  | import 'package:permission_handler/permission_handler.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | class LocationUtils { | ||||||
|  |   bool isShowConfirmDialog; | ||||||
|  |   bool isShowLocationTimeoutDialog; | ||||||
|  |   BuildContext context; | ||||||
|  |   bool isHuawei; | ||||||
|  |   final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance; | ||||||
|  | 
 | ||||||
|  |   LocationUtils({required this.isShowConfirmDialog, required this.context, this.isHuawei = false, this.isShowLocationTimeoutDialog = true}); | ||||||
|  | 
 | ||||||
|  |   void getCurrentLocation({Function(LatLng)? callBack}) async { | ||||||
|  |     Geolocator.isLocationServiceEnabled().then((value) async { | ||||||
|  |       if (value) { | ||||||
|  |         await Geolocator.checkPermission().then((permission) async { | ||||||
|  |           if (permission == LocationPermission.always || permission == LocationPermission.whileInUse) { | ||||||
|  |             // Geolocator.getCurrentPosition(locationSettings: LocationSettings(accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5))).then((value) { | ||||||
|  |             Geolocator.getLastKnownPosition().then((value) { | ||||||
|  |               setLocation(value); | ||||||
|  |               if (callBack != null) callBack(LatLng(value?.latitude ?? 24.7101433, value?.longitude ?? 46.6757709)); | ||||||
|  |             }).catchError((err) { | ||||||
|  |               print(err); | ||||||
|  |               if (isShowConfirmDialog && isShowLocationTimeoutDialog) { | ||||||
|  |                 // showLocationTimeOutDialog(failureCallBack: () { | ||||||
|  |                 //   Geolocator.openAppSettings(); | ||||||
|  |                 // }); | ||||||
|  |               } | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           if (permission == LocationPermission.denied || permission == LocationPermission.deniedForever) { | ||||||
|  |             if (Platform.isAndroid) { | ||||||
|  |               // Utils.showPermissionConsentDialog(context, TranslationBase.of(context).locationPermissionDialog, () async { | ||||||
|  |               final hasPermission = await _handlePermission(); | ||||||
|  |               if (hasPermission) { | ||||||
|  |                 // Geolocator.getCurrentPosition(locationSettings: LocationSettings(accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5))).then((value) { | ||||||
|  |                 Geolocator.getLastKnownPosition().then((value) { | ||||||
|  |                   setLocation(value); | ||||||
|  |                   if (callBack != null) callBack(LatLng(value?.latitude ?? 24.7101433, value?.longitude ?? 46.6757709)); | ||||||
|  |                 }); | ||||||
|  |               } else { | ||||||
|  |                 // if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {}); | ||||||
|  |               } | ||||||
|  |               // }); | ||||||
|  |             } else { | ||||||
|  |               if (await Permission.location.request().isGranted) { | ||||||
|  |                 getCurrentLocation(callBack: callBack); | ||||||
|  |               } else { | ||||||
|  |                 setZeroLocation(); | ||||||
|  |                 if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {}); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }).catchError((err) { | ||||||
|  |           print(err); | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         if (isShowConfirmDialog) showErrorLocationDialog(false, failureCallBack: () {}); | ||||||
|  |       } | ||||||
|  |     }).catchError((err) { | ||||||
|  |       print(err); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<bool> checkIfGPSIsEnabled() async { | ||||||
|  |     return await Geolocator.isLocationServiceEnabled(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<bool> _handlePermission() async { | ||||||
|  |     bool serviceEnabled; | ||||||
|  |     LocationPermission permission; | ||||||
|  | 
 | ||||||
|  |     serviceEnabled = await _geolocatorPlatform.isLocationServiceEnabled(); | ||||||
|  |     if (!serviceEnabled) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     permission = await _geolocatorPlatform.checkPermission(); | ||||||
|  |     if (permission == LocationPermission.denied) { | ||||||
|  |       permission = await _geolocatorPlatform.requestPermission(); | ||||||
|  |       if (permission == LocationPermission.denied) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (permission == LocationPermission.deniedForever) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // showLocationTimeOutDialog({Function()? failureCallBack}) { | ||||||
|  |   //   ConfirmDialog dialog = new ConfirmDialog( | ||||||
|  |   //       context: context, | ||||||
|  |   //       confirmMessage: TranslationBase.of(context).locationTimeoutError, | ||||||
|  |   //       okText: TranslationBase.of(context).ok, | ||||||
|  |   //       cancelText: TranslationBase.of(context).cancel_nocaps, | ||||||
|  |   //       okFunction: () { | ||||||
|  |   //         ConfirmDialog.closeAlertDialog(context); | ||||||
|  |   //         Navigator.of(context).canPop(); | ||||||
|  |   //         if (failureCallBack != null) { | ||||||
|  |   //           failureCallBack(); | ||||||
|  |   //         } | ||||||
|  |   //       }, | ||||||
|  |   //       cancelFunction: () { | ||||||
|  |   //         if (failureCallBack != null) { | ||||||
|  |   //           failureCallBack(); | ||||||
|  |   //         } | ||||||
|  |   //       }); | ||||||
|  |   //   return dialog.showAlertDialog(context); | ||||||
|  |   // } | ||||||
|  | 
 | ||||||
|  |   showErrorLocationDialog(bool isPermissionError, {Function()? failureCallBack}) { | ||||||
|  |     setLocation(null); | ||||||
|  |     // ConfirmDialog dialog = new ConfirmDialog( | ||||||
|  |     //     context: context, | ||||||
|  |     //     confirmMessage: TranslationBase.of(context).locationDialogMessage, | ||||||
|  |     //     okText: TranslationBase.of(context).confirm, | ||||||
|  |     //     cancelText: TranslationBase.of(context).cancel_nocaps, | ||||||
|  |     //     okFunction: () { | ||||||
|  |     //       ConfirmDialog.closeAlertDialog(context); | ||||||
|  |     //       if (isPermissionError) | ||||||
|  |     //         Geolocator.openAppSettings(); | ||||||
|  |     //       else | ||||||
|  |     //         Geolocator.openLocationSettings(); | ||||||
|  |     //       Navigator.of(context).canPop(); | ||||||
|  |     //       if (failureCallBack != null) { | ||||||
|  |     //         failureCallBack(); | ||||||
|  |     //       } | ||||||
|  |     //     }, | ||||||
|  |     //     cancelFunction: () { | ||||||
|  |     //       if (failureCallBack != null) { | ||||||
|  |     //         failureCallBack(); | ||||||
|  |     //       } | ||||||
|  |     //     }); | ||||||
|  |     // return dialog.showAlertDialog(context); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void setLocation(Position? position) { | ||||||
|  |     Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, position?.latitude ?? 0.0); | ||||||
|  |     Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, position?.longitude ?? 0.0); | ||||||
|  | 
 | ||||||
|  |     AppState().setUserLat = position?.latitude ?? 0.0; | ||||||
|  |     AppState().setUserLong = position?.longitude ?? 0.0; | ||||||
|  | 
 | ||||||
|  |     // projectViewModel.setLatitudeLongitude(position?.latitude ?? 0.0, position?.longitude ?? 0.0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void setZeroLocation() { | ||||||
|  |     Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, 0.0); | ||||||
|  |     Utils.saveNumFromPrefs(SharedPrefsConsts.user_lat, 0.0); | ||||||
|  | 
 | ||||||
|  |     AppState().setUserLat = 0.0; | ||||||
|  |     AppState().setUserLong = 0.0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<bool> requestPermissions() async { | ||||||
|  |     var result = await [ | ||||||
|  |       Permission.location, | ||||||
|  |     ].request(); | ||||||
|  |     return (result[Permission.location]!.isGranted || result[Permission.locationAlways]!.isGranted); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,178 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:convert'; | ||||||
|  | import 'dart:io'; | ||||||
|  | import 'dart:ui'; | ||||||
|  | 
 | ||||||
|  | import 'package:device_calendar/device_calendar.dart'; | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:manage_calendar_events/manage_calendar_events.dart' as ios; | ||||||
|  | import 'package:timezone/data/latest.dart' as tzl; | ||||||
|  | 
 | ||||||
|  | final DeviceCalendarPlugin deviceCalendarPlugin = DeviceCalendarPlugin(); | ||||||
|  | final ios.CalendarPlugin _myPlugin = ios.CalendarPlugin(); | ||||||
|  | 
 | ||||||
|  | class CalendarUtils { | ||||||
|  |   static Completer<CalendarUtils>? _completer; | ||||||
|  | 
 | ||||||
|  |   dynamic get writableCalendars => calendars.firstWhere((c) => !c.isReadOnly!); | ||||||
|  |   dynamic calendars; | ||||||
|  | 
 | ||||||
|  |   CalendarUtils._(this.calendars); | ||||||
|  | 
 | ||||||
|  |   // static Future<CalendarUtils> getInstance() async { | ||||||
|  |   //   if (_completer == null) { | ||||||
|  |   //     _completer = Completer<CalendarUtils>(); | ||||||
|  |   //     print(_completer!.isCompleted); | ||||||
|  |   //     try { | ||||||
|  |   //       final dynamic calendarsResult; | ||||||
|  |   //       if (Platform.isIOS) { | ||||||
|  |   //         calendarsResult = await _myPlugin.getCalendars(); | ||||||
|  |   //         if (!_completer!.isCompleted) { | ||||||
|  |   //           _completer?.complete(CalendarUtils._(await calendarsResult!)); | ||||||
|  |   //         } | ||||||
|  |   //       } else { | ||||||
|  |   //         calendarsResult = await deviceCalendarPlugin.retrieveCalendars(); | ||||||
|  |   //         if (!_completer!.isCompleted) { | ||||||
|  |   //           _completer?.complete(CalendarUtils._(await calendarsResult.data!)); | ||||||
|  |   //         } | ||||||
|  |   //       } | ||||||
|  |   //     } on Exception catch (e) { | ||||||
|  |   //       if (!_completer!.isCompleted) { | ||||||
|  |   //         _completer!.completeError(e); | ||||||
|  |   //       } | ||||||
|  |   //     } | ||||||
|  |   //   } | ||||||
|  |   //   return _completer!.future; | ||||||
|  |   // } | ||||||
|  | 
 | ||||||
|  |   static Future<CalendarUtils> getInstance() async { | ||||||
|  |     tzl.initializeTimeZones(); | ||||||
|  |     if (_completer != null) { | ||||||
|  |       return _completer!.future; | ||||||
|  |     } | ||||||
|  |     _completer = Completer<CalendarUtils>(); | ||||||
|  |     try { | ||||||
|  |       final dynamic calendarsResult; | ||||||
|  |       if (Platform.isIOS) { | ||||||
|  |         calendarsResult = await _myPlugin.getCalendars(); | ||||||
|  |         _completer!.complete(CalendarUtils._(calendarsResult)); | ||||||
|  |       } else { | ||||||
|  |         calendarsResult = await deviceCalendarPlugin.retrieveCalendars(); | ||||||
|  |         _completer!.complete(CalendarUtils._(calendarsResult.data)); | ||||||
|  |       } | ||||||
|  |     } catch (e) { | ||||||
|  |       _completer!.completeError(e); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return _completer!.future; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future createOrUpdateEvents({List<DateTime>? scheduleList, String? title, String? description, List<DateTime>? scheduleDateTime, List<DayOfWeek>? daysOfWeek}) async { | ||||||
|  |     tzl.initializeTimeZones(); | ||||||
|  |     List<Event> events = []; | ||||||
|  |     Location _currentLocation; | ||||||
|  |     if (DateTime.now().timeZoneName == "+04") | ||||||
|  |       _currentLocation = getLocation('Asia/Dubai'); | ||||||
|  |     else | ||||||
|  |       _currentLocation = getLocation('Asia/Riyadh'); | ||||||
|  | 
 | ||||||
|  |     scheduleDateTime!.forEach((element) { | ||||||
|  |       RecurrenceRule recurrenceRule = RecurrenceRule( | ||||||
|  |         // RecurrenceFrequency.Daily, | ||||||
|  |         // daysOfWeek: daysOfWeek, | ||||||
|  |         // endDate: element, | ||||||
|  |         until: element, frequency: Frequency.daily, | ||||||
|  |       ); | ||||||
|  |       //added byAamir Tz Time | ||||||
|  |       Event event = Event(writableCalendars!.id, | ||||||
|  |           recurrenceRule: recurrenceRule, | ||||||
|  |           start: TZDateTime.from(element, _currentLocation), | ||||||
|  |           end: TZDateTime.from(element.add(Duration(minutes: 30)), _currentLocation), | ||||||
|  |           title: title, | ||||||
|  |           description: description); | ||||||
|  |       events.add(event); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     events.forEach((element) { | ||||||
|  |       deviceCalendarPlugin.createOrUpdateEvent(element); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future createOrUpdateEvent({required String title, required String description, required String location, DateTime? scheduleDateTime, String? eventId}) async { | ||||||
|  |     RecurrenceRule recurrenceRule = RecurrenceRule( | ||||||
|  |       // RecurrenceFrequency.Daily, | ||||||
|  |       // daysOfWeek: daysOfWeek, | ||||||
|  |       // endDate: scheduleDateTime, | ||||||
|  |       until: scheduleDateTime, frequency: Frequency.daily, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     Location _currentLocation; | ||||||
|  |     // if (DateTime.now().timeZoneName == "+04") | ||||||
|  |     //   _currentLocation = getLocation('Asia/Dubai'); | ||||||
|  |     // else | ||||||
|  |     _currentLocation = getLocation('Asia/Riyadh'); | ||||||
|  | 
 | ||||||
|  |     TZDateTime scheduleDateTimeUTZ = TZDateTime.from(scheduleDateTime!, _currentLocation); | ||||||
|  | 
 | ||||||
|  |     print("writableCalendars-name:  " + writableCalendars.name); | ||||||
|  |     print("writableCalendars-Id:  " + writableCalendars.id); | ||||||
|  |     print("writableCalendarsToString:  " + writableCalendars.toString()); | ||||||
|  |     print("writableCalendarsToString:  " + writableCalendars!.id!); | ||||||
|  |     Event event = Event( | ||||||
|  |       writableCalendars!.id, | ||||||
|  |       start: scheduleDateTimeUTZ, | ||||||
|  |       end: scheduleDateTimeUTZ.add(Duration(minutes: 30)), | ||||||
|  |       title: title, | ||||||
|  |       description: description, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     ios.CalendarEvent iosCalEvent = | ||||||
|  |         ios.CalendarEvent(location: location, startDate: scheduleDateTimeUTZ, endDate: scheduleDateTimeUTZ.add(Duration(minutes: 30)), title: title, description: description, isAllDay: false); | ||||||
|  | 
 | ||||||
|  |     if (Platform.isAndroid) { | ||||||
|  |       Result<bool> result = await deviceCalendarPlugin.hasPermissions(); | ||||||
|  |       print(result); | ||||||
|  |       await deviceCalendarPlugin.createOrUpdateEvent(event).catchError((e) { | ||||||
|  |         print("catchError " + e.toString()); | ||||||
|  |       }).whenComplete(() { | ||||||
|  |         print("whenComplete Calender ID " + eventId!); | ||||||
|  |         // Utils.showToast(LocaleKeys.appoReminderSuccess.tr()); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       await _myPlugin.createEvent(calendarId: writableCalendars.id!, event: iosCalEvent).catchError((e) { | ||||||
|  |         print("catchError " + e.toString()); | ||||||
|  |       }).whenComplete(() { | ||||||
|  |         print("whenComplete Calender ID iOS " + eventId!); | ||||||
|  |         // Utils.showToast(LocaleKeys.appoReminderSuccess.tr()); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   deleteEvent(String _calendarId, String _eventId) async { | ||||||
|  |     if (Platform.isIOS) { | ||||||
|  |       await _myPlugin.deleteEvent(calendarId: _calendarId, eventId: _eventId); | ||||||
|  |     } else { | ||||||
|  |       await deviceCalendarPlugin.deleteEvent(_calendarId, _eventId); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future retrieveEvents( | ||||||
|  |     String calendarId, | ||||||
|  |     RetrieveEventsParams retrieveEventsParams, | ||||||
|  |   ) async { | ||||||
|  |     if (Platform.isIOS) { | ||||||
|  |       return await _myPlugin.getEvents(calendarId: calendarId); | ||||||
|  |     } else { | ||||||
|  |       return await deviceCalendarPlugin.retrieveEvents(calendarId, retrieveEventsParams); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future createCalendar( | ||||||
|  |     String calendarName, { | ||||||
|  |     Color? calendarColor, | ||||||
|  |     String? localAccountName, | ||||||
|  |   }) async { | ||||||
|  |     return await deviceCalendarPlugin.createCalendar(calendarName, calendarColor: calendarColor, localAccountName: localAccountName); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,191 @@ | |||||||
|  | import 'dart:math'; | ||||||
|  | import 'dart:typed_data'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; | ||||||
|  | 
 | ||||||
|  | final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); | ||||||
|  | 
 | ||||||
|  | class LocalNotification { | ||||||
|  |   Function(String payload)? _onNotificationClick; | ||||||
|  |   static LocalNotification? _instance; | ||||||
|  | 
 | ||||||
|  |   static LocalNotification? getInstance() { | ||||||
|  |     return _instance; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static init({required Function(String payload) onNotificationClick}) { | ||||||
|  |     if (_instance == null) { | ||||||
|  |       _instance = LocalNotification(); | ||||||
|  |       _instance?._onNotificationClick = onNotificationClick; | ||||||
|  |       _instance?._initialize(); | ||||||
|  |     } else { | ||||||
|  |       // assert(false,(){ | ||||||
|  |       //   //TODO fix it | ||||||
|  |       //   "LocalNotification Already Initialized"; | ||||||
|  |       // }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   _initialize() async { | ||||||
|  |     try { | ||||||
|  |       var initializationSettingsAndroid = new AndroidInitializationSettings('app_icon'); | ||||||
|  |       var initializationSettingsIOS = DarwinInitializationSettings(); | ||||||
|  |       var initializationSettings = InitializationSettings(android: initializationSettingsAndroid, iOS: initializationSettingsIOS); | ||||||
|  |       await flutterLocalNotificationsPlugin.initialize( | ||||||
|  |         initializationSettings, | ||||||
|  |         onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) { | ||||||
|  |           switch (notificationResponse.notificationResponseType) { | ||||||
|  |             case NotificationResponseType.selectedNotification: | ||||||
|  |               // selectNotificationStream.add(notificationResponse.payload); | ||||||
|  |               break; | ||||||
|  |             case NotificationResponseType.selectedNotificationAction: | ||||||
|  |               // if (notificationResponse.actionId == navigationActionId) { | ||||||
|  |               //   selectNotificationStream.add(notificationResponse.payload); | ||||||
|  |               // } | ||||||
|  |               break; | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         // onDidReceiveBackgroundNotificationResponse: notificationTapBackground, | ||||||
|  |       ); | ||||||
|  |     } catch (ex) { | ||||||
|  |       print(ex.toString()); | ||||||
|  |     } | ||||||
|  |     //   flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse notificationResponse) | ||||||
|  |     //   { | ||||||
|  |     //     switch (notificationResponse.notificationResponseType) { | ||||||
|  |     //       case NotificationResponseType.selectedNotification: | ||||||
|  |     //       // selectNotificationStream.add(notificationResponse.payload); | ||||||
|  |     //         break; | ||||||
|  |     //       case NotificationResponseType.selectedNotificationAction: | ||||||
|  |     //       // if (notificationResponse.actionId == navigationActionId) { | ||||||
|  |     //       // selectNotificationStream.add(notificationResponse.payload); | ||||||
|  |     //     } | ||||||
|  |     //     // break; | ||||||
|  |     //   },} | ||||||
|  |     // | ||||||
|  |     // , | ||||||
|  |     // | ||||||
|  |     // ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // void notificationTapBackground(NotificationResponse notificationResponse) { | ||||||
|  |   //   // ignore: avoid_print | ||||||
|  |   //   print('notification(${notificationResponse.id}) action tapped: ' | ||||||
|  |   //       '${notificationResponse.actionId} with' | ||||||
|  |   //       ' payload: ${notificationResponse.payload}'); | ||||||
|  |   //   if (notificationResponse.input?.isNotEmpty ?? false) { | ||||||
|  |   //     // ignore: avoid_print | ||||||
|  |   //     print('notification action tapped with input: ${notificationResponse.input}'); | ||||||
|  |   //   } | ||||||
|  |   // } | ||||||
|  | 
 | ||||||
|  |   var _random = new Random(); | ||||||
|  | 
 | ||||||
|  |   _randomNumber({int from = 100000}) { | ||||||
|  |     return _random.nextInt(from); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   _vibrationPattern() { | ||||||
|  |     var vibrationPattern = Int64List(4); | ||||||
|  |     vibrationPattern[0] = 0; | ||||||
|  |     vibrationPattern[1] = 1000; | ||||||
|  |     vibrationPattern[2] = 5000; | ||||||
|  |     vibrationPattern[3] = 2000; | ||||||
|  | 
 | ||||||
|  |     return vibrationPattern; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future? showNow({required String title, required String subtitle, required String payload}) { | ||||||
|  |     Future.delayed(Duration(seconds: 1)).then((result) async { | ||||||
|  |       var androidPlatformChannelSpecifics = AndroidNotificationDetails( | ||||||
|  |         'com.hmg.local_notification', | ||||||
|  |         'HMG', | ||||||
|  |         channelDescription: 'HMG', | ||||||
|  |         importance: Importance.max, | ||||||
|  |         priority: Priority.high, | ||||||
|  |         ticker: 'ticker', | ||||||
|  |         vibrationPattern: _vibrationPattern(), | ||||||
|  |         ongoing: true, | ||||||
|  |         autoCancel: false, | ||||||
|  |         usesChronometer: true, | ||||||
|  |         when: DateTime.now().millisecondsSinceEpoch - 120 * 1000, | ||||||
|  |       ); | ||||||
|  |       var iOSPlatformChannelSpecifics = DarwinNotificationDetails(); | ||||||
|  |       var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics); | ||||||
|  |       await flutterLocalNotificationsPlugin.show(25613, title, subtitle, platformChannelSpecifics, payload: payload).catchError((err) { | ||||||
|  |         print(err); | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future scheduleNotification({required DateTime scheduledNotificationDateTime, required String title, required String description}) async { | ||||||
|  |     ///vibrationPattern | ||||||
|  |     var vibrationPattern = Int64List(4); | ||||||
|  |     vibrationPattern[0] = 0; | ||||||
|  |     vibrationPattern[1] = 1000; | ||||||
|  |     vibrationPattern[2] = 5000; | ||||||
|  |     vibrationPattern[3] = 2000; | ||||||
|  | 
 | ||||||
|  |     //  var androidPlatformChannelSpecifics = AndroidNotificationDetails('active-prescriptions', 'ActivePrescriptions', | ||||||
|  |     //     channelDescription: 'ActivePrescriptionsDescription', | ||||||
|  |     //     // icon: 'secondary_icon', | ||||||
|  |     //     sound: RawResourceAndroidNotificationSound('slow_spring_board'), | ||||||
|  |     // | ||||||
|  |     //     ///change it to be as ionic | ||||||
|  |     //     // largeIcon: DrawableResourceAndroidBitmap('sample_large_icon'),///change it to be as ionic | ||||||
|  |     //     vibrationPattern: vibrationPattern, | ||||||
|  |     //     enableLights: true, | ||||||
|  |     //     color: const Color.fromARGB(255, 255, 0, 0), | ||||||
|  |     //     ledColor: const Color.fromARGB(255, 255, 0, 0), | ||||||
|  |     //     ledOnMs: 1000, | ||||||
|  |     //     ledOffMs: 500); | ||||||
|  |     // var iOSPlatformChannelSpecifics = DarwinNotificationDetails(sound: 'slow_spring_board.aiff'); | ||||||
|  | 
 | ||||||
|  |     // /change it to be as ionic | ||||||
|  |     // var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics); | ||||||
|  |     // await flutterLocalNotificationsPlugin.schedule(0, title, description, scheduledNotificationDateTime, platformChannelSpecifics); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ///Repeat notification every day at approximately 10:00:00 am | ||||||
|  |   Future showDailyAtTime() async { | ||||||
|  |    // var time = Time(10, 0, 0); | ||||||
|  |     // var androidPlatformChannelSpecifics = AndroidNotificationDetails('repeatDailyAtTime channel id', 'repeatDailyAtTime channel name', channelDescription: 'repeatDailyAtTime description'); | ||||||
|  |     // var iOSPlatformChannelSpecifics = DarwinNotificationDetails(); | ||||||
|  |     // var platformChannelSpecifics = NotificationDetails( | ||||||
|  |     //     androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics); | ||||||
|  |     // await flutterLocalNotificationsPlugin.showDailyAtTime( | ||||||
|  |     //     0, | ||||||
|  |     //     'show daily title', | ||||||
|  |     //     'Daily notification shown at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}', | ||||||
|  |     //     time, | ||||||
|  |     //     platformChannelSpecifics); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ///Repeat notification weekly on Monday at approximately 10:00:00 am | ||||||
|  |   Future showWeeklyAtDayAndTime() async { | ||||||
|  |    // var time = Time(10, 0, 0); | ||||||
|  |     // var androidPlatformChannelSpecifics = AndroidNotificationDetails('show weekly channel id', 'show weekly channel name', channelDescription: 'show weekly description'); | ||||||
|  |     // var iOSPlatformChannelSpecifics = DarwinNotificationDetails(); | ||||||
|  |     // var platformChannelSpecifics = NotificationDetails( | ||||||
|  |     //     androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics); | ||||||
|  |     // await flutterLocalNotificationsPlugin.showWeeklyAtDayAndTime( | ||||||
|  |     //     0, | ||||||
|  |     //     'show weekly title', | ||||||
|  |     //     'Weekly notification shown on Monday at approximately ${_toTwoDigitString(time.hour)}:${_toTwoDigitString(time.minute)}:${_toTwoDigitString(time.second)}', | ||||||
|  |     //     Day.Monday, | ||||||
|  |     //     time, | ||||||
|  |     //     platformChannelSpecifics); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String _toTwoDigitString(int value) { | ||||||
|  |     return value.toString().padLeft(2, '0'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future cancelNotification() async { | ||||||
|  |     await flutterLocalNotificationsPlugin.cancel(0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future cancelAllNotifications() async { | ||||||
|  |     await flutterLocalNotificationsPlugin.cancelAll(); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,383 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:developer'; | ||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:device_info_plus/device_info_plus.dart'; | ||||||
|  | import 'package:firebase_messaging/firebase_messaging.dart'; | ||||||
|  | import 'package:firebase_messaging/firebase_messaging.dart' as fir; | ||||||
|  | import 'package:firebase_core/firebase_core.dart'; | ||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter_local_notifications/flutter_local_notifications.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/LocalNotification.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/utils.dart'; | ||||||
|  | import 'package:permission_handler/permission_handler.dart'; | ||||||
|  | import 'package:uuid/uuid.dart'; | ||||||
|  | 
 | ||||||
|  | import '../consts.dart'; | ||||||
|  | 
 | ||||||
|  | // |--> Push Notification Background | ||||||
|  | @pragma('vm:entry-point') | ||||||
|  | Future<dynamic> backgroundMessageHandler(dynamic message) async { | ||||||
|  |   print("Firebase backgroundMessageHandler!!!"); | ||||||
|  |   await Firebase.initializeApp(); | ||||||
|  |   fir.RemoteMessage message_; | ||||||
|  | 
 | ||||||
|  |   if (message.data != null && (message.data['is_call'] == 'true' || message.data['is_call'] == true)) { | ||||||
|  |     // showCallkitIncoming(message); | ||||||
|  |     _incomingCall(message.data); | ||||||
|  |     return; | ||||||
|  |   } else {} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | callPage(String sessionID, String token) async {} | ||||||
|  | 
 | ||||||
|  | _incomingCall(Map<String, dynamic> data) async { | ||||||
|  |   // LandingPage.incomingCallData = IncomingCallData.fromJson(data); | ||||||
|  |   // var dataItem = await AppSharedPreferences().getObject('call_data'); | ||||||
|  |   // if(dataItem != null ) return; // to stop repeated attempt to invoke the call | ||||||
|  |   // if (LandingPage.isOpenCallPage == false) { | ||||||
|  |   //   LandingPage.isOpenCallPage = true; | ||||||
|  |   WidgetsFlutterBinding.ensureInitialized(); | ||||||
|  |   // var _currentUuid = Uuid().v4(); | ||||||
|  |   // await FlutterCallkitIncoming.showCallkitIncoming(callKitParams); | ||||||
|  |   // } | ||||||
|  |   // LandingPage.isOpenCallPage = false; | ||||||
|  |   await Future.delayed(Duration(milliseconds: 500)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Future<void> openCallPage(BuildContext context) async { | ||||||
|  |   try { | ||||||
|  |     // if (incomingCallData!.background == "0") { | ||||||
|  |     // Zoom Call Page | ||||||
|  |     // Navigator.of(context).pop(); | ||||||
|  |     Navigator.pushReplacementNamed( | ||||||
|  |       context, | ||||||
|  |       "zoom_call_page", | ||||||
|  |       // arguments: CallArguments(incomingCallData!.sessionId!, "123", "Patient", "40", "0", false, int.parse(incomingCallData!.appointmentNo!)), | ||||||
|  |     ); | ||||||
|  |     // } else { | ||||||
|  |     //   // OpenTok Call Page | ||||||
|  |     //   await Navigator.of(context).pushReplacement( | ||||||
|  |     //     MaterialPageRoute( | ||||||
|  |     //       // fullscreenDialog: true, | ||||||
|  |     //       builder: (BuildContext context) { | ||||||
|  |     //         // final caller = widget.incomingCallData.callerID; | ||||||
|  |     //         // final receiver = widget.incomingCallData.receiverID; | ||||||
|  |     //         // final host = widget.incomingCallData.server; | ||||||
|  |     //         // if(widget.incomingCallData.isWebRTC == "true"){ | ||||||
|  |     //         //   return StartVideoCall(caller: caller, receiver: receiver, iAmCaller: false, host: host); | ||||||
|  |     //         // }else{ | ||||||
|  |     //         return OpenTokConnectCallPage(apiKey: OPENTOK_API_KEY, sessionId: incomingCallData!.sessionId!, token: incomingCallData!.token!); | ||||||
|  |     //       }, | ||||||
|  |     //     ), | ||||||
|  |     //   ); | ||||||
|  |     // } | ||||||
|  |   } catch (err) { | ||||||
|  |     print(err); | ||||||
|  |     // await PlatformExceptionAlertDialog( | ||||||
|  |     //   exception: Exception(err), | ||||||
|  |     // ).show(context); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Future<void> showCallkitIncoming(Map message) async { | ||||||
|  | //   // if (message['type'] == 'ReservationCallStart') { | ||||||
|  | //     var params = CallKitParams( | ||||||
|  | //         id: DateTime.now().millisecondsSinceEpoch.toString(), | ||||||
|  | //         nameCaller: 'Dr Sulaiman Al Habib', | ||||||
|  | //         appName: 'Dr Sulaiman Al Habib', | ||||||
|  | //         avatar: 'https://play-lh.googleusercontent.com/FBNNpxb7m6eM6wtW7MV1Ffp6OXOGLI38q47zcvP29OCYA1yhYH5mZzl5itZi0TgOyZpG', | ||||||
|  | //         handle: 'LiveCare Call', | ||||||
|  | //         type: 1, | ||||||
|  | //         duration: 60000, | ||||||
|  | //         textAccept: 'Accept', | ||||||
|  | //         textDecline: 'Decline', | ||||||
|  | //         textMissedCall: 'Missed call', | ||||||
|  | //         textCallback: 'Call back', | ||||||
|  | //         extra: <String, dynamic>{ | ||||||
|  | //           // 'reservationID': message['id'], | ||||||
|  | //           'userId': '1a2b3c4d' | ||||||
|  | //         }, | ||||||
|  | //         headers: <String, dynamic>{'apiKey': 'Abc@123!', 'platform': 'flutter'}, | ||||||
|  | //         android: AndroidParams( | ||||||
|  | //           isCustomNotification: true, | ||||||
|  | //           isShowLogo: false, | ||||||
|  | //           isShowCallback: false, | ||||||
|  | //           ringtonePath: 'system_ringtone_default', | ||||||
|  | //           backgroundColor: '#424242FF', | ||||||
|  | //           // 'backgroundUrl': 'https://i.pravatar.cc/500', | ||||||
|  | //           actionColor: '#4CAF50', | ||||||
|  | //           incomingCallNotificationChannelName: "Incoming Call", | ||||||
|  | //           missedCallNotificationChannelName: "Missed Call", | ||||||
|  | //         ), | ||||||
|  | //         ios: IOSParams( | ||||||
|  | //             iconName: 'CallKitLogo', | ||||||
|  | //             handleType: '', | ||||||
|  | //             supportsVideo: true, | ||||||
|  | //             maximumCallGroups: 2, | ||||||
|  | //             maximumCallsPerCallGroup: 1, | ||||||
|  | //             audioSessionMode: 'default', | ||||||
|  | //             audioSessionActive: true, | ||||||
|  | //             audioSessionPreferredSampleRate: 44100.0, | ||||||
|  | //             audioSessionPreferredIOBufferDuration: 0.005, | ||||||
|  | //             supportsDTMF: true, | ||||||
|  | //             supportsHolding: true, | ||||||
|  | //             supportsGrouping: false, | ||||||
|  | //             supportsUngrouping: false, | ||||||
|  | //             ringtonePath: 'system_ringtone_default')); | ||||||
|  | //     await FlutterCallkitIncoming.showCallkitIncoming(params); | ||||||
|  | //   // } else if (message['type'] == 'ReservationCallFinished') { | ||||||
|  | //   //   await FlutterCallkitIncoming.endAllCalls(); | ||||||
|  | //   // } | ||||||
|  | // } | ||||||
|  | 
 | ||||||
|  | class PushNotificationHandler { | ||||||
|  |   late BuildContext context; | ||||||
|  |   static final PushNotificationHandler _instance = PushNotificationHandler._internal(); | ||||||
|  | 
 | ||||||
|  |   // late HmsApiAvailability hmsApiAvailability; | ||||||
|  | 
 | ||||||
|  |   // final voIPKit = FlutterIOSVoIPKit.instance; | ||||||
|  | 
 | ||||||
|  |   late Timer timeOutTimer; | ||||||
|  |   bool isTalking = false; | ||||||
|  | 
 | ||||||
|  |   var data = { | ||||||
|  |     "AppointmentNo": "2016059247", | ||||||
|  |     "ProjectID": "15", | ||||||
|  |     "NotificationType": "10", | ||||||
|  |     "background": "0", | ||||||
|  |     "doctorname": "Call from postman", | ||||||
|  |     "clinicname": "LIVECARE FAMILY MEDICINE AND GP", | ||||||
|  |     "speciality": "General Practioner", | ||||||
|  |     "appointmentdate": "2022-01-19", | ||||||
|  |     "appointmenttime": "12:10", | ||||||
|  |     "PatientName": "Testing", | ||||||
|  |     "session_id": "1_MX40NjIwOTk2Mn5-MTY1NDE2NDQxMjc2Mn5xc3NCZkNIejJOdzgzTkg2TmlXblhQdnl-fg", | ||||||
|  |     "token": | ||||||
|  |         "T1==cGFydG5lcl9pZD00NjIwOTk2MiZzaWc9MTliNTA3NDAxYmU0MjI5OGY5NTcxZTdhNzQyMTcyZjRjMjBhNjljZTpzZXNzaW9uX2lkPTFfTVg0ME5qSXdPVGsyTW41LU1UWTFOREUyTkRReE1qYzJNbjV4YzNOQ1prTkllakpPZHpnelRrZzJUbWxYYmxoUWRubC1mZyZjcmVhdGVfdGltZT0xNjU0MTY0NDEzJm5vbmNlPTAuNjM3ODkzNDk4NDQ2NTIxOSZyb2xlPW1vZGVyYXRvciZleHBpcmVfdGltZT0xNjU0MjUwODEzJmluaXRpYWxfbGF5b3V0X2NsYXNzX2xpc3Q9", | ||||||
|  |     "DoctorImageURL": "https://image.shutterstock.com/image-vector/sample-stamp-square-grunge-sign-260nw-1474408826.jpg", | ||||||
|  |     "callerID": "9920", | ||||||
|  |     "PatientID": "1231755", | ||||||
|  |     "is_call": "true" | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   PushNotificationHandler._internal(); | ||||||
|  | 
 | ||||||
|  |   factory PushNotificationHandler() => _instance; | ||||||
|  | 
 | ||||||
|  |   static PushNotificationHandler getInstance() => _instance; | ||||||
|  | 
 | ||||||
|  |   // void _timeOut({ | ||||||
|  |   //   int seconds = 30, | ||||||
|  |   // }) async { | ||||||
|  |   //   timeOutTimer = Timer(Duration(seconds: seconds), () async { | ||||||
|  |   //     print('🎈 example: timeOut'); | ||||||
|  |   //     final incomingCallerName = await voIPKit.getIncomingCallerName(); | ||||||
|  |   //     voIPKit.unansweredIncomingCall( | ||||||
|  |   //       skipLocalNotification: false, | ||||||
|  |   //       missedCallTitle: '📞 Missed call', | ||||||
|  |   //       missedCallBody: 'There was a call from $incomingCallerName', | ||||||
|  |   //     ); | ||||||
|  |   //   }); | ||||||
|  |   // } | ||||||
|  | 
 | ||||||
|  |   init(BuildContext context) async { | ||||||
|  |     this.context = context; | ||||||
|  | 
 | ||||||
|  |     if (Platform.isIOS) { | ||||||
|  |       // voIPKit.getVoIPToken().then((value) { | ||||||
|  |       //   print("APNS VOIP KIT TOKEN: $value"); | ||||||
|  |       //   AppSharedPreferences().setString(APNS_TOKEN, value!); | ||||||
|  |       // }); | ||||||
|  |       // | ||||||
|  |       // voIPKit.onDidUpdatePushToken = (String token) { | ||||||
|  |       //   print('🎈 example: onDidUpdatePushToken: $token'); | ||||||
|  |       // }; | ||||||
|  |       // | ||||||
|  |       // voIPKit.onDidReceiveIncomingPush = ( | ||||||
|  |       //   Map<String, dynamic> payload, | ||||||
|  |       // ) async { | ||||||
|  |       //   print('🎈 example: onDidReceiveIncomingPush $payload'); | ||||||
|  |       //   _timeOut(); | ||||||
|  |       // }; | ||||||
|  |       // | ||||||
|  |       // voIPKit.onDidRejectIncomingCall = ( | ||||||
|  |       //   String uuid, | ||||||
|  |       //   String callerId, | ||||||
|  |       // ) async { | ||||||
|  |       //   try { | ||||||
|  |       //     print('🎈 example: onDidRejectIncomingCall $uuid - $callerId'); | ||||||
|  |       //     timeOutTimer.cancel(); | ||||||
|  |       //   } catch (err) {} | ||||||
|  |       // }; | ||||||
|  |       // | ||||||
|  |       // voIPKit.onDidAcceptIncomingCall = ( | ||||||
|  |       //   String uuid, | ||||||
|  |       //   String callerId, | ||||||
|  |       // ) async { | ||||||
|  |       //   print('🎈 example: onDidAcceptIncomingCall $uuid - $callerId'); | ||||||
|  |       //   await voIPKit.acceptIncomingCall(callerState: CallStateType.calling); | ||||||
|  |       //   await voIPKit.callConnected(); | ||||||
|  |       //   await Future.delayed(Duration(seconds: 1)); | ||||||
|  |       // | ||||||
|  |       //   Navigator.pushNamed( | ||||||
|  |       //     locator<NavigationService>().navigatorKey.currentContext!, | ||||||
|  |       //     "zoom_call_page", | ||||||
|  |       //     arguments: CallArguments("hoover-dam", "123", "Patient", "40", "1", false), | ||||||
|  |       //   ); | ||||||
|  |       // | ||||||
|  |       //   await voIPKit.endCall(); | ||||||
|  |       // | ||||||
|  |       //   // Navigator.pushNamed(navigatorKey.currentContext!, VIDEO_CALL_SCREEN, | ||||||
|  |       //   //     arguments: VideoArgus( | ||||||
|  |       //   //         reservationId: int.parse(callerId), token: null, isVideo: true)); | ||||||
|  |       // | ||||||
|  |       //   timeOutTimer.cancel(); | ||||||
|  |       // }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (Platform.isAndroid) { | ||||||
|  |       try { | ||||||
|  |         final fcmToken = await FirebaseMessaging.instance.getToken().catchError((err) { | ||||||
|  |           print(err); | ||||||
|  |         }); | ||||||
|  |         if (fcmToken != null) onToken(fcmToken); | ||||||
|  |         // } | ||||||
|  |       } catch (ex) { | ||||||
|  |         print("Notification Exception: " + ex.toString()); | ||||||
|  |       } | ||||||
|  |       FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (Platform.isIOS) { | ||||||
|  |       await FirebaseMessaging.instance.getAPNSToken().then((value) async { | ||||||
|  |         log("APNS token: " + value.toString()); | ||||||
|  |         await Utils.saveStringFromPrefs(SharedPrefsConsts.APNS_TOKEN, value.toString()); | ||||||
|  |       }); | ||||||
|  |       await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions( | ||||||
|  |         alert: true, // Required to display a heads up notification | ||||||
|  |         badge: true, | ||||||
|  |         sound: true, | ||||||
|  |       ); | ||||||
|  |       final permission = await FirebaseMessaging.instance.requestPermission(); | ||||||
|  |       if (permission.authorizationStatus == AuthorizationStatus.denied) return; | ||||||
|  |     } else {} | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) async { | ||||||
|  |         if (message != null) { | ||||||
|  |           if (Platform.isIOS) | ||||||
|  |             await Future.delayed(Duration(milliseconds: 3000)).then((value) { | ||||||
|  |               if (message != null) newMessage(message); | ||||||
|  |             }); | ||||||
|  |           else if (message != null) newMessage(message); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } catch (ex) {} | ||||||
|  | 
 | ||||||
|  |     FirebaseMessaging.onMessage.listen((RemoteMessage message) async { | ||||||
|  |       print("Firebase onMessage!!!"); | ||||||
|  |       // showCallkitIncoming(); | ||||||
|  |       if (Platform.isIOS) | ||||||
|  |         await Future.delayed(Duration(milliseconds: 3000)).then((value) { | ||||||
|  |           newMessage(message); | ||||||
|  |         }); | ||||||
|  |       else | ||||||
|  |         newMessage(message); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { | ||||||
|  |       print("Firebase onMessageOpenedApp!!!"); | ||||||
|  |       if (Platform.isIOS) | ||||||
|  |         await Future.delayed(Duration(milliseconds: 3000)).then((value) { | ||||||
|  |           newMessage(message); | ||||||
|  |         }); | ||||||
|  |       else | ||||||
|  |         newMessage(message); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     FirebaseMessaging.instance.getToken().then((String? token) { | ||||||
|  |       print("Push Notification getToken: " + token!); | ||||||
|  |       onToken(token!); | ||||||
|  |     }).catchError((err) { | ||||||
|  |       print(err); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     FirebaseMessaging.instance.onTokenRefresh.listen((fcm_token) { | ||||||
|  |       print("Push Notification onTokenRefresh: " + fcm_token); | ||||||
|  |       onToken(fcm_token); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (Platform.isAndroid) { | ||||||
|  |       final deviceInfo = DeviceInfoPlugin(); | ||||||
|  |       final androidInfo = await deviceInfo.androidInfo; | ||||||
|  | 
 | ||||||
|  |       int sdkInt = androidInfo.version.sdkInt ?? 0; | ||||||
|  |       if (sdkInt >= 33) { | ||||||
|  |         await FlutterCallkitIncoming.requestFullIntentPermission(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   newMessage(RemoteMessage remoteMessage) async { | ||||||
|  |     print("Remote Message: " + remoteMessage.data.toString()); | ||||||
|  |     if (remoteMessage.data.isEmpty) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     debugPrint('the value of the remote message is ${remoteMessage.data}'); | ||||||
|  |     if (remoteMessage.data['is_call'] == 'true' || remoteMessage.data['is_call'] == true) { | ||||||
|  |       _incomingCall(remoteMessage.data); | ||||||
|  |       // showCallkitIncoming(); | ||||||
|  |     } else { | ||||||
|  |       // GetNotificationsResponseModel notification = new GetNotificationsResponseModel(); | ||||||
|  |       // | ||||||
|  |       // notification.createdOn = DateUtil.convertDateToString(DateTime.now()); | ||||||
|  |       // notification.messageTypeData = remoteMessage.data['picture']; | ||||||
|  |       // notification.message = remoteMessage.data['message']; | ||||||
|  |       // notification.notificationType = remoteMessage.data["NotificationType"].toString(); | ||||||
|  |       // if (remoteMessage.data["NotificationType"] == "2") { | ||||||
|  |       //   notification.videoURL = remoteMessage.data["VideoUrl"]; | ||||||
|  |       // } | ||||||
|  |       // | ||||||
|  |       // await NavigationService.navigateToPage(NotificationsDetailsPage( | ||||||
|  |       //   notification: notification, | ||||||
|  |       // )); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onToken(String token) async { | ||||||
|  |     print("Push Notification Token: " + token); | ||||||
|  |     await Utils.saveStringFromPrefs(SharedPrefsConsts.PUSH_TOKEN, token); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onResume() async { | ||||||
|  |     // var call_data = await AppSharedPreferences().getObject('call_data'); | ||||||
|  |     // if (call_data != null) { | ||||||
|  |     //   _incomingCall(call_data); | ||||||
|  |     // } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<void> requestPermissions() async { | ||||||
|  |     try { | ||||||
|  |       if (Platform.isIOS) { | ||||||
|  |         await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()?.requestPermissions(alert: true, badge: true, sound: true); | ||||||
|  |       } else if (Platform.isAndroid) { | ||||||
|  |         Map<Permission, PermissionStatus> statuses = await [ | ||||||
|  |           Permission.notification, | ||||||
|  |           // Permission.camera, | ||||||
|  |           // Permission.audio, | ||||||
|  |           // Permission.microphone, | ||||||
|  |         ].request(); | ||||||
|  |         print("=-=-=-=-=-=-=-=-=-=-"); | ||||||
|  |         print(statuses[Permission.notification]); | ||||||
|  |       } | ||||||
|  |     } catch (_) { | ||||||
|  |       debugPrint(_.toString()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,103 @@ | |||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/consts.dart'; | ||||||
|  | 
 | ||||||
|  | class SizeConfig { | ||||||
|  |   static double _blockWidth = 0; | ||||||
|  |   static double _blockHeight = 0; | ||||||
|  | 
 | ||||||
|  |   static double? realScreenWidth; | ||||||
|  |   static double? realScreenHeight; | ||||||
|  |   static double? screenWidth; | ||||||
|  |   static double? screenHeight; | ||||||
|  |   static double? textMultiplier; | ||||||
|  |   static double? imageSizeMultiplier; | ||||||
|  |   static double? heightMultiplier; | ||||||
|  |   static bool isPortrait = true; | ||||||
|  |   static double? widthMultiplier; | ||||||
|  |   static bool isMobilePortrait = false; | ||||||
|  |   static bool isMobile = false; | ||||||
|  |   static bool isHeightShort = false; | ||||||
|  |   static bool isHeightVeryShort = false; | ||||||
|  |   static bool isHeightMiddle = false; | ||||||
|  |   static bool isHeightLarge = false; | ||||||
|  |   static bool isWidthLarge = false; | ||||||
|  | 
 | ||||||
|  |   void init(BoxConstraints constraints, Orientation orientation) { | ||||||
|  |     realScreenHeight = constraints.maxHeight; | ||||||
|  |     realScreenWidth = constraints.maxWidth; | ||||||
|  |     if (constraints.maxWidth <= ApiConsts.maxSmallScreen) { | ||||||
|  |       isMobile = true; | ||||||
|  |     } | ||||||
|  |     if (constraints.maxHeight < 600) { | ||||||
|  |       isHeightVeryShort = true; | ||||||
|  |     } else if (constraints.maxHeight < 800) { | ||||||
|  |       isHeightShort = true; | ||||||
|  |     } else if (constraints.maxHeight < 1000) { | ||||||
|  |       isHeightMiddle = true; | ||||||
|  |     } else { | ||||||
|  |       isHeightLarge = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (constraints.maxWidth > 600) { | ||||||
|  |       isWidthLarge = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (orientation == Orientation.portrait) { | ||||||
|  |       isPortrait = true; | ||||||
|  |       if (realScreenWidth! < 450) { | ||||||
|  |         isMobilePortrait = true; | ||||||
|  |       } | ||||||
|  |       // textMultiplier = _blockHeight; | ||||||
|  |       // imageSizeMultiplier = _blockWidth; | ||||||
|  |       screenHeight = realScreenHeight; | ||||||
|  |       screenWidth = realScreenWidth; | ||||||
|  |     } else { | ||||||
|  |       isPortrait = false; | ||||||
|  |       isMobilePortrait = false; | ||||||
|  |       // textMultiplier = _blockWidth; | ||||||
|  |       // imageSizeMultiplier = _blockHeight; | ||||||
|  |       screenHeight = realScreenWidth; | ||||||
|  |       screenWidth = realScreenHeight; | ||||||
|  |     } | ||||||
|  |     _blockWidth = screenWidth! / 100; | ||||||
|  |     _blockHeight = screenHeight! / 100; | ||||||
|  | 
 | ||||||
|  |     textMultiplier = _blockHeight; | ||||||
|  |     imageSizeMultiplier = _blockWidth; | ||||||
|  |     heightMultiplier = _blockHeight; | ||||||
|  |     widthMultiplier = _blockWidth; | ||||||
|  | 
 | ||||||
|  |     print('realScreenWidth $realScreenWidth'); | ||||||
|  |     print('realScreenHeight $realScreenHeight'); | ||||||
|  |     print('textMultiplier $textMultiplier'); | ||||||
|  |     print('imageSizeMultiplier $imageSizeMultiplier'); | ||||||
|  |     print('heightMultiplier$heightMultiplier'); | ||||||
|  |     print('widthMultiplier $widthMultiplier'); | ||||||
|  |     print('isPortrait $isPortrait'); | ||||||
|  |     print('isMobilePortrait $isMobilePortrait'); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static getTextMultiplierBasedOnWidth({double? width}) { | ||||||
|  |     // TODO handel LandScape case | ||||||
|  |     if (width != null) { | ||||||
|  |       return width / 100; | ||||||
|  |     } | ||||||
|  |     return widthMultiplier; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static getWidthMultiplier({double? width}) { | ||||||
|  |     // TODO handel LandScape case | ||||||
|  |     if (width != null) { | ||||||
|  |       return width / 100; | ||||||
|  |     } | ||||||
|  |     return widthMultiplier; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static getHeightMultiplier({double? height}) { | ||||||
|  |     // TODO handel LandScape case | ||||||
|  |     if (height != null) { | ||||||
|  |       return height / 100; | ||||||
|  |     } | ||||||
|  |     return heightMultiplier; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,93 @@ | |||||||
|  | import 'package:flutter/material.dart'; // These are the Viewport values of your Figma Design. | ||||||
|  | 
 | ||||||
|  | // These are used in the code as a reference to create your UI Responsively. | ||||||
|  | const num FIGMA_DESIGN_WIDTH = 375; | ||||||
|  | const num FIGMA_DESIGN_HEIGHT = 667; | ||||||
|  | const num FIGMA_DESIGN_STATUS_BAR = 0; | ||||||
|  | 
 | ||||||
|  | extension ResponsiveExtension on num { | ||||||
|  |   double get _width => SizeUtils.width; | ||||||
|  | 
 | ||||||
|  |   double get h => ((this * _width) / FIGMA_DESIGN_WIDTH); | ||||||
|  | 
 | ||||||
|  |   double get fSize => ((this * _width) / FIGMA_DESIGN_WIDTH); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extension FormatExtension on double { | ||||||
|  |   double toDoubleValue({int fractionDigits = 2}) { | ||||||
|  |     return double.parse(this.toStringAsFixed(fractionDigits)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double isNonZero({num defaultValue = 0.0}) { | ||||||
|  |     return this > 0 ? this : defaultValue.toDouble(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | enum DeviceType { mobile, tablet, desktop } | ||||||
|  | 
 | ||||||
|  | typedef ResponsiveBuild = Widget Function( | ||||||
|  |   BuildContext context, | ||||||
|  |   Orientation orientation, | ||||||
|  |   DeviceType deviceType, | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | class Sizer extends StatelessWidget { | ||||||
|  |   const Sizer({Key? key, required this.builder}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   /// Builds the widget whenever the orientation changes. | ||||||
|  |   final ResponsiveBuild builder; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return LayoutBuilder( | ||||||
|  |       builder: (context, constraints) { | ||||||
|  |         return OrientationBuilder( | ||||||
|  |           builder: (context, orientation) { | ||||||
|  |             SizeUtils.setScreenSize(constraints, orientation); | ||||||
|  |             return builder(context, orientation, SizeUtils.deviceType); | ||||||
|  |           }, | ||||||
|  |         ); | ||||||
|  |       }, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: must_be_immutable | ||||||
|  | class SizeUtils { | ||||||
|  |   /// Device's BoxConstraints | ||||||
|  |   static late BoxConstraints boxConstraints; | ||||||
|  | 
 | ||||||
|  |   /// Device's Orientation | ||||||
|  |   static late Orientation orientation; | ||||||
|  | 
 | ||||||
|  |   /// Type of Device | ||||||
|  |   /// | ||||||
|  |   /// This can either be mobile or tablet | ||||||
|  |   static late DeviceType deviceType; | ||||||
|  | 
 | ||||||
|  |   /// Device's Height | ||||||
|  |   static late double height; | ||||||
|  | 
 | ||||||
|  |   /// Device's Width | ||||||
|  |   static late double width; | ||||||
|  | 
 | ||||||
|  |   static void setScreenSize( | ||||||
|  |     BoxConstraints constraints, | ||||||
|  |     Orientation currentOrientation, | ||||||
|  |   ) { | ||||||
|  |     boxConstraints = constraints; | ||||||
|  |     orientation = currentOrientation; | ||||||
|  |     if (orientation == Orientation.portrait) { | ||||||
|  |       width = boxConstraints.maxWidth.isNonZero( | ||||||
|  |         defaultValue: FIGMA_DESIGN_WIDTH, | ||||||
|  |       ); | ||||||
|  |       height = boxConstraints.maxHeight.isNonZero(); | ||||||
|  |     } else { | ||||||
|  |       width = boxConstraints.maxHeight.isNonZero( | ||||||
|  |         defaultValue: FIGMA_DESIGN_WIDTH, | ||||||
|  |       ); | ||||||
|  |       height = boxConstraints.maxWidth.isNonZero(); | ||||||
|  |     } | ||||||
|  |     deviceType = DeviceType.mobile; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,341 @@ | |||||||
|  | import 'package:hmg_patient_app_new/core/app_state.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/main.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/loading_dialog.dart'; | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_svg/flutter_svg.dart'; | ||||||
|  | import 'package:fluttertoast/fluttertoast.dart'; | ||||||
|  | import 'package:shared_preferences/shared_preferences.dart'; | ||||||
|  | 
 | ||||||
|  | class Utils { | ||||||
|  |   static bool _isLoadingVisible = false; | ||||||
|  | 
 | ||||||
|  |   static bool get isLoading => _isLoadingVisible; | ||||||
|  | 
 | ||||||
|  |   static void showToast(String message, {bool longDuration = true}) { | ||||||
|  |     Fluttertoast.showToast( | ||||||
|  |         msg: message, | ||||||
|  |         toastLength: longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT, | ||||||
|  |         gravity: ToastGravity.BOTTOM, | ||||||
|  |         timeInSecForIosWeb: 1, | ||||||
|  |         backgroundColor: blackColor, | ||||||
|  |         textColor: whiteColor, | ||||||
|  |         fontSize: 16.0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static bool isArabicText(String inputText) { | ||||||
|  |     bool isArabicText = false; | ||||||
|  |     final arabic = RegExp(r'^[\u0621-\u064A]+'); | ||||||
|  |     if (arabic.hasMatch(inputText)) { | ||||||
|  |       isArabicText = true; | ||||||
|  |     } else { | ||||||
|  |       isArabicText = false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return isArabicText; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String getFreeSlotsTimeText(String startTime, {bool isAddHours = false}) { | ||||||
|  |     // return DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime)!.add( | ||||||
|  |     //   Duration( | ||||||
|  |     //     hours: isAddHours ? 3 : 0, | ||||||
|  |     //   ), | ||||||
|  |     // )); | ||||||
|  |     return !isAddHours | ||||||
|  |         ? DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) | ||||||
|  |         : DateFormat('hh:mm a', AppState().isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( | ||||||
|  |             Duration( | ||||||
|  |               hours: isAddHours ? 3 : 0, | ||||||
|  |             ), | ||||||
|  |           )); | ||||||
|  |     ; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String convertStringToDateTime(String dateTimeString) { | ||||||
|  |     String timeString = dateTimeString; | ||||||
|  |     // Parse the time string using DateFormat | ||||||
|  |     DateFormat format = DateFormat.Hms(); // 'Hms' = 'HH:mm:ss' | ||||||
|  |     DateTime time = format.parse(timeString); | ||||||
|  | 
 | ||||||
|  |     DateTime now = DateTime.now(); | ||||||
|  |     DateTime dateTimeWithToday = DateTime( | ||||||
|  |       now.year, | ||||||
|  |       now.month, | ||||||
|  |       now.day, | ||||||
|  |       time.hour, | ||||||
|  |       time.minute, | ||||||
|  |       time.second, | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     return dateTimeWithToday.toIso8601String(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String getMonthDayYearDateFormatted(DateTime dateTime) { | ||||||
|  |     if (dateTime != null) | ||||||
|  |       return AppState().isArabic() | ||||||
|  |           ? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString() | ||||||
|  |           : getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); | ||||||
|  |     else | ||||||
|  |       return ""; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// get month by | ||||||
|  |   /// [month] convert month number in to month name | ||||||
|  |   static getMonth(int month) { | ||||||
|  |     switch (month) { | ||||||
|  |       case 1: | ||||||
|  |         return "January"; | ||||||
|  |       case 2: | ||||||
|  |         return "February"; | ||||||
|  |       case 3: | ||||||
|  |         return "March"; | ||||||
|  |       case 4: | ||||||
|  |         return "April"; | ||||||
|  |       case 5: | ||||||
|  |         return "May"; | ||||||
|  |       case 6: | ||||||
|  |         return "June"; | ||||||
|  |       case 7: | ||||||
|  |         return "July"; | ||||||
|  |       case 8: | ||||||
|  |         return "August"; | ||||||
|  |       case 9: | ||||||
|  |         return "September"; | ||||||
|  |       case 10: | ||||||
|  |         return "October"; | ||||||
|  |       case 11: | ||||||
|  |         return "November"; | ||||||
|  |       case 12: | ||||||
|  |         return "December"; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// get month by | ||||||
|  |   /// [month] convert month number in to month name in Arabic | ||||||
|  |   static getMonthArabic(int month) { | ||||||
|  |     switch (month) { | ||||||
|  |       case 1: | ||||||
|  |         return "يناير"; | ||||||
|  |       case 2: | ||||||
|  |         return " فبراير"; | ||||||
|  |       case 3: | ||||||
|  |         return "مارس"; | ||||||
|  |       case 4: | ||||||
|  |         return "أبريل"; | ||||||
|  |       case 5: | ||||||
|  |         return "مايو"; | ||||||
|  |       case 6: | ||||||
|  |         return "يونيو"; | ||||||
|  |       case 7: | ||||||
|  |         return "يوليو"; | ||||||
|  |       case 8: | ||||||
|  |         return "أغسطس"; | ||||||
|  |       case 9: | ||||||
|  |         return "سبتمبر"; | ||||||
|  |       case 10: | ||||||
|  |         return " اكتوبر"; | ||||||
|  |       case 11: | ||||||
|  |         return " نوفمبر"; | ||||||
|  |       case 12: | ||||||
|  |         return "ديسمبر"; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<String> getStringFromPrefs(String key) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return prefs.getString(key) ?? ""; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<bool> saveStringFromPrefs(String key, String value) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return await prefs.setString(key, value); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<int> getIntFromPrefs(String key) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return prefs.getInt(key) ?? 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<bool> saveIntFromPrefs(String key, int value) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return await prefs.setInt(key, value); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<num> getNumFromPrefs(String key) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return num.parse(prefs.getString(key) ?? "0"); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<bool> saveNumFromPrefs(String key, num value) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return await prefs.setString(key, value.toString()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Future<bool> removeFromPrefs(String key) async { | ||||||
|  |     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||||
|  |     return await prefs.remove(key); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static void showLoading({bool isNeedBinding = true}) { | ||||||
|  |     if (isNeedBinding) { | ||||||
|  |       WidgetsBinding.instance.addPostFrameCallback((_) { | ||||||
|  |         showLoadingDialog(); | ||||||
|  |       }); | ||||||
|  |     } else { | ||||||
|  |       showLoadingDialog(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static void showLoadingDialog() { | ||||||
|  |     _isLoadingVisible = true; | ||||||
|  |     showDialog( | ||||||
|  |       context: navigatorKey.currentContext!, | ||||||
|  |       barrierColor: Colors.black.withOpacity(0.5), | ||||||
|  |       builder: (BuildContext context) => LoadingDialog(), | ||||||
|  |     ) | ||||||
|  |         .then((value) { | ||||||
|  |           _isLoadingVisible = false; | ||||||
|  |         }) | ||||||
|  |         .catchError((e) {}) | ||||||
|  |         .onError( | ||||||
|  |           (error, stackTrace) {}, | ||||||
|  |         ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static void hideLoading() { | ||||||
|  |     try { | ||||||
|  |       if (_isLoadingVisible) { | ||||||
|  |         _isLoadingVisible = false; | ||||||
|  |         Navigator.of(navigatorKey.currentContext!).pop(); | ||||||
|  |       } | ||||||
|  |       _isLoadingVisible = false; | ||||||
|  |     } catch (e) {} | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static List<T> uniqueBy<T, K>(List<T> list, K Function(T) keySelector) { | ||||||
|  |     final seenKeys = <K>{}; | ||||||
|  |     return list.where((item) => seenKeys.add(keySelector(item))).toList(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static void showAppDialog(BuildContext context, String? title, String? message, VoidCallback? onTap) { | ||||||
|  |     showDialog( | ||||||
|  |       barrierDismissible: false, | ||||||
|  |       context: context, | ||||||
|  |       builder: (cxt) => ConfirmDialog( | ||||||
|  |         title: title!, | ||||||
|  |         message: message!, | ||||||
|  |         onTap: onTap, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static bool isSAUDIIDValid(String id, type) { | ||||||
|  |     if (type == 1) { | ||||||
|  |       if (id == null) { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |       try { | ||||||
|  |         id = id.toString(); | ||||||
|  |         id = id.trim(); | ||||||
|  |         var returnValue = int.parse(id); | ||||||
|  |         var sum = 0; | ||||||
|  |         if (returnValue > 0) { | ||||||
|  |           var type = int.parse(id[0]); | ||||||
|  | 
 | ||||||
|  |           if (id.length != 10) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |           if (type != 2 && type != 1) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           for (var i = 0; i < 10; i++) { | ||||||
|  |             if (i % 2 == 0) { | ||||||
|  |               var a = id[i]; | ||||||
|  |               var x = int.parse(a) * 2; | ||||||
|  |               var b = x.toString(); | ||||||
|  |               if (b.length == 1) { | ||||||
|  |                 b = "0" + b; | ||||||
|  |               } | ||||||
|  |               sum += int.parse(b[0]) + int.parse(b[1]); | ||||||
|  |             } else { | ||||||
|  |               sum += int.parse(id[i]); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           return sum % 10 == 0; | ||||||
|  |         } | ||||||
|  |       } catch (err) {} | ||||||
|  |       return false; | ||||||
|  |     } else { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Widget getNoDataWidget(BuildContext context, {String? errorText}) { | ||||||
|  |     return Column( | ||||||
|  |       mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |       crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |       children: [ | ||||||
|  |         SvgPicture.asset('assets/images/NoDataAvailableIcon.svg', width: 150.0, height: 150.0), | ||||||
|  |         (errorText ?? LocaleKeys.noDataAvailable.tr()).toText16(isCenter: true).paddingOnly(top: 15), | ||||||
|  |       ], | ||||||
|  |     ).center; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static getPhoneNumberWithoutZero(String number) { | ||||||
|  |     String newNumber = ""; | ||||||
|  |     if (number.startsWith('0')) { | ||||||
|  |       newNumber = number.substring(1); | ||||||
|  |     } else { | ||||||
|  |       newNumber = number; | ||||||
|  |     } | ||||||
|  |     return newNumber; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static String removeHtmlTags(String htmlString) { | ||||||
|  |     if (htmlString == null || htmlString.isEmpty) { | ||||||
|  |       return ''; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Replace HTML line breaks with newlines | ||||||
|  |     var withLineBreaks = | ||||||
|  |         htmlString.replaceAll(RegExp(r'<br\s*\/?>', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'<divider>', multiLine: true), '\n'); | ||||||
|  | 
 | ||||||
|  |     // Remove all other HTML tags | ||||||
|  |     var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), ''); | ||||||
|  | 
 | ||||||
|  |     // Decode HTML entities | ||||||
|  |     var decodedString = withoutTags | ||||||
|  |         .replaceAll(' ', ' ') | ||||||
|  |         .replaceAll('&', '&') | ||||||
|  |         .replaceAll('<', '<') | ||||||
|  |         .replaceAll('>', '>') | ||||||
|  |         .replaceAll('"', '"') | ||||||
|  |         .replaceAll(''', "'") | ||||||
|  |         .replaceAll('’', "'") | ||||||
|  |         .replaceAll('‘', "'") | ||||||
|  |         .replaceAll('”', '"') | ||||||
|  |         .replaceAll('“', '"'); | ||||||
|  | 
 | ||||||
|  |     // Remove extra whitespace and normalize line breaks | ||||||
|  |     var normalizedString = decodedString | ||||||
|  |         .replaceAll(RegExp(r'\n\s*\n'), '\n\n') // Replace multiple blank lines with double line break | ||||||
|  |         .replaceAll(RegExp(r' +'), ' ') // Replace multiple spaces with single space | ||||||
|  |         .trim(); // Remove leading/trailing whitespace | ||||||
|  | 
 | ||||||
|  |     return normalizedString; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget mDivider(Color color) { | ||||||
|  |     return Divider( | ||||||
|  |       // width: double.infinity, | ||||||
|  |       height: 1, | ||||||
|  |       color: color, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,16 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | extension ContextUtils on BuildContext { | ||||||
|  |   double get screenHeight => MediaQuery.of(this).size.height; | ||||||
|  |   double get screenWidth => MediaQuery.of(this).size.width; | ||||||
|  |   ThemeData get theme => Theme.of(this); | ||||||
|  |   TextTheme get textTheme => theme.textTheme; | ||||||
|  |   // TextStyle get headline1 => textTheme.headline1!; | ||||||
|  |   // TextStyle get headline2 => textTheme.headline2!; | ||||||
|  |   // TextStyle get headline3 => textTheme.headline3!; | ||||||
|  |   // TextStyle get headline4 => textTheme.headline4!; | ||||||
|  |   // TextStyle get headline5 => textTheme.headline5!; | ||||||
|  |   // TextStyle get headline6 => textTheme.headline6!; | ||||||
|  |   // TextStyle get bodyText1 => textTheme.bodyText1!; | ||||||
|  |   // TextStyle get bodyText2 => textTheme.bodyText2!; | ||||||
|  | } | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | extension IntExtensions on int { | ||||||
|  |   Widget get height => SizedBox(height: toDouble()); | ||||||
|  | 
 | ||||||
|  |   Widget get width => SizedBox(width: toDouble()); | ||||||
|  | 
 | ||||||
|  |   Widget get divider => Divider(height: toDouble(), thickness: toDouble(), color: buttonColor); | ||||||
|  | 
 | ||||||
|  |   Widget get makeItSquare => SizedBox(width: toDouble(), height: toDouble()); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,311 @@ | |||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:auto_size_text/auto_size_text.dart'; | ||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:intl/intl.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:sizer/sizer.dart'; | ||||||
|  | 
 | ||||||
|  | extension CapExtension on String { | ||||||
|  |   String get toCamelCase => "${this[0].toUpperCase()}${this.substring(1)}"; | ||||||
|  | 
 | ||||||
|  |   String get inCaps => '${this[0].toUpperCase()}${this.substring(1)}'; | ||||||
|  | 
 | ||||||
|  |   String get allInCaps => this.toUpperCase(); | ||||||
|  | 
 | ||||||
|  |   String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extension EmailValidator on String { | ||||||
|  |   Widget get toWidget => Text(this); | ||||||
|  | 
 | ||||||
|  |   Widget toText8({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text( | ||||||
|  |         this, | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         overflow: textOverflow, | ||||||
|  |         style: TextStyle( | ||||||
|  |           fontSize: 8.fSize, | ||||||
|  |           fontStyle: fontStyle ?? FontStyle.normal, | ||||||
|  |           fontWeight: isBold ? FontWeight.bold : FontWeight.normal, | ||||||
|  |           color: color ?? blackColor, | ||||||
|  |           letterSpacing: 0.64, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText10({Color? color, bool isBold = false, bool isUnderLine = false, int? maxlines, FontStyle? fontStyle, TextOverflow? textOverflow}) => Text( | ||||||
|  |         this, | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         overflow: textOverflow, | ||||||
|  |         style: TextStyle( | ||||||
|  |             fontSize: 10.fSize, | ||||||
|  |             fontStyle: fontStyle ?? FontStyle.normal, | ||||||
|  |             fontWeight: isBold ? FontWeight.bold : FontWeight.normal, | ||||||
|  |             color: color ?? blackColor, | ||||||
|  |             letterSpacing: 0.64, | ||||||
|  |             decoration: isUnderLine ? TextDecoration.underline : null, | ||||||
|  |             decorationColor: color ?? blackColor), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isCenter = false, bool isBold = false, int maxLine = 0, double letterSpacing = 0.64}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: (maxLine > 0) ? maxLine : null, | ||||||
|  |         softWrap: true, | ||||||
|  |         style: TextStyle( | ||||||
|  |           fontSize: 11.fSize, | ||||||
|  |           fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |           color: color ?? blackColor, | ||||||
|  |           letterSpacing: letterSpacing, | ||||||
|  |           decoration: isUnderLine ? TextDecoration.underline : null, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: (maxLine > 0) ? maxLine : null, | ||||||
|  |         style: TextStyle( | ||||||
|  |           fontSize: 12.fSize, | ||||||
|  |           fontWeight: isBold ? FontWeight.bold : FontWeight.normal, | ||||||
|  |           color: color ?? blackColor, | ||||||
|  |           letterSpacing: 0.64, | ||||||
|  |           decorationColor: isUnderLine ? blackColor : null, | ||||||
|  |           decoration: isUnderLine ? TextDecoration.underline : null, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => AutoSizeText( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: (maxLine > 0) ? maxLine : null, | ||||||
|  |         minFontSize: 8, | ||||||
|  |         style: TextStyle( | ||||||
|  |           fontSize: 12.fSize, | ||||||
|  |           fontWeight: isBold ? FontWeight.bold : FontWeight.normal, | ||||||
|  |           color: color ?? blackColor, | ||||||
|  |           letterSpacing: 0.64, | ||||||
|  |           decoration: isUnderLine ? TextDecoration.underline : null, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toTextAuto({ | ||||||
|  |     Color? color, | ||||||
|  |     bool isUnderLine = false, | ||||||
|  |     bool isBold = false, | ||||||
|  |     bool isCenter = false, | ||||||
|  |     int maxLine = 0, | ||||||
|  |     double fontSize = 12, | ||||||
|  |     double letterSpacing = 0.64, | ||||||
|  |     double height = 1, | ||||||
|  |     TextOverflow? textOverflow, | ||||||
|  |     FontWeight? fontWeight, | ||||||
|  |   }) => | ||||||
|  |       AutoSizeText( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: (maxLine > 0) ? maxLine : null, | ||||||
|  |         minFontSize: 5, | ||||||
|  |         overflow: textOverflow, | ||||||
|  |         softWrap: true, | ||||||
|  |         style: TextStyle( | ||||||
|  |           fontSize: fontSize, | ||||||
|  |           fontWeight: fontWeight ?? (isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |           color: color ?? blackColor, | ||||||
|  |           letterSpacing: letterSpacing, | ||||||
|  |           decoration: isUnderLine ? TextDecoration.underline : null, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText13({ | ||||||
|  |     Color? color, | ||||||
|  |     bool isUnderLine = false, | ||||||
|  |     bool isBold = false, | ||||||
|  |     bool isCenter = false, | ||||||
|  |     int maxLine = 0, | ||||||
|  |   }) => | ||||||
|  |       Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: (maxLine > 0) ? maxLine : null, | ||||||
|  |         style: TextStyle( | ||||||
|  |             fontSize: 13.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? blackColor, letterSpacing: 0.64, decoration: isUnderLine ? TextDecoration.underline : null), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         style: TextStyle( | ||||||
|  |             color: color ?? blackColor, | ||||||
|  |             fontSize: 14.fSize, | ||||||
|  |             letterSpacing: 0.64, | ||||||
|  |             fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |             decoration: isUnderLine ? TextDecoration.underline : null), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText15({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         style: TextStyle( | ||||||
|  |             color: color ?? blackColor, | ||||||
|  |             fontSize: 15.fSize, | ||||||
|  |             letterSpacing: 0.64, | ||||||
|  |             fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |             decoration: isUnderLine ? TextDecoration.underline : null), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText16({ | ||||||
|  |     Color? color, | ||||||
|  |     bool isUnderLine = false, | ||||||
|  |     bool isBold = false, | ||||||
|  |     bool isCenter = false, | ||||||
|  |     int? maxlines, | ||||||
|  |     TextAlign? textAlign, | ||||||
|  |   }) => | ||||||
|  |       Text( | ||||||
|  |         this, | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         style: TextStyle( | ||||||
|  |           color: color ?? blackColor, | ||||||
|  |           fontSize: 16.fSize, | ||||||
|  |           letterSpacing: 0.64, | ||||||
|  |           fontWeight: isBold ? FontWeight.bold : FontWeight.normal, | ||||||
|  |           decoration: isUnderLine ? TextDecoration.underline : null, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText17({Color? color, bool isBold = false, bool isCenter = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         style: TextStyle(color: color ?? blackColor, fontSize: 17.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText18({Color? color, bool isBold = false, bool isCenter = false, int? maxlines}) => Text( | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         this, | ||||||
|  |         style: TextStyle(fontSize: 18.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? blackColor, letterSpacing: 0.64), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText19({Color? color, bool isBold = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         style: TextStyle(fontSize: 19.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? blackColor, letterSpacing: 0.64), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText20({Color? color, bool isBold = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         style: TextStyle(fontSize: 20.fSize, fontWeight: isBold ? FontWeight.bold : FontWeight.normal, color: color ?? blackColor, letterSpacing: 0.64), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text( | ||||||
|  |         this, | ||||||
|  |         maxLines: maxlines, | ||||||
|  |         style: TextStyle(color: color ?? blackColor, fontSize: 21.fSize, letterSpacing: 0.64, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal)), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText22({Color? color, bool isBold = false, bool isCenter = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         style: TextStyle(height: 1, color: color ?? blackColor, fontSize: 22.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText24({Color? color, bool isBold = false, bool isCenter = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         style: TextStyle(height: 23 / 24, color: color ?? blackColor, fontSize: 24.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText32({Color? color, bool isBold = false, bool isCenter = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         textAlign: isCenter ? TextAlign.center : null, | ||||||
|  |         style: TextStyle(height: 32 / 32, color: color ?? blackColor, fontSize: 32.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toText44({Color? color, bool isBold = false}) => Text( | ||||||
|  |         this, | ||||||
|  |         style: TextStyle(height: 32 / 32, color: color ?? blackColor, fontSize: 44.fSize, letterSpacing: 0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.normal), | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { | ||||||
|  |     String upper = ""; | ||||||
|  |     String lower = ""; | ||||||
|  |     String heading = this; | ||||||
|  |     if (heading.isNotEmpty) { | ||||||
|  |       List<String> data = heading.split(" "); | ||||||
|  | 
 | ||||||
|  |       if (data.length > 1) { | ||||||
|  |         upper = data[0]; | ||||||
|  |         data.removeAt(0); | ||||||
|  |         lower = data.join(" "); | ||||||
|  |       } else { | ||||||
|  |         lower = data[0]; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (upperHeading.isNotEmpty) { | ||||||
|  |       upper = upperHeading; | ||||||
|  |     } | ||||||
|  |     if (lowerHeading.isNotEmpty) { | ||||||
|  |       lower = lowerHeading; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return Column( | ||||||
|  |       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |       mainAxisSize: MainAxisSize.min, | ||||||
|  |       children: [ | ||||||
|  |         if (upper.isNotEmpty) upper.toText12(), | ||||||
|  |         lower.toText24(isBold: true), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool isValidEmail() { | ||||||
|  |     return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String toFormattedDate() { | ||||||
|  |     String date = this.split("T")[0]; | ||||||
|  |     String time = this.split("T")[1]; | ||||||
|  |     var dates = date.split("-"); | ||||||
|  |     return "${dates[2]} ${getMonth(int.parse(dates[1]))} ${dates[0]} ${DateFormat('hh:mm a').format(DateFormat('hh:mm:ss').parse(time))}"; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String getMonth(int month) { | ||||||
|  |     switch (month) { | ||||||
|  |       case 1: | ||||||
|  |         return "January"; | ||||||
|  |       case 2: | ||||||
|  |         return "February"; | ||||||
|  |       case 3: | ||||||
|  |         return "March"; | ||||||
|  |       case 4: | ||||||
|  |         return "April"; | ||||||
|  |       case 5: | ||||||
|  |         return "May"; | ||||||
|  |       case 6: | ||||||
|  |         return "June"; | ||||||
|  |       case 7: | ||||||
|  |         return "July"; | ||||||
|  |       case 8: | ||||||
|  |         return "August"; | ||||||
|  |       case 9: | ||||||
|  |         return "September"; | ||||||
|  |       case 10: | ||||||
|  |         return "October"; | ||||||
|  |       case 11: | ||||||
|  |         return "November"; | ||||||
|  |       case 12: | ||||||
|  |         return "December"; | ||||||
|  |       default: | ||||||
|  |         return ""; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   String truncate(int max, {String suffix = ''}) { | ||||||
|  |     try { | ||||||
|  |       return length <= max ? this : '${substring(0, max - suffix.length)}$suffix'; | ||||||
|  |     } catch (e) { | ||||||
|  |       return this; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,33 @@ | |||||||
|  | // import 'package:Dleelna/classes/enums.dart'; | ||||||
|  | // | ||||||
|  | // extension SelectedAuthMethodTypesService on AuthMethodTypes { | ||||||
|  | //   int getTypeIdService() { | ||||||
|  | //     switch (this) { | ||||||
|  | //       case AuthMethodTypes.sms: | ||||||
|  | //         return 1; | ||||||
|  | //       case AuthMethodTypes.whatsApp: | ||||||
|  | //         return 2; | ||||||
|  | //       case AuthMethodTypes.fingerPrint: | ||||||
|  | //         return 3; | ||||||
|  | //       case AuthMethodTypes.faceID: | ||||||
|  | //         return 4; | ||||||
|  | //       case AuthMethodTypes.moreOptions: | ||||||
|  | //         return 5; | ||||||
|  | //     } | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | //   static getMethodsTypeService(int typeId) { | ||||||
|  | //     switch (typeId) { | ||||||
|  | //       case 1: | ||||||
|  | //         return AuthMethodTypes.sms; | ||||||
|  | //       case 2: | ||||||
|  | //         return AuthMethodTypes.whatsApp; | ||||||
|  | //       case 3: | ||||||
|  | //         return AuthMethodTypes.fingerPrint; | ||||||
|  | //       case 4: | ||||||
|  | //         return AuthMethodTypes.faceID; | ||||||
|  | //       case 5: | ||||||
|  | //         return AuthMethodTypes.moreOptions; | ||||||
|  | //     } | ||||||
|  | //   } | ||||||
|  | // } | ||||||
| @ -0,0 +1,151 @@ | |||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/widgets.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:shimmer/shimmer.dart'; | ||||||
|  | import 'package:sizer/sizer.dart'; | ||||||
|  | 
 | ||||||
|  | extension WidgetExtensions on Widget { | ||||||
|  |   Widget onPress(VoidCallback onTap) => InkWell(onTap: onTap, child: this); | ||||||
|  | 
 | ||||||
|  |   Widget get expanded => Expanded(child: this); | ||||||
|  | 
 | ||||||
|  |   Widget get center => Center(child: this); | ||||||
|  | 
 | ||||||
|  |   Widget circle(double _value) => ClipRRect(borderRadius: BorderRadius.circular(_value), child: this); | ||||||
|  | 
 | ||||||
|  |   Widget paddingAll(double _value) => Padding(padding: EdgeInsets.all(_value), child: this); | ||||||
|  | 
 | ||||||
|  |   Widget paddingSymmetrical(double horizontal, double vertical) => Padding(padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this); | ||||||
|  | 
 | ||||||
|  |   Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => | ||||||
|  |       Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); | ||||||
|  | 
 | ||||||
|  |   Widget toExpanded({int flex = 1}) => Expanded(flex: flex, child: this); | ||||||
|  | 
 | ||||||
|  |   Widget toShimmer({bool isShow = true, bool isTransparent = false}) => isShow | ||||||
|  |       ? Shimmer.fromColors( | ||||||
|  |           baseColor: Color(0xffb9bebe), | ||||||
|  |           highlightColor: Colors.white, | ||||||
|  |           child: Container( | ||||||
|  |             color: isTransparent ? Colors.transparent : Colors.white.withOpacity(0.3), | ||||||
|  |             child: this, | ||||||
|  |           ), | ||||||
|  |         ) | ||||||
|  |       : Container( | ||||||
|  |           child: this, | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |   Widget animatedSwither() => AnimatedSwitcher( | ||||||
|  |         duration: const Duration(milliseconds: 500), | ||||||
|  |         // transitionBuilder: (Widget child, Animation<double> animation) { | ||||||
|  |         //   return ScaleTransition(scale: animation, child: child); | ||||||
|  |         // }, | ||||||
|  |         switchInCurve: Curves.linearToEaseOut, | ||||||
|  |         switchOutCurve: Curves.linearToEaseOut, | ||||||
|  |         child: this, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |   Widget objectContainerView({String title = "", String note = "", bool disablePadding = false, double radius = 15}) { | ||||||
|  |     return Container( | ||||||
|  |       padding: disablePadding ? EdgeInsets.zero : const EdgeInsets.only(top: 15, bottom: 15, left: 14, right: 14), | ||||||
|  |       decoration: BoxDecoration( | ||||||
|  |         color: Colors.white, | ||||||
|  |         borderRadius: BorderRadius.circular(radius), | ||||||
|  |         boxShadow: [ | ||||||
|  |           BoxShadow( | ||||||
|  |             color: const Color(0xff000000).withOpacity(.15), | ||||||
|  |             blurRadius: 26, | ||||||
|  |             offset: const Offset(0, -3), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |       alignment: Alignment.center, | ||||||
|  |       child: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |         mainAxisSize: MainAxisSize.min, | ||||||
|  |         children: [ | ||||||
|  |           if (title.isNotEmpty) title.toText16(), | ||||||
|  |           if (title.isNotEmpty) 12.height, | ||||||
|  |           this, | ||||||
|  |           if (note.isNotEmpty) note.toText11(), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget objectContainerBorderView( | ||||||
|  |       {String title = "", String note = "", bool disablePadding = false, double radius = 20, Color? color, Color borderColor = buttonColor, bool disableWidth = false, bool isAlignment = false}) { | ||||||
|  |     return Container( | ||||||
|  |       padding: disablePadding ? EdgeInsets.zero : const EdgeInsets.only(top: 15, bottom: 15, left: 14, right: 14), | ||||||
|  |       decoration: BoxDecoration( | ||||||
|  |         borderRadius: BorderRadius.all( | ||||||
|  |           Radius.circular(radius), | ||||||
|  |         ), | ||||||
|  |         color: color, | ||||||
|  |         border: Border.all( | ||||||
|  |           color: borderColor, | ||||||
|  |           width: disableWidth ? 2 : 1, | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |       alignment: isAlignment ? Alignment.center : null, | ||||||
|  |       child: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |         mainAxisSize: MainAxisSize.min, | ||||||
|  |         children: [ | ||||||
|  |           if (title.isNotEmpty) title.toText16(), | ||||||
|  |           if (title.isNotEmpty) 12.height, | ||||||
|  |           this, | ||||||
|  |           if (note.isNotEmpty) note.toText11(), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //Height Spacers in percentages | ||||||
|  | Widget heightSpacer02per() => SizedBox(height: 0.2.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer04per() => SizedBox(height: 0.4.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer06per() => SizedBox(height: 0.6.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer08per() => SizedBox(height: 0.8.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer1per() => SizedBox(height: 1.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer2per() => SizedBox(height: 2.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer3per() => SizedBox(height: 3.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer4per() => SizedBox(height: 4.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer5per() => SizedBox(height: 5.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer8per() => SizedBox(height: 8.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer10per() => SizedBox(height: 10.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer15per() => SizedBox(height: 15.h); | ||||||
|  | 
 | ||||||
|  | Widget heightSpacer20per() => SizedBox(height: 20.h); | ||||||
|  | 
 | ||||||
|  | //Width Spacers in percentages | ||||||
|  | Widget widthSpacer02perc() => SizedBox(height: 0.2.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer04perc() => SizedBox(height: 0.4.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer06perc() => SizedBox(height: 0.6.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer08per() => SizedBox(height: 0.8.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer1per() => SizedBox(height: 1.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer2per() => SizedBox(height: 2.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer3per() => SizedBox(height: 3.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer4per() => SizedBox(height: 4.w); | ||||||
|  | 
 | ||||||
|  | Widget widthSpacer5per() => SizedBox(height: 5.w); | ||||||
| @ -0,0 +1,65 @@ | |||||||
|  | // Copyright 2022, the Chromium project authors.  Please see the AUTHORS file | ||||||
|  | // for details. All rights reserved. Use of this source code is governed by a | ||||||
|  | // BSD-style license that can be found in the LICENSE file. | ||||||
|  | 
 | ||||||
|  | // File generated by FlutterFire CLI. | ||||||
|  | // ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members | ||||||
|  | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; | ||||||
|  | import 'package:flutter/foundation.dart' | ||||||
|  |     show defaultTargetPlatform, kIsWeb, TargetPlatform; | ||||||
|  | 
 | ||||||
|  | /// Default [FirebaseOptions] for use with your Firebase apps. | ||||||
|  | /// | ||||||
|  | /// Example: | ||||||
|  | /// ```dart | ||||||
|  | /// import 'firebase_options.dart'; | ||||||
|  | /// // ... | ||||||
|  | /// await Firebase.initializeApp( | ||||||
|  | ///   options: DefaultFirebaseOptions.currentPlatform, | ||||||
|  | /// ); | ||||||
|  | /// ``` | ||||||
|  | class DefaultFirebaseOptions { | ||||||
|  |   static FirebaseOptions get currentPlatform { | ||||||
|  |     // if (kIsWeb) { | ||||||
|  |     //   return web; | ||||||
|  |     // } | ||||||
|  |     switch (defaultTargetPlatform) { | ||||||
|  |       case TargetPlatform.android: | ||||||
|  |         return android; | ||||||
|  |       case TargetPlatform.iOS: | ||||||
|  |         return ios; | ||||||
|  |       // case TargetPlatform.macOS: | ||||||
|  |       //   return macos; | ||||||
|  |       // TODO(Lyokone): Remove when FlutterFire CLI updated | ||||||
|  |       case TargetPlatform.windows: | ||||||
|  |         return android; | ||||||
|  |       default: | ||||||
|  |         throw UnsupportedError( | ||||||
|  |           'DefaultFirebaseOptions are not supported for this platform.', | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static const FirebaseOptions android = FirebaseOptions( | ||||||
|  |     apiKey: 'AIzaSyDZDeWcBlRE3YfJWYt_DCiToVnANfaj8qg', | ||||||
|  |     appId: '1:815750722565:android:62281cd3e5df4063', | ||||||
|  |     messagingSenderId: '815750722565', | ||||||
|  |     projectId: 'api-project-815750722565', | ||||||
|  |     // databaseURL: | ||||||
|  |     //     'https://flutterfire-e2e-tests-default-rtdb.europe-west1.firebasedatabase.app', | ||||||
|  |     storageBucket: 'api-project-815750722565.appspot.com', | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   static const FirebaseOptions ios = FirebaseOptions( | ||||||
|  |     apiKey: 'AIzaSyDiXnCO00li4V7Ioa2YZ_M4ECxRsu_P9tA', | ||||||
|  |     appId: '1:815750722565:ios:328ec247a81a2ca23c186c', | ||||||
|  |     messagingSenderId: '815750722565', | ||||||
|  |     projectId: 'api-project-815750722565', | ||||||
|  |     storageBucket: 'api-project-815750722565.appspot.com', | ||||||
|  |     androidClientId: | ||||||
|  |         '815750722565-m14h8mkosm7cnq6uh6rhqr54dn02d705.apps.googleusercontent.com', | ||||||
|  |     iosClientId: | ||||||
|  |         '815750722565-da8p56le8bd6apsbm9eft0jjl1rtpgkt.apps.googleusercontent.com', | ||||||
|  |     iosBundleId: 'com.HMG.HMG-Smartphone', | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: constant_identifier_names | ||||||
|  | 
 | ||||||
|  | abstract class  LocaleKeys { | ||||||
|  |   static const english = 'english'; | ||||||
|  |   static const arabic = 'arabic'; | ||||||
|  |   static const login = 'login'; | ||||||
|  |   static const noDataAvailable = 'noDataAvailable'; | ||||||
|  |   static const ok = 'ok'; | ||||||
|  |   static const confirm = 'confirm'; | ||||||
|  |   static const loadingText = 'loadingText'; | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,79 @@ | |||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:easy_localization/easy_localization.dart'; | ||||||
|  | import 'package:firebase_core/firebase_core.dart'; | ||||||
|  | import 'package:firebase_messaging/firebase_messaging.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/core/app_state.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/routes/app_routes.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/app_theme.dart'; | ||||||
|  | import 'package:logger/logger.dart'; | ||||||
|  | 
 | ||||||
|  | import 'firebase_options.dart'; | ||||||
|  | 
 | ||||||
|  | var globalMessengerKey = GlobalKey<ScaffoldMessengerState>(); | ||||||
|  | final navigatorKey = GlobalKey<NavigatorState>(); | ||||||
|  | 
 | ||||||
|  | Logger logger = Logger( | ||||||
|  |   printer: PrettyPrinter( | ||||||
|  |     lineLength: 0, | ||||||
|  |   ), | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | late AppState appState; | ||||||
|  | 
 | ||||||
|  | @pragma('vm:entry-point') | ||||||
|  | Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { | ||||||
|  |   await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); | ||||||
|  |   print("Firebase backgroundMessageHandler Main!!!"); | ||||||
|  |   // debugPrint('backgroundMessage: message => ${message.notification!.title.toString()}'); | ||||||
|  |   // messagesOpended = message.notification!.title.toString(); | ||||||
|  |   var payload = message.data; | ||||||
|  |   // showCallkitIncoming(payload); | ||||||
|  |   // await backgroundCallHandler(payload); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class MyHttpOverrides extends HttpOverrides { | ||||||
|  |   @override | ||||||
|  |   HttpClient createHttpClient(SecurityContext? context) { | ||||||
|  |     return super.createHttpClient(context)..badCertificateCallback = (X509Certificate cert, String host, int port) => true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |   runApp(const MyApp()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class MyApp extends StatelessWidget { | ||||||
|  |   const MyApp({super.key}); | ||||||
|  | 
 | ||||||
|  |   // This widget is the root of your application. | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return SafeArea( | ||||||
|  |       top: false, // Set to true if you want to avoid the notch area as well | ||||||
|  |       bottom: Platform.isIOS ? false : true, | ||||||
|  |       child: MaterialApp( | ||||||
|  |         title: 'Dr. AlHabib', | ||||||
|  |         builder: (context, mchild) { | ||||||
|  |           return MediaQuery( | ||||||
|  |               data: MediaQuery.of(context).copyWith( | ||||||
|  |                 textScaler: TextScaler.linear(1.0), | ||||||
|  |               ), //set desired text scale factor here | ||||||
|  |               child: mchild!); | ||||||
|  |         }, | ||||||
|  |         showSemanticsDebugger: false, | ||||||
|  |         debugShowCheckedModeBanner: false, | ||||||
|  |         localizationsDelegates: context.localizationDelegates, | ||||||
|  |         supportedLocales: context.supportedLocales, | ||||||
|  |         locale: context.locale, | ||||||
|  |         initialRoute: AppRoutes.initialRoute, | ||||||
|  |         routes: AppRoutes.routes, | ||||||
|  |         theme: AppTheme.getTheme( | ||||||
|  |           EasyLocalization.of(context)?.locale.languageCode == "ar", | ||||||
|  |         ), | ||||||
|  |         navigatorKey: navigatorKey, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | class LandingPage extends StatefulWidget { | ||||||
|  |   const LandingPage({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<LandingPage> createState() => _LandingPageState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _LandingPageState extends State<LandingPage> { | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return const Placeholder(); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/splashPage.dart'; | ||||||
|  | 
 | ||||||
|  | class AppRoutes { | ||||||
|  |   static const String initialRoute = '/initialRoute'; | ||||||
|  | 
 | ||||||
|  |   static Map<String, WidgetBuilder> get routes => {initialRoute: (context) => SplashPage()}; | ||||||
|  | } | ||||||
| @ -0,0 +1,300 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:convert'; | ||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/services/api_exception.dart'; | ||||||
|  | import 'package:http/http.dart'; | ||||||
|  | import 'package:http/io_client.dart'; | ||||||
|  | 
 | ||||||
|  | import '../core/app_state.dart'; | ||||||
|  | import '../core/consts.dart'; | ||||||
|  | import '../core/utils/utils.dart'; | ||||||
|  | import '../main.dart'; | ||||||
|  | 
 | ||||||
|  | // ignore_for_file: avoid_annotating_with_dynamic | ||||||
|  | 
 | ||||||
|  | typedef FactoryConstructor<U> = U Function(dynamic); | ||||||
|  | 
 | ||||||
|  | class APIError { | ||||||
|  |   int? errorCode; | ||||||
|  |   String? errorMessage; | ||||||
|  | 
 | ||||||
|  |   APIError(this.errorCode, this.errorMessage); | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() => {'errorCode': errorCode, 'errorMessage': errorMessage}; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return jsonEncode(this); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | APIException _throwAPIException(Response response, Function retryCallBack) { | ||||||
|  |   switch (response.statusCode) { | ||||||
|  |     case 200: | ||||||
|  |       APIError? apiError; | ||||||
|  |       if (response.body != null && response.body.isNotEmpty) { | ||||||
|  |         var jsonError = jsonDecode(response.body); | ||||||
|  |         print(jsonError); | ||||||
|  |         apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage']); | ||||||
|  |       } | ||||||
|  |       return APIException(APIException.BAD_REQUEST, error: apiError); | ||||||
|  |     case 400: | ||||||
|  |       APIError? apiError; | ||||||
|  |       if (response.body != null && response.body.isNotEmpty) { | ||||||
|  |         var jsonError = jsonDecode(response.body); | ||||||
|  |         apiError = APIError(jsonError['ErrorCode'], jsonError['ErrorMessage']); | ||||||
|  |       } | ||||||
|  |       return APIException(APIException.BAD_REQUEST, error: apiError); | ||||||
|  |     case 401: | ||||||
|  |       return APIException(APIException.UNAUTHORIZED); | ||||||
|  |     case 403: | ||||||
|  |       return APIException(APIException.FORBIDDEN); | ||||||
|  |     case 404: | ||||||
|  |       return APIException(APIException.NOT_FOUND); | ||||||
|  |     case 500: | ||||||
|  |       return APIException(APIException.INTERNAL_SERVER_ERROR); | ||||||
|  |     case 444: | ||||||
|  |       var downloadUrl = response.headers["location"]; | ||||||
|  |       return APIException(APIException.UPGRADE_REQUIRED, arguments: downloadUrl); | ||||||
|  |     default: | ||||||
|  |       return APIException(APIException.OTHER); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | abstract class IApiClient { | ||||||
|  |   Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject, | ||||||
|  |       {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}); | ||||||
|  | 
 | ||||||
|  |   Future<Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}); | ||||||
|  | 
 | ||||||
|  |   Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}); | ||||||
|  | 
 | ||||||
|  |   void setHomeUrl(String url); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class ApiClient implements IApiClient { | ||||||
|  |   // static final ApiClient _instance = ApiClient._internal(); | ||||||
|  | 
 | ||||||
|  |   // ApiClient._internal(); | ||||||
|  | 
 | ||||||
|  |   // factory ApiClient() => _instance; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject, | ||||||
|  |       {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isFormData = false}) async { | ||||||
|  |     var _headers = {'Accept': 'application/json'}; | ||||||
|  |     if (headers != null && headers.isNotEmpty) { | ||||||
|  |       _headers.addAll(headers); | ||||||
|  |     } | ||||||
|  |     if (!kReleaseMode) { | ||||||
|  |       print("Url:$url"); | ||||||
|  |       var bodyJson = json.encode(jsonObject); | ||||||
|  |       print("body:$bodyJson"); | ||||||
|  |     } | ||||||
|  |     var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes, isFormData: isFormData); | ||||||
|  |     // try { | ||||||
|  |     if (!kReleaseMode) { | ||||||
|  |       logger.i("res: " + response.body); | ||||||
|  |     } | ||||||
|  |     var jsonData = jsonDecode(response.body); | ||||||
|  |     if (jsonData["IsAuthenticated"] != null) { | ||||||
|  |       AppState().setIsAuthenticated = jsonData["IsAuthenticated"]; | ||||||
|  |     } | ||||||
|  |     if (jsonData["ErrorMessage"] == null) { | ||||||
|  |       return factoryConstructor(jsonData); | ||||||
|  |     } else { | ||||||
|  |       APIError? apiError; | ||||||
|  |       apiError = APIError(jsonData['ErrorCode'], jsonData['ErrorEndUserMessage']); | ||||||
|  |       throw APIException(APIException.BAD_REQUEST, error: apiError); | ||||||
|  |     } | ||||||
|  |     // } catch (ex) { | ||||||
|  |     //   if (ex is APIException) { | ||||||
|  |     //     rethrow; | ||||||
|  |     //   } else { | ||||||
|  |     //     throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex); | ||||||
|  |     //   } | ||||||
|  |     // } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> postJsonForResponse<T>(String url, T jsonObject, | ||||||
|  |       {String? token, | ||||||
|  |       Map<String, dynamic>? queryParameters, | ||||||
|  |       Map<String, String>? headers, | ||||||
|  |       int retryTimes = 0, | ||||||
|  |       bool isFormData = false, | ||||||
|  |       bool isAuthAPI = false, | ||||||
|  |       bool isHISAPI = false, | ||||||
|  |       bool isSanedAPI = false, | ||||||
|  |       bool isPutRequest = false}) async { | ||||||
|  |     String? requestBody; | ||||||
|  |     late Map<String, String> stringObj; | ||||||
|  |     if (jsonObject != null) { | ||||||
|  |       requestBody = jsonEncode(jsonObject); | ||||||
|  |       if (headers == null) { | ||||||
|  |         headers = {'Content-Type': 'application/json'}; | ||||||
|  |       } else { | ||||||
|  |         headers['Content-Type'] = 'application/json'; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Response> _postForResponse(String url, requestBody, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async { | ||||||
|  |     try { | ||||||
|  |       var _headers = <String, String>{}; | ||||||
|  |       // if (token != null) { | ||||||
|  |       //   _headers['Authorization'] = 'Bearer $token'; | ||||||
|  |       // } | ||||||
|  | 
 | ||||||
|  |       if (headers != null && headers.isNotEmpty) { | ||||||
|  |         _headers.addAll(headers); | ||||||
|  |       } | ||||||
|  |       if (queryParameters != null) { | ||||||
|  |         // var queryString = new Uri(queryParameters: queryParameters).query; | ||||||
|  |         var queryString = Uri(queryParameters: queryParameters.map((key, value) => MapEntry(key, value == null ? null : value.toString()))).query; | ||||||
|  |         url = url + '?' + queryString; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // if (!kReleaseMode && url.contains("saned")) { | ||||||
|  |       if (!kReleaseMode) { | ||||||
|  |         print("Url: $url"); | ||||||
|  |         print("Headers: $_headers"); | ||||||
|  |         // var bodyJson = json.encode(requestBody); | ||||||
|  |         print("body: $requestBody"); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       var response = await _post(Uri.parse(url), body: requestBody, headers: _headers).timeout(Duration(seconds: 120)); | ||||||
|  | 
 | ||||||
|  |       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||||
|  |         return response; | ||||||
|  |       } else { | ||||||
|  |         throw _throwAPIException(response, () { | ||||||
|  |           // _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes, isAuthAPI: isAuthAPI); | ||||||
|  |           postJsonForResponse(url, requestBody); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } on SocketException catch (e) { | ||||||
|  |       if (retryTimes > 0) { | ||||||
|  |         print('will retry after 3 seconds...'); | ||||||
|  |         await Future.delayed(Duration(seconds: 3)); | ||||||
|  |         return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); | ||||||
|  |       } else { | ||||||
|  |         throw APIException(APIException.OTHER, arguments: e); | ||||||
|  |       } | ||||||
|  |     } on HttpException catch (e) { | ||||||
|  |       if (retryTimes > 0) { | ||||||
|  |         print('will retry after 3 seconds...'); | ||||||
|  |         await Future.delayed(Duration(seconds: 3)); | ||||||
|  |         return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); | ||||||
|  |       } else { | ||||||
|  |         throw APIException(APIException.OTHER, arguments: e); | ||||||
|  |       } | ||||||
|  |     } on TimeoutException catch (e) { | ||||||
|  |       throw APIException(APIException.TIMEOUT, arguments: e); | ||||||
|  |     } on ClientException catch (e) { | ||||||
|  |       if (retryTimes > 0) { | ||||||
|  |         print('will retry after 3 seconds...'); | ||||||
|  |         await Future.delayed(Duration(seconds: 3)); | ||||||
|  |         return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); | ||||||
|  |       } else { | ||||||
|  |         throw APIException(APIException.OTHER, arguments: e); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isAuthAPI = false}) async { | ||||||
|  |     logger.i("Url:$url"); | ||||||
|  |     if (headers == null) { | ||||||
|  |       headers = {'Content-Type': 'application/json'}; | ||||||
|  |     } else { | ||||||
|  |       headers['Content-Type'] = 'application/json'; | ||||||
|  |     } | ||||||
|  |     return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes, isAuthAPI: isAuthAPI); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Response> _getForResponse(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0, bool isAuthAPI = false}) async { | ||||||
|  |     try { | ||||||
|  |       var _headers = <String, String>{}; | ||||||
|  |       if (token != null) { | ||||||
|  |         _headers['Authorization'] = 'Bearer $token'; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (headers != null && headers.isNotEmpty) { | ||||||
|  |         _headers.addAll(headers); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (isAuthAPI) { | ||||||
|  |         String token = await Utils.getStringFromPrefs(SharedPrefsConsts.appAuthToken); | ||||||
|  |         _headers['Authorization'] = "Bearer $token"; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (queryParameters != null) { | ||||||
|  |         var queryString = new Uri(queryParameters: queryParameters).query; | ||||||
|  |         url = url + '?' + queryString; | ||||||
|  |       } | ||||||
|  |       var response = await _get(Uri.parse(url), headers: _headers).timeout(Duration(seconds: 60)); | ||||||
|  | 
 | ||||||
|  |       if (response.statusCode >= 200 && response.statusCode < 300) { | ||||||
|  |         return response; | ||||||
|  |       } else { | ||||||
|  |         throw _throwAPIException(response, () { | ||||||
|  |           _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes); | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } on SocketException catch (e) { | ||||||
|  |       if (retryTimes > 0) { | ||||||
|  |         print('will retry after 3 seconds...'); | ||||||
|  |         await Future.delayed(Duration(seconds: 3)); | ||||||
|  |         return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); | ||||||
|  |       } else { | ||||||
|  |         throw APIException(APIException.OTHER, arguments: e); | ||||||
|  |       } | ||||||
|  |     } on HttpException catch (e) { | ||||||
|  |       if (retryTimes > 0) { | ||||||
|  |         print('will retry after 3 seconds...'); | ||||||
|  |         await Future.delayed(Duration(seconds: 3)); | ||||||
|  |         return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); | ||||||
|  |       } else { | ||||||
|  |         throw APIException(APIException.OTHER, arguments: e); | ||||||
|  |       } | ||||||
|  |     } on TimeoutException catch (e) { | ||||||
|  |       throw APIException(APIException.TIMEOUT, arguments: e); | ||||||
|  |     } on ClientException catch (e) { | ||||||
|  |       if (retryTimes > 0) { | ||||||
|  |         print('will retry after 3 seconds...'); | ||||||
|  |         await Future.delayed(Duration(seconds: 3)); | ||||||
|  |         return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); | ||||||
|  |       } else { | ||||||
|  |         throw APIException(APIException.OTHER, arguments: e); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Response> _get(url, {Map<String, String>? headers}) => _withClient((client) => client.get(url, headers: headers)); | ||||||
|  | 
 | ||||||
|  |   bool _certificateCheck(X509Certificate cert, String host, int port) => true; | ||||||
|  | 
 | ||||||
|  |   Future<T> _withClient<T>(Future<T> Function(Client) fn) async { | ||||||
|  |     var httpClient = HttpClient()..badCertificateCallback = _certificateCheck; | ||||||
|  |     var client = IOClient(httpClient); | ||||||
|  |     try { | ||||||
|  |       return await fn(client); | ||||||
|  |     } finally { | ||||||
|  |       client.close(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Future<Response> _post(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding)); | ||||||
|  | 
 | ||||||
|  |   Future<Response> _put(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.put(url, headers: headers, body: body, encoding: encoding)); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void setHomeUrl(String url) { | ||||||
|  |     // TODO: implement setHomeUrl | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
|  | import 'package:hmg_patient_app_new/services/api_client.dart'; | ||||||
|  | 
 | ||||||
|  | class APIException implements Exception { | ||||||
|  |   static const String BAD_REQUEST = 'api_common_bad_request'; | ||||||
|  |   static const String UNAUTHORIZED = 'api_common_unauthorized'; | ||||||
|  |   static const String FORBIDDEN = 'api_common_forbidden'; | ||||||
|  |   static const String NOT_FOUND = 'api_common_not_found'; | ||||||
|  |   static const String INTERNAL_SERVER_ERROR = 'api_common_internal_server_error'; | ||||||
|  |   static const String UPGRADE_REQUIRED = 'api_common_upgrade_required'; | ||||||
|  |   static const String BAD_RESPONSE_FORMAT = 'api_common_bad_response_format'; | ||||||
|  |   static const String OTHER = 'api_common_http_error'; | ||||||
|  |   static const String TIMEOUT = 'api_common_http_timeout'; | ||||||
|  |   static const String UNKNOWN = 'unexpected_error'; | ||||||
|  | 
 | ||||||
|  |   final String message; | ||||||
|  |   final APIError? error; | ||||||
|  |   final arguments; | ||||||
|  | 
 | ||||||
|  |   const APIException(this.message, {this.arguments, this.error}); | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() => {'message': message, 'error': error, 'arguments': '$arguments'}; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() { | ||||||
|  |     return jsonEncode(this); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,117 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | import 'dart:convert'; | ||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_svg/flutter_svg.dart'; | ||||||
|  | import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/presentation/home/landing_page.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  | 
 | ||||||
|  | import 'core/utils/LocalNotification.dart'; | ||||||
|  | import 'core/utils/push-notification-handler.dart'; | ||||||
|  | 
 | ||||||
|  | class SplashPage extends StatefulWidget { | ||||||
|  |   @override | ||||||
|  |   _SplashScreenState createState() => _SplashScreenState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _SplashScreenState extends State<SplashPage> { | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     print("Splash init called............."); | ||||||
|  |     Timer(Duration(seconds: 1, milliseconds: 500), () async { | ||||||
|  |       LocalNotification.init(onNotificationClick: (payload) {}); | ||||||
|  |       Navigator.of(context).pushReplacement( | ||||||
|  |         MaterialPageRoute( | ||||||
|  |           builder: (BuildContext context) => LandingPage(), | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |       // } else {} | ||||||
|  |     }); | ||||||
|  |     // }, | ||||||
|  |     // ); | ||||||
|  | 
 | ||||||
|  |     var zoom = ZoomVideoSdk(); | ||||||
|  |     InitConfig initConfig = InitConfig( | ||||||
|  |       domain: "zoom.us", | ||||||
|  |       enableLog: true, | ||||||
|  |     ); | ||||||
|  |     zoom.initSdk(initConfig); | ||||||
|  | 
 | ||||||
|  |     // AppSharedPreferences().getAll().then((value) { | ||||||
|  |     //   debugPrint("ALL SHARED PREFERENCES!!!!!"); | ||||||
|  |     //   debugPrint(jsonEncode(value)); | ||||||
|  |     // }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// load the Privilege from service | ||||||
|  |   Future loadPrivilege() async { | ||||||
|  |     // ProjectViewModel projectProvider = Provider.of<ProjectViewModel>(context, listen: false); | ||||||
|  |     // projectProvider.setPrivilegeModelList(privilege: _privilegeService.privilegeModelList); | ||||||
|  |     // projectProvider.setVidaPlusProjectList(_privilegeService.vidaPlusProjectListModel); | ||||||
|  |     // projectProvider.setHMCProjectList(_privilegeService.hMCProjectListModel); | ||||||
|  |     // projectProvider.setProjectsDetailList(_privilegeService.projectDetailListModel); | ||||||
|  |     // double lat = await AppSharedPreferences().getDouble(USER_LAT) ?? 0.0; | ||||||
|  |     // double long = await AppSharedPreferences().getDouble(USER_LONG) ?? 0.0; | ||||||
|  |     // AppSharedPreferences().clear(); // Clearing Shared Preferences On App Launch | ||||||
|  |     // await AppSharedPreferences().setDouble(USER_LAT, lat); | ||||||
|  |     // await AppSharedPreferences().setDouble(USER_LONG, long); | ||||||
|  |     // AppSharedPreferences().setString(APP_LANGUAGE, projectProvider.isArabic ? "ar" : "en"); | ||||||
|  |     // var themeNotifier = Provider.of<ThemeNotifier>(context, listen: false); | ||||||
|  |     // themeNotifier.setTheme(defaultTheme(fontName: projectProvider.isArabic ? 'Cairo' : 'Poppins')); | ||||||
|  |     PushNotificationHandler().init(context); // Asyncronously | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       backgroundColor: Color(0xffF8F8F8), | ||||||
|  |       body: Stack( | ||||||
|  |         alignment: Alignment.center, | ||||||
|  |         children: [ | ||||||
|  |           Padding( | ||||||
|  |             padding: EdgeInsets.symmetric(horizontal: 53), | ||||||
|  |             child: Image.asset( | ||||||
|  |               'assets/images/new/hmg_logo.png', | ||||||
|  |               fit: BoxFit.fitWidth, | ||||||
|  |               width: MediaQuery.of(context).size.width, | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           Align( | ||||||
|  |             alignment: Alignment.bottomCenter, | ||||||
|  |             child: Column( | ||||||
|  |               mainAxisSize: MainAxisSize.min, | ||||||
|  |               children: [ | ||||||
|  |                 Text( | ||||||
|  |                   "Powered by", | ||||||
|  |                   style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff333C45), letterSpacing: -0.56, height: 16 / 14), | ||||||
|  |                 ), | ||||||
|  |                 SizedBox( | ||||||
|  |                   height: 5, | ||||||
|  |                 ), | ||||||
|  |                 SvgPicture.asset( | ||||||
|  |                   'assets/images/new/cloud_logo.svg', | ||||||
|  |                   width: 40, | ||||||
|  |                   height: 40, | ||||||
|  |                 ), | ||||||
|  |                 SizedBox( | ||||||
|  |                   height: 7, | ||||||
|  |                 ), | ||||||
|  |                 // Text( | ||||||
|  |                 //   "Version 1.1.0", | ||||||
|  |                 //   style: TextStyle(fontSize: 10, fontWeight: FontWeight.w400, color: Color(0xff3989898), letterSpacing: 0, height: 12 / 10), | ||||||
|  |                 // ), | ||||||
|  |                 SizedBox( | ||||||
|  |                   height: 18, | ||||||
|  |                 ) | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |           ) | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,58 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | 
 | ||||||
|  | class AppTheme { | ||||||
|  |   static ThemeData getTheme(isArabic) => ThemeData( | ||||||
|  |         fontFamily: isArabic ? 'NotoSansArabic' : 'Jost', | ||||||
|  |         primarySwatch: Colors.red, | ||||||
|  |         visualDensity: VisualDensity.adaptivePlatformDensity, | ||||||
|  |         brightness: Brightness.light, | ||||||
|  |         pageTransitionsTheme: const PageTransitionsTheme( | ||||||
|  |           builders: { | ||||||
|  |             TargetPlatform.android: ZoomPageTransitionsBuilder(), | ||||||
|  |             TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), | ||||||
|  |           }, | ||||||
|  |         ), | ||||||
|  |         hintColor: Colors.grey[400], | ||||||
|  |         // colorScheme: ColorScheme.fromSwatch(accentColor: MyColors.backgroundColor), | ||||||
|  |         disabledColor: Colors.grey[300], | ||||||
|  |         // errorColor: const Colors, | ||||||
|  |         // scaffoldBackgroundColor: MyColors.backgroundColor, | ||||||
|  |         textSelectionTheme: const TextSelectionThemeData(cursorColor: Colors.grey, selectionColor: Color.fromRGBO(80, 100, 253, 0.5), selectionHandleColor: Colors.grey), | ||||||
|  |         canvasColor: Colors.white, | ||||||
|  |         // backgroundColor: const Color.fromRGBO(255, 255, 255, 1), | ||||||
|  |         highlightColor: Colors.grey[100]!.withOpacity(0.4), | ||||||
|  |         splashColor: Colors.transparent, | ||||||
|  |         // primaryColor: primaryColor, | ||||||
|  |         // primaryColorDark: primaryColor, | ||||||
|  |         // toggleableActiveColor: secondaryColor, | ||||||
|  |         // indicatorColor: secondaryColor, | ||||||
|  |         bottomSheetTheme: const BottomSheetThemeData( | ||||||
|  |           backgroundColor: Color(0xFFE0E0E0), | ||||||
|  |         ), | ||||||
|  |         // primaryTextTheme: const TextTheme( | ||||||
|  |         //   bodyText2: TextStyle(color: Colors.white), | ||||||
|  |         // ), | ||||||
|  |         // iconTheme: const IconThemeData(color: MyColors.darkTextColor), | ||||||
|  |         // textTheme: const TextTheme( | ||||||
|  |         //   bodyText1: TextStyle(color: Colors.black, letterSpacing: 0.6), | ||||||
|  |         //   headline1: TextStyle(color: Colors.white, letterSpacing: 0.6), | ||||||
|  |         //   headline2: TextStyle(color: Colors.white, letterSpacing: 0.6), | ||||||
|  |         // ), | ||||||
|  |         floatingActionButtonTheme: const FloatingActionButtonThemeData(highlightElevation: 2, disabledElevation: 0, elevation: 2), | ||||||
|  |         appBarTheme: AppBarTheme( | ||||||
|  |           color: const Color(0xff515A5D), | ||||||
|  |           elevation: 0.0, | ||||||
|  |           actionsIconTheme: IconThemeData( | ||||||
|  |             color: Colors.grey[800], | ||||||
|  |           ), | ||||||
|  |           systemOverlayStyle: SystemUiOverlayStyle.light, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extension ExtendedRevoCheckTheme on TextTheme { | ||||||
|  |   //add custom styles and colors here | ||||||
|  |   //taken from https://medium.com/@crizantlai/flutter-how-to-extend-themedata-b5b987a95bb5 | ||||||
|  |   TextStyle get price => const TextStyle(color: Colors.redAccent); | ||||||
|  | } | ||||||
| @ -0,0 +1,18 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | const mainPurple = Color(0xFF7954F7); | ||||||
|  | const purpleBg = Color(0xFFAEA4FC); | ||||||
|  | const deepPurple = Color(0xFF7C65E7); | ||||||
|  | const logoColor = Color(0xFF7C65E7); | ||||||
|  | const buttonColor = Color(0xFF6A46F5); | ||||||
|  | const splashBgColor = Color(0xFF3C355D); | ||||||
|  | const whiteColor = Color(0xFFffffff); | ||||||
|  | const blackColor = Color(0xFF000000); | ||||||
|  | const lightGray = Color(0xFFF4F5F7); | ||||||
|  | const lightPurple = Color(0xFFB7A3E6); | ||||||
|  | const scaffoldBgColor = Color(0xFFF8F8F8); | ||||||
|  | const lightGreyEFColor = Color(0xffeaeaff); | ||||||
|  | const greyF7Color = Color(0xffF7F7F7); | ||||||
|  | const lightGrayColor = Color(0xff808080); | ||||||
|  | const buttonGrayColor = Color(0xffF1F1F1); | ||||||
|  | const lightPurpleAlpha = Color(0x5AB7A3E6); | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | import 'package:hmg_patient_app_new/widgets/arrow_back.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | 
 | ||||||
|  | AppBar AppBarWidget( | ||||||
|  |   BuildContext context, { | ||||||
|  |   required String title, | ||||||
|  |   String? image, | ||||||
|  |   bool isNeedLeading = true, | ||||||
|  |   List<Widget>? actions, | ||||||
|  |   Function? onTap, | ||||||
|  |   Color? backgroundColor, | ||||||
|  |   Color? foregroundColor, | ||||||
|  |   bool? isCenter, | ||||||
|  | }) { | ||||||
|  |   return AppBar( | ||||||
|  |     // leadingWidth: 0, | ||||||
|  |     titleSpacing: -8, | ||||||
|  |     leading: isNeedLeading | ||||||
|  |         ? ArrowBack( | ||||||
|  |             onTap: onTap, | ||||||
|  |             color: foregroundColor ?? whiteColor, | ||||||
|  |           ) | ||||||
|  |         : Container(), | ||||||
|  |     title: title.toText16(color: foregroundColor ?? whiteColor, isBold: true), | ||||||
|  |     centerTitle: isCenter ?? true, | ||||||
|  |     elevation: 0, | ||||||
|  |     backgroundColor: backgroundColor ?? mainPurple, | ||||||
|  |     actions: actions, | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | class ArrowBack extends StatelessWidget { | ||||||
|  |   final Function? onTap; | ||||||
|  |   final Color color; | ||||||
|  | 
 | ||||||
|  |   ArrowBack({Key? key, this.onTap, this.color = whiteColor}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     // ProjectViewModel projectViewModel = Provider.of(context); | ||||||
|  |     return GestureDetector( | ||||||
|  |       behavior: HitTestBehavior.opaque, | ||||||
|  |       onTap: Feedback.wrapForTap(() { | ||||||
|  |         onTap != null ? onTap!() : Navigator.maybePop(context); | ||||||
|  |       }, context), | ||||||
|  |       child: Icon( | ||||||
|  |         Icons.arrow_back_ios, | ||||||
|  |         color: color, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,58 @@ | |||||||
|  | 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/theme/colors.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_svg/flutter_svg.dart'; | ||||||
|  | 
 | ||||||
|  | class AttachmentOptions extends StatelessWidget { | ||||||
|  |   VoidCallback onCameraTap; | ||||||
|  |   VoidCallback onGalleryTap; | ||||||
|  |   VoidCallback onFilesTap; | ||||||
|  |   bool showFilesOption; | ||||||
|  | 
 | ||||||
|  |   AttachmentOptions({Key? key, required this.onCameraTap, required this.onGalleryTap, required this.onFilesTap, this.showFilesOption = true}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return SizedBox( | ||||||
|  |       width: double.infinity, | ||||||
|  |       child: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |         children: [ | ||||||
|  |           "Upload Attachment".toSectionHeading(), | ||||||
|  |           "Select from gallery or open camera".toText11(weight: FontWeight.w500), | ||||||
|  |           GridView( | ||||||
|  |             gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 105 / 105, crossAxisSpacing: 9, mainAxisSpacing: 9), | ||||||
|  |             physics: const NeverScrollableScrollPhysics(), | ||||||
|  |             padding: const EdgeInsets.only(top: 21, bottom: 14), | ||||||
|  |             shrinkWrap: true, | ||||||
|  |             children: [ | ||||||
|  |               itemView("open_camera.svg", "Open\nCamera", onCameraTap), | ||||||
|  |               itemView("gallery.svg", "Upload from\nGallery", onGalleryTap), | ||||||
|  |               if (showFilesOption) itemView("files.svg", "Upload from\nFiles", onFilesTap), | ||||||
|  |             ], | ||||||
|  |           ) | ||||||
|  |         ], | ||||||
|  |       ).paddingOnly(left: 21, right: 21, bottom: 21), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget itemView(String icon, String title, VoidCallback onTap) { | ||||||
|  |     return InkWell( | ||||||
|  |       onTap: onTap, | ||||||
|  |       child: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |         children: [ | ||||||
|  |           SvgPicture.asset("assets/images/$icon", color: mainPurple,), | ||||||
|  |           title.toText11(isBold: true), | ||||||
|  |         ], | ||||||
|  |       ).paddingOnly(left: 13, right: 13, top: 16, bottom: 12).expanded.objectContainerBorderView( | ||||||
|  |             disablePadding: true, | ||||||
|  |             radius: 10, | ||||||
|  |             color: greyF7Color.withOpacity(.48), | ||||||
|  |             borderColor: lightGreyEFColor.withOpacity(.48), | ||||||
|  |           ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,89 @@ | |||||||
|  | import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | void showMyBottomSheet(BuildContext context, {required Widget child, required VoidCallback callBackFunc, String? type}) { | ||||||
|  |   showModalBottomSheet<String>( | ||||||
|  |     context: context, | ||||||
|  |     isScrollControlled: true, | ||||||
|  |     backgroundColor: Colors.transparent, | ||||||
|  |     builder: (BuildContext context) { | ||||||
|  |       return Container( | ||||||
|  |       constraints:  BoxConstraints( | ||||||
|  |       maxHeight: type =='CONTINUE_ACTION' ? MediaQuery.of(context).size.height *.75 :  double.infinity,), | ||||||
|  |         decoration: const BoxDecoration( | ||||||
|  |           color: Colors.white, | ||||||
|  |           borderRadius: BorderRadius.only( | ||||||
|  |             topRight: Radius.circular(25), | ||||||
|  |             topLeft: Radius.circular(25), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |         padding: MediaQuery.of(context).viewInsets, | ||||||
|  |         clipBehavior: Clip.antiAlias, | ||||||
|  |         child:SingleChildScrollView(child: Column( | ||||||
|  |           mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |           mainAxisSize: MainAxisSize.min, | ||||||
|  |           children: <Widget>[ | ||||||
|  |             13.height, | ||||||
|  |             Container( | ||||||
|  |               height: 6, | ||||||
|  |               width: 60, | ||||||
|  |               decoration: const BoxDecoration( | ||||||
|  |                 color: Color(0xff9A9A9A), | ||||||
|  |                 borderRadius: BorderRadius.all( | ||||||
|  |                   Radius.circular(20), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             8.height, | ||||||
|  |             child, | ||||||
|  |           ], | ||||||
|  |         )), | ||||||
|  |       ); | ||||||
|  |     }, | ||||||
|  |   ).then((value) { | ||||||
|  |     // print("BACK FROM DELEGATE!!!!"); | ||||||
|  |     // print("value: $value"); | ||||||
|  |     if (value == "delegate_reload") { | ||||||
|  |       callBackFunc(); | ||||||
|  |     } | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class BottomSheetItem extends StatelessWidget { | ||||||
|  |   final Function onTap; | ||||||
|  |   final IconData icon; | ||||||
|  |   final String title; | ||||||
|  |   final Color color; | ||||||
|  | 
 | ||||||
|  |   const BottomSheetItem({Key? key, required this.onTap, required this.title, required this.icon, this.color = Colors.black}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return InkWell( | ||||||
|  |       onTap: () { | ||||||
|  |         if (onTap != null) { | ||||||
|  |           Navigator.pop(context); | ||||||
|  |           onTap(); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       child: Padding( | ||||||
|  |         padding: EdgeInsets.symmetric(horizontal: 18.0, vertical: 18.0), | ||||||
|  |         child: Row( | ||||||
|  |           children: <Widget>[ | ||||||
|  |             if (icon != null) | ||||||
|  |               Icon( | ||||||
|  |                 icon, | ||||||
|  |                 color: color, | ||||||
|  |                 size: 18.0, | ||||||
|  |               ), | ||||||
|  |             if (icon != null) SizedBox(width: 24.0), | ||||||
|  |             Text( | ||||||
|  |               title ?? "", | ||||||
|  |               style: TextStyle(color: color), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,34 @@ | |||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | import '../theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | class CategoryButtons extends StatelessWidget { | ||||||
|  |   CategoryButtons({super.key, required this.label, required this.selected, required this.onTap}); | ||||||
|  | 
 | ||||||
|  |   late String label; | ||||||
|  |   late bool selected; | ||||||
|  |   late Function onTap; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Expanded( | ||||||
|  |       child: InkWell( | ||||||
|  |         onTap: () { | ||||||
|  |           onTap(); | ||||||
|  |         }, | ||||||
|  |         child: Container( | ||||||
|  |           height: 64.h, | ||||||
|  |           decoration: BoxDecoration( | ||||||
|  |             color: selected ? mainPurple : Colors.grey.shade200, | ||||||
|  |             borderRadius: BorderRadius.circular(16), | ||||||
|  |           ), | ||||||
|  |           child: Center( | ||||||
|  |             child: label.toText13(color: selected ? Colors.white : Colors.black, isBold: true), | ||||||
|  |           ), | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,73 @@ | |||||||
|  | // Dropdown Field | ||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | import '../theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | class DropdownField<T> extends StatelessWidget { | ||||||
|  |   late String hint; | ||||||
|  |   late T? value; | ||||||
|  |   late IconData? icon; | ||||||
|  |   late List<T>? items; | ||||||
|  |   late ValueChanged<T?>? onChanged; | ||||||
|  |   late bool isEnabled; | ||||||
|  | 
 | ||||||
|  |   final String keyName = "dropdown_field"; | ||||||
|  | 
 | ||||||
|  |   DropdownField({ | ||||||
|  |     required this.hint, | ||||||
|  |     this.value, | ||||||
|  |     this.icon, | ||||||
|  |     this.items, | ||||||
|  |     this.onChanged, | ||||||
|  |     this.isEnabled = true, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Container( | ||||||
|  |       height: 54.h, | ||||||
|  |       margin: const EdgeInsets.symmetric(vertical: 8), | ||||||
|  |       padding: const EdgeInsets.symmetric(horizontal: 16), | ||||||
|  |       decoration: BoxDecoration( | ||||||
|  |         color: const Color(0xFFF4F5F7), | ||||||
|  |         borderRadius: BorderRadius.circular(13), | ||||||
|  |       ), | ||||||
|  |       child: Row( | ||||||
|  |         children: [ | ||||||
|  |           if (icon != null) Icon(icon, color: mainPurple), | ||||||
|  |           if (icon != null) SizedBox(width: 8.h), | ||||||
|  |           Expanded( | ||||||
|  |             child: items != null | ||||||
|  |                 ? DropdownButton<T>( | ||||||
|  |                     value: value, | ||||||
|  |                     isExpanded: true, | ||||||
|  |                     icon: const Icon(Icons.keyboard_arrow_down_rounded, color: mainPurple), | ||||||
|  |                     underline: const SizedBox(), | ||||||
|  |                     items: items! | ||||||
|  |                         .map( | ||||||
|  |                           (item) => DropdownMenuItem( | ||||||
|  |                             value: item, | ||||||
|  |                             child: item.toString().toText15(isBold: true), | ||||||
|  |                           ), | ||||||
|  |                         ) | ||||||
|  |                         .toList(), | ||||||
|  |                     hint: hint.toText15(color: Colors.black45), | ||||||
|  |                     onChanged: onChanged, | ||||||
|  |                   ) | ||||||
|  |                 : Text( | ||||||
|  |                     value?.toString() ?? hint, | ||||||
|  |                     style: TextStyle( | ||||||
|  |                       color: value != null ? Colors.black : Colors.black45, | ||||||
|  |                       fontWeight: value != null ? FontWeight.bold : FontWeight.normal, | ||||||
|  |                       fontSize: 15, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,133 @@ | |||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_svg/svg.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | extension WithContainer on Widget { | ||||||
|  |   Widget get insideContainer => Container( | ||||||
|  |         color: Colors.white, | ||||||
|  |         padding: const EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), | ||||||
|  |         child: this, | ||||||
|  |       ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class DefaultButton extends StatelessWidget { | ||||||
|  |   final String text; | ||||||
|  |   final VoidCallback? onPress; | ||||||
|  |   final Color textColor; | ||||||
|  |   final Color? color; | ||||||
|  |   final Color? disabledColor; | ||||||
|  |   final IconData? iconData; | ||||||
|  |   final String? svgIcon; | ||||||
|  |   final double? fontSize; | ||||||
|  |   final bool isTextExpanded; | ||||||
|  |   final int count; | ||||||
|  |   final List<Color>? colors; | ||||||
|  |   final double height; | ||||||
|  |   final double borderRadius; | ||||||
|  | 
 | ||||||
|  |   const DefaultButton(this.text, this.onPress, | ||||||
|  |       {Key? key, | ||||||
|  |       this.color, | ||||||
|  |       this.isTextExpanded = true, | ||||||
|  |       this.svgIcon, | ||||||
|  |       this.disabledColor, | ||||||
|  |       this.count = 0, | ||||||
|  |       this.textColor = Colors.white, | ||||||
|  |       this.iconData, | ||||||
|  |       this.fontSize, | ||||||
|  |       this.colors, | ||||||
|  |       this.height = 50, | ||||||
|  |       this.borderRadius = 100}) | ||||||
|  |       : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return InkWell( | ||||||
|  |       onTap: onPress, | ||||||
|  |       child: Container( | ||||||
|  |         height: height, | ||||||
|  |         decoration: BoxDecoration( | ||||||
|  |           borderRadius: BorderRadius.circular(borderRadius), | ||||||
|  |           gradient: onPress == null | ||||||
|  |               ? LinearGradient( | ||||||
|  |                   colors: <Color>[ | ||||||
|  |                     disabledColor ?? const Color(0xffEAEAEA), | ||||||
|  |                     disabledColor ?? const Color(0xffEAEAEA), | ||||||
|  |                   ], | ||||||
|  |                 ) | ||||||
|  |               : LinearGradient( | ||||||
|  |                   transform: const GradientRotation(.83), | ||||||
|  |                   begin: Alignment.topRight, | ||||||
|  |                   end: Alignment.bottomLeft, | ||||||
|  |                   colors: colors ?? | ||||||
|  |                       <Color>[ | ||||||
|  |                         buttonColor, | ||||||
|  |                         buttonColor, | ||||||
|  |                       ], | ||||||
|  |                 ), | ||||||
|  |         ), | ||||||
|  |         child: Row( | ||||||
|  |           mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |           children: <Widget>[ | ||||||
|  |             if (iconData != null) Icon(iconData, color: textColor), | ||||||
|  |             if (svgIcon != null) SvgPicture.asset(svgIcon ?? "", color: textColor), | ||||||
|  |             if (!isTextExpanded) | ||||||
|  |               Padding( | ||||||
|  |                 padding: EdgeInsets.only( | ||||||
|  |                   left: (iconData ?? svgIcon) != null ? 6 : 0, | ||||||
|  |                 ), | ||||||
|  |                 child: Text( | ||||||
|  |                   text, | ||||||
|  |                   textAlign: TextAlign.center, | ||||||
|  |                   style: TextStyle( | ||||||
|  |                     fontSize: fontSize ?? 18.fSize, | ||||||
|  |                     fontWeight: FontWeight.w600, | ||||||
|  |                     color: textColor, | ||||||
|  |                     letterSpacing: -0.48, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             if (isTextExpanded) | ||||||
|  |               Expanded( | ||||||
|  |                 child: Text( | ||||||
|  |                   text, | ||||||
|  |                   textAlign: TextAlign.center, | ||||||
|  |                   style: TextStyle( | ||||||
|  |                     fontSize: fontSize ?? 18.fSize, | ||||||
|  |                     fontWeight: FontWeight.w600, | ||||||
|  |                     color: textColor, | ||||||
|  |                     letterSpacing: -0.48, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             if (count > 0) | ||||||
|  |               Align( | ||||||
|  |                 alignment: Alignment.topCenter, | ||||||
|  |                 child: Container( | ||||||
|  |                   margin: const EdgeInsets.only(top: 6, bottom: 6), | ||||||
|  |                   padding: const EdgeInsets.only(left: 5, right: 5), | ||||||
|  |                   alignment: Alignment.center, | ||||||
|  |                   height: 16, | ||||||
|  |                   decoration: BoxDecoration( | ||||||
|  |                     borderRadius: BorderRadius.circular(10.0), | ||||||
|  |                     color: Colors.white, | ||||||
|  |                   ), | ||||||
|  |                   child: Text( | ||||||
|  |                     "$count", | ||||||
|  |                     textAlign: TextAlign.center, | ||||||
|  |                     style: const TextStyle( | ||||||
|  |                       fontSize: 12, | ||||||
|  |                       fontWeight: FontWeight.w700, | ||||||
|  |                       color: Color(0xffD02127), | ||||||
|  |                       letterSpacing: -0.6, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ) | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,65 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.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'; | ||||||
|  | import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/default_button.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | import '../../theme/colors.dart'; | ||||||
|  | 
 | ||||||
|  | class ConfirmDialog extends StatelessWidget { | ||||||
|  |   final String? title; | ||||||
|  |   final String message; | ||||||
|  |   final String? okTitle; | ||||||
|  |   final VoidCallback? onTap; | ||||||
|  |   final VoidCallback? onCloseTap; | ||||||
|  | 
 | ||||||
|  |   const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Dialog( | ||||||
|  |       backgroundColor: Colors.white, | ||||||
|  |       shape: const RoundedRectangleBorder(), | ||||||
|  |       insetPadding: const EdgeInsets.only(left: 21, right: 21), | ||||||
|  |       child: Padding( | ||||||
|  |         padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28), | ||||||
|  |         child: Column( | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |           mainAxisSize: MainAxisSize.min, | ||||||
|  |           children: [ | ||||||
|  |             Row( | ||||||
|  |               crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |               children: [ | ||||||
|  |                 Expanded( | ||||||
|  |                   child: Text( | ||||||
|  |                     title ?? LocaleKeys.confirm.tr(), | ||||||
|  |                     style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: blackColor, height: 35 / 24, letterSpacing: -0.96), | ||||||
|  |                   ).paddingOnly(top: 16), | ||||||
|  |                 ), | ||||||
|  |                 IconButton( | ||||||
|  |                   padding: EdgeInsets.zero, | ||||||
|  |                   icon: const Icon(Icons.close), | ||||||
|  |                   color: blackColor, | ||||||
|  |                   constraints: const BoxConstraints(), | ||||||
|  |                   onPressed: () => onCloseTap ?? Navigator.pop(context), | ||||||
|  |                   // onPressed: () => Navigator.pop(context), | ||||||
|  |                 ) | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |             14.height, | ||||||
|  |             message.toText16(color: lightGrayColor), | ||||||
|  |             28.height, | ||||||
|  |             DefaultButton( | ||||||
|  |               okTitle ?? LocaleKeys.ok.tr(), | ||||||
|  |               onTap ?? () => Navigator.pop(context), | ||||||
|  |               textColor: Colors.white, | ||||||
|  |               //color: Ap.green, | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,214 @@ | |||||||
|  | import 'dart:convert'; | ||||||
|  | import 'dart:io'; | ||||||
|  | 
 | ||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/attachment_options.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/widgets/bottom_sheet.dart'; | ||||||
|  | import 'package:file_picker/file_picker.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:image_picker/image_picker.dart'; | ||||||
|  | 
 | ||||||
|  | final ImagePicker picker = ImagePicker(); | ||||||
|  | 
 | ||||||
|  | class ImageOptions { | ||||||
|  |   static void showImageOptionsNew(BuildContext context, bool showFilesOption, Function(String, File) image) { | ||||||
|  |     showMyBottomSheet( | ||||||
|  |       context, | ||||||
|  |       callBackFunc: () {}, | ||||||
|  |       child: AttachmentOptions( | ||||||
|  |         showFilesOption: showFilesOption, | ||||||
|  |         onCameraTap: () async { | ||||||
|  |           if (Platform.isAndroid) { | ||||||
|  |             cameraImageAndroid(image); | ||||||
|  |           } else { | ||||||
|  |             File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 20))?.path ?? ""); | ||||||
|  |             // XFile? media = await picker.pickMedia(); | ||||||
|  |             String? fileName = _image.path; | ||||||
|  |             var bytes = File(fileName!).readAsBytesSync(); | ||||||
|  |             String base64Encode = base64.encode(bytes); | ||||||
|  |             if (base64Encode != null) { | ||||||
|  |               image(base64Encode, _image); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         onGalleryTap: () async { | ||||||
|  |           if (Platform.isAndroid) { | ||||||
|  |             galleryImageAndroid(image); | ||||||
|  |           } else { | ||||||
|  |             File _image = File((await picker.pickMedia())?.path ?? ""); | ||||||
|  |             String fileName = _image.path; | ||||||
|  |             var bytes = File(fileName).readAsBytesSync(); | ||||||
|  |             String base64Encode = base64.encode(bytes); | ||||||
|  |             if (base64Encode != null) { | ||||||
|  |               image(base64Encode, _image); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         onFilesTap: () async { | ||||||
|  |           FilePickerResult? result = await FilePicker.platform.pickFiles( | ||||||
|  |             type: FileType.custom, | ||||||
|  |             allowedExtensions: [ | ||||||
|  |               'jpg', | ||||||
|  |               'jpeg ', | ||||||
|  |               'pdf', | ||||||
|  |               'txt', | ||||||
|  |               'docx', | ||||||
|  |               'doc', | ||||||
|  |               'pptx', | ||||||
|  |               'xlsx', | ||||||
|  |               'png', | ||||||
|  |               'rar', | ||||||
|  |               'zip', | ||||||
|  |             ], | ||||||
|  |           ); | ||||||
|  |           List<File> files = result!.paths.map((path) => File(path!)).toList(); | ||||||
|  |           image(result.files.first.path.toString(), files.first); | ||||||
|  |         }, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | // static void showImageOptions(BuildContext context, Function(String, File) image) { | ||||||
|  | //   showModalBottomSheet( | ||||||
|  | //       backgroundColor: Colors.transparent, | ||||||
|  | //       context: context, | ||||||
|  | //       builder: (BuildContext bc) { | ||||||
|  | //         return _BottomSheet( | ||||||
|  | //           children: <Widget>[ | ||||||
|  | //             _BottomSheetItem( | ||||||
|  | //               title: "Select File Source", | ||||||
|  | //               onTap: () {}, | ||||||
|  | //               icon: Icons.file_present, | ||||||
|  | //               color: MyColors.black, | ||||||
|  | //             ), | ||||||
|  | //             _BottomSheetItem( | ||||||
|  | //               title: "Gallery", | ||||||
|  | //               icon: Icons.image, | ||||||
|  | //               onTap: () async { | ||||||
|  | //                 if (Platform.isAndroid) { | ||||||
|  | //                   galleryImageAndroid(image); | ||||||
|  | //                 } else { | ||||||
|  | //                   File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.gallery, imageQuality: 10))?.path ?? ""); | ||||||
|  | //                   String fileName = _image.path; | ||||||
|  | //                   var bytes = File(fileName).readAsBytesSync(); | ||||||
|  | //                   String base64Encode = base64.encode(bytes); | ||||||
|  | //                   if (base64Encode != null) { | ||||||
|  | //                     image(base64Encode, _image); | ||||||
|  | //                   } | ||||||
|  | //                 } | ||||||
|  | //               }, | ||||||
|  | //             ), | ||||||
|  | //             _BottomSheetItem( | ||||||
|  | //               title: "Camera", | ||||||
|  | //               icon: Icons.camera_alt, | ||||||
|  | //               onTap: () async { | ||||||
|  | //                 if (Platform.isAndroid) { | ||||||
|  | //                   cameraImageAndroid(image); | ||||||
|  | //                 } else { | ||||||
|  | //                   File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 10))?.path ?? ""); | ||||||
|  | //                   String fileName = _image.path; | ||||||
|  | //                   var bytes = File(fileName).readAsBytesSync(); | ||||||
|  | //                   String base64Encode = base64.encode(bytes); | ||||||
|  | //                   if (base64Encode != null) { | ||||||
|  | //                     image(base64Encode, _image); | ||||||
|  | //                   } | ||||||
|  | //                 } | ||||||
|  | //               }, | ||||||
|  | //             ), | ||||||
|  | //             _BottomSheetItem( | ||||||
|  | //               title: "Cancel", | ||||||
|  | //               onTap: () {}, | ||||||
|  | //               icon: Icons.cancel, | ||||||
|  | //               color: MyColors.redColor, | ||||||
|  | //             ) | ||||||
|  | //           ], | ||||||
|  | //         ); | ||||||
|  | //       }); | ||||||
|  | // } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void galleryImageAndroid(Function(String, File) image) async { | ||||||
|  |   File _image = File((await picker.pickMedia())?.path ?? ""); | ||||||
|  | 
 | ||||||
|  |   String fileName = _image.path; | ||||||
|  |   var bytes = File(fileName).readAsBytesSync(); | ||||||
|  |   String base64Encode = base64.encode(bytes); | ||||||
|  |   if (base64Encode != null) { | ||||||
|  |     image(base64Encode, _image); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void cameraImageAndroid(Function(String, File) image) async { | ||||||
|  |   File _image = File((await picker.pickMedia())?.path ?? ""); | ||||||
|  |   String fileName = _image.path; | ||||||
|  |   var bytes = File(fileName).readAsBytesSync(); | ||||||
|  |   String base64Encode = base64.encode(bytes); | ||||||
|  |   if (base64Encode != null) { | ||||||
|  |     image(base64Encode, _image); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _BottomSheet extends StatelessWidget { | ||||||
|  |   final List<Widget> children; | ||||||
|  | 
 | ||||||
|  |   const _BottomSheet({Key? key, required this.children}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Container( | ||||||
|  |       padding: const EdgeInsets.symmetric(vertical: 12.0), | ||||||
|  |       decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor, borderRadius: const BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))), | ||||||
|  |       child: SafeArea( | ||||||
|  |         top: false, | ||||||
|  |         child: Column( | ||||||
|  |           mainAxisSize: MainAxisSize.min, | ||||||
|  |           children: <Widget>[ | ||||||
|  |             Container( | ||||||
|  |               decoration: BoxDecoration(color: Theme.of(context).dividerColor, borderRadius: BorderRadius.circular(3.0)), | ||||||
|  |               width: 40.0, | ||||||
|  |               height: 6.0, | ||||||
|  |             ), | ||||||
|  |             ...children | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _BottomSheetItem extends StatelessWidget { | ||||||
|  |   final Function onTap; | ||||||
|  |   final IconData icon; | ||||||
|  |   final String title; | ||||||
|  |   final Color color; | ||||||
|  | 
 | ||||||
|  |   _BottomSheetItem({Key? key, required this.onTap, required this.title, required this.icon, this.color = mainPurple}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return InkWell( | ||||||
|  |       onTap: () { | ||||||
|  |         if (onTap != null) { | ||||||
|  |           Navigator.pop(context); | ||||||
|  |           onTap(); | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       child: Padding( | ||||||
|  |         padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 18.0), | ||||||
|  |         child: Row( | ||||||
|  |           children: <Widget>[ | ||||||
|  |             if (icon != null) | ||||||
|  |               Icon( | ||||||
|  |                 icon, | ||||||
|  |                 color: color, | ||||||
|  |                 size: 18.0, | ||||||
|  |               ), | ||||||
|  |             if (icon != null) const SizedBox(width: 24.0), | ||||||
|  |             title.toText17(), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,164 @@ | |||||||
|  | import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | // import 'package:sizer/sizer.dart'; | ||||||
|  | 
 | ||||||
|  | class CustomTextField extends StatefulWidget { | ||||||
|  |   final String labelText; | ||||||
|  |   final String? hintText; | ||||||
|  |   final TextEditingController controller; | ||||||
|  |   final VoidCallback? suffixTap; | ||||||
|  |   final IconData? suffixIcon; | ||||||
|  |   final bool isEnable; | ||||||
|  |   final bool hasSelection; | ||||||
|  |   final int? lines; | ||||||
|  |   final bool isInputTypeNum; | ||||||
|  |   final bool isTextIsPassword; | ||||||
|  |   final bool isBackgroundEnable; | ||||||
|  |   final bool isEnableBorder; | ||||||
|  |   final double verticalPadding; | ||||||
|  |   final double horizontalPadding; | ||||||
|  |   final Function(String)? onChange; | ||||||
|  |   final Function()? onEditComplete; | ||||||
|  |   final Function(String)? onSubmit; | ||||||
|  |   final Function? onClick; | ||||||
|  |   final FocusNode? focusNode; | ||||||
|  |   List<TextInputFormatter>? inputFormatters; | ||||||
|  | 
 | ||||||
|  |   CustomTextField( | ||||||
|  |     this.labelText, | ||||||
|  |     this.controller, { | ||||||
|  |     Key? key, | ||||||
|  |     this.isTextIsPassword = false, | ||||||
|  |     this.suffixTap, | ||||||
|  |     this.suffixIcon, | ||||||
|  |     this.hintText, | ||||||
|  |     this.isEnable = true, | ||||||
|  |     this.hasSelection = false, | ||||||
|  |     this.isEnableBorder = true, | ||||||
|  |     this.lines = 1, | ||||||
|  |     this.onChange, | ||||||
|  |     this.onEditComplete, | ||||||
|  |     this.onSubmit, | ||||||
|  |     this.onClick, | ||||||
|  |     this.isInputTypeNum = false, | ||||||
|  |     this.isBackgroundEnable = false, | ||||||
|  |     this.focusNode, | ||||||
|  |     this.verticalPadding = 15, | ||||||
|  |     this.horizontalPadding = 16, | ||||||
|  |     this.inputFormatters, | ||||||
|  |   }) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   CustomTextFieldState createState() => CustomTextFieldState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class CustomTextFieldState extends State<CustomTextField> { | ||||||
|  |   late bool isObscureText; | ||||||
|  |   late FocusNode focusNode; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     focusNode = FocusNode(); | ||||||
|  |     isObscureText = widget.isTextIsPassword; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return InkWell( | ||||||
|  |       onTap: () { | ||||||
|  |         focusNode.requestFocus(); | ||||||
|  |         if (widget.hasSelection) widget.onClick!(); | ||||||
|  |       }, | ||||||
|  |       child: Container( | ||||||
|  |         padding: EdgeInsets.only(left: widget.horizontalPadding, right: widget.horizontalPadding, bottom: widget.verticalPadding, top: widget.verticalPadding), | ||||||
|  |         alignment: Alignment.center, | ||||||
|  |         decoration: BoxDecoration( | ||||||
|  |           borderRadius: BorderRadius.circular(15), | ||||||
|  |           color: widget.isBackgroundEnable ? const Color(0xffF7F7F7) : Colors.white, | ||||||
|  |           border: Border.all(color: widget.isEnableBorder ? Colors.grey.shade300 : Colors.transparent, width: 1), | ||||||
|  |         ), | ||||||
|  |         child: Row( | ||||||
|  |           children: [ | ||||||
|  |             Expanded( | ||||||
|  |               child: Column( | ||||||
|  |                 mainAxisSize: MainAxisSize.min, | ||||||
|  |                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                 mainAxisAlignment: MainAxisAlignment.start, | ||||||
|  |                 children: [ | ||||||
|  |                   Text( | ||||||
|  |                     widget.labelText, | ||||||
|  |                     style: TextStyle( | ||||||
|  |                       fontSize: 12.h, | ||||||
|  |                       fontWeight: FontWeight.w600, | ||||||
|  |                       color: const Color(0xff2B353E), | ||||||
|  |                       letterSpacing: -0.44, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                   TextField( | ||||||
|  |                     focusNode: focusNode, | ||||||
|  |                     autofocus: false, | ||||||
|  |                     enabled: widget.isEnable, | ||||||
|  |                     scrollPadding: EdgeInsets.zero, | ||||||
|  |                     keyboardType: widget.isInputTypeNum ? TextInputType.number : TextInputType.text, | ||||||
|  |                     controller: widget.controller, | ||||||
|  |                     maxLines: widget.lines, | ||||||
|  |                     obscuringCharacter: "*", | ||||||
|  |                     obscureText: isObscureText, | ||||||
|  |                     onChanged: widget.onChange, | ||||||
|  |                     onEditingComplete: widget.onEditComplete, | ||||||
|  |                     onSubmitted: widget.onSubmit, | ||||||
|  |                     inputFormatters: widget.inputFormatters, | ||||||
|  |                     style: const TextStyle( | ||||||
|  |                       fontSize: 16, | ||||||
|  |                       height: 21 / 14, | ||||||
|  |                       fontWeight: FontWeight.w400, | ||||||
|  |                       color: Color(0xff2B353E), | ||||||
|  |                       letterSpacing: -0.44, | ||||||
|  |                     ), | ||||||
|  |                     decoration: InputDecoration( | ||||||
|  |                       isDense: true, | ||||||
|  |                       hintText: widget.hintText, | ||||||
|  |                       hintStyle: const TextStyle( | ||||||
|  |                         fontSize: 14, | ||||||
|  |                         height: 21 / 14, | ||||||
|  |                         fontWeight: FontWeight.w400, | ||||||
|  |                         color: Color(0xff575757), | ||||||
|  |                         letterSpacing: -0.56, | ||||||
|  |                       ), | ||||||
|  |                       suffixIconConstraints: const BoxConstraints(minWidth: 50), | ||||||
|  |                       suffixIcon: widget.suffixTap == null ? null : IconButton(icon: Icon(Icons.mic, color: blackColor), onPressed: widget.suffixTap), | ||||||
|  |                       contentPadding: EdgeInsets.zero, | ||||||
|  |                       border: InputBorder.none, | ||||||
|  |                       focusedBorder: InputBorder.none, | ||||||
|  |                       enabledBorder: InputBorder.none, | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             if (widget.isTextIsPassword) ...[ | ||||||
|  |               16.width, | ||||||
|  |               Icon(isObscureText ? Icons.visibility_rounded : Icons.visibility_off_rounded).onPress(() { | ||||||
|  |                 setState(() { | ||||||
|  |                   isObscureText = !isObscureText; | ||||||
|  |                 }); | ||||||
|  |               }) | ||||||
|  |             ], | ||||||
|  |             if (widget.hasSelection) const Icon(Icons.keyboard_arrow_down_outlined), | ||||||
|  |             if (widget.suffixIcon != null && widget.suffixTap == null) Icon(widget.suffixIcon!), | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | import 'package:easy_localization/easy_localization.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/generated/locale_keys.g.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:flutter/cupertino.dart'; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/rendering.dart'; | ||||||
|  | import 'package:lottie/lottie.dart'; | ||||||
|  | 
 | ||||||
|  | class LoadingDialog extends StatefulWidget { | ||||||
|  |   LoadingDialog({Key? key}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   _LoadingDialogState createState() { | ||||||
|  |     return _LoadingDialogState(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _LoadingDialogState extends State<LoadingDialog> { | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Dialog( | ||||||
|  |       insetPadding: const EdgeInsets.symmetric(horizontal: 60.0, vertical: 24.0), | ||||||
|  |       shape: RoundedRectangleBorder( | ||||||
|  |         borderRadius: BorderRadius.circular(16), | ||||||
|  |       ), | ||||||
|  |       elevation: 0, | ||||||
|  |       backgroundColor: whiteColor, | ||||||
|  |       child: Column( | ||||||
|  |         crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |         mainAxisSize: MainAxisSize.min, | ||||||
|  |         children: [ | ||||||
|  |           Center( | ||||||
|  |             child: Padding( | ||||||
|  |               padding: const EdgeInsets.all(24.0), | ||||||
|  |               child: Column( | ||||||
|  |                 children: [ | ||||||
|  |                   Lottie.asset('assets/json/loading_animation.json', repeat: true, reverse: false, frameRate: FrameRate(60)), | ||||||
|  |                   24.height, | ||||||
|  |                   LocaleKeys.loadingText.tr(context: context).toText16(color: blackColor) | ||||||
|  |                 ], | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |             // Image.asset( | ||||||
|  |             //   "assets/images/loading.gif", | ||||||
|  |             //   height: 96.0, | ||||||
|  |             //   width: 96.0, | ||||||
|  |             // ), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,377 @@ | |||||||
|  | import 'dart:async'; | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/animation.dart'; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/rendering.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | 
 | ||||||
|  | typedef OnDone = void Function(String text); | ||||||
|  | 
 | ||||||
|  | class ProvidedPinBoxTextAnimation { | ||||||
|  |   static AnimatedSwitcherTransitionBuilder scalingTransition = (child, animation) { | ||||||
|  |     return ScaleTransition( | ||||||
|  |       child: child, | ||||||
|  |       scale: animation, | ||||||
|  |     ); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   static AnimatedSwitcherTransitionBuilder defaultNoTransition = (Widget child, Animation<double> animation) { | ||||||
|  |     return child; | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class OTPWidget extends StatefulWidget { | ||||||
|  |   final int maxLength; | ||||||
|  |   late TextEditingController? controller; | ||||||
|  | 
 | ||||||
|  |   final Color defaultBorderColor; | ||||||
|  |   final Color pinBoxColor; | ||||||
|  |   final double pinBoxBorderWidth; | ||||||
|  |   final double pinBoxRadius; | ||||||
|  |   final bool hideDefaultKeyboard; | ||||||
|  | 
 | ||||||
|  |   final TextStyle? pinTextStyle; | ||||||
|  |   final double pinBoxHeight; | ||||||
|  |   final double pinBoxWidth; | ||||||
|  |   final OnDone? onDone; | ||||||
|  |   final bool hasError; | ||||||
|  |   final Color errorBorderColor; | ||||||
|  |   final Color textBorderColor; | ||||||
|  |   final Function(String)? onTextChanged; | ||||||
|  |   final bool autoFocus; | ||||||
|  |   final FocusNode? focusNode; | ||||||
|  |   final AnimatedSwitcherTransitionBuilder? pinTextAnimatedSwitcherTransition; | ||||||
|  |   final Duration pinTextAnimatedSwitcherDuration; | ||||||
|  |   final TextDirection textDirection; | ||||||
|  |   final TextInputType keyboardType; | ||||||
|  |   final EdgeInsets pinBoxOuterPadding; | ||||||
|  | 
 | ||||||
|  |   OTPWidget({ | ||||||
|  |     Key? key, | ||||||
|  |     this.maxLength = 4, | ||||||
|  |     this.controller, | ||||||
|  |     this.pinBoxWidth = 70.0, | ||||||
|  |     this.pinBoxHeight = 70.0, | ||||||
|  |     this.pinTextStyle, | ||||||
|  |     this.onDone, | ||||||
|  |     this.defaultBorderColor = Colors.black, | ||||||
|  |     this.textBorderColor = Colors.black, | ||||||
|  |     this.pinTextAnimatedSwitcherTransition, | ||||||
|  |     this.pinTextAnimatedSwitcherDuration = const Duration(), | ||||||
|  |     this.hasError = false, | ||||||
|  |     this.errorBorderColor = Colors.red, | ||||||
|  |     this.onTextChanged, | ||||||
|  |     this.autoFocus = false, | ||||||
|  |     this.focusNode, | ||||||
|  |     this.textDirection = TextDirection.ltr, | ||||||
|  |     this.keyboardType = TextInputType.number, | ||||||
|  |     this.pinBoxOuterPadding = const EdgeInsets.symmetric(horizontal: 4.0), | ||||||
|  |     this.pinBoxColor = Colors.white, | ||||||
|  |     this.pinBoxBorderWidth = 2.0, | ||||||
|  |     this.pinBoxRadius = 0, | ||||||
|  |     this.hideDefaultKeyboard = false, | ||||||
|  |   }) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<StatefulWidget> createState() { | ||||||
|  |     return OTPWidgetState(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixin { | ||||||
|  |  late  AnimationController _highlightAnimationController; | ||||||
|  |   late FocusNode focusNode; | ||||||
|  |   String text = ""; | ||||||
|  |   int currentIndex = 0; | ||||||
|  |   List<String> strList = []; | ||||||
|  |   bool hasFocus = false; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void didUpdateWidget(OTPWidget oldWidget) { | ||||||
|  |     super.didUpdateWidget(oldWidget); | ||||||
|  |     focusNode = widget.focusNode ?? focusNode; | ||||||
|  | 
 | ||||||
|  |     if (oldWidget.maxLength < widget.maxLength) { | ||||||
|  |       setState(() { | ||||||
|  |         currentIndex = text.length; | ||||||
|  |       }); | ||||||
|  |       widget.controller?.text = text; | ||||||
|  |       widget.controller?.selection = TextSelection.collapsed(offset: text.length); | ||||||
|  |     } else if (oldWidget.maxLength > widget.maxLength && widget.maxLength > 0 && text.length > 0 && text.length > widget.maxLength) { | ||||||
|  |       setState(() { | ||||||
|  |         text = text.substring(0, widget.maxLength); | ||||||
|  |         currentIndex = text.length; | ||||||
|  |       }); | ||||||
|  |       widget.controller?.text = text; | ||||||
|  |       widget.controller?.selection = TextSelection.collapsed(offset: text.length); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   _calculateStrList() { | ||||||
|  |     if (strList.length > widget.maxLength) { | ||||||
|  |       strList.length = widget.maxLength; | ||||||
|  |     } | ||||||
|  |     while (strList.length < widget.maxLength) { | ||||||
|  |       strList.add(""); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void initState() { | ||||||
|  |     super.initState(); | ||||||
|  |     focusNode = widget.focusNode ?? FocusNode(); | ||||||
|  |     _highlightAnimationController = AnimationController(vsync: this); | ||||||
|  |     _initTextController(); | ||||||
|  |     _calculateStrList(); | ||||||
|  |     widget.controller!.addListener(_controllerListener); | ||||||
|  |     focusNode.addListener(_focusListener); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _controllerListener() { | ||||||
|  |     if (mounted == true) { | ||||||
|  |       setState(() { | ||||||
|  |         _initTextController(); | ||||||
|  |       }); | ||||||
|  |       var onTextChanged = widget.onTextChanged; | ||||||
|  |       if (onTextChanged != null) { | ||||||
|  |         onTextChanged(widget.controller?.text ?? ""); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _focusListener() { | ||||||
|  |     if (mounted == true) { | ||||||
|  |       setState(() { | ||||||
|  |         hasFocus = focusNode?.hasFocus ?? false; | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _initTextController() { | ||||||
|  |     if (widget.controller == null) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     strList.clear(); | ||||||
|  |     var text = widget.controller?.text ?? ""; | ||||||
|  |     if (text.isNotEmpty) { | ||||||
|  |       if (text.length > widget.maxLength) { | ||||||
|  |         throw Exception("TextEditingController length exceeded maxLength!"); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     for (var i = 0; i < text.length; i++) { | ||||||
|  |       strList.add(text[i]); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   double get _width { | ||||||
|  |     var width = 0.0; | ||||||
|  |     for (var i = 0; i < widget.maxLength; i++) { | ||||||
|  |       width += widget.pinBoxWidth; | ||||||
|  |       if (i == 0) { | ||||||
|  |         width += widget.pinBoxOuterPadding.left; | ||||||
|  |       } else if (i + 1 == widget.maxLength) { | ||||||
|  |         width += widget.pinBoxOuterPadding.right; | ||||||
|  |       } else { | ||||||
|  |         width += widget.pinBoxOuterPadding.left; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return width; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     if (widget.focusNode == null) { | ||||||
|  |       focusNode.dispose(); | ||||||
|  |     } else { | ||||||
|  |       focusNode.removeListener(_focusListener); | ||||||
|  |     } | ||||||
|  |     _highlightAnimationController.dispose(); | ||||||
|  |     widget.controller?.removeListener(_controllerListener); | ||||||
|  | 
 | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Stack( | ||||||
|  |       children: <Widget>[ | ||||||
|  |         _otpTextInput(), | ||||||
|  |         _touchPinBoxRow(), | ||||||
|  |       ], | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget _touchPinBoxRow() { | ||||||
|  |     return widget.hideDefaultKeyboard | ||||||
|  |         ? _pinBoxRow(context) | ||||||
|  |         : GestureDetector( | ||||||
|  |             behavior: HitTestBehavior.opaque, | ||||||
|  |             onTap: () { | ||||||
|  |               if (hasFocus) { | ||||||
|  |                 FocusScope.of(context).requestFocus(FocusNode()); | ||||||
|  |                 Future.delayed(Duration(milliseconds: 100), () { | ||||||
|  |                   FocusScope.of(context).requestFocus(focusNode); | ||||||
|  |                 }); | ||||||
|  |               } else { | ||||||
|  |                 FocusScope.of(context).requestFocus(focusNode); | ||||||
|  |               } | ||||||
|  |             }, | ||||||
|  |             child: _pinBoxRow(context), | ||||||
|  |           ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget _otpTextInput() { | ||||||
|  |     var transparentBorder = OutlineInputBorder( | ||||||
|  |       borderSide: BorderSide( | ||||||
|  |         color: Colors.transparent, | ||||||
|  |         width: 0.0, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |     return Container( | ||||||
|  |       width: _width, | ||||||
|  |       height: widget.pinBoxHeight, | ||||||
|  |       child: TextField( | ||||||
|  |         autofocus: !kIsWeb ? widget.autoFocus : false, | ||||||
|  |         enableInteractiveSelection: false, | ||||||
|  |         focusNode: focusNode, | ||||||
|  |         controller: widget.controller, | ||||||
|  |         keyboardType: widget.keyboardType, | ||||||
|  |         inputFormatters: widget.keyboardType == TextInputType.number ? <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly] : null, | ||||||
|  |         style: TextStyle( | ||||||
|  |           height: 0.1, | ||||||
|  |           color: Colors.transparent, | ||||||
|  |         ), | ||||||
|  |         decoration: InputDecoration( | ||||||
|  |           contentPadding: EdgeInsets.all(0), | ||||||
|  |           focusedErrorBorder: transparentBorder, | ||||||
|  |           errorBorder: transparentBorder, | ||||||
|  |           disabledBorder: transparentBorder, | ||||||
|  |           enabledBorder: transparentBorder, | ||||||
|  |           focusedBorder: transparentBorder, | ||||||
|  |           counterText: null, | ||||||
|  |           counterStyle: null, | ||||||
|  |           helperStyle: TextStyle( | ||||||
|  |             height: 0.0, | ||||||
|  |             color: Colors.transparent, | ||||||
|  |           ), | ||||||
|  |           labelStyle: TextStyle(height: 0.1), | ||||||
|  |           fillColor: Colors.transparent, | ||||||
|  |           border: InputBorder.none, | ||||||
|  |         ), | ||||||
|  |         cursorColor: Colors.transparent, | ||||||
|  |         showCursor: false, | ||||||
|  |         maxLength: widget.maxLength, | ||||||
|  |         onChanged: _onTextChanged, | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _onTextChanged(text) { | ||||||
|  |     var onTextChanged = widget.onTextChanged; | ||||||
|  |     if (onTextChanged != null) { | ||||||
|  |       onTextChanged(text); | ||||||
|  |     } | ||||||
|  |     setState(() { | ||||||
|  |       this.text = text; | ||||||
|  |       if (text.length >= currentIndex) { | ||||||
|  |         for (int i = currentIndex; i < text.length; i++) { | ||||||
|  |           strList[i] = text[i]; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       currentIndex = text.length; | ||||||
|  |     }); | ||||||
|  |     if (text.length == widget.maxLength) { | ||||||
|  |       FocusScope.of(context).requestFocus(FocusNode()); | ||||||
|  |       var onDone = widget.onDone; | ||||||
|  |       if (onDone != null) { | ||||||
|  |         onDone(text); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget _pinBoxRow(BuildContext context) { | ||||||
|  |     _calculateStrList(); | ||||||
|  |     List<Widget> pinCodes = List.generate(widget.maxLength, (int i) { | ||||||
|  |       return _buildPinCode(i, context); | ||||||
|  |     }); | ||||||
|  |     return Row( | ||||||
|  |       children: pinCodes, | ||||||
|  |       mainAxisSize: MainAxisSize.min, | ||||||
|  |       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget _buildPinCode(int i, BuildContext context) { | ||||||
|  |     Color borderColor; | ||||||
|  |     Color pinBoxColor = widget.pinBoxColor; | ||||||
|  | 
 | ||||||
|  |     if (widget.hasError) { | ||||||
|  |       borderColor = widget.errorBorderColor; | ||||||
|  |     } else if (i < text.length) { | ||||||
|  |       borderColor = widget.textBorderColor; | ||||||
|  |     } else { | ||||||
|  |       borderColor = widget.defaultBorderColor; | ||||||
|  |       pinBoxColor = widget.pinBoxColor; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     EdgeInsets insets; | ||||||
|  |     if (i == 0) { | ||||||
|  |       insets = EdgeInsets.only( | ||||||
|  |         left: 0, | ||||||
|  |         top: widget.pinBoxOuterPadding.top, | ||||||
|  |         right: widget.pinBoxOuterPadding.right, | ||||||
|  |         bottom: widget.pinBoxOuterPadding.bottom, | ||||||
|  |       ); | ||||||
|  |     } else if (i == strList.length - 1) { | ||||||
|  |       insets = EdgeInsets.only( | ||||||
|  |         left: widget.pinBoxOuterPadding.left, | ||||||
|  |         top: widget.pinBoxOuterPadding.top, | ||||||
|  |         right: 0, | ||||||
|  |         bottom: widget.pinBoxOuterPadding.bottom, | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       insets = widget.pinBoxOuterPadding; | ||||||
|  |     } | ||||||
|  |     return Container( | ||||||
|  |       key: ValueKey<String>("container$i"), | ||||||
|  |       alignment: Alignment.center, | ||||||
|  |       padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 1.0), | ||||||
|  |       margin: insets, | ||||||
|  |       child: _animatedTextBox(strList[i], i), | ||||||
|  |       decoration: BoxDecoration( | ||||||
|  |         border: Border.all( | ||||||
|  |           color: borderColor, | ||||||
|  |           width: widget.pinBoxBorderWidth, | ||||||
|  |         ), | ||||||
|  |         color: pinBoxColor, | ||||||
|  |         borderRadius: BorderRadius.circular(widget.pinBoxRadius), | ||||||
|  |       ), | ||||||
|  |       width: widget.pinBoxWidth, | ||||||
|  |       height: widget.pinBoxHeight, | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Widget _animatedTextBox(String text, int i) { | ||||||
|  |     if (widget.pinTextAnimatedSwitcherTransition != null) { | ||||||
|  |       return AnimatedSwitcher( | ||||||
|  |         duration: widget.pinTextAnimatedSwitcherDuration, | ||||||
|  |         transitionBuilder: widget.pinTextAnimatedSwitcherTransition ?? | ||||||
|  |             (Widget child, Animation<double> animation) { | ||||||
|  |               return child; | ||||||
|  |             }, | ||||||
|  |         child: Text( | ||||||
|  |           text, | ||||||
|  |           key: ValueKey<String>("$text$i"), | ||||||
|  |           style: widget.pinTextStyle, | ||||||
|  |         ), | ||||||
|  |       ); | ||||||
|  |     } else { | ||||||
|  |       return Text( | ||||||
|  |         text, | ||||||
|  |         key: ValueKey<String>("${strList[i]}$i"), | ||||||
|  |         style: widget.pinTextStyle, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,115 @@ | |||||||
|  | import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | 
 | ||||||
|  | class PhoneNumberInput extends StatefulWidget { | ||||||
|  |   final void Function(String fullNumber)? onChanged; | ||||||
|  |   final TextEditingController textController; | ||||||
|  | 
 | ||||||
|  |   const PhoneNumberInput({Key? key, this.onChanged, required this.textController}) : super(key: key); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   State<PhoneNumberInput> createState() => _PhoneNumberInputState(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class _PhoneNumberInputState extends State<PhoneNumberInput> { | ||||||
|  |   String _selectedCountryCode = '+966'; | ||||||
|  |   final List<String> _countryCodes = ['+966', '+971', '+1', '+44', '+91']; | ||||||
|  | 
 | ||||||
|  |   void _onCountryCodeChanged(String? code) { | ||||||
|  |     if (code != null) { | ||||||
|  |       setState(() { | ||||||
|  |         _selectedCountryCode = code; | ||||||
|  |       }); | ||||||
|  |       _notifyChanged(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _onPhoneChanged(String value) { | ||||||
|  |     _notifyChanged(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void _notifyChanged() { | ||||||
|  |     if (widget.onChanged != null) { | ||||||
|  |       widget.onChanged!( | ||||||
|  |         '$_selectedCountryCode${widget.textController.text}', | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void dispose() { | ||||||
|  |     widget.textController.dispose(); | ||||||
|  |     super.dispose(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Container( | ||||||
|  |       padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), | ||||||
|  |       decoration: BoxDecoration( | ||||||
|  |         color: const Color(0xFFF4F5F7), | ||||||
|  |         borderRadius: BorderRadius.circular(16), | ||||||
|  |       ), | ||||||
|  |       child: Row( | ||||||
|  |         children: [ | ||||||
|  |           // Country Code Dropdown | ||||||
|  |           DropdownButtonHideUnderline( | ||||||
|  |             child: DropdownButton<String>( | ||||||
|  |               value: _selectedCountryCode, | ||||||
|  |               items: _countryCodes | ||||||
|  |                   .map( | ||||||
|  |                     (code) => DropdownMenuItem( | ||||||
|  |                       value: code, | ||||||
|  |                       child: code.toText16(color: mainPurple, isBold: true), | ||||||
|  |                     ), | ||||||
|  |                   ) | ||||||
|  |                   .toList(), | ||||||
|  |               onChanged: _onCountryCodeChanged, | ||||||
|  |               icon: const Icon( | ||||||
|  |                 Icons.keyboard_arrow_down_rounded, | ||||||
|  |                 color: mainPurple, | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |           const SizedBox(width: 8), | ||||||
|  |           // Phone number input | ||||||
|  |           Expanded( | ||||||
|  |             child: TextField( | ||||||
|  |               onTapOutside: (event) { | ||||||
|  |                 FocusScope.of(context).unfocus(); | ||||||
|  |               }, | ||||||
|  |               controller: widget.textController, | ||||||
|  |               keyboardType: TextInputType.number, | ||||||
|  |               textInputAction: TextInputAction.done, | ||||||
|  |               maxLength: 10, | ||||||
|  |               inputFormatters: <TextInputFormatter>[ | ||||||
|  |                 FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), | ||||||
|  |               ], | ||||||
|  |               decoration: const InputDecoration( | ||||||
|  |                 counterStyle: TextStyle( | ||||||
|  |                   height: double.minPositive, | ||||||
|  |                 ), | ||||||
|  |                 counterText: "", | ||||||
|  |                 hintText: '5xxxxxxxx', | ||||||
|  |                 border: InputBorder.none, | ||||||
|  |                 hintStyle: TextStyle( | ||||||
|  |                   color: Colors.black38, | ||||||
|  |                   letterSpacing: 2, | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |               style: const TextStyle( | ||||||
|  |                 fontSize: 18, | ||||||
|  |                 fontWeight: FontWeight.bold, | ||||||
|  |                 letterSpacing: 2, | ||||||
|  |                 color: Colors.black, | ||||||
|  |               ), | ||||||
|  |               onChanged: _onPhoneChanged, | ||||||
|  |             ), | ||||||
|  |           ), | ||||||
|  |         ], | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,256 @@ | |||||||
|  | // import 'package:easy_localization/src/public_ext.dart'; | ||||||
|  | // import 'package:flutter/material.dart'; | ||||||
|  | // import 'package:flutter_svg/svg.dart'; | ||||||
|  | // import 'package:MyCity/extensions/int_extensions.dart'; | ||||||
|  | // import 'package:MyCity/extensions/string_extensions.dart'; | ||||||
|  | // import 'package:MyCity/extensions/widget_extensions.dart'; | ||||||
|  | // | ||||||
|  | // import 'package:shimmer/shimmer.dart'; | ||||||
|  | // | ||||||
|  | // class GetAttendanceTrackingShimmer extends StatelessWidget { | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     return Container( | ||||||
|  | //       width: double.infinity, | ||||||
|  | //       height: double.infinity, | ||||||
|  | //       clipBehavior: Clip.antiAlias, | ||||||
|  | //       decoration: BoxDecoration( | ||||||
|  | //         color: Colors.white, | ||||||
|  | //         borderRadius: BorderRadius.circular(15), | ||||||
|  | //         boxShadow: [ | ||||||
|  | //           BoxShadow( | ||||||
|  | //             color: const Color(0xff000000).withOpacity(.05), | ||||||
|  | //             blurRadius: 26, | ||||||
|  | //             offset: const Offset(0, -3), | ||||||
|  | //           ), | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //       child: Stack( | ||||||
|  | //         alignment: Alignment.center, | ||||||
|  | //         children: [ | ||||||
|  | //           // SvgPicture.asset("assets/images/"), | ||||||
|  | //           Column( | ||||||
|  | //             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //             children: [ | ||||||
|  | //               Expanded( | ||||||
|  | //                 child: Column( | ||||||
|  | //                   mainAxisSize: MainAxisSize.min, | ||||||
|  | //                   crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //                   children: [ | ||||||
|  | //                     LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true).toShimmer(), | ||||||
|  | //                     16.height, | ||||||
|  | //                     "07:55:12".toText10(color: Colors.white, isBold: true).toShimmer(), | ||||||
|  | //                     3.height, | ||||||
|  | //                     LocaleKeys.timeLeftToday.tr().toText10(color: Colors.white).toShimmer(), | ||||||
|  | //                     9.height, | ||||||
|  | //                     const ClipRRect( | ||||||
|  | //                       borderRadius: BorderRadius.all( | ||||||
|  | //                         Radius.circular(20), | ||||||
|  | //                       ), | ||||||
|  | //                       child: LinearProgressIndicator( | ||||||
|  | //                         value: 0.7, | ||||||
|  | //                         minHeight: 8, | ||||||
|  | //                         valueColor: const AlwaysStoppedAnimation<Color>(Colors.white), | ||||||
|  | //                         backgroundColor: const Color(0xff196D73), | ||||||
|  | //                       ), | ||||||
|  | //                     ).toShimmer(), | ||||||
|  | //                   ], | ||||||
|  | //                 ).paddingOnly(top: 12, right: 15, left: 12), | ||||||
|  | //               ), | ||||||
|  | //               Row( | ||||||
|  | //                 children: [ | ||||||
|  | //                   Expanded( | ||||||
|  | //                     child: Column( | ||||||
|  | //                       mainAxisSize: MainAxisSize.min, | ||||||
|  | //                       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //                       children: [ | ||||||
|  | //                         LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(), | ||||||
|  | //                       ], | ||||||
|  | //                     ).paddingOnly(left: 12), | ||||||
|  | //                   ), | ||||||
|  | //                   Container( | ||||||
|  | //                     width: 45, | ||||||
|  | //                     height: 45, | ||||||
|  | //                     // color: Colors.blue, | ||||||
|  | //                     padding: const EdgeInsets.only(left: 14, right: 14), | ||||||
|  | //                   ).toShimmer(), | ||||||
|  | //                 ], | ||||||
|  | //               ), | ||||||
|  | //             ], | ||||||
|  | //           ), | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //     ); | ||||||
|  | //   } | ||||||
|  | // } | ||||||
|  | // | ||||||
|  | // class MenuShimmer extends StatelessWidget { | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     return Container( | ||||||
|  | //       clipBehavior: Clip.antiAlias, | ||||||
|  | //       decoration: BoxDecoration( | ||||||
|  | //         color: Colors.white, | ||||||
|  | //         borderRadius: BorderRadius.circular(15), | ||||||
|  | //         boxShadow: [ | ||||||
|  | //           BoxShadow( | ||||||
|  | //             color: const Color(0xff000000).withOpacity(.05), | ||||||
|  | //             blurRadius: 26, | ||||||
|  | //             offset: const Offset(0, -3), | ||||||
|  | //           ), | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //       child: Column( | ||||||
|  | //         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  | //         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //         children: [ | ||||||
|  | //           LocaleKeys.workList.tr().toText12(color: Colors.white).toShimmer(), | ||||||
|  | //           Row( | ||||||
|  | //             children: [ | ||||||
|  | //               Expanded( | ||||||
|  | //                 flex: 3, | ||||||
|  | //                 child: 123.toString().toText10(color: Colors.white, isBold: true).toShimmer(), | ||||||
|  | //               ), | ||||||
|  | //               12.width, | ||||||
|  | //               SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white).toShimmer() | ||||||
|  | //             ], | ||||||
|  | //           ) | ||||||
|  | //         ], | ||||||
|  | //       ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), | ||||||
|  | //     ); | ||||||
|  | //   } | ||||||
|  | // } | ||||||
|  | // | ||||||
|  | // class ServicesHeaderShimmer extends StatelessWidget { | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     return Row( | ||||||
|  | //       crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  | //       children: [ | ||||||
|  | //         Expanded( | ||||||
|  | //           child: Column( | ||||||
|  | //             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //             mainAxisSize: MainAxisSize.min, | ||||||
|  | //             children: [ | ||||||
|  | //               "Other".tr().toText10().toShimmer(), | ||||||
|  | //               6.height, | ||||||
|  | //               LocaleKeys.services.tr().toText12(isBold: true).toShimmer(), | ||||||
|  | //             ], | ||||||
|  | //           ), | ||||||
|  | //         ), | ||||||
|  | //         LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true).toShimmer(), | ||||||
|  | //       ], | ||||||
|  | //     ); | ||||||
|  | //   } | ||||||
|  | // } | ||||||
|  | // | ||||||
|  | // class ServicesMenuShimmer extends StatelessWidget { | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     return Container( | ||||||
|  | //       decoration: BoxDecoration( | ||||||
|  | //         color: Colors.white, | ||||||
|  | //         borderRadius: BorderRadius.circular(15), | ||||||
|  | //         boxShadow: [ | ||||||
|  | //           BoxShadow( | ||||||
|  | //             color: const Color(0xff000000).withOpacity(.05), | ||||||
|  | //             blurRadius: 26, | ||||||
|  | //             offset: const Offset(0, -3), | ||||||
|  | //           ), | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //       child: Column( | ||||||
|  | //         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  | //         crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //         children: [ | ||||||
|  | //           SvgPicture.asset("assets/images/monthly_attendance.svg").toShimmer(), | ||||||
|  | //           Column( | ||||||
|  | //             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //             mainAxisAlignment: MainAxisAlignment.start, | ||||||
|  | //             children: [ | ||||||
|  | //               "Attendan".toText11(isBold: false).toShimmer(), | ||||||
|  | //               5.height, | ||||||
|  | //               Row( | ||||||
|  | //                 crossAxisAlignment: CrossAxisAlignment.end, | ||||||
|  | //                 children: [ | ||||||
|  | //                   Expanded( | ||||||
|  | //                     child: LocaleKeys.attendance.tr().toText11(isBold: false).toShimmer(), | ||||||
|  | //                   ), | ||||||
|  | //                   6.width, | ||||||
|  | //                   SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer() | ||||||
|  | //                 ], | ||||||
|  | //               ), | ||||||
|  | //             ], | ||||||
|  | //           ) | ||||||
|  | //         ], | ||||||
|  | //       ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), | ||||||
|  | //     ); | ||||||
|  | //   } | ||||||
|  | // } | ||||||
|  | // | ||||||
|  | // class ChatHomeShimmer extends StatelessWidget { | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     return Container( | ||||||
|  | //         width: double.infinity, | ||||||
|  | //         padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), | ||||||
|  | //         child: Column( | ||||||
|  | //           mainAxisSize: MainAxisSize.max, | ||||||
|  | //           children: <Widget>[ | ||||||
|  | //             Expanded( | ||||||
|  | //               child: Shimmer.fromColors( | ||||||
|  | //                 baseColor: Colors.white, | ||||||
|  | //                 highlightColor: Colors.grey.shade100, | ||||||
|  | //                 child: ListView.builder( | ||||||
|  | //                   itemBuilder: (_, __) => Padding( | ||||||
|  | //                     padding: const EdgeInsets.only(bottom: 8.0), | ||||||
|  | //                     child: Row( | ||||||
|  | //                       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //                       children: <Widget>[ | ||||||
|  | //                         Container( | ||||||
|  | //                           width: 48.0, | ||||||
|  | //                           height: 48.0, | ||||||
|  | //                           decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(40))), | ||||||
|  | //                         ), | ||||||
|  | //                         const Padding( | ||||||
|  | //                           padding: EdgeInsets.symmetric(horizontal: 8.0), | ||||||
|  | //                         ), | ||||||
|  | //                         Expanded( | ||||||
|  | //                           child: Column( | ||||||
|  | //                             crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //                             children: <Widget>[ | ||||||
|  | //                               Container( | ||||||
|  | //                                 width: double.infinity, | ||||||
|  | //                                 height: 8.0, | ||||||
|  | //                                 color: Colors.white, | ||||||
|  | //                               ), | ||||||
|  | //                               const Padding( | ||||||
|  | //                                 padding: EdgeInsets.symmetric(vertical: 2.0), | ||||||
|  | //                               ), | ||||||
|  | //                               Container( | ||||||
|  | //                                 width: double.infinity, | ||||||
|  | //                                 height: 8.0, | ||||||
|  | //                                 color: Colors.white, | ||||||
|  | //                               ), | ||||||
|  | //                               const Padding( | ||||||
|  | //                                 padding: EdgeInsets.symmetric(vertical: 2.0), | ||||||
|  | //                               ), | ||||||
|  | //                               Container( | ||||||
|  | //                                 width: 40.0, | ||||||
|  | //                                 height: 8.0, | ||||||
|  | //                                 color: Colors.white, | ||||||
|  | //                               ), | ||||||
|  | //                             ], | ||||||
|  | //                           ), | ||||||
|  | //                         ) | ||||||
|  | //                       ], | ||||||
|  | //                     ), | ||||||
|  | //                   ), | ||||||
|  | //                   itemCount: 6, | ||||||
|  | //                 ), | ||||||
|  | //               ), | ||||||
|  | //             ), | ||||||
|  | //           ], | ||||||
|  | //         )); | ||||||
|  | //   } | ||||||
|  | // } | ||||||
| @ -0,0 +1,43 @@ | |||||||
|  | import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; | ||||||
|  | import 'package:hmg_patient_app_new/theme/colors.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | class MoviesShimmerWidget extends StatelessWidget { | ||||||
|  |   const MoviesShimmerWidget({super.key}); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return SizedBox( | ||||||
|  |       child: Container( | ||||||
|  |           decoration: BoxDecoration( | ||||||
|  |             borderRadius: const BorderRadius.all( | ||||||
|  |               Radius.circular(10), | ||||||
|  |             ), | ||||||
|  |             border: Border.all(color: lightGreyEFColor, width: 1), | ||||||
|  |             boxShadow: [ | ||||||
|  |               BoxShadow( | ||||||
|  |                 color: const Color(0xff000000).withOpacity(.05), | ||||||
|  |                 blurRadius: 26, | ||||||
|  |                 offset: const Offset(0, -3), | ||||||
|  |               ), | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|  |           child: Padding( | ||||||
|  |             padding: const EdgeInsets.all(12.0), | ||||||
|  |             child: Column( | ||||||
|  |               children: [ | ||||||
|  |                 Container(height: 100).toShimmer(), | ||||||
|  |                 16.height, | ||||||
|  |                 Container(height: 24).toShimmer(), | ||||||
|  |                 16.height, | ||||||
|  |                 Container(height: 48).toShimmer(), | ||||||
|  |                 16.height, | ||||||
|  |                 Container(height: 24).toShimmer(), | ||||||
|  |                 8.height | ||||||
|  |               ], | ||||||
|  |             ), | ||||||
|  |           )), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | 
 | ||||||
|  | /// FadePage animation | ||||||
|  | /// [page] | ||||||
|  | class FadePage extends PageRouteBuilder { | ||||||
|  |   final Widget? page; | ||||||
|  | 
 | ||||||
|  |   FadePage({this.page}) | ||||||
|  |       : super( | ||||||
|  |           opaque: false, | ||||||
|  |           fullscreenDialog: true, | ||||||
|  |           barrierDismissible: true, | ||||||
|  |           barrierColor: Colors.black.withOpacity(0.8), | ||||||
|  |           pageBuilder: ( | ||||||
|  |             BuildContext context, | ||||||
|  |             Animation<double> animation, | ||||||
|  |             Animation<double> secondaryAnimation, | ||||||
|  |           ) => | ||||||
|  |               page!, | ||||||
|  |           transitionDuration: const Duration(milliseconds: 600), | ||||||
|  |           transitionsBuilder: ( | ||||||
|  |             BuildContext context, | ||||||
|  |             Animation<double> animation, | ||||||
|  |             Animation<double> secondaryAnimation, | ||||||
|  |             Widget child, | ||||||
|  |           ) { | ||||||
|  |             return FadeTransition(opacity: animation, child: child); | ||||||
|  |           }, | ||||||
|  |         ); | ||||||
|  | } | ||||||
| @ -0,0 +1,146 @@ | |||||||
|  | // import 'package:diplomaticquarterapp/core/model/vital_sign/vital_sign_res_model.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/utils.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/utils_new.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/widgets/charts/app_time_series_chart.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/widgets/charts/show_chart.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/widgets/charts/sync_fu_chart.dart'; | ||||||
|  | // import 'package:flutter/material.dart'; | ||||||
|  | // import 'package:provider/provider.dart'; | ||||||
|  | // | ||||||
|  | // import 'LineChartCurved.dart'; | ||||||
|  | // | ||||||
|  | // class VitalSingChartAndDetials extends StatefulWidget { | ||||||
|  | //   VitalSingChartAndDetials({ | ||||||
|  | //     Key? key, | ||||||
|  | //     required this.vitalList, | ||||||
|  | //     required this.name, | ||||||
|  | //     required this.viewKey, | ||||||
|  | //     required this.title1, | ||||||
|  | //     required this.title2, | ||||||
|  | //   }) : super(key: key); | ||||||
|  | // | ||||||
|  | //   final List<VitalSignResModel> vitalList; | ||||||
|  | //   final String name; | ||||||
|  | //   final String viewKey; | ||||||
|  | //   final String title1; | ||||||
|  | //   final String title2; | ||||||
|  | // | ||||||
|  | //   @override | ||||||
|  | //   _VitalSingChartAndDetialsState createState() => _VitalSingChartAndDetialsState(); | ||||||
|  | // } | ||||||
|  | // | ||||||
|  | // class _VitalSingChartAndDetialsState extends State<VitalSingChartAndDetials> { | ||||||
|  | //   List<TimeSeriesSales2> timeSeriesData = []; | ||||||
|  | //   bool isExpended = true; | ||||||
|  | //   late ProjectViewModel projectViewModel; | ||||||
|  | // | ||||||
|  | //   @override | ||||||
|  | //   void initState() { | ||||||
|  | //     // TODO: implement initState | ||||||
|  | //     super.initState(); | ||||||
|  | //     generateData(); | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     projectViewModel = Provider.of(context); | ||||||
|  | //     return SingleChildScrollView( | ||||||
|  | //       child: Column( | ||||||
|  | //         children: <Widget>[ | ||||||
|  | //           Container( | ||||||
|  | //             height: 400, | ||||||
|  | //             child: Container( | ||||||
|  | //               decoration: cardRadius(12), | ||||||
|  | //               margin: EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 8), | ||||||
|  | //               child: ShowChart( | ||||||
|  | //                 title: widget.name, | ||||||
|  | //                 timeSeries: timeSeriesData, | ||||||
|  | //                 indexes: timeSeriesData.length ~/ 5.5, | ||||||
|  | //                 horizontalInterval: 8, | ||||||
|  | //               ), | ||||||
|  | //               // child: SyncFuChart(), | ||||||
|  | //             ), | ||||||
|  | //           ), | ||||||
|  | //           Container( | ||||||
|  | //             decoration: cardRadius(12), | ||||||
|  | //             margin: EdgeInsets.only(left: 16, top: 8, right: 16, bottom: 16), | ||||||
|  | //             child: Padding( | ||||||
|  | //               padding: const EdgeInsets.all(12.0), | ||||||
|  | //               child: Column( | ||||||
|  | //                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //                 children: [ | ||||||
|  | //                   Text( | ||||||
|  | //                     TranslationBase.of(context).graphDetails, | ||||||
|  | //                     style: TextStyle( | ||||||
|  | //                       fontSize: 16, | ||||||
|  | //                       letterSpacing: -0.64, | ||||||
|  | //                       fontWeight: FontWeight.w600, | ||||||
|  | //                     ), | ||||||
|  | //                   ), | ||||||
|  | //                   Table( | ||||||
|  | //                     columnWidths: { | ||||||
|  | //                       0: FlexColumnWidth(2), | ||||||
|  | //                       1: FlexColumnWidth(1), | ||||||
|  | //                       // 2: FlexColumnWidth(1), | ||||||
|  | //                     }, | ||||||
|  | //                     children: fullData(widget.vitalList, context), | ||||||
|  | //                   ), | ||||||
|  | //                 ], | ||||||
|  | //               ), | ||||||
|  | //             ), | ||||||
|  | //           ) | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //     ); | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | //   List<TableRow> fullData(List<VitalSignResModel> labResultList, context) { | ||||||
|  | //     List<TableRow> tableRow = []; | ||||||
|  | //     tableRow.add( | ||||||
|  | //       TableRow( | ||||||
|  | //         children: [ | ||||||
|  | //           Utils.tableColumnTitle(widget.title1), | ||||||
|  | //           Utils.tableColumnTitle(widget.title2), | ||||||
|  | //           // Utils.tableColumnTitle(TranslationBase.of(context).range), | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //     ); | ||||||
|  | // | ||||||
|  | //     for (int i = 0; i < labResultList.length; i++) { | ||||||
|  | //       var data = labResultList[i].toJson()[widget.viewKey]; | ||||||
|  | //       if (data != 0) | ||||||
|  | //         tableRow.add( | ||||||
|  | //           TableRow( | ||||||
|  | //             children: [ | ||||||
|  | //               Utils.tableColumnValue( | ||||||
|  | //                   "${projectViewModel.isArabic ? DateUtil.getWeekDayArabic(labResultList[i].vitalSignDate!.weekday) : DateUtil.getWeekDay(labResultList[i].vitalSignDate!.weekday)}, ${labResultList[i].vitalSignDate!.day} ${projectViewModel.isArabic ? DateUtil.getMonthArabic(labResultList[i].vitalSignDate!.month) : DateUtil.getMonth(labResultList[i].vitalSignDate!.month)}, ${labResultList[i].vitalSignDate!.year}", | ||||||
|  | //                   isLast: i == (labResultList.length - 1), mProjectViewModel: projectViewModel), | ||||||
|  | //               Utils.tableColumnValue('${labResultList[i].toJson()[widget.viewKey]}', isLast: i == (labResultList.length - 1), mProjectViewModel: projectViewModel), | ||||||
|  | //             ], | ||||||
|  | //           ), | ||||||
|  | //         ); | ||||||
|  | //     } | ||||||
|  | // | ||||||
|  | //     return tableRow; | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | //   generateData() { | ||||||
|  | //     if (widget.vitalList.length > 0) { | ||||||
|  | //       widget.vitalList.reversed.toList().forEach( | ||||||
|  | //         (element) { | ||||||
|  | //           if (element.toJson()[widget.viewKey] != null && element.toJson()[widget.viewKey]?.toInt() != 0) | ||||||
|  | //             timeSeriesData.add( | ||||||
|  | //               TimeSeriesSales2( | ||||||
|  | //                 DateTime(element.vitalSignDate!.year, element.vitalSignDate!.month, element.vitalSignDate!.day), | ||||||
|  | //                 element.toJson()[widget.viewKey].toDouble(), | ||||||
|  | //               ), | ||||||
|  | //             ); | ||||||
|  | //         }, | ||||||
|  | //       ); | ||||||
|  | //     } | ||||||
|  | //     return timeSeriesData.reversed.toList(); | ||||||
|  | //   } | ||||||
|  | // } | ||||||
| @ -0,0 +1,149 @@ | |||||||
|  | // import 'package:diplomaticquarterapp/core/model/vital_sign/vital_sign_res_model.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/utils.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/uitl/utils_new.dart'; | ||||||
|  | // import 'package:diplomaticquarterapp/widgets/charts/app_time_series_chart.dart'; | ||||||
|  | // import 'package:flutter/material.dart'; | ||||||
|  | // import 'package:provider/provider.dart'; | ||||||
|  | // | ||||||
|  | // import 'LineChartCurvedBloodPressure.dart'; | ||||||
|  | // | ||||||
|  | // class VitalSingChartBloodPressure extends StatelessWidget { | ||||||
|  | //   VitalSingChartBloodPressure({ | ||||||
|  | //     Key? key, | ||||||
|  | //     required this.vitalList, | ||||||
|  | //     required this.name, | ||||||
|  | //     required this.viewKey1, | ||||||
|  | //     required this.viewKey2, | ||||||
|  | //     required this.title1, | ||||||
|  | //     required this.title2, | ||||||
|  | //     required this.title3, | ||||||
|  | //   }) : super(key: key); | ||||||
|  | // | ||||||
|  | //   final List<VitalSignResModel> vitalList; | ||||||
|  | //   final String name; | ||||||
|  | //   final String viewKey1; | ||||||
|  | //   final String viewKey2; | ||||||
|  | //   final String title1; | ||||||
|  | //   final String title2; | ||||||
|  | //   final String title3; | ||||||
|  | //   List<TimeSeriesSales2> timeSeriesData1 = []; | ||||||
|  | //   List<TimeSeriesSales2> timeSeriesData2 = []; | ||||||
|  | //   late ProjectViewModel projectViewModel; | ||||||
|  | // | ||||||
|  | //   @override | ||||||
|  | //   Widget build(BuildContext context) { | ||||||
|  | //     projectViewModel = Provider.of(context); | ||||||
|  | //     generateData(); | ||||||
|  | //     return SingleChildScrollView( | ||||||
|  | //       child: Column( | ||||||
|  | //         children: <Widget>[ | ||||||
|  | //           Container( | ||||||
|  | //             decoration: cardRadius(12), | ||||||
|  | //             margin: EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 8), | ||||||
|  | //             child: LineChartCurvedBloodPressure( | ||||||
|  | //               title: name, | ||||||
|  | //               timeSeries1: timeSeriesData1, | ||||||
|  | //               timeSeries2: timeSeriesData2, | ||||||
|  | //               indexes: timeSeriesData1.length ~/ 5.5, | ||||||
|  | //             ), | ||||||
|  | //           ), | ||||||
|  | // | ||||||
|  | //           // VitalSignBloodPressureWidget( | ||||||
|  | //           //   vitalList: vitalList, | ||||||
|  | //           //   title1: title1, | ||||||
|  | //           //   title2: title2, | ||||||
|  | //           //   title3: title3, | ||||||
|  | //           //   viewKey1: viewKey1, | ||||||
|  | //           //   viewKey2: viewKey2, | ||||||
|  | //           // ), | ||||||
|  | //           Container( | ||||||
|  | //             decoration: cardRadius(12), | ||||||
|  | //             margin: EdgeInsets.only(left: 16, top: 8, right: 16, bottom: 16), | ||||||
|  | //             child: Padding( | ||||||
|  | //               padding: const EdgeInsets.all(12.0), | ||||||
|  | //               child: Column( | ||||||
|  | //                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  | //                 children: [ | ||||||
|  | //                   Text( | ||||||
|  | //                     TranslationBase.of(context).graphDetails, | ||||||
|  | //                     style: TextStyle( | ||||||
|  | //                       fontSize: 16, | ||||||
|  | //                       letterSpacing: -0.64, | ||||||
|  | //                       fontWeight: FontWeight.w600, | ||||||
|  | //                     ), | ||||||
|  | //                   ), | ||||||
|  | //                   Table( | ||||||
|  | //                     columnWidths: { | ||||||
|  | //                       0: FlexColumnWidth(2), | ||||||
|  | //                       1: FlexColumnWidth(1), | ||||||
|  | //                       // 2: FlexColumnWidth(1), | ||||||
|  | //                     }, | ||||||
|  | //                     children: fullData(vitalList, context), | ||||||
|  | //                   ), | ||||||
|  | //                 ], | ||||||
|  | //               ), | ||||||
|  | //             ), | ||||||
|  | //           ) | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //     ); | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | //   List<TableRow> fullData(List<VitalSignResModel> labResultList, context) { | ||||||
|  | //     List<TableRow> tableRow = []; | ||||||
|  | //     tableRow.add( | ||||||
|  | //       TableRow( | ||||||
|  | //         children: [ | ||||||
|  | //           Utils.tableColumnTitle(title1), | ||||||
|  | //           Utils.tableColumnTitle(title2), | ||||||
|  | //           Utils.tableColumnTitle(title3), | ||||||
|  | //           // Utils.tableColumnTitle(TranslationBase.of(context).range), | ||||||
|  | //         ], | ||||||
|  | //       ), | ||||||
|  | //     ); | ||||||
|  | // | ||||||
|  | //     for (int i = 0; i < labResultList.length; i++) { | ||||||
|  | //       var data = labResultList[i].toJson()[viewKey1]; | ||||||
|  | //       if (data != 0) | ||||||
|  | //         tableRow.add( | ||||||
|  | //           TableRow( | ||||||
|  | //             children: [ | ||||||
|  | //               Utils.tableColumnValue( | ||||||
|  | //                   "${projectViewModel.isArabic ? DateUtil.getWeekDayArabic(labResultList[i].vitalSignDate!.weekday) : DateUtil.getWeekDay(labResultList[i].vitalSignDate!.weekday)}, ${labResultList[i].vitalSignDate!.day} ${projectViewModel.isArabic ? DateUtil.getMonthArabic(labResultList[i].vitalSignDate!.month) : DateUtil.getMonth(labResultList[i].vitalSignDate!.month)}, ${labResultList[i].vitalSignDate!.year}", | ||||||
|  | //                   isLast: i == (labResultList.length - 1), mProjectViewModel: projectViewModel), | ||||||
|  | //               Utils.tableColumnValue('${labResultList[i].toJson()[viewKey1]}', isLast: i == (labResultList.length - 1), mProjectViewModel: projectViewModel), | ||||||
|  | //               Utils.tableColumnValue('${labResultList[i].toJson()[viewKey2]}', isLast: i == (labResultList.length - 1), mProjectViewModel: projectViewModel), | ||||||
|  | //             ], | ||||||
|  | //           ), | ||||||
|  | //         ); | ||||||
|  | //     } | ||||||
|  | // | ||||||
|  | //     return tableRow; | ||||||
|  | //   } | ||||||
|  | // | ||||||
|  | //   generateData() { | ||||||
|  | //     if (vitalList.length > 0) { | ||||||
|  | //       vitalList.reversed.toList().forEach( | ||||||
|  | //         (element) { | ||||||
|  | //           if (element.toJson()[viewKey1]?.toInt() != 0) | ||||||
|  | //             timeSeriesData1.add( | ||||||
|  | //               TimeSeriesSales2( | ||||||
|  | //                 new DateTime(element.vitalSignDate!.year, element.vitalSignDate!.month, element.vitalSignDate!.day), | ||||||
|  | //                 element.toJson()[viewKey1].toDouble(), | ||||||
|  | //               ), | ||||||
|  | //             ); | ||||||
|  | //           if (element.toJson()[viewKey2]?.toInt() != 0) | ||||||
|  | //             timeSeriesData2.add( | ||||||
|  | //               TimeSeriesSales2( | ||||||
|  | //                 new DateTime(element.vitalSignDate!.year, element.vitalSignDate!.month, element.vitalSignDate!.day), | ||||||
|  | //                 element.toJson()[viewKey2].toDouble(), | ||||||
|  | //               ), | ||||||
|  | //             ); | ||||||
|  | //         }, | ||||||
|  | //       ); | ||||||
|  | //     } | ||||||
|  | //   } | ||||||
|  | // } | ||||||
											
												
													File diff suppressed because it is too large
													Load Diff
												
											
										
									
								| @ -0,0 +1,76 @@ | |||||||
|  | name: hmg_patient_app_new | ||||||
|  | description: "New HMG Patient App" | ||||||
|  | publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ||||||
|  | 
 | ||||||
|  | version: 1.0.0+1 | ||||||
|  | 
 | ||||||
|  | environment: | ||||||
|  |   sdk: ">=3.6.0 <4.0.0" | ||||||
|  | 
 | ||||||
|  | dependencies: | ||||||
|  |   flutter: | ||||||
|  |     sdk: flutter | ||||||
|  |   flutter_localizations: | ||||||
|  |     sdk: flutter | ||||||
|  |   cupertino_icons: ^1.0.8 | ||||||
|  |   cached_network_image: ^3.4.1 | ||||||
|  |   http: ^1.5.0 | ||||||
|  |   flutter_svg: ^2.2.0 | ||||||
|  |   shared_preferences: ^2.5.3 | ||||||
|  |   connectivity_plus: ^6.1.5 | ||||||
|  |   auto_size_text: ^3.0.0 | ||||||
|  |   shimmer: ^3.0.0 | ||||||
|  |   sizer: ^3.1.3 | ||||||
|  |   easy_localization: ^3.0.8 | ||||||
|  |   fluttertoast: ^8.2.12 | ||||||
|  |   flutter_rating_bar: ^4.0.1 | ||||||
|  |   firebase_messaging: ^15.2.10 | ||||||
|  |   #  firebase_messaging: any | ||||||
|  |   firebase_core: any | ||||||
|  |   #  firebase_core: ^3.13.1 | ||||||
|  |   permission_handler: ^12.0.1 | ||||||
|  |   flutter_local_notifications: ^19.4.1 | ||||||
|  |   injector: ^4.0.0 | ||||||
|  |   provider: ^6.1.5+1 | ||||||
|  |   just_audio: ^0.10.4 | ||||||
|  |   flutter_callkit_incoming: | ||||||
|  |     git: | ||||||
|  |       url: https://github.com/hiennguyen92/flutter_callkit_incoming.git | ||||||
|  |       ref: dev | ||||||
|  |   url_launcher: ^6.3.2 | ||||||
|  |   logger: ^2.6.1 | ||||||
|  |   lottie: ^3.3.1 | ||||||
|  |   flutter_ios_voip_kit_karmm: ^0.8.0 | ||||||
|  |   image_picker: ^1.2.0 | ||||||
|  |   file_picker: ^10.3.2 | ||||||
|  |   local_auth: ^2.3.0 | ||||||
|  |   share_plus: ^11.1.0 | ||||||
|  |   device_calendar: | ||||||
|  |     git: https://github.com/bardram/device_calendar | ||||||
|  |   manage_calendar_events: ^2.0.3 | ||||||
|  |   flutter_inappwebview: ^6.1.5 | ||||||
|  |   #  webview_flutter: ^4.13.0 | ||||||
|  |   syncfusion_flutter_calendar: ^30.2.7 | ||||||
|  |   device_info_plus: ^11.5.0 | ||||||
|  |   uuid: ^4.5.1 | ||||||
|  |   health: ^13.1.3 | ||||||
|  |   #  health: 12.0.1 | ||||||
|  |   fl_chart: ^1.0.0 | ||||||
|  |   geolocator: ^14.0.2 | ||||||
|  |   dropdown_search: ^6.0.2 | ||||||
|  |   google_maps_flutter: ^2.12.3 | ||||||
|  |   flutter_zoom_videosdk: ^2.1.10 | ||||||
|  | 
 | ||||||
|  |   web: any | ||||||
|  | 
 | ||||||
|  | dev_dependencies: | ||||||
|  |   flutter_test: | ||||||
|  |     sdk: flutter | ||||||
|  |   flutter_lints: ^5.0.0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | flutter: | ||||||
|  |   uses-material-design: true | ||||||
|  |   assets: | ||||||
|  |     - assets/ | ||||||
|  |     - assets/langs/ | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | // This is a basic Flutter widget test. | ||||||
|  | // | ||||||
|  | // To perform an interaction with a widget in your test, use the WidgetTester | ||||||
|  | // utility in the flutter_test package. For example, you can send tap and scroll | ||||||
|  | // gestures. You can also use WidgetTester to find child widgets in the widget | ||||||
|  | // tree, read text, and verify that the values of widget properties are correct. | ||||||
|  | 
 | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter_test/flutter_test.dart'; | ||||||
|  | 
 | ||||||
|  | import 'package:hmg_patient_app_new/main.dart'; | ||||||
|  | 
 | ||||||
|  | void main() { | ||||||
|  |   testWidgets('Counter increments smoke test', (WidgetTester tester) async { | ||||||
|  |     // Build our app and trigger a frame. | ||||||
|  |     await tester.pumpWidget(const MyApp()); | ||||||
|  | 
 | ||||||
|  |     // Verify that our counter starts at 0. | ||||||
|  |     expect(find.text('0'), findsOneWidget); | ||||||
|  |     expect(find.text('1'), findsNothing); | ||||||
|  | 
 | ||||||
|  |     // Tap the '+' icon and trigger a frame. | ||||||
|  |     await tester.tap(find.byIcon(Icons.add)); | ||||||
|  |     await tester.pump(); | ||||||
|  | 
 | ||||||
|  |     // Verify that our counter has incremented. | ||||||
|  |     expect(find.text('0'), findsNothing); | ||||||
|  |     expect(find.text('1'), findsOneWidget); | ||||||
|  |   }); | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in New Issue
	
	 Haroon Amjad
						Haroon Amjad