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