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