Merge branch 'master' of https://gitlab.com/Cloud_Solution/diplomatic-quarter into sultan
@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIICkDCCAXgCAQAwSzEjMCEGCSqGSIb3DQEJARYUaGFyb29uNjEzOEBnbWFpbC5j
|
||||||
|
b20xFzAVBgNVBAMMDk1vaGFtZWQgTWVrYXd5MQswCQYDVQQGEwJBRTCCASIwDQYJ
|
||||||
|
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnHqsyE7WfiVcE1Lpa4t4OVO6qlll2q
|
||||||
|
1djs0XG06R/dlDtIqv4940/XLj+hU93mzAcVvFW4DSIEdD3InM3+T6oMTjPu6meU
|
||||||
|
69h9ryaVkluQRrT/tdGI1EKO4MWGMe4MDIt7DqMhMfAcxTwekwdxdKaCEhaw3qnA
|
||||||
|
l/64AelY6URW1pHHJMA0VV7j+pE3jVNai+muMXPrhQ1VrOrV8FftpY3bEeRJR2Cl
|
||||||
|
T0tv0LhEMu4SfLnVWCzGQQC82hilDw3rH3ZDs8DFxF9agNVdwKlYamarh1dQXwRq
|
||||||
|
Yx2+sjY1/51r9L4VS+GAh9ECxz0e+43NpzfZ/N+mTeDYKDepaBwPQ6kCAwEAAaAA
|
||||||
|
MA0GCSqGSIb3DQEBCwUAA4IBAQB89OyLfywKT7ftmpEqCmgsmaJexb580q9w8wOk
|
||||||
|
1JhJkNV5ec+p1dnge2NZeJ4LGII/5wmPj1vANNW0GZdmJDgnC+2gg9toq1QLCAsF
|
||||||
|
rW7/LMpgAoEH+P5bhrHV9RRv6BQi0ZmN0apBHjp/pqZfm2Cl5jQPEWjUEf2tIF4l
|
||||||
|
LSKdok6IPO9n4Fgyk0XdUNSEhgVhsLtZkGiXnkI1YovKDnupTFYPXMLp103bc9zP
|
||||||
|
xDxwscvOysNDijlzZAkJPg2z8NrJIRDrKvLRHzxQwZ/1LHVB/51bp/1iyks3vOjh
|
||||||
|
qw5XVsrtGAjCjU9md7q3XkPSyKzhK9UqPdOxdvl1OY0KKIIY
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
{\rtf1\ansi\ansicpg1252\cocoartf2513
|
||||||
|
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
|
||||||
|
{\colortbl;\red255\green255\blue255;}
|
||||||
|
{\*\expandedcolortbl;;}
|
||||||
|
\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
|
||||||
|
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
|
||||||
|
|
||||||
|
\f0\fs24 \cf0 Hmg54321}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CLIENT_ID</key>
|
||||||
|
<string>864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r.apps.googleusercontent.com</string>
|
||||||
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
|
<string>com.googleusercontent.apps.864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r</string>
|
||||||
|
<key>API_KEY</key>
|
||||||
|
<string>AIzaSyA_6ayGCk4fly7o7eTVBrj9kuHBYHMAOfs</string>
|
||||||
|
<key>GCM_SENDER_ID</key>
|
||||||
|
<string>864393916058</string>
|
||||||
|
<key>PLIST_VERSION</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>BUNDLE_ID</key>
|
||||||
|
<string>com.cloud.diplomaticquarterapp</string>
|
||||||
|
<key>PROJECT_ID</key>
|
||||||
|
<string>diplomaticquarter-d2385</string>
|
||||||
|
<key>STORAGE_BUCKET</key>
|
||||||
|
<string>diplomaticquarter-d2385.appspot.com</string>
|
||||||
|
<key>IS_ADS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_ANALYTICS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_APPINVITE_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_GCM_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>GOOGLE_APP_ID</key>
|
||||||
|
<string>1:864393916058:ios:13f787bbfe6051f8b97923</string>
|
||||||
|
<key>DATABASE_URL</key>
|
||||||
|
<string>https://diplomaticquarter-d2385.firebaseio.com</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "864393916058",
|
||||||
|
"firebase_url": "https://diplomaticquarter-d2385.firebaseio.com",
|
||||||
|
"project_id": "diplomaticquarter-d2385",
|
||||||
|
"storage_bucket": "diplomaticquarter-d2385.appspot.com"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:864393916058:android:5b5a65cd6d8c18b4b97923",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.cloud.diplomaticquarterapp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyBdV3mos1BPhUzNKCj2KANJtiO3o2zh9IM"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 212 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "864393916058",
|
||||||
|
"firebase_url": "https://diplomaticquarter-d2385.firebaseio.com",
|
||||||
|
"project_id": "diplomaticquarter-d2385",
|
||||||
|
"storage_bucket": "diplomaticquarter-d2385.appspot.com"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:864393916058:android:5b5a65cd6d8c18b4b97923",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.cloud.diplomaticquarterapp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyBdV3mos1BPhUzNKCj2KANJtiO3o2zh9IM"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": [
|
||||||
|
{
|
||||||
|
"client_id": "864393916058-tphjrn8j39ntevt32ekcvmll8aue7qql.apps.googleusercontent.com",
|
||||||
|
"client_type": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CLIENT_ID</key>
|
||||||
|
<string>864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r.apps.googleusercontent.com</string>
|
||||||
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
|
<string>com.googleusercontent.apps.864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r</string>
|
||||||
|
<key>API_KEY</key>
|
||||||
|
<string>AIzaSyA_6ayGCk4fly7o7eTVBrj9kuHBYHMAOfs</string>
|
||||||
|
<key>GCM_SENDER_ID</key>
|
||||||
|
<string>864393916058</string>
|
||||||
|
<key>PLIST_VERSION</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>BUNDLE_ID</key>
|
||||||
|
<string>com.cloud.diplomaticquarterapp</string>
|
||||||
|
<key>PROJECT_ID</key>
|
||||||
|
<string>diplomaticquarter-d2385</string>
|
||||||
|
<key>STORAGE_BUCKET</key>
|
||||||
|
<string>diplomaticquarter-d2385.appspot.com</string>
|
||||||
|
<key>IS_ADS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_ANALYTICS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_APPINVITE_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_GCM_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>GOOGLE_APP_ID</key>
|
||||||
|
<string>1:864393916058:ios:13f787bbfe6051f8b97923</string>
|
||||||
|
<key>DATABASE_URL</key>
|
||||||
|
<string>https://diplomaticquarter-d2385.firebaseio.com</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CLIENT_ID</key>
|
||||||
|
<string>864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r.apps.googleusercontent.com</string>
|
||||||
|
<key>REVERSED_CLIENT_ID</key>
|
||||||
|
<string>com.googleusercontent.apps.864393916058-ekeb4s8tgfo58dutv0l54399t7ivr06r</string>
|
||||||
|
<key>API_KEY</key>
|
||||||
|
<string>AIzaSyA_6ayGCk4fly7o7eTVBrj9kuHBYHMAOfs</string>
|
||||||
|
<key>GCM_SENDER_ID</key>
|
||||||
|
<string>864393916058</string>
|
||||||
|
<key>PLIST_VERSION</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>BUNDLE_ID</key>
|
||||||
|
<string>com.cloud.diplomaticquarterapp</string>
|
||||||
|
<key>PROJECT_ID</key>
|
||||||
|
<string>diplomaticquarter-d2385</string>
|
||||||
|
<key>STORAGE_BUCKET</key>
|
||||||
|
<string>diplomaticquarter-d2385.appspot.com</string>
|
||||||
|
<key>IS_ADS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_ANALYTICS_ENABLED</key>
|
||||||
|
<false></false>
|
||||||
|
<key>IS_APPINVITE_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_GCM_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>IS_SIGNIN_ENABLED</key>
|
||||||
|
<true></true>
|
||||||
|
<key>GOOGLE_APP_ID</key>
|
||||||
|
<string>1:864393916058:ios:13f787bbfe6051f8b97923</string>
|
||||||
|
<key>DATABASE_URL</key>
|
||||||
|
<string>https://diplomaticquarter-d2385.firebaseio.com</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict/>
|
||||||
|
</plist>
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
class AppointmentDetails {
|
||||||
|
String setupID;
|
||||||
|
int projectID;
|
||||||
|
int patientID;
|
||||||
|
int appointmentNo;
|
||||||
|
int clinicID;
|
||||||
|
int doctorID;
|
||||||
|
String startTime;
|
||||||
|
String endTime;
|
||||||
|
String appointmentDate;
|
||||||
|
String clinicName;
|
||||||
|
String doctorImageURL;
|
||||||
|
String doctorName;
|
||||||
|
String projectName;
|
||||||
|
|
||||||
|
AppointmentDetails(
|
||||||
|
{this.setupID,
|
||||||
|
this.projectID,
|
||||||
|
this.patientID,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.clinicID,
|
||||||
|
this.doctorID,
|
||||||
|
this.startTime,
|
||||||
|
this.endTime,
|
||||||
|
this.appointmentDate,
|
||||||
|
this.clinicName,
|
||||||
|
this.doctorImageURL,
|
||||||
|
this.doctorName,
|
||||||
|
this.projectName});
|
||||||
|
|
||||||
|
AppointmentDetails.fromJson(Map<String, dynamic> json) {
|
||||||
|
setupID = json['SetupID'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
patientID = json['PatientID'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
appointmentDate = json['AppointmentDate'];
|
||||||
|
clinicName = json['ClinicName'];
|
||||||
|
doctorImageURL = json['DoctorImageURL'];
|
||||||
|
doctorName = json['DoctorName'];
|
||||||
|
projectName = json['ProjectName'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['SetupID'] = this.setupID;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['PatientID'] = this.patientID;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['AppointmentDate'] = this.appointmentDate;
|
||||||
|
data['ClinicName'] = this.clinicName;
|
||||||
|
data['DoctorImageURL'] = this.doctorImageURL;
|
||||||
|
data['DoctorName'] = this.doctorName;
|
||||||
|
data['ProjectName'] = this.projectName;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
class AppointmentRate {
|
||||||
|
int rate;
|
||||||
|
int appointmentNo;
|
||||||
|
int projectID;
|
||||||
|
int doctorID;
|
||||||
|
int clinicID;
|
||||||
|
String note;
|
||||||
|
String mobileNumber;
|
||||||
|
int createdBy;
|
||||||
|
int editedBy;
|
||||||
|
double versionID;
|
||||||
|
int channel;
|
||||||
|
int languageID;
|
||||||
|
String iPAdress;
|
||||||
|
String generalid;
|
||||||
|
int patientOutSA;
|
||||||
|
String sessionID;
|
||||||
|
bool isDentalAllowedBackend;
|
||||||
|
int deviceTypeID;
|
||||||
|
int patientID;
|
||||||
|
String tokenID;
|
||||||
|
int patientTypeID;
|
||||||
|
int patientType;
|
||||||
|
|
||||||
|
AppointmentRate(
|
||||||
|
{this.rate,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.projectID,
|
||||||
|
this.doctorID,
|
||||||
|
this.clinicID,
|
||||||
|
this.note,
|
||||||
|
this.mobileNumber,
|
||||||
|
this.createdBy,
|
||||||
|
this.editedBy,
|
||||||
|
this.versionID,
|
||||||
|
this.channel,
|
||||||
|
this.languageID,
|
||||||
|
this.iPAdress,
|
||||||
|
this.generalid,
|
||||||
|
this.patientOutSA,
|
||||||
|
this.sessionID,
|
||||||
|
this.isDentalAllowedBackend,
|
||||||
|
this.deviceTypeID,
|
||||||
|
this.patientID,
|
||||||
|
this.tokenID,
|
||||||
|
this.patientTypeID,
|
||||||
|
this.patientType});
|
||||||
|
|
||||||
|
AppointmentRate.fromJson(Map<String, dynamic> json) {
|
||||||
|
rate = json['Rate'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
note = json['Note'];
|
||||||
|
mobileNumber = json['MobileNumber'];
|
||||||
|
createdBy = json['CreatedBy'];
|
||||||
|
editedBy = json['EditedBy'];
|
||||||
|
versionID = json['VersionID'];
|
||||||
|
channel = json['Channel'];
|
||||||
|
languageID = json['LanguageID'];
|
||||||
|
iPAdress = json['IPAdress'];
|
||||||
|
generalid = json['generalid'];
|
||||||
|
patientOutSA = json['PatientOutSA'];
|
||||||
|
sessionID = json['SessionID'];
|
||||||
|
isDentalAllowedBackend = json['isDentalAllowedBackend'];
|
||||||
|
deviceTypeID = json['DeviceTypeID'];
|
||||||
|
patientID = json['PatientID'];
|
||||||
|
tokenID = json['TokenID'];
|
||||||
|
patientTypeID = json['PatientTypeID'];
|
||||||
|
patientType = json['PatientType'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['Rate'] = this.rate;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['Note'] = this.note;
|
||||||
|
data['MobileNumber'] = this.mobileNumber;
|
||||||
|
data['CreatedBy'] = this.createdBy;
|
||||||
|
data['EditedBy'] = this.editedBy;
|
||||||
|
data['VersionID'] = this.versionID;
|
||||||
|
data['Channel'] = this.channel;
|
||||||
|
data['LanguageID'] = this.languageID;
|
||||||
|
data['IPAdress'] = this.iPAdress;
|
||||||
|
data['generalid'] = this.generalid;
|
||||||
|
data['PatientOutSA'] = this.patientOutSA;
|
||||||
|
data['SessionID'] = this.sessionID;
|
||||||
|
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
|
||||||
|
data['DeviceTypeID'] = this.deviceTypeID;
|
||||||
|
data['PatientID'] = this.patientID;
|
||||||
|
data['TokenID'] = this.tokenID;
|
||||||
|
data['PatientTypeID'] = this.patientTypeID;
|
||||||
|
data['PatientType'] = this.patientType;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
class AppoitmentRated {
|
||||||
|
String setupID;
|
||||||
|
int projectID;
|
||||||
|
int appointmentNo;
|
||||||
|
String appointmentDate;
|
||||||
|
String appointmentDateN;
|
||||||
|
int appointmentType;
|
||||||
|
String bookDate;
|
||||||
|
int patientType;
|
||||||
|
int patientID;
|
||||||
|
int clinicID;
|
||||||
|
int doctorID;
|
||||||
|
String endDate;
|
||||||
|
String startTime;
|
||||||
|
String endTime;
|
||||||
|
int status;
|
||||||
|
int visitType;
|
||||||
|
int visitFor;
|
||||||
|
int patientStatusType;
|
||||||
|
int companyID;
|
||||||
|
int bookedBy;
|
||||||
|
String bookedOn;
|
||||||
|
int confirmedBy;
|
||||||
|
String confirmedOn;
|
||||||
|
int arrivalChangedBy;
|
||||||
|
String arrivedOn;
|
||||||
|
int editedBy;
|
||||||
|
String editedOn;
|
||||||
|
Null doctorName;
|
||||||
|
String doctorNameN;
|
||||||
|
String statusDesc;
|
||||||
|
String statusDescN;
|
||||||
|
bool vitalStatus;
|
||||||
|
Null vitalSignAppointmentNo;
|
||||||
|
int episodeID;
|
||||||
|
String doctorTitle;
|
||||||
|
bool isAppoitmentLiveCare;
|
||||||
|
|
||||||
|
AppoitmentRated(
|
||||||
|
{this.setupID,
|
||||||
|
this.projectID,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.appointmentDate,
|
||||||
|
this.appointmentDateN,
|
||||||
|
this.appointmentType,
|
||||||
|
this.bookDate,
|
||||||
|
this.patientType,
|
||||||
|
this.patientID,
|
||||||
|
this.clinicID,
|
||||||
|
this.doctorID,
|
||||||
|
this.endDate,
|
||||||
|
this.startTime,
|
||||||
|
this.endTime,
|
||||||
|
this.status,
|
||||||
|
this.visitType,
|
||||||
|
this.visitFor,
|
||||||
|
this.patientStatusType,
|
||||||
|
this.companyID,
|
||||||
|
this.bookedBy,
|
||||||
|
this.bookedOn,
|
||||||
|
this.confirmedBy,
|
||||||
|
this.confirmedOn,
|
||||||
|
this.arrivalChangedBy,
|
||||||
|
this.arrivedOn,
|
||||||
|
this.editedBy,
|
||||||
|
this.editedOn,
|
||||||
|
this.doctorName,
|
||||||
|
this.doctorNameN,
|
||||||
|
this.statusDesc,
|
||||||
|
this.statusDescN,
|
||||||
|
this.vitalStatus,
|
||||||
|
this.vitalSignAppointmentNo,
|
||||||
|
this.episodeID,
|
||||||
|
this.doctorTitle,
|
||||||
|
this.isAppoitmentLiveCare});
|
||||||
|
|
||||||
|
AppoitmentRated.fromJson(Map<String, dynamic> json) {
|
||||||
|
try {
|
||||||
|
setupID = json['SetupID'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
appointmentDate = json['AppointmentDate'];
|
||||||
|
appointmentDateN = json['AppointmentDateN'];
|
||||||
|
appointmentType = json['AppointmentType'];
|
||||||
|
bookDate = json['BookDate'];
|
||||||
|
patientType = json['PatientType'];
|
||||||
|
patientID = json['PatientID'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
endDate = json['EndDate'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
status = json['Status'];
|
||||||
|
visitType = json['VisitType'];
|
||||||
|
visitFor = json['VisitFor'];
|
||||||
|
patientStatusType = json['PatientStatusType'];
|
||||||
|
companyID = json['CompanyID'];
|
||||||
|
bookedBy = json['BookedBy'];
|
||||||
|
bookedOn = json['BookedOn'];
|
||||||
|
confirmedBy = json['ConfirmedBy'];
|
||||||
|
confirmedOn = json['ConfirmedOn'];
|
||||||
|
arrivalChangedBy = json['ArrivalChangedBy'];
|
||||||
|
arrivedOn = json['ArrivedOn'];
|
||||||
|
editedBy = json['EditedBy'];
|
||||||
|
editedOn = json['EditedOn'];
|
||||||
|
doctorName = json['DoctorName'];
|
||||||
|
doctorNameN = json['DoctorNameN'];
|
||||||
|
statusDesc = json['StatusDesc'];
|
||||||
|
statusDescN = json['StatusDescN'];
|
||||||
|
vitalStatus = json['VitalStatus'];
|
||||||
|
vitalSignAppointmentNo = json['VitalSignAppointmentNo'];
|
||||||
|
episodeID = json['EpisodeID'];
|
||||||
|
doctorTitle = json['DoctorTitle'];
|
||||||
|
isAppoitmentLiveCare = json['IsAppoitmentLiveCare'];
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['SetupID'] = this.setupID;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['AppointmentDate'] = this.appointmentDate;
|
||||||
|
data['AppointmentDateN'] = this.appointmentDateN;
|
||||||
|
data['AppointmentType'] = this.appointmentType;
|
||||||
|
data['BookDate'] = this.bookDate;
|
||||||
|
data['PatientType'] = this.patientType;
|
||||||
|
data['PatientID'] = this.patientID;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['EndDate'] = this.endDate;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['Status'] = this.status;
|
||||||
|
data['VisitType'] = this.visitType;
|
||||||
|
data['VisitFor'] = this.visitFor;
|
||||||
|
data['PatientStatusType'] = this.patientStatusType;
|
||||||
|
data['CompanyID'] = this.companyID;
|
||||||
|
data['BookedBy'] = this.bookedBy;
|
||||||
|
data['BookedOn'] = this.bookedOn;
|
||||||
|
data['ConfirmedBy'] = this.confirmedBy;
|
||||||
|
data['ConfirmedOn'] = this.confirmedOn;
|
||||||
|
data['ArrivalChangedBy'] = this.arrivalChangedBy;
|
||||||
|
data['ArrivedOn'] = this.arrivedOn;
|
||||||
|
data['EditedBy'] = this.editedBy;
|
||||||
|
data['EditedOn'] = this.editedOn;
|
||||||
|
data['DoctorName'] = this.doctorName;
|
||||||
|
data['DoctorNameN'] = this.doctorNameN;
|
||||||
|
data['StatusDesc'] = this.statusDesc;
|
||||||
|
data['StatusDescN'] = this.statusDescN;
|
||||||
|
data['VitalStatus'] = this.vitalStatus;
|
||||||
|
data['VitalSignAppointmentNo'] = this.vitalSignAppointmentNo;
|
||||||
|
data['EpisodeID'] = this.episodeID;
|
||||||
|
data['DoctorTitle'] = this.doctorTitle;
|
||||||
|
data['IsAppoitmentLiveCare'] = this.isAppoitmentLiveCare;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
import 'package:diplomaticquarterapp/config/config.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appointment_rate.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appoitment_rated.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appointment_details.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/service/base_service.dart';
|
||||||
|
|
||||||
|
class AppointmentRateService extends BaseService {
|
||||||
|
List<AppoitmentRated> appointmentRatedList = List();
|
||||||
|
AppointmentDetails appointmentDetails;
|
||||||
|
|
||||||
|
Future getIsLastAppointmentRatedList() async {
|
||||||
|
hasError = false;
|
||||||
|
await baseAppClient.post(IS_LAST_APPOITMENT_RATED,
|
||||||
|
onSuccess: (dynamic response, int statusCode) {
|
||||||
|
appointmentRatedList.clear();
|
||||||
|
response['IsLastAppoitmentRatedList'].forEach((appoint) {
|
||||||
|
appointmentRatedList.add(AppoitmentRated.fromJson(appoint));
|
||||||
|
});
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
hasError = true;
|
||||||
|
super.error = error;
|
||||||
|
}, body: Map());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future getAppointmentDetails() async {
|
||||||
|
hasError = false;
|
||||||
|
Map<String, dynamic> bodyData = Map();
|
||||||
|
bodyData['AppointmentNumber'] = appointmentRatedList[0].appointmentNo;
|
||||||
|
bodyData['ProjectID'] = appointmentRatedList[0].projectID;
|
||||||
|
await baseAppClient.post(GET_APPOINTMENT_DETAILS_BY_NO,
|
||||||
|
onSuccess: (dynamic response, int statusCode) {
|
||||||
|
if (response['AppointmentDetails'] != null)
|
||||||
|
appointmentDetails =
|
||||||
|
AppointmentDetails.fromJson(response['AppointmentDetails']);
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
hasError = true;
|
||||||
|
super.error = error;
|
||||||
|
}, body: bodyData);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future sendAppointmentRate(int rate, int appointmentNo, int projectID,
|
||||||
|
int doctorID, int clinicID, String note) async {
|
||||||
|
hasError = false;
|
||||||
|
AppointmentRate appointmentRate = AppointmentRate();
|
||||||
|
appointmentRate.rate = rate;
|
||||||
|
appointmentRate.appointmentNo = appointmentNo;
|
||||||
|
appointmentRate.projectID = projectID;
|
||||||
|
appointmentRate.doctorID = doctorID;
|
||||||
|
appointmentRate.clinicID = clinicID;
|
||||||
|
appointmentRate.note = note;
|
||||||
|
|
||||||
|
await baseAppClient.post(GET_APPOINTMENT_DETAILS_BY_NO,
|
||||||
|
onSuccess: (dynamic response, int statusCode) {},
|
||||||
|
onFailure: (String error, int statusCode) {
|
||||||
|
hasError = true;
|
||||||
|
super.error = error;
|
||||||
|
}, body: appointmentRate.toJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
AppoitmentRated get lastAppointmentRated {
|
||||||
|
if (appointmentRatedList.length > 0)
|
||||||
|
return appointmentRatedList[appointmentRatedList.length - 1];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAppointmentRated(AppoitmentRated appointmentRated) {
|
||||||
|
appointmentRatedList.remove(appointmentRated);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllAppAppointmentRate() => appointmentRatedList.clear();
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:diplomaticquarterapp/config/config.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appoitment_rated.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/service/base_service.dart';
|
||||||
|
|
||||||
|
class DashboardService extends BaseService {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:diplomaticquarterapp/core/enum/viewstate.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appointment_details.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appoitment_rated.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/service/appointment_rate_service.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/base_view_model.dart';
|
||||||
|
import '../../locator.dart';
|
||||||
|
|
||||||
|
class AppointmentRateViewModel extends BaseViewModel {
|
||||||
|
AppointmentRateService _appointmentRateService =
|
||||||
|
locator<AppointmentRateService>();
|
||||||
|
bool isHaveAppointmentNotRate = false;
|
||||||
|
|
||||||
|
AppointmentDetails get appointmentDetails => _appointmentRateService.appointmentDetails;
|
||||||
|
|
||||||
|
|
||||||
|
Future getIsLastAppointmentRatedList() async {
|
||||||
|
isHaveAppointmentNotRate = false;
|
||||||
|
setState(ViewState.Busy);
|
||||||
|
await _appointmentRateService.getIsLastAppointmentRatedList();
|
||||||
|
if (_appointmentRateService.hasError) {
|
||||||
|
error = _appointmentRateService.error;
|
||||||
|
setState(ViewState.Error);
|
||||||
|
} else {
|
||||||
|
if(_appointmentRateService.appointmentRatedList.length>0)
|
||||||
|
await getAppointmentDetails();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAppointmentDetails() async {
|
||||||
|
await _appointmentRateService.getAppointmentDetails();
|
||||||
|
if (_appointmentRateService.hasError) {
|
||||||
|
error = _appointmentRateService.error;
|
||||||
|
setState(ViewState.Error);
|
||||||
|
} else {
|
||||||
|
if (_appointmentRateService.appointmentDetails != null)
|
||||||
|
isHaveAppointmentNotRate = true;
|
||||||
|
setState(ViewState.Idle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future sendAppointmentRate(int rate, int appointmentNo, int projectID,
|
||||||
|
int doctorID, int clinicID, String note) async {
|
||||||
|
setState(ViewState.BusyLocal);
|
||||||
|
await _appointmentRateService.sendAppointmentRate(
|
||||||
|
rate, appointmentNo, projectID, doctorID, clinicID, note);
|
||||||
|
if (_appointmentRateService.hasError) {
|
||||||
|
error = _appointmentRateService.error;
|
||||||
|
setState(ViewState.ErrorLocal);
|
||||||
|
} else {
|
||||||
|
setState(ViewState.Idle);
|
||||||
|
_appointmentRateService.deleteAllAppAppointmentRate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AppoitmentRated get lastAppointmentRated =>
|
||||||
|
_appointmentRateService.lastAppointmentRated;
|
||||||
|
|
||||||
|
deleteAppointmentRated(AppoitmentRated appointmentRated) =>
|
||||||
|
_appointmentRateService.deleteAppointmentRated(appointmentRated);
|
||||||
|
|
||||||
|
deleteAppAppointmentRate() =>
|
||||||
|
_appointmentRateService.deleteAllAppAppointmentRate();
|
||||||
|
|
||||||
|
setIsRated(bool isRated) {
|
||||||
|
this.isHaveAppointmentNotRate = isRated;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:diplomaticquarterapp/core/enum/viewstate.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/service/dashboard_service.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/base_view_model.dart';
|
||||||
|
import '../../locator.dart';
|
||||||
|
|
||||||
|
class DashboardViewModel extends BaseViewModel {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
class PatientERVirtualHistoryResponse {
|
||||||
|
List<ErRequestHistoryList> erRequestHistoryList;
|
||||||
|
|
||||||
|
PatientERVirtualHistoryResponse({this.erRequestHistoryList});
|
||||||
|
|
||||||
|
PatientERVirtualHistoryResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['ErRequestHistoryList'] != null) {
|
||||||
|
erRequestHistoryList = new List<ErRequestHistoryList>();
|
||||||
|
json['ErRequestHistoryList'].forEach((v) {
|
||||||
|
erRequestHistoryList.add(new ErRequestHistoryList.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
if (this.erRequestHistoryList != null) {
|
||||||
|
data['ErRequestHistoryList'] =
|
||||||
|
this.erRequestHistoryList.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ErRequestHistoryList {
|
||||||
|
dynamic appointmentNo;
|
||||||
|
String arrivalTime;
|
||||||
|
int callDuration;
|
||||||
|
int callStatus;
|
||||||
|
String clientRequestID;
|
||||||
|
String doctorID;
|
||||||
|
String doctorName;
|
||||||
|
String doctorNameN;
|
||||||
|
String doctorTitle;
|
||||||
|
String exWaitingTime;
|
||||||
|
bool isAppointmentHaveRating;
|
||||||
|
int patCount;
|
||||||
|
int projectID;
|
||||||
|
String sArrivalTime;
|
||||||
|
int serviceID;
|
||||||
|
String stringCallStatus;
|
||||||
|
int vCID;
|
||||||
|
int watingtimeInteger;
|
||||||
|
|
||||||
|
ErRequestHistoryList(
|
||||||
|
{this.appointmentNo,
|
||||||
|
this.arrivalTime,
|
||||||
|
this.callDuration,
|
||||||
|
this.callStatus,
|
||||||
|
this.clientRequestID,
|
||||||
|
this.doctorID,
|
||||||
|
this.doctorName,
|
||||||
|
this.doctorNameN,
|
||||||
|
this.doctorTitle,
|
||||||
|
this.exWaitingTime,
|
||||||
|
this.isAppointmentHaveRating,
|
||||||
|
this.patCount,
|
||||||
|
this.projectID,
|
||||||
|
this.sArrivalTime,
|
||||||
|
this.serviceID,
|
||||||
|
this.stringCallStatus,
|
||||||
|
this.vCID,
|
||||||
|
this.watingtimeInteger});
|
||||||
|
|
||||||
|
ErRequestHistoryList.fromJson(Map<String, dynamic> json) {
|
||||||
|
appointmentNo = json['AppointmentNo'] != null ? json['AppointmentNo'] : "0";
|
||||||
|
arrivalTime = json['ArrivalTime'];
|
||||||
|
callDuration = json['CallDuration'];
|
||||||
|
callStatus = json['CallStatus'];
|
||||||
|
clientRequestID = json['ClientRequestID'];
|
||||||
|
doctorID = json['DoctorID'];
|
||||||
|
doctorName = json['DoctorName'];
|
||||||
|
doctorNameN = json['DoctorNameN'];
|
||||||
|
doctorTitle = json['DoctorTitle'];
|
||||||
|
exWaitingTime = json['Ex_WaitingTime'];
|
||||||
|
isAppointmentHaveRating = json['IsAppointmentHaveRating'];
|
||||||
|
patCount = json['Pat_Count'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
sArrivalTime = json['SArrivalTime'];
|
||||||
|
serviceID = json['ServiceID'];
|
||||||
|
stringCallStatus = json['StringCallStatus'];
|
||||||
|
vCID = json['VC_ID'];
|
||||||
|
watingtimeInteger = json['WatingtimeInteger'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['ArrivalTime'] = this.arrivalTime;
|
||||||
|
data['CallDuration'] = this.callDuration;
|
||||||
|
data['CallStatus'] = this.callStatus;
|
||||||
|
data['ClientRequestID'] = this.clientRequestID;
|
||||||
|
data['DoctorID'] = this.doctorID;
|
||||||
|
data['DoctorName'] = this.doctorName;
|
||||||
|
data['DoctorNameN'] = this.doctorNameN;
|
||||||
|
data['DoctorTitle'] = this.doctorTitle;
|
||||||
|
data['Ex_WaitingTime'] = this.exWaitingTime;
|
||||||
|
data['IsAppointmentHaveRating'] = this.isAppointmentHaveRating;
|
||||||
|
data['Pat_Count'] = this.patCount;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['SArrivalTime'] = this.sArrivalTime;
|
||||||
|
data['ServiceID'] = this.serviceID;
|
||||||
|
data['StringCallStatus'] = this.stringCallStatus;
|
||||||
|
data['VC_ID'] = this.vCID;
|
||||||
|
data['WatingtimeInteger'] = this.watingtimeInteger;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
class ClinicsServiceTimingsResponse {
|
||||||
|
List<PatientERGetClinicsServiceTimingsList>
|
||||||
|
patientERGetClinicsServiceTimingsList;
|
||||||
|
|
||||||
|
ClinicsServiceTimingsResponse({this.patientERGetClinicsServiceTimingsList});
|
||||||
|
|
||||||
|
ClinicsServiceTimingsResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['PatientER_GetClinicsServiceTimingsList'] != null) {
|
||||||
|
patientERGetClinicsServiceTimingsList =
|
||||||
|
new List<PatientERGetClinicsServiceTimingsList>();
|
||||||
|
json['PatientER_GetClinicsServiceTimingsList'].forEach((v) {
|
||||||
|
patientERGetClinicsServiceTimingsList
|
||||||
|
.add(new PatientERGetClinicsServiceTimingsList.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
if (this.patientERGetClinicsServiceTimingsList != null) {
|
||||||
|
data['PatientER_GetClinicsServiceTimingsList'] = this
|
||||||
|
.patientERGetClinicsServiceTimingsList
|
||||||
|
.map((v) => v.toJson())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PatientERGetClinicsServiceTimingsList {
|
||||||
|
int iD;
|
||||||
|
int serviceID;
|
||||||
|
Null shiftID;
|
||||||
|
int dayOfWeek;
|
||||||
|
String dayOfWeekStr;
|
||||||
|
Null startTime;
|
||||||
|
Null endTime;
|
||||||
|
bool isActive;
|
||||||
|
String createdOn;
|
||||||
|
String createdBy;
|
||||||
|
bool projectOutSA;
|
||||||
|
String dayOfWeekStrN;
|
||||||
|
List<ShiftTimings> shiftTimings;
|
||||||
|
|
||||||
|
PatientERGetClinicsServiceTimingsList(
|
||||||
|
{this.iD,
|
||||||
|
this.serviceID,
|
||||||
|
this.shiftID,
|
||||||
|
this.dayOfWeek,
|
||||||
|
this.dayOfWeekStr,
|
||||||
|
this.startTime,
|
||||||
|
this.endTime,
|
||||||
|
this.isActive,
|
||||||
|
this.createdOn,
|
||||||
|
this.createdBy,
|
||||||
|
this.projectOutSA,
|
||||||
|
this.dayOfWeekStrN,
|
||||||
|
this.shiftTimings});
|
||||||
|
|
||||||
|
PatientERGetClinicsServiceTimingsList.fromJson(Map<String, dynamic> json) {
|
||||||
|
iD = json['ID'];
|
||||||
|
serviceID = json['ServiceID'];
|
||||||
|
shiftID = json['ShiftID'];
|
||||||
|
dayOfWeek = json['DayOfWeek'];
|
||||||
|
dayOfWeekStr = json['DayOfWeekStr'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
isActive = json['IsActive'];
|
||||||
|
createdOn = json['CreatedOn'];
|
||||||
|
createdBy = json['CreatedBy'];
|
||||||
|
projectOutSA = json['ProjectOutSA'];
|
||||||
|
dayOfWeekStrN = json['DayOfWeekStrN'];
|
||||||
|
if (json['ShiftTimings'] != null) {
|
||||||
|
shiftTimings = new List<ShiftTimings>();
|
||||||
|
json['ShiftTimings'].forEach((v) {
|
||||||
|
shiftTimings.add(new ShiftTimings.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['ID'] = this.iD;
|
||||||
|
data['ServiceID'] = this.serviceID;
|
||||||
|
data['ShiftID'] = this.shiftID;
|
||||||
|
data['DayOfWeek'] = this.dayOfWeek;
|
||||||
|
data['DayOfWeekStr'] = this.dayOfWeekStr;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['IsActive'] = this.isActive;
|
||||||
|
data['CreatedOn'] = this.createdOn;
|
||||||
|
data['CreatedBy'] = this.createdBy;
|
||||||
|
data['ProjectOutSA'] = this.projectOutSA;
|
||||||
|
data['DayOfWeekStrN'] = this.dayOfWeekStrN;
|
||||||
|
if (this.shiftTimings != null) {
|
||||||
|
data['ShiftTimings'] = this.shiftTimings.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShiftTimings {
|
||||||
|
String endTime;
|
||||||
|
int shiftID;
|
||||||
|
String startTime;
|
||||||
|
|
||||||
|
ShiftTimings({this.endTime, this.shiftID, this.startTime});
|
||||||
|
|
||||||
|
ShiftTimings.fromJson(Map<String, dynamic> json) {
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
shiftID = json['ShiftID'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['ShiftID'] = this.shiftID;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
class ERAppointmentFeesResponse {
|
||||||
|
GetERAppointmentFeesList getERAppointmentFeesList;
|
||||||
|
|
||||||
|
ERAppointmentFeesResponse({this.getERAppointmentFeesList});
|
||||||
|
|
||||||
|
ERAppointmentFeesResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
getERAppointmentFeesList = json['GetERAppointmentFeesList'] != null
|
||||||
|
? new GetERAppointmentFeesList.fromJson(
|
||||||
|
json['GetERAppointmentFeesList'])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
if (this.getERAppointmentFeesList != null) {
|
||||||
|
data['GetERAppointmentFeesList'] = this.getERAppointmentFeesList.toJson();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GetERAppointmentFeesList {
|
||||||
|
String amount;
|
||||||
|
String companyName;
|
||||||
|
bool isInsured;
|
||||||
|
bool isShowInsuranceUpdateModule;
|
||||||
|
String tax;
|
||||||
|
String total;
|
||||||
|
String currency;
|
||||||
|
|
||||||
|
GetERAppointmentFeesList(
|
||||||
|
{this.amount,
|
||||||
|
this.companyName,
|
||||||
|
this.isInsured,
|
||||||
|
this.isShowInsuranceUpdateModule,
|
||||||
|
this.tax,
|
||||||
|
this.total,
|
||||||
|
this.currency});
|
||||||
|
|
||||||
|
GetERAppointmentFeesList.fromJson(Map<String, dynamic> json) {
|
||||||
|
amount = json['Amount'];
|
||||||
|
companyName = json['CompanyName'];
|
||||||
|
isInsured = json['IsInsured'];
|
||||||
|
isShowInsuranceUpdateModule = json['IsShowInsuranceUpdateModule'];
|
||||||
|
tax = json['Tax'];
|
||||||
|
total = json['Total'];
|
||||||
|
currency = json['currency'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['Amount'] = this.amount;
|
||||||
|
data['CompanyName'] = this.companyName;
|
||||||
|
data['IsInsured'] = this.isInsured;
|
||||||
|
data['IsShowInsuranceUpdateModule'] = this.isShowInsuranceUpdateModule;
|
||||||
|
data['Tax'] = this.tax;
|
||||||
|
data['Total'] = this.total;
|
||||||
|
data['currency'] = this.currency;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
class IncomingCallData {
|
||||||
|
String msgID;
|
||||||
|
String notfID;
|
||||||
|
String notificationForeground;
|
||||||
|
String count;
|
||||||
|
String message;
|
||||||
|
String appointmentNo;
|
||||||
|
String title;
|
||||||
|
String projectID;
|
||||||
|
String notificationType;
|
||||||
|
String background;
|
||||||
|
String doctorname;
|
||||||
|
String clinicname;
|
||||||
|
String speciality;
|
||||||
|
String appointmentdate;
|
||||||
|
String appointmenttime;
|
||||||
|
String type;
|
||||||
|
String sessionId;
|
||||||
|
String identity;
|
||||||
|
String name;
|
||||||
|
String videoUrl;
|
||||||
|
String picture;
|
||||||
|
String isCall;
|
||||||
|
String sound;
|
||||||
|
|
||||||
|
IncomingCallData(
|
||||||
|
{this.msgID,
|
||||||
|
this.notfID,
|
||||||
|
this.notificationForeground,
|
||||||
|
this.count,
|
||||||
|
this.message,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.title,
|
||||||
|
this.projectID,
|
||||||
|
this.notificationType,
|
||||||
|
this.background,
|
||||||
|
this.doctorname,
|
||||||
|
this.clinicname,
|
||||||
|
this.speciality,
|
||||||
|
this.appointmentdate,
|
||||||
|
this.appointmenttime,
|
||||||
|
this.type,
|
||||||
|
this.sessionId,
|
||||||
|
this.identity,
|
||||||
|
this.name,
|
||||||
|
this.videoUrl,
|
||||||
|
this.picture,
|
||||||
|
this.isCall,
|
||||||
|
this.sound});
|
||||||
|
|
||||||
|
IncomingCallData.fromJson(Map<String, dynamic> json) {
|
||||||
|
msgID = json['msgID'];
|
||||||
|
notfID = json['notfID'];
|
||||||
|
notificationForeground = json['notification_foreground'];
|
||||||
|
count = json['count'];
|
||||||
|
message = json['message'];
|
||||||
|
appointmentNo = json['AppointmentNo'];
|
||||||
|
title = json['title'];
|
||||||
|
projectID = json['ProjectID'];
|
||||||
|
notificationType = json['NotificationType'];
|
||||||
|
background = json['background'];
|
||||||
|
doctorname = json['doctorname'];
|
||||||
|
clinicname = json['clinicname'];
|
||||||
|
speciality = json['speciality'];
|
||||||
|
appointmentdate = json['appointmentdate'];
|
||||||
|
appointmenttime = json['appointmenttime'];
|
||||||
|
type = json['type'];
|
||||||
|
sessionId = json['session_id'];
|
||||||
|
identity = json['identity'];
|
||||||
|
name = json['name'];
|
||||||
|
videoUrl = json['videoUrl'];
|
||||||
|
picture = json['picture'];
|
||||||
|
isCall = json['is_call'];
|
||||||
|
sound = json['sound'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['msgID'] = this.msgID;
|
||||||
|
data['notfID'] = this.notfID;
|
||||||
|
data['notification_foreground'] = this.notificationForeground;
|
||||||
|
data['count'] = this.count;
|
||||||
|
data['message'] = this.message;
|
||||||
|
data['AppointmentNo'] = this.appointmentNo;
|
||||||
|
data['title'] = this.title;
|
||||||
|
data['ProjectID'] = this.projectID;
|
||||||
|
data['NotificationType'] = this.notificationType;
|
||||||
|
data['background'] = this.background;
|
||||||
|
data['doctorname'] = this.doctorname;
|
||||||
|
data['clinicname'] = this.clinicname;
|
||||||
|
data['speciality'] = this.speciality;
|
||||||
|
data['appointmentdate'] = this.appointmentdate;
|
||||||
|
data['appointmenttime'] = this.appointmenttime;
|
||||||
|
data['type'] = this.type;
|
||||||
|
data['session_id'] = this.sessionId;
|
||||||
|
data['identity'] = this.identity;
|
||||||
|
data['name'] = this.name;
|
||||||
|
data['videoUrl'] = this.videoUrl;
|
||||||
|
data['picture'] = this.picture;
|
||||||
|
data['is_call'] = this.isCall;
|
||||||
|
data['sound'] = this.sound;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,129 @@
|
|||||||
|
class LiveCareClinicsListResponse {
|
||||||
|
List<PatientERGetClinicsList> patientERGetClinicsList;
|
||||||
|
|
||||||
|
LiveCareClinicsListResponse({this.patientERGetClinicsList});
|
||||||
|
|
||||||
|
LiveCareClinicsListResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
if (json['PatientER_GetClinicsList'] != null) {
|
||||||
|
patientERGetClinicsList = new List<PatientERGetClinicsList>();
|
||||||
|
json['PatientER_GetClinicsList'].forEach((v) {
|
||||||
|
patientERGetClinicsList.add(new PatientERGetClinicsList.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
if (this.patientERGetClinicsList != null) {
|
||||||
|
data['PatientER_GetClinicsList'] =
|
||||||
|
this.patientERGetClinicsList.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PatientERGetClinicsList {
|
||||||
|
int iD;
|
||||||
|
int serviceID;
|
||||||
|
String serviceName;
|
||||||
|
String serviceNameN;
|
||||||
|
int clinicID;
|
||||||
|
int age;
|
||||||
|
bool isCheckAgeBelow;
|
||||||
|
int gender;
|
||||||
|
bool isActive;
|
||||||
|
String createdOn;
|
||||||
|
String createdBy;
|
||||||
|
int isOnline;
|
||||||
|
Null endTime;
|
||||||
|
bool projectOutSA;
|
||||||
|
List<ShiftTimings> shiftTimings;
|
||||||
|
Null startTime;
|
||||||
|
|
||||||
|
PatientERGetClinicsList(
|
||||||
|
{this.iD,
|
||||||
|
this.serviceID,
|
||||||
|
this.serviceName,
|
||||||
|
this.serviceNameN,
|
||||||
|
this.clinicID,
|
||||||
|
this.age,
|
||||||
|
this.isCheckAgeBelow,
|
||||||
|
this.gender,
|
||||||
|
this.isActive,
|
||||||
|
this.createdOn,
|
||||||
|
this.createdBy,
|
||||||
|
this.isOnline,
|
||||||
|
this.endTime,
|
||||||
|
this.projectOutSA,
|
||||||
|
this.shiftTimings,
|
||||||
|
this.startTime});
|
||||||
|
|
||||||
|
PatientERGetClinicsList.fromJson(Map<String, dynamic> json) {
|
||||||
|
iD = json['ID'];
|
||||||
|
serviceID = json['ServiceID'];
|
||||||
|
serviceName = json['ServiceName'];
|
||||||
|
serviceNameN = json['ServiceNameN'];
|
||||||
|
clinicID = json['ClinicID'];
|
||||||
|
age = json['Age'];
|
||||||
|
isCheckAgeBelow = json['IsCheckAgeBelow'];
|
||||||
|
gender = json['Gender'];
|
||||||
|
isActive = json['IsActive'];
|
||||||
|
createdOn = json['CreatedOn'];
|
||||||
|
createdBy = json['CreatedBy'];
|
||||||
|
isOnline = json['IsOnline'];
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
projectOutSA = json['ProjectOutSA'];
|
||||||
|
if (json['ShiftTimings'] != null) {
|
||||||
|
shiftTimings = new List<ShiftTimings>();
|
||||||
|
json['ShiftTimings'].forEach((v) {
|
||||||
|
shiftTimings.add(new ShiftTimings.fromJson(v));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['ID'] = this.iD;
|
||||||
|
data['ServiceID'] = this.serviceID;
|
||||||
|
data['ServiceName'] = this.serviceName;
|
||||||
|
data['ServiceNameN'] = this.serviceNameN;
|
||||||
|
data['ClinicID'] = this.clinicID;
|
||||||
|
data['Age'] = this.age;
|
||||||
|
data['IsCheckAgeBelow'] = this.isCheckAgeBelow;
|
||||||
|
data['Gender'] = this.gender;
|
||||||
|
data['IsActive'] = this.isActive;
|
||||||
|
data['CreatedOn'] = this.createdOn;
|
||||||
|
data['CreatedBy'] = this.createdBy;
|
||||||
|
data['IsOnline'] = this.isOnline;
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['ProjectOutSA'] = this.projectOutSA;
|
||||||
|
if (this.shiftTimings != null) {
|
||||||
|
data['ShiftTimings'] = this.shiftTimings.map((v) => v.toJson()).toList();
|
||||||
|
}
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShiftTimings {
|
||||||
|
String endTime;
|
||||||
|
int shiftID;
|
||||||
|
String startTime;
|
||||||
|
|
||||||
|
ShiftTimings({this.endTime, this.shiftID, this.startTime});
|
||||||
|
|
||||||
|
ShiftTimings.fromJson(Map<String, dynamic> json) {
|
||||||
|
endTime = json['EndTime'];
|
||||||
|
shiftID = json['ShiftID'];
|
||||||
|
startTime = json['StartTime'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['EndTime'] = this.endTime;
|
||||||
|
data['ShiftID'] = this.shiftID;
|
||||||
|
data['StartTime'] = this.startTime;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/room_validators.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/twilio_enums.dart';
|
||||||
|
|
||||||
|
class RoomModel with RoomValidators {
|
||||||
|
final String name;
|
||||||
|
final bool isLoading;
|
||||||
|
final bool isSubmitted;
|
||||||
|
final String token;
|
||||||
|
final String identity;
|
||||||
|
final TwilioRoomType type;
|
||||||
|
|
||||||
|
RoomModel({
|
||||||
|
this.name,
|
||||||
|
this.isLoading = false,
|
||||||
|
this.isSubmitted = false,
|
||||||
|
this.token,
|
||||||
|
this.identity,
|
||||||
|
this.type = TwilioRoomType.groupSmall,
|
||||||
|
});
|
||||||
|
|
||||||
|
static String getTypeText(TwilioRoomType type) {
|
||||||
|
switch (type) {
|
||||||
|
case TwilioRoomType.peerToPeer:
|
||||||
|
return 'peer 2 peer';
|
||||||
|
break;
|
||||||
|
case TwilioRoomType.group:
|
||||||
|
return 'large (max 50 participants)';
|
||||||
|
break;
|
||||||
|
case TwilioRoomType.groupSmall:
|
||||||
|
return 'small (max 4 participants)';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
String get nameErrorText {
|
||||||
|
return isSubmitted && !nameValidator.isValid(name) ? invalidNameErrorText : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String get typeText {
|
||||||
|
return RoomModel.getTypeText(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get canSubmit {
|
||||||
|
return nameValidator.isValid(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
RoomModel copyWith({
|
||||||
|
String name,
|
||||||
|
bool isLoading,
|
||||||
|
bool isSubmitted,
|
||||||
|
String token,
|
||||||
|
String identity,
|
||||||
|
TwilioRoomType type,
|
||||||
|
}) {
|
||||||
|
return RoomModel(
|
||||||
|
name: name ?? this.name,
|
||||||
|
token: token ?? this.token,
|
||||||
|
identity: identity ?? this.identity,
|
||||||
|
isLoading: isLoading ?? this.isLoading,
|
||||||
|
isSubmitted: isSubmitted ?? this.isSubmitted,
|
||||||
|
type: type ?? this.type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/validators.dart';
|
||||||
|
|
||||||
|
mixin RoomValidators {
|
||||||
|
final StringValidator nameValidator = NonEmptyStringValidator();
|
||||||
|
final String invalidNameErrorText = 'Room name can\'t be empty';
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
enum TwilioRoomType {
|
||||||
|
peerToPeer,
|
||||||
|
group,
|
||||||
|
groupSmall,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TwilioRoomStatus {
|
||||||
|
completed,
|
||||||
|
inProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TwilioStatusCallbackMethod {
|
||||||
|
GET,
|
||||||
|
POST,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TwilioVideoCodec { VP8, H264 }
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
abstract class StringValidator {
|
||||||
|
bool isValid(String value);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NonEmptyStringValidator implements StringValidator {
|
||||||
|
@override
|
||||||
|
bool isValid(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value.isNotEmpty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ClippedVideo extends StatefulWidget {
|
||||||
|
final double width;
|
||||||
|
final double height;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const ClippedVideo({
|
||||||
|
Key key,
|
||||||
|
@required this.width,
|
||||||
|
@required this.height,
|
||||||
|
@required this.child,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ClippedVideoState createState() => _ClippedVideoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ClippedVideoState extends State<ClippedVideo> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: widget.width,
|
||||||
|
height: widget.height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.white24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: ClipRRect(
|
||||||
|
child: widget.child,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,238 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:after_layout/after_layout.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/widgets/circle_button.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ConferenceButtonBar extends StatefulWidget {
|
||||||
|
final VoidCallback onVideoEnabled;
|
||||||
|
final VoidCallback onAudioEnabled;
|
||||||
|
final VoidCallback onHangup;
|
||||||
|
final VoidCallback onSwitchCamera;
|
||||||
|
final VoidCallback onPersonAdd;
|
||||||
|
final VoidCallback onPersonRemove;
|
||||||
|
final void Function(double) onHeight;
|
||||||
|
final VoidCallback onHide;
|
||||||
|
final VoidCallback onShow;
|
||||||
|
final Stream<bool> videoEnabled;
|
||||||
|
final Stream<bool> audioEnabled;
|
||||||
|
|
||||||
|
const ConferenceButtonBar({
|
||||||
|
Key key,
|
||||||
|
this.onVideoEnabled,
|
||||||
|
this.onAudioEnabled,
|
||||||
|
this.onHangup,
|
||||||
|
this.onSwitchCamera,
|
||||||
|
this.onPersonAdd,
|
||||||
|
this.onPersonRemove,
|
||||||
|
@required this.videoEnabled,
|
||||||
|
@required this.audioEnabled,
|
||||||
|
this.onHeight,
|
||||||
|
this.onHide,
|
||||||
|
this.onShow,
|
||||||
|
}) : assert(videoEnabled != null),
|
||||||
|
assert(audioEnabled != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ConferenceButtonBarState createState() => _ConferenceButtonBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConferenceButtonBarState extends State<ConferenceButtonBar> with AfterLayoutMixin<ConferenceButtonBar> {
|
||||||
|
var _bottom = -100.0;
|
||||||
|
Timer _timer;
|
||||||
|
int _remaining;
|
||||||
|
var _videoEnabled = true;
|
||||||
|
var _audioEnabled = true;
|
||||||
|
double _hidden;
|
||||||
|
double _visible;
|
||||||
|
final _keyButtonBarHeight = GlobalKey();
|
||||||
|
|
||||||
|
final Duration timeout = const Duration(seconds: 5);
|
||||||
|
final Duration ms = const Duration(milliseconds: 1);
|
||||||
|
final Duration periodicDuration = const Duration(milliseconds: 100);
|
||||||
|
|
||||||
|
Timer startTimeout([int milliseconds]) {
|
||||||
|
final duration = milliseconds == null ? timeout : ms * milliseconds;
|
||||||
|
_remaining = duration.inMilliseconds;
|
||||||
|
return Timer.periodic(periodicDuration, (Timer timer) {
|
||||||
|
_remaining -= periodicDuration.inMilliseconds;
|
||||||
|
if (_remaining <= 0) {
|
||||||
|
timer.cancel();
|
||||||
|
_toggleBar();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _pauseTimer() {
|
||||||
|
if (_timer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_timer.cancel();
|
||||||
|
_timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resumeTimer() {
|
||||||
|
// resume the timer only when there is no timer active or when
|
||||||
|
// the bar is not already hidden.
|
||||||
|
if ((_timer != null && _timer.isActive) || _bottom == _hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_timer = startTimeout(_remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleBar() {
|
||||||
|
setState(() {
|
||||||
|
_bottom = _bottom == _visible ? _hidden : _visible;
|
||||||
|
if (_bottom == _visible && widget.onShow != null) {
|
||||||
|
widget.onShow();
|
||||||
|
}
|
||||||
|
if (_bottom == _hidden && widget.onHide != null) {
|
||||||
|
widget.onHide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleBarOnEnd() {
|
||||||
|
if (_timer != null) {
|
||||||
|
if (_timer.isActive) {
|
||||||
|
_timer.cancel();
|
||||||
|
}
|
||||||
|
_timer = null;
|
||||||
|
}
|
||||||
|
if (_bottom == 0) {
|
||||||
|
_timer = startTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_timer = startTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
_visible = MediaQuery.of(context).viewPadding.bottom;
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void afterFirstLayout(BuildContext context) {
|
||||||
|
final RenderBox renderBoxButtonBar = _keyButtonBarHeight.currentContext.findRenderObject();
|
||||||
|
final heightButtonBar = renderBoxButtonBar.size.height;
|
||||||
|
// Because the `didChangeDependencies` fires before the `afterFirstLayout`, we can use the `_visible` property here.
|
||||||
|
_hidden = -(heightButtonBar + _visible);
|
||||||
|
widget.onHeight(heightButtonBar);
|
||||||
|
_toggleBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
if (_timer != null && _timer.isActive) {
|
||||||
|
_timer.cancel();
|
||||||
|
_timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Positioned(
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: GestureDetector(
|
||||||
|
key: Key('show-hide-button-bar-gesture'),
|
||||||
|
behavior: HitTestBehavior.translucent,
|
||||||
|
onTapDown: (_) => _pauseTimer(),
|
||||||
|
onTapUp: (_) => _toggleBar(),
|
||||||
|
onTapCancel: () => _resumeTimer(),
|
||||||
|
child: Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
AnimatedPositioned(
|
||||||
|
key: Key('button-bar'),
|
||||||
|
bottom: _bottom,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.linear,
|
||||||
|
child: _buildRow(context),
|
||||||
|
onEnd: _toggleBarOnEnd,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPressed(VoidCallback callback) {
|
||||||
|
if (callback != null) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
if (_timer != null && _timer.isActive) {
|
||||||
|
_timer.cancel();
|
||||||
|
}
|
||||||
|
_timer = startTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRow(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
key: _keyButtonBarHeight,
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: <Widget>[
|
||||||
|
CircleButton(
|
||||||
|
child: StreamBuilder<bool>(
|
||||||
|
stream: widget.videoEnabled,
|
||||||
|
initialData: _videoEnabled,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
_videoEnabled = snapshot.data;
|
||||||
|
return Icon(
|
||||||
|
_videoEnabled ? Icons.videocam : Icons.videocam_off,
|
||||||
|
color: Colors.white,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
key: Key('camera-button'),
|
||||||
|
onPressed: () => _onPressed(widget.onVideoEnabled),
|
||||||
|
),
|
||||||
|
CircleButton(
|
||||||
|
child: StreamBuilder<bool>(
|
||||||
|
stream: widget.audioEnabled,
|
||||||
|
initialData: _audioEnabled,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
_audioEnabled = snapshot.data;
|
||||||
|
return Icon(
|
||||||
|
_audioEnabled ? Icons.mic : Icons.mic_off,
|
||||||
|
color: Colors.white,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
key: Key('microphone-button'),
|
||||||
|
onPressed: () => _onPressed(widget.onAudioEnabled),
|
||||||
|
),
|
||||||
|
CircleButton(
|
||||||
|
radius: 35,
|
||||||
|
child: const RotationTransition(
|
||||||
|
turns: AlwaysStoppedAnimation<double>(135 / 360),
|
||||||
|
child: Icon(
|
||||||
|
Icons.phone,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 40,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
color: Colors.red.withAlpha(200),
|
||||||
|
key: Key('hangup-button'),
|
||||||
|
onPressed: () => _onPressed(widget.onHangup),
|
||||||
|
),
|
||||||
|
CircleButton(
|
||||||
|
child: const Icon(Icons.switch_camera, color: Colors.white),
|
||||||
|
key: Key('switch-camera-button'),
|
||||||
|
onPressed: () => _onPressed(widget.onSwitchCamera),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,388 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/room_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/conference_button_bar.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/conference_room.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/draggable_publisher.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/widgets/noise_box.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/widgets/platform_alert_dialog.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:wakelock/wakelock.dart';
|
||||||
|
|
||||||
|
class ConferencePage extends StatefulWidget {
|
||||||
|
final RoomModel roomModel;
|
||||||
|
|
||||||
|
const ConferencePage({Key key, this.roomModel}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ConferencePageState createState() => _ConferencePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ConferencePageState extends State<ConferencePage> {
|
||||||
|
final StreamController<bool> _onButtonBarVisibleStreamController = StreamController<bool>.broadcast();
|
||||||
|
final StreamController<double> _onButtonBarHeightStreamController = StreamController<double>.broadcast();
|
||||||
|
ConferenceRoom _conferenceRoom;
|
||||||
|
StreamSubscription _onConferenceRoomException;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_lockInPortrait();
|
||||||
|
_connectToRoom();
|
||||||
|
_wakeLock(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _connectToRoom() async {
|
||||||
|
try {
|
||||||
|
final conferenceRoom = ConferenceRoom(
|
||||||
|
name: widget.roomModel.name,
|
||||||
|
token: widget.roomModel.token,
|
||||||
|
identity: widget.roomModel.identity,
|
||||||
|
);
|
||||||
|
await conferenceRoom.connect();
|
||||||
|
setState(() {
|
||||||
|
_conferenceRoom = conferenceRoom;
|
||||||
|
_onConferenceRoomException = _conferenceRoom.onException.listen((err) async {
|
||||||
|
await PlatformAlertDialog(
|
||||||
|
title: err is PlatformException ? err.message : 'An error occured',
|
||||||
|
content: err is PlatformException ? err.details : err.toString(),
|
||||||
|
defaultActionText: 'OK',
|
||||||
|
).show(context);
|
||||||
|
});
|
||||||
|
_conferenceRoom.addListener(_conferenceRoomUpdated);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
print(err);
|
||||||
|
await PlatformAlertDialog(
|
||||||
|
title: err is PlatformException ? err.message : 'An error occured',
|
||||||
|
content: err is PlatformException ? err.details : err.toString(),
|
||||||
|
defaultActionText: 'OK',
|
||||||
|
).show(context);
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _lockInPortrait() async {
|
||||||
|
await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
|
||||||
|
DeviceOrientation.portraitUp,
|
||||||
|
DeviceOrientation.portraitDown,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_freePortraitLock();
|
||||||
|
_wakeLock(false);
|
||||||
|
_disposeStreamsAndSubscriptions();
|
||||||
|
if (_conferenceRoom != null) _conferenceRoom.removeListener(_conferenceRoomUpdated);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _freePortraitLock() async {
|
||||||
|
await SystemChrome.setPreferredOrientations(<DeviceOrientation>[
|
||||||
|
DeviceOrientation.landscapeRight,
|
||||||
|
DeviceOrientation.landscapeLeft,
|
||||||
|
DeviceOrientation.portraitUp,
|
||||||
|
DeviceOrientation.portraitDown,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _disposeStreamsAndSubscriptions() async {
|
||||||
|
if (_onButtonBarVisibleStreamController != null) await _onButtonBarVisibleStreamController.close();
|
||||||
|
if (_onButtonBarHeightStreamController != null) await _onButtonBarHeightStreamController.close();
|
||||||
|
if (_onConferenceRoomException != null) await _onConferenceRoomException.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return WillPopScope(
|
||||||
|
onWillPop: () async => false,
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: Colors.black,
|
||||||
|
body: _conferenceRoom == null ? showProgress() : buildLayout(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutBuilder buildLayout() {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
return Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
_buildParticipants(context, constraints.biggest, _conferenceRoom),
|
||||||
|
ConferenceButtonBar(
|
||||||
|
audioEnabled: _conferenceRoom.onAudioEnabled,
|
||||||
|
videoEnabled: _conferenceRoom.onVideoEnabled,
|
||||||
|
onAudioEnabled: _conferenceRoom.toggleAudioEnabled,
|
||||||
|
onVideoEnabled: _conferenceRoom.toggleVideoEnabled,
|
||||||
|
onHangup: _onHangup,
|
||||||
|
onSwitchCamera: _conferenceRoom.switchCamera,
|
||||||
|
onPersonAdd: _onPersonAdd,
|
||||||
|
onPersonRemove: _onPersonRemove,
|
||||||
|
onHeight: _onHeightBar,
|
||||||
|
onShow: _onShowBar,
|
||||||
|
onHide: _onHideBar,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget showProgress() {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Center(child: CircularProgressIndicator()),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Connecting to the call...',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onHangup() async {
|
||||||
|
print('onHangup');
|
||||||
|
await _conferenceRoom.disconnect();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPersonAdd() {
|
||||||
|
print('onPersonAdd');
|
||||||
|
try {
|
||||||
|
_conferenceRoom.addDummy(
|
||||||
|
child: Stack(
|
||||||
|
children: <Widget>[
|
||||||
|
const Placeholder(),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
(_conferenceRoom.participants.length + 1).toString(),
|
||||||
|
style: const TextStyle(
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
blurRadius: 3.0,
|
||||||
|
color: Color.fromARGB(255, 0, 0, 0),
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
blurRadius: 8.0,
|
||||||
|
color: Color.fromARGB(255, 255, 255, 255),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
fontSize: 80,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} on PlatformException catch (err) {
|
||||||
|
PlatformAlertDialog(
|
||||||
|
title: err.message,
|
||||||
|
content: err.details,
|
||||||
|
defaultActionText: 'OK',
|
||||||
|
).show(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPersonRemove() {
|
||||||
|
print('onPersonRemove');
|
||||||
|
_conferenceRoom.removeDummy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildParticipants(BuildContext context, Size size, ConferenceRoom conferenceRoom) {
|
||||||
|
final children = <Widget>[];
|
||||||
|
final length = conferenceRoom.participants.length;
|
||||||
|
|
||||||
|
if (length <= 2) {
|
||||||
|
_buildOverlayLayout(context, size, children);
|
||||||
|
return Stack(children: children);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buildInCols(bool removeLocalBeforeChunking, bool moveLastOfEachRowToNextRow, int columns) {
|
||||||
|
_buildLayoutInGrid(
|
||||||
|
context,
|
||||||
|
size,
|
||||||
|
children,
|
||||||
|
removeLocalBeforeChunking: removeLocalBeforeChunking,
|
||||||
|
moveLastOfEachRowToNextRow: moveLastOfEachRowToNextRow,
|
||||||
|
columns: columns,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length <= 3) {
|
||||||
|
buildInCols(true, false, 1);
|
||||||
|
} else if (length == 5) {
|
||||||
|
buildInCols(false, true, 2);
|
||||||
|
} else if (length <= 6 || length == 8) {
|
||||||
|
buildInCols(false, false, 2);
|
||||||
|
} else if (length == 7 || length == 9) {
|
||||||
|
buildInCols(true, false, 2);
|
||||||
|
} else if (length == 10) {
|
||||||
|
buildInCols(false, true, 3);
|
||||||
|
} else if (length == 13 || length == 16) {
|
||||||
|
buildInCols(true, false, 3);
|
||||||
|
} else if (length <= 18) {
|
||||||
|
buildInCols(false, false, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _buildOverlayLayout(BuildContext context, Size size, List<Widget> children) {
|
||||||
|
final participants = _conferenceRoom.participants;
|
||||||
|
if (participants.length == 1) {
|
||||||
|
children.add(_buildNoiseBox());
|
||||||
|
} else {
|
||||||
|
final remoteParticipant = participants.firstWhere((ParticipantWidget participant) => participant.isRemote, orElse: () => null);
|
||||||
|
if (remoteParticipant != null) {
|
||||||
|
children.add(remoteParticipant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null);
|
||||||
|
if (localParticipant != null) {
|
||||||
|
children.add(DraggablePublisher(
|
||||||
|
key: Key('publisher'),
|
||||||
|
child: localParticipant,
|
||||||
|
availableScreenSize: size,
|
||||||
|
onButtonBarVisible: _onButtonBarVisibleStreamController.stream,
|
||||||
|
onButtonBarHeight: _onButtonBarHeightStreamController.stream,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _buildLayoutInGrid(
|
||||||
|
BuildContext context,
|
||||||
|
Size size,
|
||||||
|
List<Widget> children, {
|
||||||
|
bool removeLocalBeforeChunking = false,
|
||||||
|
bool moveLastOfEachRowToNextRow = false,
|
||||||
|
int columns = 2,
|
||||||
|
}) {
|
||||||
|
final participants = _conferenceRoom.participants;
|
||||||
|
ParticipantWidget localParticipant;
|
||||||
|
if (removeLocalBeforeChunking) {
|
||||||
|
localParticipant = participants.firstWhere((ParticipantWidget participant) => !participant.isRemote, orElse: () => null);
|
||||||
|
if (localParticipant != null) {
|
||||||
|
participants.remove(localParticipant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final chunkedParticipants = chunk(array: participants, size: columns);
|
||||||
|
if (localParticipant != null) {
|
||||||
|
chunkedParticipants.last.add(localParticipant);
|
||||||
|
participants.add(localParticipant);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moveLastOfEachRowToNextRow) {
|
||||||
|
for (var i = 0; i < chunkedParticipants.length - 1; i++) {
|
||||||
|
var participant = chunkedParticipants[i].removeLast();
|
||||||
|
chunkedParticipants[i + 1].insert(0, participant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final participantChunk in chunkedParticipants) {
|
||||||
|
final rowChildren = <Widget>[];
|
||||||
|
for (final participant in participantChunk) {
|
||||||
|
rowChildren.add(
|
||||||
|
Container(
|
||||||
|
width: size.width / participantChunk.length,
|
||||||
|
height: size.height / chunkedParticipants.length,
|
||||||
|
child: participant,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
children.add(
|
||||||
|
Container(
|
||||||
|
height: size.height / chunkedParticipants.length,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: rowChildren,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NoiseBox _buildNoiseBox() {
|
||||||
|
return NoiseBox(
|
||||||
|
density: NoiseBoxDensity.xLow,
|
||||||
|
backgroundColor: Colors.grey.shade900,
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black54,
|
||||||
|
width: double.infinity,
|
||||||
|
height: 40,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Waiting for another participant to connect to the call...',
|
||||||
|
key: Key('text-wait'),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<T>> chunk<T>({@required List<T> array, @required int size}) {
|
||||||
|
final result = <List<T>>[];
|
||||||
|
if (array.isEmpty || size <= 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
var first = 0;
|
||||||
|
var last = size;
|
||||||
|
final totalLoop = array.length % size == 0 ? array.length ~/ size : array.length ~/ size + 1;
|
||||||
|
for (var i = 0; i < totalLoop; i++) {
|
||||||
|
if (last > array.length) {
|
||||||
|
result.add(array.sublist(first, array.length));
|
||||||
|
} else {
|
||||||
|
result.add(array.sublist(first, last));
|
||||||
|
}
|
||||||
|
first = last;
|
||||||
|
last = last + size;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onHeightBar(double height) {
|
||||||
|
_onButtonBarHeightStreamController.add(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onShowBar() {
|
||||||
|
setState(() {
|
||||||
|
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom, SystemUiOverlay.top]);
|
||||||
|
});
|
||||||
|
_onButtonBarVisibleStreamController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onHideBar() {
|
||||||
|
setState(() {
|
||||||
|
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
|
||||||
|
});
|
||||||
|
_onButtonBarVisibleStreamController.add(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _wakeLock(bool enable) async {
|
||||||
|
try {
|
||||||
|
return await (enable ? Wakelock.enable() : Wakelock.disable());
|
||||||
|
} catch (err) {
|
||||||
|
print('Unable to change the Wakelock and set it to $enable');
|
||||||
|
print(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _conferenceRoomUpdated() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,530 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/participant_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:twilio_programmable_video/twilio_programmable_video.dart';
|
||||||
|
|
||||||
|
class ConferenceRoom with ChangeNotifier {
|
||||||
|
final String name;
|
||||||
|
final String token;
|
||||||
|
final String identity;
|
||||||
|
|
||||||
|
final StreamController<bool> _onAudioEnabledStreamController = StreamController<bool>.broadcast();
|
||||||
|
Stream<bool> onAudioEnabled;
|
||||||
|
final StreamController<bool> _onVideoEnabledStreamController = StreamController<bool>.broadcast();
|
||||||
|
Stream<bool> onVideoEnabled;
|
||||||
|
final StreamController<Exception> _onExceptionStreamController = StreamController<Exception>.broadcast();
|
||||||
|
Stream<Exception> onException;
|
||||||
|
|
||||||
|
final Completer<Room> _completer = Completer<Room>();
|
||||||
|
|
||||||
|
final List<ParticipantWidget> _participants = [];
|
||||||
|
final List<ParticipantBuffer> _participantBuffer = [];
|
||||||
|
final List<StreamSubscription> _streamSubscriptions = [];
|
||||||
|
final List<RemoteDataTrack> _dataTracks = [];
|
||||||
|
final List<String> _messages = [];
|
||||||
|
|
||||||
|
CameraCapturer _cameraCapturer;
|
||||||
|
Room _room;
|
||||||
|
Timer _timer;
|
||||||
|
|
||||||
|
ConferenceRoom({
|
||||||
|
@required this.name,
|
||||||
|
@required this.token,
|
||||||
|
@required this.identity,
|
||||||
|
}) {
|
||||||
|
onAudioEnabled = _onAudioEnabledStreamController.stream;
|
||||||
|
onVideoEnabled = _onVideoEnabledStreamController.stream;
|
||||||
|
onException = _onExceptionStreamController.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ParticipantWidget> get participants {
|
||||||
|
return [..._participants];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Room> connect() async {
|
||||||
|
print('ConferenceRoom.connect()');
|
||||||
|
try {
|
||||||
|
await TwilioProgrammableVideo.debug(dart: true, native: true);
|
||||||
|
await TwilioProgrammableVideo.setSpeakerphoneOn(true);
|
||||||
|
|
||||||
|
_cameraCapturer = CameraCapturer(CameraSource.FRONT_CAMERA);
|
||||||
|
var connectOptions = ConnectOptions(
|
||||||
|
token,
|
||||||
|
roomName: name,
|
||||||
|
preferredAudioCodecs: [OpusCodec()],
|
||||||
|
audioTracks: [LocalAudioTrack(true)],
|
||||||
|
dataTracks: [LocalDataTrack()],
|
||||||
|
videoTracks: [LocalVideoTrack(true, _cameraCapturer)],
|
||||||
|
enableDominantSpeaker: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
_room = await TwilioProgrammableVideo.connect(connectOptions);
|
||||||
|
|
||||||
|
_streamSubscriptions.add(_room.onConnected.listen(_onConnected));
|
||||||
|
_streamSubscriptions.add(_room.onConnectFailure.listen(_onConnectFailure));
|
||||||
|
|
||||||
|
return _completer.future;
|
||||||
|
} catch (err) {
|
||||||
|
print(err);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> disconnect() async {
|
||||||
|
print('ConferenceRoom.disconnect()');
|
||||||
|
if (_timer != null) {
|
||||||
|
_timer.cancel();
|
||||||
|
}
|
||||||
|
await _room.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
print('ConferenceRoom.dispose()');
|
||||||
|
_disposeStreamsAndSubscriptions();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _disposeStreamsAndSubscriptions() async {
|
||||||
|
await _onAudioEnabledStreamController.close();
|
||||||
|
await _onVideoEnabledStreamController.close();
|
||||||
|
await _onExceptionStreamController.close();
|
||||||
|
for (var streamSubscription in _streamSubscriptions) {
|
||||||
|
await streamSubscription.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendMessage(String message) async {
|
||||||
|
final tracks = _room.localParticipant.localDataTracks;
|
||||||
|
final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
|
||||||
|
if (localDataTrack == null || _messages.isNotEmpty) {
|
||||||
|
print('ConferenceRoom.sendMessage => Track is not available yet, buffering message.');
|
||||||
|
_messages.add(message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await localDataTrack.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendBufferMessage(ByteBuffer message) async {
|
||||||
|
final tracks = _room.localParticipant.localDataTracks;
|
||||||
|
final localDataTrack = tracks.isEmpty ? null : tracks[0].localDataTrack;
|
||||||
|
if (localDataTrack == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await localDataTrack.sendBuffer(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> toggleVideoEnabled() async {
|
||||||
|
final tracks = _room.localParticipant.localVideoTracks;
|
||||||
|
final localVideoTrack = tracks.isEmpty ? null : tracks[0].localVideoTrack;
|
||||||
|
if (localVideoTrack == null) {
|
||||||
|
print('ConferenceRoom.toggleVideoEnabled() => Track is not available yet!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await localVideoTrack.enable(!localVideoTrack.isEnabled);
|
||||||
|
|
||||||
|
var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var participant = _participants[index];
|
||||||
|
_participants.replaceRange(
|
||||||
|
index,
|
||||||
|
index + 1,
|
||||||
|
[
|
||||||
|
participant.copyWith(videoEnabled: localVideoTrack.isEnabled),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
print('ConferenceRoom.toggleVideoEnabled() => ${localVideoTrack.isEnabled}');
|
||||||
|
_onVideoEnabledStreamController.add(localVideoTrack.isEnabled);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> toggleAudioEnabled() async {
|
||||||
|
final tracks = _room.localParticipant.localAudioTracks;
|
||||||
|
final localAudioTrack = tracks.isEmpty ? null : tracks[0].localAudioTrack;
|
||||||
|
if (localAudioTrack == null) {
|
||||||
|
print('ConferenceRoom.toggleAudioEnabled() => Track is not available yet!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await localAudioTrack.enable(!localAudioTrack.isEnabled);
|
||||||
|
|
||||||
|
var index = _participants.indexWhere((ParticipantWidget participant) => !participant.isRemote);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var participant = _participants[index];
|
||||||
|
_participants.replaceRange(
|
||||||
|
index,
|
||||||
|
index + 1,
|
||||||
|
[
|
||||||
|
participant.copyWith(audioEnabled: localAudioTrack.isEnabled),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
print('ConferenceRoom.toggleAudioEnabled() => ${localAudioTrack.isEnabled}');
|
||||||
|
_onAudioEnabledStreamController.add(localAudioTrack.isEnabled);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> switchCamera() async {
|
||||||
|
print('ConferenceRoom.switchCamera()');
|
||||||
|
try {
|
||||||
|
await _cameraCapturer.switchCamera();
|
||||||
|
} on FormatException catch (e) {
|
||||||
|
print(
|
||||||
|
'ConferenceRoom.switchCamera() failed because of FormatException with message: ${e.message}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addDummy({Widget child}) {
|
||||||
|
print('ConferenceRoom.addDummy()');
|
||||||
|
if (_participants.length >= 18) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'ConferenceRoom.maximumReached',
|
||||||
|
message: 'Maximum reached',
|
||||||
|
details: 'Currently the lay-out can only render a maximum of 18 participants',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_participants.insert(
|
||||||
|
0,
|
||||||
|
ParticipantWidget(
|
||||||
|
id: (_participants.length + 1).toString(),
|
||||||
|
child: child,
|
||||||
|
isRemote: true,
|
||||||
|
audioEnabled: true,
|
||||||
|
videoEnabled: true,
|
||||||
|
isDummy: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeDummy() {
|
||||||
|
print('ConferenceRoom.removeDummy()');
|
||||||
|
var dummy = _participants.firstWhere((participant) => participant.isDummy, orElse: () => null);
|
||||||
|
if (dummy != null) {
|
||||||
|
_participants.remove(dummy);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onConnected(Room room) {
|
||||||
|
print('ConferenceRoom._onConnected => state: ${room.state}');
|
||||||
|
|
||||||
|
// When connected for the first time, add remote participant listeners
|
||||||
|
_streamSubscriptions.add(_room.onParticipantConnected.listen(_onParticipantConnected));
|
||||||
|
_streamSubscriptions.add(_room.onParticipantDisconnected.listen(_onParticipantDisconnected));
|
||||||
|
_streamSubscriptions.add(_room.onDominantSpeakerChange.listen(_onDominantSpeakerChanged));
|
||||||
|
// Only add ourselves when connected for the first time too.
|
||||||
|
_participants.add(
|
||||||
|
_buildParticipant(
|
||||||
|
child: room.localParticipant.localVideoTracks[0].localVideoTrack.widget(),
|
||||||
|
id: identity,
|
||||||
|
audioEnabled: true,
|
||||||
|
videoEnabled: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
for (final remoteParticipant in room.remoteParticipants) {
|
||||||
|
var participant = _participants.firstWhere((participant) => participant.id == remoteParticipant.sid, orElse: () => null);
|
||||||
|
if (participant == null) {
|
||||||
|
print('Adding participant that was already present in the room ${remoteParticipant.sid}, before I connected');
|
||||||
|
_addRemoteParticipantListeners(remoteParticipant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We have to listen for the [onDataTrackPublished] event on the [LocalParticipant] in
|
||||||
|
// order to be able to use the [send] method.
|
||||||
|
_streamSubscriptions.add(room.localParticipant.onDataTrackPublished.listen(_onLocalDataTrackPublished));
|
||||||
|
notifyListeners();
|
||||||
|
_completer.complete(room);
|
||||||
|
|
||||||
|
_timer = Timer.periodic(const Duration(minutes: 1), (_) {
|
||||||
|
// Let's see if we can send some data over the DataTrack API
|
||||||
|
sendMessage('And another minute has passed since I connected...');
|
||||||
|
// Also try the ByteBuffer way of sending data
|
||||||
|
final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits;
|
||||||
|
var bytes = Uint8List.fromList(list);
|
||||||
|
sendBufferMessage(bytes.buffer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onLocalDataTrackPublished(LocalDataTrackPublishedEvent event) {
|
||||||
|
// Send buffered messages, if any...
|
||||||
|
while (_messages.isNotEmpty) {
|
||||||
|
var message = _messages.removeAt(0);
|
||||||
|
print('Sending buffered message: $message');
|
||||||
|
event.localDataTrackPublication.localDataTrack.send(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onConnectFailure(RoomConnectFailureEvent event) {
|
||||||
|
print('ConferenceRoom._onConnectFailure: ${event.exception}');
|
||||||
|
_completer.completeError(event.exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDominantSpeakerChanged(DominantSpeakerChangedEvent event) {
|
||||||
|
print('ConferenceRoom._onDominantSpeakerChanged: ${event.remoteParticipant.identity}');
|
||||||
|
var oldDominantParticipant = _participants.firstWhere((p) => p.isDominant, orElse: () => null);
|
||||||
|
if (oldDominantParticipant != null) {
|
||||||
|
var oldDominantParticipantIndex = _participants.indexOf(oldDominantParticipant);
|
||||||
|
_participants.replaceRange(oldDominantParticipantIndex, oldDominantParticipantIndex + 1, [oldDominantParticipant.copyWith(isDominant: false)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var newDominantParticipant = _participants.firstWhere((p) => p.id == event.remoteParticipant.sid);
|
||||||
|
var newDominantParticipantIndex = _participants.indexOf(newDominantParticipant);
|
||||||
|
_participants.replaceRange(newDominantParticipantIndex, newDominantParticipantIndex + 1, [newDominantParticipant.copyWith(isDominant: true)]);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onParticipantConnected(RoomParticipantConnectedEvent event) {
|
||||||
|
print('ConferenceRoom._onParticipantConnected, ${event.remoteParticipant.sid}');
|
||||||
|
_addRemoteParticipantListeners(event.remoteParticipant);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onParticipantDisconnected(RoomParticipantDisconnectedEvent event) {
|
||||||
|
print('ConferenceRoom._onParticipantDisconnected: ${event.remoteParticipant.sid}');
|
||||||
|
_participants.removeWhere((ParticipantWidget p) => p.id == event.remoteParticipant.sid);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticipantWidget _buildParticipant({
|
||||||
|
@required Widget child,
|
||||||
|
@required String id,
|
||||||
|
@required bool audioEnabled,
|
||||||
|
@required bool videoEnabled,
|
||||||
|
RemoteParticipant remoteParticipant,
|
||||||
|
}) {
|
||||||
|
return ParticipantWidget(
|
||||||
|
id: remoteParticipant?.sid,
|
||||||
|
isRemote: remoteParticipant != null,
|
||||||
|
child: child,
|
||||||
|
audioEnabled: audioEnabled,
|
||||||
|
videoEnabled: videoEnabled,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addRemoteParticipantListeners(RemoteParticipant remoteParticipant) {
|
||||||
|
print('ConferenceRoom._addRemoteParticipantListeners() => Adding listeners to remoteParticipant ${remoteParticipant.sid}');
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackDisabled.listen(_onAudioTrackDisabled));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackEnabled.listen(_onAudioTrackEnabled));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackPublished.listen(_onAudioTrackPublished));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackSubscribed.listen(_onAudioTrackSubscribed));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackSubscriptionFailed.listen(_onAudioTrackSubscriptionFailed));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackUnpublished.listen(_onAudioTrackUnpublished));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onAudioTrackUnsubscribed.listen(_onAudioTrackUnsubscribed));
|
||||||
|
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onDataTrackPublished.listen(_onDataTrackPublished));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onDataTrackSubscribed.listen(_onDataTrackSubscribed));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onDataTrackSubscriptionFailed.listen(_onDataTrackSubscriptionFailed));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onDataTrackUnpublished.listen(_onDataTrackUnpublished));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onDataTrackUnsubscribed.listen(_onDataTrackUnsubscribed));
|
||||||
|
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackDisabled.listen(_onVideoTrackDisabled));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackEnabled.listen(_onVideoTrackEnabled));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackPublished.listen(_onVideoTrackPublished));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackSubscribed.listen(_onVideoTrackSubscribed));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackSubscriptionFailed.listen(_onVideoTrackSubscriptionFailed));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackUnpublished.listen(_onVideoTrackUnpublished));
|
||||||
|
_streamSubscriptions.add(remoteParticipant.onVideoTrackUnsubscribed.listen(_onVideoTrackUnsubscribed));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackDisabled(RemoteAudioTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
|
||||||
|
_setRemoteAudioEnabled(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackEnabled(RemoteAudioTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}, isEnabled: ${event.remoteAudioTrackPublication.isTrackEnabled}');
|
||||||
|
_setRemoteAudioEnabled(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackPublished(RemoteAudioTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackPublished(), ${event.remoteParticipant.sid}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackSubscribed(RemoteAudioTrackSubscriptionEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
||||||
|
_addOrUpdateParticipant(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackSubscriptionFailed(RemoteAudioTrackSubscriptionFailedEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
||||||
|
_onExceptionStreamController.add(
|
||||||
|
PlatformException(
|
||||||
|
code: 'ConferenceRoom.audioTrackSubscriptionFailed',
|
||||||
|
message: 'AudioTrack Subscription Failed',
|
||||||
|
details: event.exception.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackUnpublished(RemoteAudioTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrackPublication.trackSid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onAudioTrackUnsubscribed(RemoteAudioTrackSubscriptionEvent event) {
|
||||||
|
print('ConferenceRoom._onAudioTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteAudioTrack.sid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDataTrackPublished(RemoteDataTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onDataTrackPublished(), ${event.remoteParticipant.sid}}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDataTrackSubscribed(RemoteDataTrackSubscriptionEvent event) {
|
||||||
|
print('ConferenceRoom._onDataTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
||||||
|
final dataTrack = event.remoteDataTrackPublication.remoteDataTrack;
|
||||||
|
_dataTracks.add(dataTrack);
|
||||||
|
_streamSubscriptions.add(dataTrack.onMessage.listen(_onMessage));
|
||||||
|
_streamSubscriptions.add(dataTrack.onBufferMessage.listen(_onBufferMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDataTrackSubscriptionFailed(RemoteDataTrackSubscriptionFailedEvent event) {
|
||||||
|
print('ConferenceRoom._onDataTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
||||||
|
_onExceptionStreamController.add(
|
||||||
|
PlatformException(
|
||||||
|
code: 'ConferenceRoom.dataTrackSubscriptionFailed',
|
||||||
|
message: 'DataTrack Subscription Failed',
|
||||||
|
details: event.exception.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDataTrackUnpublished(RemoteDataTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onDataTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteDataTrackPublication.trackSid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDataTrackUnsubscribed(RemoteDataTrackSubscriptionEvent event) {
|
||||||
|
print('ConferenceRoom._onDataTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteDataTrack.sid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackDisabled(RemoteVideoTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackDisabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
|
||||||
|
_setRemoteVideoEnabled(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackEnabled(RemoteVideoTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackEnabled(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}, isEnabled: ${event.remoteVideoTrackPublication.isTrackEnabled}');
|
||||||
|
_setRemoteVideoEnabled(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackPublished(RemoteVideoTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackPublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackSubscribed(RemoteVideoTrackSubscriptionEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackSubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
|
||||||
|
_addOrUpdateParticipant(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackSubscriptionFailed(RemoteVideoTrackSubscriptionFailedEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackSubscriptionFailed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
||||||
|
_onExceptionStreamController.add(
|
||||||
|
PlatformException(
|
||||||
|
code: 'ConferenceRoom.videoTrackSubscriptionFailed',
|
||||||
|
message: 'VideoTrack Subscription Failed',
|
||||||
|
details: event.exception.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackUnpublished(RemoteVideoTrackEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackUnpublished(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrackPublication.trackSid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVideoTrackUnsubscribed(RemoteVideoTrackSubscriptionEvent event) {
|
||||||
|
print('ConferenceRoom._onVideoTrackUnsubscribed(), ${event.remoteParticipant.sid}, ${event.remoteVideoTrack.sid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onMessage(RemoteDataTrackStringMessageEvent event) {
|
||||||
|
print('onMessage => ${event.remoteDataTrack.sid}, ${event.message}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onBufferMessage(RemoteDataTrackBufferMessageEvent event) {
|
||||||
|
print('onBufferMessage => ${event.remoteDataTrack.sid}, ${String.fromCharCodes(event.message.asUint8List())}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setRemoteAudioEnabled(RemoteAudioTrackEvent event) {
|
||||||
|
if (event.remoteAudioTrackPublication == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var participant = _participants[index];
|
||||||
|
_participants.replaceRange(
|
||||||
|
index,
|
||||||
|
index + 1,
|
||||||
|
[
|
||||||
|
participant.copyWith(audioEnabled: event.remoteAudioTrackPublication.isTrackEnabled),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setRemoteVideoEnabled(RemoteVideoTrackEvent event) {
|
||||||
|
if (event.remoteVideoTrackPublication == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var index = _participants.indexWhere((ParticipantWidget participant) => participant.id == event.remoteParticipant.sid);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var participant = _participants[index];
|
||||||
|
_participants.replaceRange(
|
||||||
|
index,
|
||||||
|
index + 1,
|
||||||
|
[
|
||||||
|
participant.copyWith(videoEnabled: event.remoteVideoTrackPublication.isTrackEnabled),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addOrUpdateParticipant(RemoteParticipantEvent event) {
|
||||||
|
print('ConferenceRoom._addOrUpdateParticipant(), ${event.remoteParticipant.sid}');
|
||||||
|
final participant = _participants.firstWhere(
|
||||||
|
(ParticipantWidget participant) => participant.id == event.remoteParticipant.sid,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
if (participant != null) {
|
||||||
|
print('Participant found: ${participant.id}, updating A/V enabled values');
|
||||||
|
_setRemoteVideoEnabled(event);
|
||||||
|
_setRemoteAudioEnabled(event);
|
||||||
|
} else {
|
||||||
|
final bufferedParticipant = _participantBuffer.firstWhere(
|
||||||
|
(ParticipantBuffer participant) => participant.id == event.remoteParticipant.sid,
|
||||||
|
orElse: () => null,
|
||||||
|
);
|
||||||
|
if (bufferedParticipant != null) {
|
||||||
|
_participantBuffer.remove(bufferedParticipant);
|
||||||
|
} else if (event is RemoteAudioTrackEvent) {
|
||||||
|
print('Audio subscription came first, waiting for the video subscription...');
|
||||||
|
_participantBuffer.add(
|
||||||
|
ParticipantBuffer(
|
||||||
|
id: event.remoteParticipant.sid,
|
||||||
|
audioEnabled: event.remoteAudioTrackPublication?.remoteAudioTrack?.isEnabled ?? true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event is RemoteVideoTrackSubscriptionEvent) {
|
||||||
|
print('New participant, adding: ${event.remoteParticipant.sid}');
|
||||||
|
_participants.insert(
|
||||||
|
0,
|
||||||
|
_buildParticipant(
|
||||||
|
child: event.remoteVideoTrack.widget(),
|
||||||
|
id: event.remoteParticipant.sid,
|
||||||
|
remoteParticipant: event.remoteParticipant,
|
||||||
|
audioEnabled: bufferedParticipant?.audioEnabled ?? true,
|
||||||
|
videoEnabled: event.remoteVideoTrackPublication?.remoteVideoTrack?.isEnabled ?? true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/clipped_video.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DraggablePublisher extends StatefulWidget {
|
||||||
|
final Size availableScreenSize;
|
||||||
|
final Widget child;
|
||||||
|
final double scaleFactor;
|
||||||
|
final Stream<bool> onButtonBarVisible;
|
||||||
|
final Stream<double> onButtonBarHeight;
|
||||||
|
|
||||||
|
const DraggablePublisher({
|
||||||
|
Key key,
|
||||||
|
@required this.availableScreenSize,
|
||||||
|
this.child,
|
||||||
|
@required this.onButtonBarVisible,
|
||||||
|
@required this.onButtonBarHeight,
|
||||||
|
|
||||||
|
/// The portion of the screen the DraggableWidget should use.
|
||||||
|
this.scaleFactor = .25,
|
||||||
|
}) : assert(scaleFactor != null && scaleFactor > 0 && scaleFactor <= .4),
|
||||||
|
assert(availableScreenSize != null),
|
||||||
|
assert(onButtonBarVisible != null),
|
||||||
|
assert(onButtonBarHeight != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DraggablePublisherState createState() => _DraggablePublisherState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DraggablePublisherState extends State<DraggablePublisher> {
|
||||||
|
bool _isButtonBarVisible = true;
|
||||||
|
double _buttonBarHeight = 0;
|
||||||
|
double _width;
|
||||||
|
double _height;
|
||||||
|
double _top;
|
||||||
|
double _left;
|
||||||
|
double _viewPaddingTop;
|
||||||
|
double _viewPaddingBottom;
|
||||||
|
final double _padding = 8.0;
|
||||||
|
final Duration _duration300ms = const Duration(milliseconds: 300);
|
||||||
|
final Duration _duration0ms = const Duration(milliseconds: 0);
|
||||||
|
Duration _duration;
|
||||||
|
StreamSubscription _streamSubscription;
|
||||||
|
StreamSubscription _streamHeightSubscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_duration = _duration300ms;
|
||||||
|
_width = widget.availableScreenSize.width * widget.scaleFactor;
|
||||||
|
_height = _width * (widget.availableScreenSize.height / widget.availableScreenSize.width);
|
||||||
|
_top = widget.availableScreenSize.height - (_buttonBarHeight + _padding) - _height;
|
||||||
|
_left = widget.availableScreenSize.width - _padding - _width;
|
||||||
|
|
||||||
|
_streamSubscription = widget.onButtonBarVisible.listen(_buttonBarVisible);
|
||||||
|
_streamHeightSubscription = widget.onButtonBarHeight.listen(_getButtonBarHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
_viewPaddingTop = mediaQuery.viewPadding.top;
|
||||||
|
_viewPaddingBottom = mediaQuery.viewPadding.bottom;
|
||||||
|
super.didChangeDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_streamSubscription.cancel();
|
||||||
|
_streamHeightSubscription.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _getButtonBarHeight(double height) {
|
||||||
|
setState(() {
|
||||||
|
_buttonBarHeight = height;
|
||||||
|
_positionWidget();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _buttonBarVisible(bool visible) {
|
||||||
|
if (!mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isButtonBarVisible = visible;
|
||||||
|
if (_duration == _duration300ms) {
|
||||||
|
// only position the widget when we are not currently dragging it around
|
||||||
|
_positionWidget();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AnimatedPositioned(
|
||||||
|
top: _top,
|
||||||
|
left: _left,
|
||||||
|
width: _width,
|
||||||
|
height: _height,
|
||||||
|
duration: _duration,
|
||||||
|
child: Listener(
|
||||||
|
onPointerDown: (_) => _duration = _duration0ms,
|
||||||
|
onPointerMove: (PointerMoveEvent event) {
|
||||||
|
setState(() {
|
||||||
|
_left = (_left + event.delta.dx).roundToDouble();
|
||||||
|
_top = (_top + event.delta.dy).roundToDouble();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onPointerUp: (_) => _positionWidget(),
|
||||||
|
onPointerCancel: (_) => _positionWidget(),
|
||||||
|
child: ClippedVideo(
|
||||||
|
height: _height,
|
||||||
|
width: _width,
|
||||||
|
child: widget.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getCurrentStatusBarHeight() {
|
||||||
|
if (_isButtonBarVisible) {
|
||||||
|
return _viewPaddingTop;
|
||||||
|
}
|
||||||
|
final _defaultViewPaddingTop = Platform.isIOS ? 20.0 : Platform.isAndroid ? 24.0 : 0.0;
|
||||||
|
if (_viewPaddingTop > _defaultViewPaddingTop) {
|
||||||
|
// There must be a hardware notch in the display.
|
||||||
|
return _viewPaddingTop;
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getCurrentButtonBarHeight() {
|
||||||
|
if (_isButtonBarVisible) {
|
||||||
|
return _buttonBarHeight + _viewPaddingBottom;
|
||||||
|
}
|
||||||
|
return _viewPaddingBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _positionWidget() {
|
||||||
|
// Determine the center of the object being dragged so we can decide
|
||||||
|
// in which corner the object should be placed.
|
||||||
|
var dx = (_width / 2) + _left;
|
||||||
|
dx = dx < 0 ? 0 : dx >= widget.availableScreenSize.width ? widget.availableScreenSize.width - 1 : dx;
|
||||||
|
var dy = (_height / 2) + _top;
|
||||||
|
dy = dy < 0 ? 0 : dy >= widget.availableScreenSize.height ? widget.availableScreenSize.height - 1 : dy;
|
||||||
|
final draggableCenter = Offset(dx, dy);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_duration = _duration300ms;
|
||||||
|
if (Rect.fromLTRB(0, 0, widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
|
||||||
|
// Top-left
|
||||||
|
_top = _getCurrentStatusBarHeight() + _padding;
|
||||||
|
_left = _padding;
|
||||||
|
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, 0, widget.availableScreenSize.width, widget.availableScreenSize.height / 2).contains(draggableCenter)) {
|
||||||
|
// Top-right
|
||||||
|
_top = _getCurrentStatusBarHeight() + _padding;
|
||||||
|
_left = widget.availableScreenSize.width - _padding - _width;
|
||||||
|
} else if (Rect.fromLTRB(0, widget.availableScreenSize.height / 2, widget.availableScreenSize.width / 2, widget.availableScreenSize.height).contains(draggableCenter)) {
|
||||||
|
// Bottom-left
|
||||||
|
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
|
||||||
|
_left = _padding;
|
||||||
|
} else if (Rect.fromLTRB(widget.availableScreenSize.width / 2, widget.availableScreenSize.height / 2, widget.availableScreenSize.width, widget.availableScreenSize.height).contains(draggableCenter)) {
|
||||||
|
// Bottom-right
|
||||||
|
_top = widget.availableScreenSize.height - (_getCurrentButtonBarHeight() + _padding) - _height;
|
||||||
|
_left = widget.availableScreenSize.width - _padding - _width;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,197 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ParticipantBuffer {
|
||||||
|
final bool audioEnabled;
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
ParticipantBuffer({
|
||||||
|
@required this.audioEnabled,
|
||||||
|
@required this.id,
|
||||||
|
}) : assert(audioEnabled != null),
|
||||||
|
assert(id != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParticipantWidget extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
final String id;
|
||||||
|
final bool audioEnabled;
|
||||||
|
final bool videoEnabled;
|
||||||
|
final bool isRemote;
|
||||||
|
final bool isDummy;
|
||||||
|
final bool isDominant;
|
||||||
|
|
||||||
|
const ParticipantWidget({
|
||||||
|
Key key,
|
||||||
|
@required this.child,
|
||||||
|
@required this.audioEnabled,
|
||||||
|
@required this.videoEnabled,
|
||||||
|
@required this.id,
|
||||||
|
@required this.isRemote,
|
||||||
|
this.isDominant = false,
|
||||||
|
this.isDummy = false,
|
||||||
|
}) : assert(child != null),
|
||||||
|
assert(audioEnabled != null),
|
||||||
|
assert(videoEnabled != null),
|
||||||
|
assert(isRemote != null),
|
||||||
|
assert(isDominant != null),
|
||||||
|
assert(isDummy != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
ParticipantWidget copyWith({
|
||||||
|
Widget child,
|
||||||
|
bool audioEnabled,
|
||||||
|
bool videoEnabled,
|
||||||
|
bool isDominant,
|
||||||
|
}) {
|
||||||
|
return ParticipantWidget(
|
||||||
|
id: id,
|
||||||
|
child: child ?? this.child,
|
||||||
|
audioEnabled: audioEnabled ?? this.audioEnabled,
|
||||||
|
videoEnabled: videoEnabled ?? this.videoEnabled,
|
||||||
|
isDominant: isDominant ?? this.isDominant,
|
||||||
|
isRemote: isRemote,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final children = <Widget>[];
|
||||||
|
final icons = <Widget>[];
|
||||||
|
if (!videoEnabled) {
|
||||||
|
icons.add(_buildVideoEnabledIcon());
|
||||||
|
children.add(
|
||||||
|
ClipRect(
|
||||||
|
// Need to clip this BackdropFilter, otherwise it will blur the entire screen
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(color: Colors.black.withOpacity(.1)),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
children.add(child);
|
||||||
|
}
|
||||||
|
children.add(Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
duration: Duration(milliseconds: 500),
|
||||||
|
opacity: isDominant ? 1 : 0,
|
||||||
|
child: Icon(
|
||||||
|
Icons.volume_up,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
if (!audioEnabled) {
|
||||||
|
icons.add(_buildAudioEnabledIcon());
|
||||||
|
}
|
||||||
|
if (icons.isNotEmpty) {
|
||||||
|
if (isRemote) {
|
||||||
|
final rows = <Widget>[];
|
||||||
|
rows.add(_buildRow(icons));
|
||||||
|
if (!audioEnabled && !videoEnabled) {
|
||||||
|
rows.add(_buildRow(_fitText('The camera and microphone are off', Colors.white24)));
|
||||||
|
} else if (!audioEnabled) {
|
||||||
|
rows.add(_buildRow(_fitText('The microphone is off', Colors.black26)));
|
||||||
|
} else if (!videoEnabled) {
|
||||||
|
rows.add(_buildRow(_fitText('The camera is off', Colors.white24)));
|
||||||
|
}
|
||||||
|
children.add(
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: rows,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
children.add(Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: icons,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _fitText(String text, Color color) {
|
||||||
|
return [
|
||||||
|
Flexible(
|
||||||
|
child: FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
|
||||||
|
child: Text(text, maxLines: 1, style: _buildTextStyle(color)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle _buildTextStyle(Color color) {
|
||||||
|
return TextStyle(
|
||||||
|
color: color,
|
||||||
|
shadows: <Shadow>[
|
||||||
|
Shadow(
|
||||||
|
blurRadius: 1.0,
|
||||||
|
color: Color.fromARGB(255, 0, 0, 0),
|
||||||
|
),
|
||||||
|
Shadow(
|
||||||
|
blurRadius: 1.0,
|
||||||
|
color: Color.fromARGB(24, 255, 255, 255),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
fontSize: 15,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildRow(List<Widget> children) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAudioEnabledIcon() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: CircleAvatar(
|
||||||
|
maxRadius: 15,
|
||||||
|
child: FittedBox(
|
||||||
|
child: Icon(
|
||||||
|
Icons.mic_off,
|
||||||
|
color: Colors.black,
|
||||||
|
key: Key('microphone-off-icon'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.white24,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildVideoEnabledIcon() {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: CircleAvatar(
|
||||||
|
maxRadius: 15,
|
||||||
|
child: FittedBox(
|
||||||
|
child: Icon(
|
||||||
|
Icons.videocam_off,
|
||||||
|
color: Colors.black,
|
||||||
|
key: Key('videocam-off-icon'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.white24,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ButtonToProgress extends StatefulWidget {
|
||||||
|
final double height;
|
||||||
|
final double progressHeight;
|
||||||
|
final String loadingText;
|
||||||
|
final Duration duration;
|
||||||
|
final TextStyle loadingTextStyle;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final Stream<bool> onLoading;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const ButtonToProgress({
|
||||||
|
Key key,
|
||||||
|
this.height = 40.0,
|
||||||
|
this.progressHeight = 5.0,
|
||||||
|
this.loadingText,
|
||||||
|
this.duration = const Duration(milliseconds: 300),
|
||||||
|
this.loadingTextStyle,
|
||||||
|
this.onPressed,
|
||||||
|
this.onLoading,
|
||||||
|
@required this.child,
|
||||||
|
}) : assert(child != null),
|
||||||
|
assert(height != null && height > 0),
|
||||||
|
assert(progressHeight != null && progressHeight > 0 && progressHeight <= height),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ButtonToProgressState createState() => _ButtonToProgressState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ButtonToProgressState extends State<ButtonToProgress> {
|
||||||
|
double _height;
|
||||||
|
double _opacity = 0;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
StreamSubscription<bool> _subscription;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_height = widget.height;
|
||||||
|
if (widget.onLoading != null) {
|
||||||
|
_subscription = widget.onLoading.listen((bool isLoading) {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = isLoading;
|
||||||
|
_height = isLoading ? widget.progressHeight : widget.height;
|
||||||
|
_opacity = isLoading ? 1 : 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
if (_subscription != null) {
|
||||||
|
_subscription.cancel();
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: widget.height,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
if (widget.loadingText == null)
|
||||||
|
Container()
|
||||||
|
else
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: widget.progressHeight),
|
||||||
|
child: AnimatedOpacity(
|
||||||
|
child: Center(
|
||||||
|
child: FittedBox(
|
||||||
|
child: Text(
|
||||||
|
widget.loadingText,
|
||||||
|
style: widget.loadingTextStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
opacity: _opacity,
|
||||||
|
duration: Duration(milliseconds: widget.duration.inMilliseconds + 200),
|
||||||
|
curve: Curves.easeInCubic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
AnimatedPadding(
|
||||||
|
duration: widget.duration,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: math.max(widget.height - _height, 0),
|
||||||
|
),
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: widget.duration,
|
||||||
|
height: _height,
|
||||||
|
width: double.infinity,
|
||||||
|
child: _isLoading ? const LinearProgressIndicator() : widget.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:native_device_orientation/native_device_orientation.dart';
|
||||||
|
|
||||||
|
class CircleButton extends StatefulWidget {
|
||||||
|
final VoidCallback onLongPress;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
final GestureTapDownCallback onTapDown;
|
||||||
|
final VoidCallback onTapCancel;
|
||||||
|
final Widget child;
|
||||||
|
final Color color;
|
||||||
|
final double radius;
|
||||||
|
|
||||||
|
const CircleButton({
|
||||||
|
Key key,
|
||||||
|
this.onLongPress,
|
||||||
|
this.onPressed,
|
||||||
|
this.child,
|
||||||
|
this.color,
|
||||||
|
this.radius = 25.0,
|
||||||
|
this.onTapCancel,
|
||||||
|
this.onTapDown,
|
||||||
|
}) : assert(radius != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CircleButtonState createState() => _CircleButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CircleButtonState extends State<CircleButton> {
|
||||||
|
double _rotationAngle = 0.0;
|
||||||
|
|
||||||
|
final Stream<NativeDeviceOrientation> _orientationStream = NativeDeviceOrientationCommunicator().onOrientationChanged(useSensor: true);
|
||||||
|
StreamSubscription<NativeDeviceOrientation> _orientationSubscription;
|
||||||
|
|
||||||
|
void _handleOrientationChange(NativeDeviceOrientation orientation) {
|
||||||
|
var targetAngle = 0.0;
|
||||||
|
switch (orientation) {
|
||||||
|
case NativeDeviceOrientation.unknown:
|
||||||
|
case NativeDeviceOrientation.portraitUp:
|
||||||
|
targetAngle = 0.0;
|
||||||
|
break;
|
||||||
|
case NativeDeviceOrientation.portraitDown:
|
||||||
|
targetAngle = 180.0;
|
||||||
|
break;
|
||||||
|
case NativeDeviceOrientation.landscapeLeft:
|
||||||
|
targetAngle = 90.0;
|
||||||
|
break;
|
||||||
|
case NativeDeviceOrientation.landscapeRight:
|
||||||
|
targetAngle = 270.0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_rotationAngle = targetAngle;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_orientationSubscription = _orientationStream.listen(
|
||||||
|
_handleOrientationChange,
|
||||||
|
onError: (dynamic err) => print(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_orientationSubscription.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final size = 2 * widget.radius;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: (widget.color ?? Colors.blue).withAlpha(200),
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(widget.radius),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: GestureDetector(
|
||||||
|
onLongPress: widget.onLongPress,
|
||||||
|
onTapDown: widget.onTapDown,
|
||||||
|
onTapCancel: widget.onTapCancel,
|
||||||
|
child: RawMaterialButton(
|
||||||
|
onPressed: widget.onPressed,
|
||||||
|
child: RotationTransition(
|
||||||
|
child: widget.child,
|
||||||
|
turns: AlwaysStoppedAnimation<double>(_rotationAngle / 360),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum NoiseBoxDensity {
|
||||||
|
high,
|
||||||
|
medium,
|
||||||
|
low,
|
||||||
|
xHigh,
|
||||||
|
xLow,
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoiseBox extends StatefulWidget {
|
||||||
|
final NoiseBoxDensity density;
|
||||||
|
final Color backgroundColor;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const NoiseBox({
|
||||||
|
Key key,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.child,
|
||||||
|
this.density = NoiseBoxDensity.medium,
|
||||||
|
}) : assert(density != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_NoiseBoxState createState() => _NoiseBoxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NoiseBoxState extends State<NoiseBox> with SingleTickerProviderStateMixin {
|
||||||
|
AnimationController _animationController;
|
||||||
|
int _density;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(seconds: 60),
|
||||||
|
);
|
||||||
|
_animationController.repeat();
|
||||||
|
switch (widget.density) {
|
||||||
|
case NoiseBoxDensity.high:
|
||||||
|
_density = 5;
|
||||||
|
break;
|
||||||
|
case NoiseBoxDensity.medium:
|
||||||
|
_density = 7;
|
||||||
|
break;
|
||||||
|
case NoiseBoxDensity.low:
|
||||||
|
_density = 10;
|
||||||
|
break;
|
||||||
|
case NoiseBoxDensity.xHigh:
|
||||||
|
_density = 3;
|
||||||
|
break;
|
||||||
|
case NoiseBoxDensity.xLow:
|
||||||
|
_density = 12;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) => Container(
|
||||||
|
color: widget.backgroundColor,
|
||||||
|
width: constraints.biggest.width,
|
||||||
|
height: constraints.biggest.height,
|
||||||
|
child: AnimatedBuilder(
|
||||||
|
animation: _animationController,
|
||||||
|
builder: (BuildContext context, Widget w) {
|
||||||
|
final children = <Widget>[
|
||||||
|
CustomPaint(
|
||||||
|
painter: NoisePainter(
|
||||||
|
width: constraints.biggest.width,
|
||||||
|
height: constraints.biggest.height,
|
||||||
|
density: _density,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
if (widget.child != null) {
|
||||||
|
children.add(widget.child);
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoisePainter extends CustomPainter {
|
||||||
|
final double width;
|
||||||
|
final double height;
|
||||||
|
final int density;
|
||||||
|
|
||||||
|
NoisePainter({
|
||||||
|
@required this.width,
|
||||||
|
@required this.height,
|
||||||
|
@required this.density,
|
||||||
|
}) : assert(width != null),
|
||||||
|
assert(height != null),
|
||||||
|
assert(density != null && density >= 3 && density < math.min(width, height));
|
||||||
|
|
||||||
|
List<Color> colors = <Color>[
|
||||||
|
Colors.black,
|
||||||
|
Colors.grey,
|
||||||
|
Colors.blueGrey,
|
||||||
|
Colors.red,
|
||||||
|
Colors.green,
|
||||||
|
Colors.blue,
|
||||||
|
Colors.white,
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final random = math.Random();
|
||||||
|
for (var w = 0; w < width; w += density) {
|
||||||
|
for (var h = 0; h < height; h += density) {
|
||||||
|
final offset = Offset(
|
||||||
|
random.nextDouble() * width,
|
||||||
|
random.nextDouble() * height,
|
||||||
|
);
|
||||||
|
final paint = Paint();
|
||||||
|
paint.color = colors[random.nextInt(colors.length)];
|
||||||
|
paint.strokeWidth = random.nextDouble() * 2;
|
||||||
|
|
||||||
|
canvas.drawPoints(PointMode.points, <Offset>[offset], paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(CustomPainter oldDelegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
|
import './platform_widget.dart';
|
||||||
|
|
||||||
|
class PlatformAlertDialog extends PlatformWidget {
|
||||||
|
PlatformAlertDialog({@required this.title, @required this.content, @required this.defaultActionText, this.cancelActionText})
|
||||||
|
: assert(title != null),
|
||||||
|
assert(content != null),
|
||||||
|
assert(defaultActionText != null);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String content;
|
||||||
|
final String defaultActionText;
|
||||||
|
final String cancelActionText;
|
||||||
|
|
||||||
|
Future<bool> show(BuildContext context) async {
|
||||||
|
return Platform.isIOS
|
||||||
|
? await showCupertinoDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => this,
|
||||||
|
)
|
||||||
|
: await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) => this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildCupertinoWidget(BuildContext context) {
|
||||||
|
return CupertinoAlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(content),
|
||||||
|
actions: _buildActions(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildMaterialWidget(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(content),
|
||||||
|
actions: _buildActions(context),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildActions(BuildContext context) {
|
||||||
|
final actions = <Widget>[];
|
||||||
|
if (cancelActionText != null) {
|
||||||
|
actions.add(
|
||||||
|
PlatformAlertDialogAction(
|
||||||
|
child: Text(cancelActionText),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
actions.add(
|
||||||
|
PlatformAlertDialogAction(
|
||||||
|
child: Text(defaultActionText),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlatformAlertDialogAction extends PlatformWidget {
|
||||||
|
PlatformAlertDialogAction({
|
||||||
|
this.child,
|
||||||
|
this.onPressed,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildCupertinoWidget(BuildContext context) {
|
||||||
|
return CupertinoDialogAction(
|
||||||
|
child: child,
|
||||||
|
onPressed: onPressed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildMaterialWidget(BuildContext context) {
|
||||||
|
return FlatButton(
|
||||||
|
child: child,
|
||||||
|
onPressed: onPressed,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import './platform_alert_dialog.dart';
|
||||||
|
|
||||||
|
class PlatformExceptionAlertDialog extends PlatformAlertDialog {
|
||||||
|
PlatformExceptionAlertDialog({
|
||||||
|
String title = 'An error occurred',
|
||||||
|
@required Exception exception,
|
||||||
|
}) : super(
|
||||||
|
title: title,
|
||||||
|
content: exception is PlatformException ? _message(exception) : exception.toString(),
|
||||||
|
defaultActionText: 'OK',
|
||||||
|
);
|
||||||
|
|
||||||
|
static String _message(PlatformException exception) {
|
||||||
|
return _errors[exception.code] ?? (exception.details != null ? (exception.details['message'] ?? exception.message) : exception.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Map<String, String> _errors = <String, String>{
|
||||||
|
'ERROR_CODE': 'Error description...',
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
abstract class PlatformWidget extends StatelessWidget {
|
||||||
|
Widget buildCupertinoWidget(BuildContext context);
|
||||||
|
|
||||||
|
Widget buildMaterialWidget(BuildContext context);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (Platform.isIOS) {
|
||||||
|
return buildCupertinoWidget(context);
|
||||||
|
}
|
||||||
|
return buildMaterialWidget(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
typedef ResponsiveBuilder = Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
Size size,
|
||||||
|
);
|
||||||
|
|
||||||
|
class ResponsiveSafeArea extends StatelessWidget {
|
||||||
|
const ResponsiveSafeArea({
|
||||||
|
@required ResponsiveBuilder builder,
|
||||||
|
Key key,
|
||||||
|
}) : responsiveBuilder = builder,
|
||||||
|
assert(builder != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
final ResponsiveBuilder responsiveBuilder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SafeArea(
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
return responsiveBuilder(
|
||||||
|
context,
|
||||||
|
constraints.biggest,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,255 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/IncomingCallData.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/room_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/conference_page.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/conference/widgets/platform_exception_alert_dialog.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
||||||
|
class IncomingCall extends StatefulWidget {
|
||||||
|
IncomingCallData incomingCallData;
|
||||||
|
|
||||||
|
IncomingCall({@required this.incomingCallData});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_IncomingCallState createState() => _IncomingCallState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _IncomingCallState extends State<IncomingCall>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
AnimationController _animationController;
|
||||||
|
|
||||||
|
final player = AudioPlayer();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_animationController =
|
||||||
|
AnimationController(vsync: this, duration: Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => _runAnimation());
|
||||||
|
|
||||||
|
print(widget.incomingCallData.doctorname);
|
||||||
|
print(widget.incomingCallData.clinicname);
|
||||||
|
print(widget.incomingCallData.speciality);
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
player.stop();
|
||||||
|
disposeAudioResources();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AppScaffold(
|
||||||
|
isShowAppBar: false,
|
||||||
|
body: SafeArea(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(color: Colors.grey[700]),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 30.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text("Incoming Video Call",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 26.0,
|
||||||
|
color: Colors.white,
|
||||||
|
letterSpacing: 1.0)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.fromLTRB(50.0, 30.0, 50.0, 20.0),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/new-design/hmg_full_logo_hd_white.png'),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.fromLTRB(30.0, 10.0, 30.0, 0.0),
|
||||||
|
child: Divider(
|
||||||
|
color: Colors.white,
|
||||||
|
thickness: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text("Dr Eyad Ismail Abu Jayab",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 0.8,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 10.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text("ENT Clinic",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
letterSpacing: 0.8,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 10.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Text("Speciality",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
letterSpacing: 0.8,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[900].withOpacity(0.8),
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(20.0),
|
||||||
|
margin: EdgeInsets.only(top: 20.0),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Appointment Information",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 1.0,
|
||||||
|
color: Colors.white)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20.0),
|
||||||
|
child: Text("Sun, 15th Dec, 2019, 09:00",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
letterSpacing: 1.0,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 20.0),
|
||||||
|
child: Text("ENT Clinic",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20.0,
|
||||||
|
letterSpacing: 1.0,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 100.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: <Widget>[
|
||||||
|
RotationTransition(
|
||||||
|
turns: Tween(begin: 0.0, end: -.1)
|
||||||
|
.chain(CurveTween(curve: Curves.elasticIn))
|
||||||
|
.animate(_animationController),
|
||||||
|
child: Container(
|
||||||
|
child: RawMaterialButton(
|
||||||
|
onPressed: () {
|
||||||
|
_submit();
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: Colors.green,
|
||||||
|
child: Icon(
|
||||||
|
Icons.call,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 35.0,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(15.0),
|
||||||
|
shape: CircleBorder(),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Container(
|
||||||
|
child: RawMaterialButton(
|
||||||
|
onPressed: () {
|
||||||
|
backToHome();
|
||||||
|
},
|
||||||
|
elevation: 2.0,
|
||||||
|
fillColor: Colors.red,
|
||||||
|
child: Icon(
|
||||||
|
Icons.call_end,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 35.0,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.all(15.0),
|
||||||
|
shape: CircleBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _runAnimation() async {
|
||||||
|
setAudioFile();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
await _animationController.forward();
|
||||||
|
await _animationController.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _submit() async {
|
||||||
|
backToHome();
|
||||||
|
try {
|
||||||
|
final roomModel = RoomModel(
|
||||||
|
name: widget.incomingCallData.name,
|
||||||
|
token: widget.incomingCallData.sessionId,
|
||||||
|
identity: widget.incomingCallData.identity);
|
||||||
|
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
MaterialPageRoute<ConferencePage>(
|
||||||
|
fullscreenDialog: true,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
ConferencePage(roomModel: roomModel),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
print(err);
|
||||||
|
await PlatformExceptionAlertDialog(
|
||||||
|
exception: err,
|
||||||
|
).show(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void backToHome() {
|
||||||
|
player.stop();
|
||||||
|
// disposeAudioResources();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
disposeAudioResources() async {
|
||||||
|
await player.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAudioFile() async {
|
||||||
|
player.stop();
|
||||||
|
await player.setVolume(1.0); // full volume
|
||||||
|
try {
|
||||||
|
await player.setAsset('assets/sounds/ring_60Sec.mp3').then((value) {
|
||||||
|
player.setLoopMode(LoopMode.one); // loop ring sound
|
||||||
|
player.play();
|
||||||
|
}).catchError((err) {
|
||||||
|
print("Error: $err");
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print("Error: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCarePendingRequest.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/clinic_list.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/livecare_logs.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||||
|
|
||||||
|
class LiveCareHome extends StatefulWidget {
|
||||||
|
static bool showFooterButton = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LiveCareHomeState createState() => _LiveCareHomeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveCareHomeState extends State<LiveCareHome>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
TabController _tabController;
|
||||||
|
|
||||||
|
bool isDataLoaded = false;
|
||||||
|
bool hasLiveCareRequest = false;
|
||||||
|
|
||||||
|
List<ErRequestHistoryList> erRequestHistoryList;
|
||||||
|
|
||||||
|
ErRequestHistoryList pendingERRequestHistoryList;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_tabController = new TabController(length: 2, vsync: this);
|
||||||
|
erRequestHistoryList = List();
|
||||||
|
|
||||||
|
pendingERRequestHistoryList = new ErRequestHistoryList();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) => getLiveCareHistory());
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AppScaffold(
|
||||||
|
appBarTitle: "LiveCare",
|
||||||
|
isShowAppBar: true,
|
||||||
|
body: Container(
|
||||||
|
child: Column(children: [
|
||||||
|
/// this is will not colored with theme data
|
||||||
|
TabBar(
|
||||||
|
tabs: [
|
||||||
|
Tab(text: TranslationBase.of(context).consultation),
|
||||||
|
Tab(text: TranslationBase.of(context).logs),
|
||||||
|
],
|
||||||
|
controller: _tabController,
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
color: Colors.grey[600],
|
||||||
|
thickness: 0.5,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
isDataLoaded && !hasLiveCareRequest
|
||||||
|
? ClinicList(
|
||||||
|
getLiveCareHistory: getLiveCareHistory,
|
||||||
|
)
|
||||||
|
: isDataLoaded
|
||||||
|
? LiveCarePendingRequest(
|
||||||
|
getLiveCareHistory: getLiveCareHistory,
|
||||||
|
pendingERRequestHistoryList:
|
||||||
|
pendingERRequestHistoryList)
|
||||||
|
: Container(),
|
||||||
|
isDataLoaded
|
||||||
|
? LiveCareLogs(
|
||||||
|
erRequestHistoryList: erRequestHistoryList,
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
|
controller: _tabController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getLiveCareHistory() {
|
||||||
|
setState(() {
|
||||||
|
isDataLoaded = false;
|
||||||
|
hasLiveCareRequest = false;
|
||||||
|
});
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
PatientERVirtualHistoryResponse patientERVirtualHistoryResponse =
|
||||||
|
new PatientERVirtualHistoryResponse();
|
||||||
|
service
|
||||||
|
.getLivecareHistory(context)
|
||||||
|
.then((res) {
|
||||||
|
setState(() {
|
||||||
|
print(res['ErRequestHistoryList'].length);
|
||||||
|
if (res['ErRequestHistoryList'].length != 0) {
|
||||||
|
patientERVirtualHistoryResponse =
|
||||||
|
PatientERVirtualHistoryResponse.fromJson(res);
|
||||||
|
erRequestHistoryList =
|
||||||
|
patientERVirtualHistoryResponse.erRequestHistoryList;
|
||||||
|
|
||||||
|
if (patientERVirtualHistoryResponse
|
||||||
|
.erRequestHistoryList[0].callStatus <
|
||||||
|
4) {
|
||||||
|
pendingERRequestHistoryList =
|
||||||
|
patientERVirtualHistoryResponse.erRequestHistoryList[0];
|
||||||
|
hasLiveCareRequest = true;
|
||||||
|
} else {
|
||||||
|
hasLiveCareRequest = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catchError((err) {
|
||||||
|
print(err);
|
||||||
|
})
|
||||||
|
.showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6))
|
||||||
|
.then((value) {
|
||||||
|
setState(() {
|
||||||
|
isDataLoaded = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/ClinicsServiceTimingsResponse.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ClinicTimingsDialog extends StatefulWidget {
|
||||||
|
final clinicName;
|
||||||
|
final List<PatientERGetClinicsServiceTimingsList>
|
||||||
|
patientERGetClinicsServiceTimingsList;
|
||||||
|
|
||||||
|
ClinicTimingsDialog(
|
||||||
|
{@required this.clinicName,
|
||||||
|
@required this.patientERGetClinicsServiceTimingsList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ClinicTimingsDialogState createState() => _ClinicTimingsDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ClinicTimingsDialogState extends State<ClinicTimingsDialog> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
print(widget.patientERGetClinicsServiceTimingsList);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var size = MediaQuery.of(context).size;
|
||||||
|
final double itemHeight = ((size.height - kToolbarHeight - 24) * 0.42) / 2;
|
||||||
|
final double itemWidth = size.width / 2;
|
||||||
|
return Container(
|
||||||
|
child: Dialog(
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||||
|
child: Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.68,
|
||||||
|
margin: EdgeInsets.all(20.0),
|
||||||
|
width: 450.0,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Text("Clinic Schedule",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 1.0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(bottom: 20.0, top: 10.0),
|
||||||
|
child: Text(widget.clinicName,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
CustomScrollView(
|
||||||
|
primary: false,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
slivers: <Widget>[
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0),
|
||||||
|
sliver: SliverGrid.count(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
childAspectRatio: (itemWidth / itemHeight),
|
||||||
|
children: widget.patientERGetClinicsServiceTimingsList
|
||||||
|
.map((e) => Container(
|
||||||
|
height: 10.0,
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text(e.dayOfWeekStr),
|
||||||
|
Text(e.shiftTimings[0].startTime +
|
||||||
|
" - " +
|
||||||
|
e.shiftTimings[0].endTime),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.toList()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 1.0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 30.0,
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Text("OK",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,212 @@
|
|||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/feedback/feedback_home_page.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/dialogs/confirm_dialog.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||||
|
|
||||||
|
class LiveCareHistoryCard extends StatefulWidget {
|
||||||
|
ErRequestHistoryList erRequestHistoryList;
|
||||||
|
|
||||||
|
LiveCareHistoryCard({this.erRequestHistoryList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LiveCareHistoryCardState createState() => _LiveCareHistoryCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveCareHistoryCardState extends State<LiveCareHistoryCard> {
|
||||||
|
AuthenticatedUser authUser = new AuthenticatedUser();
|
||||||
|
AppSharedPreferences sharedPref = AppSharedPreferences();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
getAuthenticatedUser();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.all(10.0),
|
||||||
|
child: Card(
|
||||||
|
margin: EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 8.0),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height * 0.22,
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Requested date:",
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 10.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(widget.erRequestHistoryList.sArrivalTime,
|
||||||
|
style: TextStyle(fontSize: 14.0)),
|
||||||
|
Text(
|
||||||
|
"Call Duration\n" +
|
||||||
|
getCallTime(
|
||||||
|
widget.erRequestHistoryList.callDuration),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 14.0, color: Colors.grey[600])),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.all(7.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
color: Colors.green,
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.only(top: 5.0, bottom: 5.0),
|
||||||
|
child: Text(widget.erRequestHistoryList.stringCallStatus,
|
||||||
|
style: TextStyle(fontSize: 14.0, color: Colors.white)),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(5.0),
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
print("Invoice");
|
||||||
|
openInvoice();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.content_paste, color: Colors.blue),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 10.0),
|
||||||
|
child: Text("Invoice",
|
||||||
|
textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
print("Complaints");
|
||||||
|
openComplaint();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.add, size: 24.0, color: Colors.red),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 10.0),
|
||||||
|
child: Text("Complaints",
|
||||||
|
textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
print("Rate Dr & Appointment");
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.star,
|
||||||
|
size: 24.0, color: Colors.yellow[700]),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.2,
|
||||||
|
margin: EdgeInsets.only(left: 10.0),
|
||||||
|
child: Text("Rate Dr & Appointment",
|
||||||
|
overflow: TextOverflow.clip,
|
||||||
|
textAlign: TextAlign.center),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
openInvoice() {
|
||||||
|
ConfirmDialog dialog = new ConfirmDialog(
|
||||||
|
context: context,
|
||||||
|
confirmMessage: "Send a copy of this invoice to the email: " +
|
||||||
|
authUser.emailAddress,
|
||||||
|
okText: TranslationBase.of(context).confirm,
|
||||||
|
cancelText: TranslationBase.of(context).cancel_nocaps,
|
||||||
|
okFunction: () => {sendInvoiceEmail(context)},
|
||||||
|
cancelFunction: () => {});
|
||||||
|
dialog.showAlertDialog(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
openComplaint() {
|
||||||
|
Navigator.push(context, FadePage(page: FeedbackHomePage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInvoiceEmail(context) {
|
||||||
|
ConfirmDialog.closeAlertDialog(context);
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
service
|
||||||
|
.sendLiveCareInvoiceEmail(
|
||||||
|
widget.erRequestHistoryList.appointmentNo.toString(),
|
||||||
|
widget.erRequestHistoryList.projectID,
|
||||||
|
authUser.emailAddress,
|
||||||
|
context)
|
||||||
|
.then((res) {
|
||||||
|
AppToast.showSuccessToast(message: "LiveCare invoice sent successfully");
|
||||||
|
}).catchError((err) {
|
||||||
|
AppToast.showErrorToast(message: err);
|
||||||
|
print(err);
|
||||||
|
}).showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
getAuthenticatedUser() async {
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
setState(() {
|
||||||
|
authUser = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCallTime(int number) {
|
||||||
|
number = number.round();
|
||||||
|
var hours = (number / 60 / 60).floor();
|
||||||
|
var minutes = (number / 60).floor() - (hours * 60).floor();
|
||||||
|
var seconds = number % 60;
|
||||||
|
return '${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LiveCareInfoDialog extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
child: Dialog(
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||||
|
child: Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.61,
|
||||||
|
margin: EdgeInsets.all(20.0),
|
||||||
|
width: 450.0,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Icon(Icons.info_outline,
|
||||||
|
size: 80.0, color: Colors.red[900]),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 20.0),
|
||||||
|
child: Text("Important Instructions",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red[900])),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 20.0),
|
||||||
|
child: Text(
|
||||||
|
"Please make sure that you're logged in on Al Habib Mobile Application",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 20.0),
|
||||||
|
child: Icon(Icons.not_interested,
|
||||||
|
size: 80.0, color: Colors.red[900]),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 40.0),
|
||||||
|
child: Text(
|
||||||
|
"Otherwise, you will not receive the doctor's call.",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red[900])),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 1.0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 40.0,
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context, false);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Text("Cancel",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0, color: Colors.red[700])),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Text("Ok",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,296 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/ERAppointmentFeesResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LiveCarePaymentDialog extends StatefulWidget {
|
||||||
|
GetERAppointmentFeesList getERAppointmentFeesList;
|
||||||
|
int waitingTime;
|
||||||
|
String clinicName;
|
||||||
|
|
||||||
|
LiveCarePaymentDialog(
|
||||||
|
{@required this.getERAppointmentFeesList, @required this.waitingTime, @required this.clinicName});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LiveCarePaymentDialogState createState() => _LiveCarePaymentDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveCarePaymentDialogState extends State<LiveCarePaymentDialog> {
|
||||||
|
int _selected = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var size = MediaQuery.of(context).size;
|
||||||
|
final double itemHeight = ((size.height - kToolbarHeight - 24) * 0.42) / 2;
|
||||||
|
final double itemWidth = size.width / 2;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
child: Dialog(
|
||||||
|
shape:
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0)),
|
||||||
|
child: Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.691,
|
||||||
|
margin: EdgeInsets.all(20.0),
|
||||||
|
width: 450.0,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: Text("Online Consultation",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 1.0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Text("Waiting time to start LiveCare consultation",
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(fontSize: 13.0)),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
child: Icon(Icons.access_time,
|
||||||
|
size: 36.0, color: Colors.red[800]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(widget.waitingTime.toString() + " Minutes",
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.red[900])),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(bottom: 10.0, top: 10.0),
|
||||||
|
child: Text(widget.clinicName,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style:
|
||||||
|
TextStyle(fontSize: 22.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black87,
|
||||||
|
borderRadius: new BorderRadius.only(
|
||||||
|
topLeft: const Radius.circular(5.0),
|
||||||
|
topRight: const Radius.circular(5.0),
|
||||||
|
),
|
||||||
|
border: Border.all(color: Colors.black87)),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(top: 5.0),
|
||||||
|
padding: EdgeInsets.all(5.0),
|
||||||
|
child: Text("Consultation fee",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: new BorderRadius.only(
|
||||||
|
bottomLeft: const Radius.circular(5.0),
|
||||||
|
bottomRight: const Radius.circular(5.0),
|
||||||
|
),
|
||||||
|
border: Border.all(color: Colors.black54)),
|
||||||
|
child: Table(
|
||||||
|
children: [
|
||||||
|
TableRow(children: [
|
||||||
|
TableCell(
|
||||||
|
child: _getNormalText(
|
||||||
|
TranslationBase.of(context).patientShareToDo)),
|
||||||
|
TableCell(
|
||||||
|
child: _getNormalText(
|
||||||
|
widget.getERAppointmentFeesList.amount +
|
||||||
|
" " +
|
||||||
|
widget.getERAppointmentFeesList.currency)),
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
TableCell(
|
||||||
|
child: _getNormalText(
|
||||||
|
TranslationBase.of(context).patientTaxToDo)),
|
||||||
|
TableCell(
|
||||||
|
child: _getNormalText(
|
||||||
|
widget.getERAppointmentFeesList.tax +
|
||||||
|
" " +
|
||||||
|
widget.getERAppointmentFeesList.currency)),
|
||||||
|
]),
|
||||||
|
TableRow(children: [
|
||||||
|
TableCell(
|
||||||
|
child: _getMarginText(TranslationBase.of(context)
|
||||||
|
.patientShareTotalToDo)),
|
||||||
|
TableCell(
|
||||||
|
child: _getMarginText(
|
||||||
|
widget.getERAppointmentFeesList.total +
|
||||||
|
" " +
|
||||||
|
widget.getERAppointmentFeesList.currency)),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.only(top: 10.0, bottom: 10.0, left: 10.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: new BorderRadius.all(
|
||||||
|
const Radius.circular(5.0),
|
||||||
|
),
|
||||||
|
color: Colors.green[200].withOpacity(0.5)),
|
||||||
|
margin: EdgeInsets.only(top: 20.0),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Image.asset("assets/images/new-design/alert-triangle.png"),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(left: 10.0),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.55,
|
||||||
|
child: Text(
|
||||||
|
"If you're Insurance patient, you have only have to pay the co-payment",
|
||||||
|
style: TextStyle(fontSize: 13.0)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(top: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: new Radio(
|
||||||
|
value: 1,
|
||||||
|
groupValue: _selected,
|
||||||
|
onChanged: onRadioChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
child: new Text(
|
||||||
|
'I Accept the Terms And Conditions',
|
||||||
|
style: new TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Container(
|
||||||
|
//// alignment: Alignment.centerRight,
|
||||||
|
// child: new Text(
|
||||||
|
// 'Click Here',
|
||||||
|
// textAlign: TextAlign.end,
|
||||||
|
// style: new TextStyle(fontSize: 16.0),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 1.0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(top: 10.0),
|
||||||
|
child: new Text(
|
||||||
|
'You can pay by the following Options:',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: new TextStyle(fontSize: 14.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 5.0),
|
||||||
|
child: Image.asset(
|
||||||
|
"assets/images/new-design/payment_options_invoice_confirmation.png",
|
||||||
|
width: 300),
|
||||||
|
),
|
||||||
|
Divider(
|
||||||
|
thickness: 1.0,
|
||||||
|
color: Colors.grey[400],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
height: 40.0,
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pop(context, false);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Text("Cancel",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0, color: Colors.red[700])),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
if(_selected == 0) {
|
||||||
|
AppToast.showErrorToast(message: "Please accept terms & conditions to continue");
|
||||||
|
} else {
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
child: Text("Ok",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRadioChanged(int value) {
|
||||||
|
setState(() {
|
||||||
|
_selected = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getNormalText(text) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 10.0, right: 10.0),
|
||||||
|
child: Text(text,
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: 'Open-Sans',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
color: Colors.black)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getMarginText(text) {
|
||||||
|
return Container(
|
||||||
|
margin: EdgeInsets.only(top: 10.0, right: 10.0, bottom: 10.0),
|
||||||
|
child: Text(text,
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: 'Open-Sans',
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
import 'package:circular_countdown_timer/circular_countdown_timer.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/date_uitl.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||||
|
|
||||||
|
class LiveCarePendingRequest extends StatefulWidget {
|
||||||
|
ErRequestHistoryList pendingERRequestHistoryList;
|
||||||
|
final Function getLiveCareHistory;
|
||||||
|
|
||||||
|
LiveCarePendingRequest(
|
||||||
|
{@required this.getLiveCareHistory, this.pendingERRequestHistoryList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LiveCarePendingRequestState createState() => _LiveCarePendingRequestState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveCarePendingRequestState extends State<LiveCarePendingRequest> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
// TODO: implement initState
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.grey[300]),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.all(15.0),
|
||||||
|
padding: EdgeInsets.all(10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text("In Progress:",
|
||||||
|
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
margin: EdgeInsets.only(top: 10.0),
|
||||||
|
child: Text("Estimated Waiting Time: ",
|
||||||
|
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, -50.0, 0.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: CircularCountDownTimer(
|
||||||
|
duration:
|
||||||
|
widget.pendingERRequestHistoryList.watingtimeInteger * 60,
|
||||||
|
width: MediaQuery.of(context).size.width / 3,
|
||||||
|
height: MediaQuery.of(context).size.height / 3,
|
||||||
|
color: Colors.white,
|
||||||
|
fillColor: Colors.green[700],
|
||||||
|
strokeWidth: 15.0,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 22.0,
|
||||||
|
color: Colors.black87,
|
||||||
|
fontWeight: FontWeight.bold),
|
||||||
|
isReverse: true,
|
||||||
|
isTimerTextShown: true,
|
||||||
|
onComplete: () {
|
||||||
|
print('Countdown Ended');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, -60.0, 0.0),
|
||||||
|
child: Divider(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
thickness: 0.7,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, -50.0, 0.0),
|
||||||
|
child: Text("Requested date:",
|
||||||
|
style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, -30.0, 0.0),
|
||||||
|
child: Text(
|
||||||
|
DateUtil.getDateFormatted(
|
||||||
|
widget.pendingERRequestHistoryList.arrivalTime),
|
||||||
|
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, -20.0, 0.0),
|
||||||
|
padding: EdgeInsets.all(7.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||||
|
color: Colors.red[800],
|
||||||
|
),
|
||||||
|
margin: EdgeInsets.only(top: 5.0, bottom: 5.0),
|
||||||
|
child: Text(widget.pendingERRequestHistoryList.stringCallStatus,
|
||||||
|
style: TextStyle(fontSize: 14.0, color: Colors.white)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, 0.0, 0.0),
|
||||||
|
child: Divider(
|
||||||
|
color: Colors.grey[500],
|
||||||
|
thickness: 0.7,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
transform: Matrix4.translationValues(0.0, 10.0, 0.0),
|
||||||
|
child: Text(
|
||||||
|
"Your turn is after " +
|
||||||
|
widget.pendingERRequestHistoryList.patCount.toString() +
|
||||||
|
" Patients",
|
||||||
|
style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
transform: Matrix4.translationValues(0.0, 130.0, 0.0),
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
child: ButtonTheme(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
minWidth: MediaQuery.of(context).size.width,
|
||||||
|
height: 45.0,
|
||||||
|
child: RaisedButton(
|
||||||
|
color: Colors.red[800],
|
||||||
|
textColor: Colors.white,
|
||||||
|
disabledTextColor: Colors.white,
|
||||||
|
disabledColor: new Color(0xFFbcc2c4),
|
||||||
|
onPressed: () {
|
||||||
|
cancelLiveCareRequest();
|
||||||
|
},
|
||||||
|
child: Text(TranslationBase.of(context).cancel,
|
||||||
|
style: TextStyle(fontSize: 18.0)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelLiveCareRequest() {
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
service
|
||||||
|
.cancelLiveCareRequest(widget.pendingERRequestHistoryList.vCID, context)
|
||||||
|
.then((res) {
|
||||||
|
AppToast.showSuccessToast(
|
||||||
|
message: "LiveCare request cancelled successfully");
|
||||||
|
})
|
||||||
|
.catchError((err) {
|
||||||
|
print(err);
|
||||||
|
})
|
||||||
|
.showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6))
|
||||||
|
.then((value) {
|
||||||
|
widget.getLiveCareHistory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/ClinicsServiceTimingsResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/LiveCareClinicsListResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||||
|
|
||||||
|
import 'ClinicTimingsDialog.dart';
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class ClinicCard extends StatefulWidget {
|
||||||
|
bool isSelected;
|
||||||
|
final PatientERGetClinicsList patientERGetClinicsList;
|
||||||
|
var languageID;
|
||||||
|
|
||||||
|
ClinicCard(
|
||||||
|
{this.isSelected,
|
||||||
|
this.languageID,
|
||||||
|
@required this.patientERGetClinicsList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_State createState() => _State();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _State extends State<ClinicCard> {
|
||||||
|
ClinicsServiceTimingsResponse clinicsServiceTimingsResponse;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
clinicsServiceTimingsResponse = new ClinicsServiceTimingsResponse();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Card(
|
||||||
|
margin: EdgeInsets.fromLTRB(13.0, 10.0, 8.0, 8.0),
|
||||||
|
color: widget.isSelected ? Colors.blue : Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
padding: EdgeInsets.all(12.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
widget.languageID == 'ar'
|
||||||
|
? widget.patientERGetClinicsList.serviceNameN
|
||||||
|
: widget.patientERGetClinicsList.serviceName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16.0,
|
||||||
|
color:
|
||||||
|
widget.isSelected ? Colors.white : Colors.black)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
getClinicTimings(widget.patientERGetClinicsList);
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
margin: EdgeInsets.fromLTRB(8.0, 10.0, 8.0, 8.0),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.access_time, size: 26.0, color: Colors.red[800]),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getClinicTimings(PatientERGetClinicsList patientERGetClinicsList) {
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
service
|
||||||
|
.getLivecareClinicTiming(patientERGetClinicsList.serviceID, context)
|
||||||
|
.then((res) {
|
||||||
|
if (res['MessageStatus'] == 1) {
|
||||||
|
setState(() {
|
||||||
|
clinicsServiceTimingsResponse =
|
||||||
|
ClinicsServiceTimingsResponse.fromJson(res);
|
||||||
|
print(clinicsServiceTimingsResponse
|
||||||
|
.patientERGetClinicsServiceTimingsList.length);
|
||||||
|
|
||||||
|
showGeneralDialog(
|
||||||
|
barrierColor: Colors.black.withOpacity(0.5),
|
||||||
|
transitionBuilder: (context, a1, a2, widget) {
|
||||||
|
final curvedValue =
|
||||||
|
Curves.easeInOutBack.transform(a1.value) - 1.0;
|
||||||
|
return Transform(
|
||||||
|
transform:
|
||||||
|
Matrix4.translationValues(0.0, curvedValue * 200, 0.0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: a1.value,
|
||||||
|
child: ClinicTimingsDialog(
|
||||||
|
clinicName: patientERGetClinicsList.serviceName,
|
||||||
|
patientERGetClinicsServiceTimingsList:
|
||||||
|
clinicsServiceTimingsResponse
|
||||||
|
.patientERGetClinicsServiceTimingsList,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionDuration: Duration(milliseconds: 500),
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierLabel: '',
|
||||||
|
context: context,
|
||||||
|
pageBuilder: (context, animation1, animation2) {});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
AppToast.showErrorToast(message: res['ErrorEndUserMessage']);
|
||||||
|
}
|
||||||
|
}).catchError((err) {
|
||||||
|
print(err);
|
||||||
|
}).showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,388 @@
|
|||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/Appointments/AppoimentAllHistoryResultList.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/ERAppointmentFeesResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/LiveCare/LiveCareClinicsListResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/ToDoList/payment_method_select.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCareInfoDialog.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCarePaymentDialog.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/clinic_card.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/appointment_services/GetDoctorsList.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/livecare_services/livecare_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/utils.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/in_app_browser/InAppBrowser.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:smart_progress_bar/smart_progress_bar.dart';
|
||||||
|
|
||||||
|
class ClinicList extends StatefulWidget {
|
||||||
|
final Function getLiveCareHistory;
|
||||||
|
|
||||||
|
ClinicList({@required this.getLiveCareHistory});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_clinic_listState createState() => _clinic_listState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _clinic_listState extends State<ClinicList> {
|
||||||
|
int currentSelectedIndex = 0;
|
||||||
|
LiveCareClinicsListResponse liveCareClinicsListResponse;
|
||||||
|
|
||||||
|
bool isDataLoaded = false;
|
||||||
|
var languageID;
|
||||||
|
|
||||||
|
int selectedClinicID = 1;
|
||||||
|
String selectedClinicName = "-";
|
||||||
|
|
||||||
|
AppSharedPreferences sharedPref = AppSharedPreferences();
|
||||||
|
|
||||||
|
AuthenticatedUser authUser;
|
||||||
|
AuthProvider authProvider = new AuthProvider();
|
||||||
|
|
||||||
|
MyInAppBrowser browser;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
liveCareClinicsListResponse = new LiveCareClinicsListResponse();
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
// Future.delayed(new Duration(milliseconds: 1200), () {
|
||||||
|
getLiveCareClinicsList();
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
getLanguageID();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AppScaffold(
|
||||||
|
isShowAppBar: false,
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: isDataLoaded
|
||||||
|
? Container(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.all(15.0),
|
||||||
|
child: Text("Online Clinics: ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20.0, fontWeight: FontWeight.bold)),
|
||||||
|
),
|
||||||
|
ListView.builder(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: ScrollPhysics(),
|
||||||
|
padding: EdgeInsets.all(0.0),
|
||||||
|
itemCount: liveCareClinicsListResponse
|
||||||
|
.patientERGetClinicsList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return InkWell(
|
||||||
|
onTap: () {
|
||||||
|
updateSelectedIndex(liveCareClinicsListResponse
|
||||||
|
.patientERGetClinicsList[index]);
|
||||||
|
},
|
||||||
|
child: ClinicCard(
|
||||||
|
isSelected: selectedClinicID ==
|
||||||
|
liveCareClinicsListResponse
|
||||||
|
.patientERGetClinicsList[index]
|
||||||
|
.serviceID
|
||||||
|
? true
|
||||||
|
: false,
|
||||||
|
patientERGetClinicsList: liveCareClinicsListResponse
|
||||||
|
.patientERGetClinicsList[index],
|
||||||
|
languageID: languageID,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 80.0,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
),
|
||||||
|
bottomSheet: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: 50.0,
|
||||||
|
margin: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
|
||||||
|
child: ButtonTheme(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
),
|
||||||
|
minWidth: MediaQuery.of(context).size.width * 0.7,
|
||||||
|
height: 45.0,
|
||||||
|
child: RaisedButton(
|
||||||
|
color: new Color(0xFF60686b),
|
||||||
|
textColor: Colors.white,
|
||||||
|
disabledTextColor: Colors.white,
|
||||||
|
disabledColor: new Color(0xFFbcc2c4),
|
||||||
|
onPressed: startLiveCare,
|
||||||
|
child: Text("Start", style: TextStyle(fontSize: 18.0)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startLiveCare() {
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
ERAppointmentFeesResponse erAppointmentFeesResponse =
|
||||||
|
new ERAppointmentFeesResponse();
|
||||||
|
service
|
||||||
|
.getERAppointmentFees(selectedClinicID, context)
|
||||||
|
.then((res) {
|
||||||
|
erAppointmentFeesResponse = ERAppointmentFeesResponse.fromJson(res);
|
||||||
|
})
|
||||||
|
.catchError((err) {
|
||||||
|
print(err);
|
||||||
|
})
|
||||||
|
.showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6))
|
||||||
|
.then((value) {
|
||||||
|
getERAppointmentTime(
|
||||||
|
erAppointmentFeesResponse.getERAppointmentFeesList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getERAppointmentTime(GetERAppointmentFeesList getERAppointmentFeesList) {
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
service.getERAppointmentTime(selectedClinicID, context).then((res) {
|
||||||
|
print(res['WatingtimeInteger']);
|
||||||
|
showLiveCarePaymentDialog(
|
||||||
|
getERAppointmentFeesList, res['WatingtimeInteger']);
|
||||||
|
}).catchError((err) {
|
||||||
|
print(err);
|
||||||
|
}).showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
showLiveCarePaymentDialog(
|
||||||
|
GetERAppointmentFeesList getERAppointmentFeesList, int waitingTime) {
|
||||||
|
showGeneralDialog(
|
||||||
|
barrierColor: Colors.black.withOpacity(0.5),
|
||||||
|
transitionBuilder: (context, a1, a2, widget) {
|
||||||
|
final curvedValue =
|
||||||
|
Curves.easeInOutBack.transform(a1.value) - 1.0;
|
||||||
|
return Transform(
|
||||||
|
transform:
|
||||||
|
Matrix4.translationValues(0.0, curvedValue * 200, 0.0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: a1.value,
|
||||||
|
child: LiveCarePaymentDialog(
|
||||||
|
getERAppointmentFeesList: getERAppointmentFeesList,
|
||||||
|
waitingTime: waitingTime,
|
||||||
|
clinicName: selectedClinicName),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionDuration: Duration(milliseconds: 500),
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierLabel: '',
|
||||||
|
context: context,
|
||||||
|
pageBuilder: (context, animation1, animation2) {})
|
||||||
|
.then((value) {
|
||||||
|
if (value) {
|
||||||
|
if (getERAppointmentFeesList.total == "0" ||
|
||||||
|
getERAppointmentFeesList.total == "0.0") {
|
||||||
|
showLiveCareInfoDialog(getERAppointmentFeesList);
|
||||||
|
} else {
|
||||||
|
navigateToPaymentMethod(getERAppointmentFeesList, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showLiveCareInfoDialog(
|
||||||
|
GetERAppointmentFeesList getERAppointmentFeesList) async {
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
setState(() {
|
||||||
|
authUser = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showGeneralDialog(
|
||||||
|
barrierColor: Colors.black.withOpacity(0.5),
|
||||||
|
transitionBuilder: (context, a1, a2, widget) {
|
||||||
|
final curvedValue =
|
||||||
|
Curves.easeInOutBack.transform(a1.value) - 1.0;
|
||||||
|
return Transform(
|
||||||
|
transform:
|
||||||
|
Matrix4.translationValues(0.0, curvedValue * 200, 0.0),
|
||||||
|
child: Opacity(
|
||||||
|
opacity: a1.value,
|
||||||
|
child: LiveCareInfoDialog(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
transitionDuration: Duration(milliseconds: 500),
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierLabel: '',
|
||||||
|
context: context,
|
||||||
|
pageBuilder: (context, animation1, animation2) {})
|
||||||
|
.then((value) {
|
||||||
|
if (value) {
|
||||||
|
if (getERAppointmentFeesList.total == "0" ||
|
||||||
|
getERAppointmentFeesList.total == "0.0") {
|
||||||
|
addNewCallForPatientER(authUser.patientID.toString() +
|
||||||
|
"" +
|
||||||
|
DateTime.now().millisecondsSinceEpoch.toString());
|
||||||
|
} else {
|
||||||
|
navigateToPaymentMethod(getERAppointmentFeesList, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future navigateToPaymentMethod(
|
||||||
|
GetERAppointmentFeesList getERAppointmentFeesList, context) async {
|
||||||
|
AppoitmentAllHistoryResultList appo = new AppoitmentAllHistoryResultList();
|
||||||
|
|
||||||
|
appo.clinicID = selectedClinicID;
|
||||||
|
appo.appointmentNo = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
setState(() {
|
||||||
|
authUser = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Navigator.push(
|
||||||
|
context, MaterialPageRoute(builder: (context) => PaymentMethod()))
|
||||||
|
.then((value) {
|
||||||
|
print(value);
|
||||||
|
if (value != null) {
|
||||||
|
openPayment(value, authUser,
|
||||||
|
double.parse(getERAppointmentFeesList.total), appo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openPayment(String paymentMethod, AuthenticatedUser authenticatedUser,
|
||||||
|
double amount, AppoitmentAllHistoryResultList appo) {
|
||||||
|
browser = new MyInAppBrowser(
|
||||||
|
onExitCallback: onBrowserExit,
|
||||||
|
appo: appo,
|
||||||
|
onLoadStartCallback: onBrowserLoadStart);
|
||||||
|
|
||||||
|
browser.openPaymentBrowser(
|
||||||
|
amount,
|
||||||
|
"LiveCare Payment",
|
||||||
|
Utils.getAppointmentTransID(12, appo.clinicID, appo.appointmentNo),
|
||||||
|
"12",
|
||||||
|
authenticatedUser.emailAddress,
|
||||||
|
paymentMethod,
|
||||||
|
authenticatedUser,
|
||||||
|
browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBrowserLoadStart(String url) {
|
||||||
|
print("onBrowserLoadStart");
|
||||||
|
print(url);
|
||||||
|
|
||||||
|
MyInAppBrowser.successURLS.forEach((element) {
|
||||||
|
if (url.contains(element)) {
|
||||||
|
if (browser.isOpened()) browser.close();
|
||||||
|
MyInAppBrowser.isPaymentDone = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
MyInAppBrowser.errorURLS.forEach((element) {
|
||||||
|
if (url.contains(element)) {
|
||||||
|
if (browser.isOpened()) browser.close();
|
||||||
|
MyInAppBrowser.isPaymentDone = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBrowserExit(AppoitmentAllHistoryResultList appo, bool isPaymentMade) {
|
||||||
|
print("onBrowserExit Called!!!!");
|
||||||
|
if (isPaymentMade) checkPaymentStatus(appo);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPaymentStatus(AppoitmentAllHistoryResultList appo) {
|
||||||
|
DoctorsListService service = new DoctorsListService();
|
||||||
|
service
|
||||||
|
.checkPaymentStatus(
|
||||||
|
Utils.getAppointmentTransID(
|
||||||
|
appo.projectID, appo.clinicID, appo.appointmentNo),
|
||||||
|
context)
|
||||||
|
.then((res) {
|
||||||
|
print("Printing Payment Status Reponse!!!!");
|
||||||
|
print(res);
|
||||||
|
String paymentInfo = res['Response_Message'];
|
||||||
|
if (paymentInfo == 'Success') {
|
||||||
|
addNewCallForPatientER(Utils.getAppointmentTransID(
|
||||||
|
appo.projectID, appo.clinicID, appo.appointmentNo));
|
||||||
|
} else {
|
||||||
|
AppToast.showErrorToast(message: res['Response_Message']);
|
||||||
|
}
|
||||||
|
}).catchError((err) {
|
||||||
|
print(err);
|
||||||
|
}).showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
addNewCallForPatientER(String clientRequestID) {
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
service
|
||||||
|
.addNewCallForPatientER(selectedClinicID, clientRequestID, context)
|
||||||
|
.then((res) {
|
||||||
|
AppToast.showSuccessToast(
|
||||||
|
message: "New Call has been added successfully");
|
||||||
|
}).catchError((err) {
|
||||||
|
print(err);
|
||||||
|
}).showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6)).then((value) {
|
||||||
|
widget.getLiveCareHistory();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getLanguageID() async {
|
||||||
|
languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLiveCareClinicsList() {
|
||||||
|
isDataLoaded = false;
|
||||||
|
LiveCareService service = new LiveCareService();
|
||||||
|
service.getLivecareClinics(context).then((res) {
|
||||||
|
print(res['PatientER_GetClinicsList'].length);
|
||||||
|
if (res['MessageStatus'] == 1) {
|
||||||
|
setState(() {
|
||||||
|
liveCareClinicsListResponse =
|
||||||
|
LiveCareClinicsListResponse.fromJson(res);
|
||||||
|
print(liveCareClinicsListResponse.patientERGetClinicsList.length);
|
||||||
|
selectedClinicID =
|
||||||
|
liveCareClinicsListResponse.patientERGetClinicsList[0].serviceID;
|
||||||
|
selectedClinicName = liveCareClinicsListResponse
|
||||||
|
.patientERGetClinicsList[0].serviceName;
|
||||||
|
isDataLoaded = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
isDataLoaded = true;
|
||||||
|
AppToast.showErrorToast(message: res['ErrorEndUserMessage']);
|
||||||
|
}
|
||||||
|
}).catchError((err) {
|
||||||
|
print(err);
|
||||||
|
}).showProgressBar(
|
||||||
|
text: "Loading", backgroundColor: Colors.blue.withOpacity(0.6));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSelectedIndex(PatientERGetClinicsList patientERGetClinicsList) {
|
||||||
|
setState(() {
|
||||||
|
selectedClinicID = patientERGetClinicsList.serviceID;
|
||||||
|
selectedClinicName = patientERGetClinicsList.serviceName;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:diplomaticquarterapp/models/FamilyFiles/PatientERVirtualHistoryResponse.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/livecare/widgets/LiveCareHistoryCard.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LiveCareLogs extends StatefulWidget {
|
||||||
|
List<ErRequestHistoryList> erRequestHistoryList;
|
||||||
|
|
||||||
|
LiveCareLogs({@required this.erRequestHistoryList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LiveCareLogsState createState() => _LiveCareLogsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveCareLogsState extends State<LiveCareLogs> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: ScrollPhysics(),
|
||||||
|
padding: EdgeInsets.all(0.0),
|
||||||
|
itemCount: widget.erRequestHistoryList.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return widget.erRequestHistoryList[index].callStatus < 4
|
||||||
|
? Container()
|
||||||
|
: LiveCareHistoryCard(
|
||||||
|
erRequestHistoryList: widget.erRequestHistoryList[index]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,240 @@
|
|||||||
|
import 'package:diplomaticquarterapp/core/enum/viewstate.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appointment_details.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appoitment_rated.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/appointment_rate_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/landing/landing_page.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/avatar/large_avatar.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/buttons/button.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/buttons/secondary_button.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/drawer/app_drawer_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/input/text_field.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
|
||||||
|
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hexcolor/hexcolor.dart';
|
||||||
|
|
||||||
|
class RateAppointmentClinic extends StatefulWidget {
|
||||||
|
final AppointmentDetails appointmentDetails;
|
||||||
|
final String doctorNote;
|
||||||
|
final int doctorRate;
|
||||||
|
|
||||||
|
RateAppointmentClinic(
|
||||||
|
{this.appointmentDetails, this.doctorRate, this.doctorNote});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_RateAppointmentClinicState createState() => _RateAppointmentClinicState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RateAppointmentClinicState extends State<RateAppointmentClinic> {
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
String note = "";
|
||||||
|
int rating = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BaseView<AppointmentRateViewModel>(
|
||||||
|
builder: (_, model, w) => AppScaffold(
|
||||||
|
baseViewModel: model,
|
||||||
|
body: Scaffold(
|
||||||
|
backgroundColor: Colors.grey[200],
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
textTheme: TextTheme(
|
||||||
|
headline6:
|
||||||
|
TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
title: Text('Rate'),
|
||||||
|
leading: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(Icons.menu),
|
||||||
|
color: Colors.white,
|
||||||
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
drawer: SafeArea(child: AppDrawer()),
|
||||||
|
body: FractionallySizedBox(
|
||||||
|
widthFactor: 1,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
Texts(
|
||||||
|
'How would you rate your last visit to the Clinic',
|
||||||
|
bold: true,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
border: Border.all(color: Colors.white, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
LargeAvatar(
|
||||||
|
name: model.appointmentDetails.clinicName,
|
||||||
|
width: 110,
|
||||||
|
height: 110,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 22,
|
||||||
|
),
|
||||||
|
Texts(
|
||||||
|
model.appointmentDetails.projectName,
|
||||||
|
bold: true,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Texts(
|
||||||
|
model.appointmentDetails.clinicName,
|
||||||
|
bold: true,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Texts(
|
||||||
|
'Please rate the Clinic',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
...List.generate(
|
||||||
|
5,
|
||||||
|
(index) => AnimatedSwitcher(
|
||||||
|
duration: Duration(milliseconds: 1000),
|
||||||
|
switchInCurve: Curves.elasticOut,
|
||||||
|
switchOutCurve: Curves.elasticIn,
|
||||||
|
transitionBuilder:
|
||||||
|
(Widget child, Animation<double> animation) {
|
||||||
|
return ScaleTransition(
|
||||||
|
child: child, scale: animation);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
key: ValueKey<int>(rating),
|
||||||
|
child: IconButton(
|
||||||
|
iconSize: 55.0,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
rating = index + 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
color: rating >= (index + 1)
|
||||||
|
? Color.fromRGBO(255, 186, 0, 1.0)
|
||||||
|
: Colors.grey[400],
|
||||||
|
// Theme.of(context).hintColor,
|
||||||
|
icon: Icon(rating >= (index + 1)
|
||||||
|
? EvaIcons.star
|
||||||
|
: EvaIcons.star)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 12),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomSheet: Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.15,
|
||||||
|
width: double.infinity,
|
||||||
|
color: Colors.grey[200],
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.9,
|
||||||
|
child: SecondaryButton(
|
||||||
|
onTap: () async {
|
||||||
|
if (rating > 0) {
|
||||||
|
model
|
||||||
|
.sendAppointmentRate(
|
||||||
|
rating,
|
||||||
|
widget.appointmentDetails.appointmentNo,
|
||||||
|
widget.appointmentDetails.projectID,
|
||||||
|
widget.appointmentDetails.doctorID,
|
||||||
|
widget.appointmentDetails.clinicID,
|
||||||
|
note)
|
||||||
|
.then(
|
||||||
|
(value) => {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
FadePage(
|
||||||
|
page: LandingPage(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
AppToast.showErrorToast(
|
||||||
|
message: 'please rate the clinic');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: "Rate",
|
||||||
|
disabled: model.state == ViewState.BusyLocal,
|
||||||
|
loading: model.state == ViewState.BusyLocal,
|
||||||
|
textColor: Theme.of(context).backgroundColor),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
FadePage(
|
||||||
|
page: LandingPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Texts(
|
||||||
|
'Later',
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
color: Hexcolor('#151DFE'),
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,241 @@
|
|||||||
|
import 'package:diplomaticquarterapp/core/enum/viewstate.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/model/rate/appointment_details.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/viewModels/appointment_rate_view_model.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/landing/landing_page.dart';
|
||||||
|
import 'package:diplomaticquarterapp/pages/rateAppointment/rate_appointment_clinic.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/avatar/large_avatar.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/buttons/secondary_button.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/data_display/text.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/drawer/app_drawer_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/input/text_field.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
|
||||||
|
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
|
||||||
|
import 'package:eva_icons_flutter/eva_icons_flutter.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hexcolor/hexcolor.dart';
|
||||||
|
|
||||||
|
class RateAppointmentDoctor extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_RateAppointmentDoctorState createState() => _RateAppointmentDoctorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
String note = "";
|
||||||
|
int rating = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BaseView<AppointmentRateViewModel>(
|
||||||
|
builder: (_, model, w) => AppScaffold(
|
||||||
|
baseViewModel: model,
|
||||||
|
body: Scaffold(
|
||||||
|
backgroundColor: Colors.grey[200],
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
textTheme: TextTheme(
|
||||||
|
headline6:
|
||||||
|
TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
title: Text('Rate'),
|
||||||
|
leading: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return IconButton(
|
||||||
|
icon: Icon(Icons.menu),
|
||||||
|
color: Colors.white,
|
||||||
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
drawer: SafeArea(child: AppDrawer()),
|
||||||
|
body: FractionallySizedBox(
|
||||||
|
widthFactor: 1,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
Texts(
|
||||||
|
'How would you rate your last visit to the doctor',
|
||||||
|
bold: true,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
border: Border.all(color: Colors.white, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
LargeAvatar(
|
||||||
|
name: model.appointmentDetails.doctorName,
|
||||||
|
url: model.appointmentDetails.doctorImageURL,
|
||||||
|
width: 110,
|
||||||
|
height: 110,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 22,
|
||||||
|
),
|
||||||
|
Texts(
|
||||||
|
model.appointmentDetails.doctorName,
|
||||||
|
bold: true,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Texts(
|
||||||
|
model.appointmentDetails.clinicName,
|
||||||
|
bold: true,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 8,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Texts(
|
||||||
|
'Please rate the doctor',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
...List.generate(
|
||||||
|
5,
|
||||||
|
(index) => AnimatedSwitcher(
|
||||||
|
duration: Duration(milliseconds: 1000),
|
||||||
|
switchInCurve: Curves.elasticOut,
|
||||||
|
switchOutCurve: Curves.elasticIn,
|
||||||
|
transitionBuilder:
|
||||||
|
(Widget child, Animation<double> animation) {
|
||||||
|
return ScaleTransition(
|
||||||
|
child: child, scale: animation);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
key: ValueKey<int>(rating),
|
||||||
|
child: IconButton(
|
||||||
|
iconSize: 55.0,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
rating = index + 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
color: rating >= (index + 1)
|
||||||
|
? Color.fromRGBO(255, 186, 0, 1.0)
|
||||||
|
: Colors.grey[400],
|
||||||
|
// Theme.of(context).hintColor,
|
||||||
|
icon: Icon(rating >= (index + 1)
|
||||||
|
? EvaIcons.star
|
||||||
|
: EvaIcons.star)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
Form(
|
||||||
|
key: formKey,
|
||||||
|
child: TextFields(
|
||||||
|
hintText: "Notes",
|
||||||
|
minLines: 4,
|
||||||
|
maxLines: 4,
|
||||||
|
validator: (value) {
|
||||||
|
if (value.isEmpty)
|
||||||
|
return 'Please enter your note';
|
||||||
|
else if (rating == 0) return 'Rating cannot be \"0\"';
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
note = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 12,),
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.9,
|
||||||
|
child: SecondaryButton(
|
||||||
|
onTap: () async {
|
||||||
|
final form = formKey.currentState;
|
||||||
|
if (form.validate()) {
|
||||||
|
form.save();
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
FadePage(
|
||||||
|
page: RateAppointmentClinic(
|
||||||
|
appointmentDetails:
|
||||||
|
model.appointmentDetails,
|
||||||
|
doctorNote: note,
|
||||||
|
doctorRate: rating,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: "Next",
|
||||||
|
disabled: model.state == ViewState.BusyLocal,
|
||||||
|
loading: model.state == ViewState.BusyLocal,
|
||||||
|
textColor: Theme.of(context).backgroundColor),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 12,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
FadePage(
|
||||||
|
page: LandingPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Texts(
|
||||||
|
'Later',
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
color: Hexcolor('#151DFE'),
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,304 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:diplomaticquarterapp/config/config.dart';
|
||||||
|
import 'package:diplomaticquarterapp/config/shared_pref_kay.dart';
|
||||||
|
import 'package:diplomaticquarterapp/core/service/base_service.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.dart';
|
||||||
|
import 'package:diplomaticquarterapp/models/Request.dart';
|
||||||
|
import 'package:diplomaticquarterapp/services/authentication/auth_provider.dart';
|
||||||
|
import 'package:diplomaticquarterapp/uitl/app_shared_preferences.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LiveCareService extends BaseService {
|
||||||
|
AppSharedPreferences sharedPref = AppSharedPreferences();
|
||||||
|
AppGlobal appGlobal = new AppGlobal();
|
||||||
|
|
||||||
|
AuthenticatedUser authUser = new AuthenticatedUser();
|
||||||
|
AuthProvider authProvider = new AuthProvider();
|
||||||
|
|
||||||
|
Future<Map> getLivecareClinics(BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
Request req = appGlobal.getPublicRequest();
|
||||||
|
request = {
|
||||||
|
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||||
|
"IPAdress": "10.20.10.20",
|
||||||
|
"VersionID": req.VersionID,
|
||||||
|
"Channel": req.Channel,
|
||||||
|
"generalid": 'Cs2020@2016\$2958',
|
||||||
|
"PatientOutSA": 0,
|
||||||
|
"TokenID": "",
|
||||||
|
"DeviceTypeID": req.DeviceTypeID,
|
||||||
|
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||||
|
"Age": authUser.age != null ? authUser.age : 0,
|
||||||
|
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||||
|
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(GET_LIVECARE_CLINICS,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> getLivecareHistory(BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
// Request req = appGlobal.getPublicRequest();
|
||||||
|
request = {
|
||||||
|
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||||
|
"TokenID": "",
|
||||||
|
"SessionID": "YckwoXhUmWBsnHKEKig"
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(GET_LIVECARE_HISTORY,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> getLivecareClinicTiming(
|
||||||
|
int serviceID, BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
Request req = appGlobal.getPublicRequest();
|
||||||
|
request = {
|
||||||
|
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||||
|
"IPAdress": "10.20.10.20",
|
||||||
|
"VersionID": req.VersionID,
|
||||||
|
"Channel": req.Channel,
|
||||||
|
"generalid": 'Cs2020@2016\$2958',
|
||||||
|
"PatientOutSA": 0,
|
||||||
|
"ServiceID": serviceID,
|
||||||
|
"DeviceTypeID": req.DeviceTypeID,
|
||||||
|
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||||
|
"Age": authUser.age != null ? authUser.age : 0,
|
||||||
|
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||||
|
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(GET_LIVECARE_CLINIC_TIMING,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> getERAppointmentFees(int serviceID, BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
Request req = appGlobal.getPublicRequest();
|
||||||
|
request = {
|
||||||
|
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||||
|
"IPAdress": "10.20.10.20",
|
||||||
|
"VersionID": req.VersionID,
|
||||||
|
"Channel": req.Channel,
|
||||||
|
"generalid": 'Cs2020@2016\$2958',
|
||||||
|
"PatientOutSA": 0,
|
||||||
|
"ServiceID": serviceID,
|
||||||
|
"ProjectID": 15,
|
||||||
|
"DeviceTypeID": req.DeviceTypeID,
|
||||||
|
"PatientType": authUser.patientType != null ? authUser.patientType : 0,
|
||||||
|
"PatientTypeID": authUser.patientType != null ? authUser.patientType : 0,
|
||||||
|
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||||
|
"Age": authUser.age != null ? authUser.age : 0,
|
||||||
|
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||||
|
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(GET_ER_APPOINTMENT_FEES,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> getERAppointmentTime(int serviceID, BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
Request req = appGlobal.getPublicRequest();
|
||||||
|
request = {
|
||||||
|
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||||
|
"IPAdress": "10.20.10.20",
|
||||||
|
"VersionID": req.VersionID,
|
||||||
|
"Channel": req.Channel,
|
||||||
|
"generalid": 'Cs2020@2016\$2958',
|
||||||
|
"PatientOutSA": 0,
|
||||||
|
"ServiceID": serviceID,
|
||||||
|
"ProjectID": 15,
|
||||||
|
"DeviceTypeID": req.DeviceTypeID,
|
||||||
|
"PatientType": authUser.patientType != null ? authUser.patientType : 0,
|
||||||
|
"PatientTypeID": authUser.patientType != null ? authUser.patientType : 0,
|
||||||
|
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||||
|
"Age": authUser.age != null ? authUser.age : 0,
|
||||||
|
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||||
|
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(GET_ER_APPOINTMENT_TIME,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> addNewCallForPatientER(
|
||||||
|
int serviceID, String clientRequestID, BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
String deviceToken;
|
||||||
|
getDeviceToken().then((value) {
|
||||||
|
print(value);
|
||||||
|
deviceToken = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
var languageID = await sharedPref.getString(APP_LANGUAGE);
|
||||||
|
Request req = appGlobal.getPublicRequest();
|
||||||
|
request = {
|
||||||
|
"LanguageID": languageID == 'ar' ? 1 : 2,
|
||||||
|
"IPAdress": "10.20.10.20",
|
||||||
|
"VersionID": req.VersionID,
|
||||||
|
"Channel": req.Channel,
|
||||||
|
"generalid": 'Cs2020@2016\$2958',
|
||||||
|
"PatientOutSA": 0,
|
||||||
|
"ErServiceID": serviceID,
|
||||||
|
"ClientRequestID": clientRequestID,
|
||||||
|
"DeviceToken": deviceToken,
|
||||||
|
"VoipToken": "",
|
||||||
|
"Latitude": "24.708488",
|
||||||
|
"Longitude": "46.665925",
|
||||||
|
"DeviceType": Platform.isIOS ? 'iOS' : 'Android',
|
||||||
|
"PatientType": authUser.patientType != null ? authUser.patientType : 0,
|
||||||
|
"PatientTypeID": authUser.patientType != null ? authUser.patientType : 0,
|
||||||
|
"SessionID": "YckwoXhUmWBsnHKEKig",
|
||||||
|
"Age": authUser.age != null ? authUser.age : 0,
|
||||||
|
"PatientID": authUser.patientID != null ? authUser.patientID : 0,
|
||||||
|
"Gender": authUser.gender != null ? authUser.gender : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(ADD_NEW_CALL_FOR_PATIENT_ER,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> getDeviceToken() async {
|
||||||
|
String deviceToken = await sharedPref.getString(PUSH_TOKEN);
|
||||||
|
return deviceToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> cancelLiveCareRequest(int vc_id, BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
request = {"VCID": vc_id};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(CANCEL_LIVECARE_REQUEST,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map> sendLiveCareInvoiceEmail(String appoNo, int projectID,
|
||||||
|
String emailAddress, BuildContext context) async {
|
||||||
|
Map<String, dynamic> request;
|
||||||
|
|
||||||
|
if (await this.sharedPref.getObject(USER_PROFILE) != null) {
|
||||||
|
var data = AuthenticatedUser.fromJson(
|
||||||
|
await this.sharedPref.getObject(USER_PROFILE));
|
||||||
|
authUser = data;
|
||||||
|
}
|
||||||
|
request = {
|
||||||
|
"To": emailAddress,
|
||||||
|
"ProjectID": projectID,
|
||||||
|
"AppointmentNo": appoNo
|
||||||
|
};
|
||||||
|
|
||||||
|
dynamic localRes;
|
||||||
|
|
||||||
|
await baseAppClient.post(SEND_LIVECARE_INVOICE_EMAIL,
|
||||||
|
onSuccess: (response, statusCode) async {
|
||||||
|
localRes = response;
|
||||||
|
}, onFailure: (String error, int statusCode) {
|
||||||
|
throw error;
|
||||||
|
}, body: request);
|
||||||
|
return Future.value(localRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NavigationService {
|
||||||
|
final GlobalKey<NavigatorState> navigatorKey =
|
||||||
|
new GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
Future<dynamic> navigateTo(String routeName) {
|
||||||
|
return navigatorKey.currentState.pushNamed(routeName);
|
||||||
|
}
|
||||||
|
}
|
||||||