Merge branch 'development_v3.3' into dev_3.3_BLE

# Conflicts:
#	lib/config/localized_values.dart
#	lib/core/service/client/base_app_client.dart
#	lib/uitl/translations_delegate_base.dart
dev_3.3_BLE
haroon amjad 2 years ago
commit 55b447926e

@ -32,6 +32,7 @@ target 'Runner' do
use_modular_headers!
pod 'OpenTok', '~> 2.22.0'
pod 'VTO2Lib'
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
@ -53,9 +54,34 @@ post_install do |installer|
]
build_configuration.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386'
build_configuration.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
xcconfig_path = build_configuration.base_configuration_reference.real_path
xcconfig = File.read(xcconfig_path)
xcconfig_mod = xcconfig.gsub(/DT_TOOLCHAIN_DIR/, "TOOLCHAIN_DIR")
File.open(xcconfig_path, "w") { |file| file << xcconfig_mod }
if build_configuration.build_settings['WRAPPER_EXTENSION'] == 'bundle'
build_configuration.build_settings['DEVELOPMENT_TEAM'] = '3A359E86ZF'
end
end
end
end
post_integrate do |installer|
compiler_flags_key = 'COMPILER_FLAGS'
project_path = 'Pods/Pods.xcodeproj'
project = Xcodeproj::Project.open(project_path)
project.targets.each do |target|
target.build_phases.each do |build_phase|
if build_phase.is_a?(Xcodeproj::Project::Object::PBXSourcesBuildPhase)
build_phase.files.each do |file|
if !file.settings.nil? && file.settings.key?(compiler_flags_key)
compiler_flags = file.settings[compiler_flags_key]
file.settings[compiler_flags_key] = compiler_flags.gsub(/-DOS_OBJECT_USE_OBJC=0\s*/, '')
end
end
end
end
end
project.save()
end

@ -326,7 +326,7 @@ var UPDATE_COVID_QUESTIONNAIRE = 'Services/Doctors.svc/REST/COVID19_Questionnari
var CHANNEL = 3;
var GENERAL_ID = 'Cs2020@2016\$2958';
var IP_ADDRESS = '10.20.10.20';
var VERSION_ID = 10.8;
var VERSION_ID = 10.9;
var SETUP_ID = '91877';
var LANGUAGE = 2;
// var PATIENT_OUT_SA = 0;

@ -1894,4 +1894,10 @@ const Map localizedValues = {
"dischargeDate": {"en": "Discharge Date", "ar": "تاريخ التفريغ"},
"selectAdmissionText": {"en": "Please select one of the admissions from below to view medical reports:", "ar": "يرجى تحديد أحد حالات القبول من الأسفل لعرض التقارير الطبية:"},
"spirometer": {"en": "Spirometer", "ar": "مقياس التنفس"},
"invalidEligibility": {"en": "You cannot make online payment because you are not eligible to use the provided service.", "ar": "لا يمكنك إجراء الدفع عبر الإنترنت لأنك غير مؤهل لاستخدام الخدمة المقدمة."},
"invalidInsurance": {"en": "You cannot make online payment because you do not have a valid insurance.", "ar": "لا يمكنك إجراء الدفع عبر الإنترنت لأنه ليس لديك تأمين صالح."},
"continueCash": {"en": "Continue as cash", "ar": "تواصل نقدا"},
"updateInsurance": {"en": "Update insurance", "ar": "تحديث التأمين"},
"downloadReport": {"en": "Download Report", "ar": "تحميل تقرير المختبر"},
"habibCallCenter": {"en": "Please contact AlHabib call center to update your insurance manually.", "ar": "يرجى الاتصال بمركز اتصال الحبيب لتحديث التأمين الخاص بك يدوياً."},
};

@ -25,34 +25,38 @@ class RequestSendLabReportEmail {
String invoiceNo;
String orderDate;
String orderNo;
bool isDownload;
int doctorID;
RequestSendLabReportEmail(
{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,
this.to,
this.dateofBirth,
this.patientIditificationNum,
this.patientMobileNumber,
this.patientName,
this.setupID,
this.projectName,
this.clinicName,
this.doctorName,
this.projectID,
this.invoiceNo,
this.orderDate,
this.orderNo});
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.patientOutSA,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID,
this.patientID,
this.tokenID,
this.patientTypeID,
this.patientType,
this.to,
this.dateofBirth,
this.patientIditificationNum,
this.patientMobileNumber,
this.patientName,
this.setupID,
this.projectName,
this.clinicName,
this.doctorName,
this.projectID,
this.invoiceNo,
this.orderDate,
this.orderNo,
this.isDownload,
this.doctorID});
RequestSendLabReportEmail.fromJson(Map<String, dynamic> json) {
versionID = json['VersionID'];
@ -81,6 +85,8 @@ class RequestSendLabReportEmail {
invoiceNo = json['InvoiceNo'];
orderDate = json['OrderDate'];
orderNo = json['OrderNo'];
isDownload = json['IsDownload'];
doctorID = json['DoctorID'];
}
Map<String, dynamic> toJson() {
@ -111,6 +117,8 @@ class RequestSendLabReportEmail {
data['InvoiceNo'] = this.invoiceNo;
data['OrderDate'] = this.orderDate;
data['OrderNo'] = this.orderNo;
data['IsDownload'] = this.isDownload;
data['DoctorID'] = this.doctorID;
return data;
}
}

@ -149,7 +149,7 @@ class BaseAppClient {
// body['IdentificationNo'] = 1023854217;
// body['MobileNo'] = "531940021";
// body['PatientID'] = 3615065; //3844083
// body['PatientID'] = 4767370; //3844083
// body['TokenID'] = "@dm!n";
// Patient ID: 3027574

@ -11,6 +11,8 @@ import 'package:diplomaticquarterapp/models/Authentication/authenticated_user.da
class LabsService extends BaseService {
List<PatientLabOrders> patientLabOrdersList = List();
String labReportPDF = "";
Future getPatientLabOrdersList() async {
hasError = false;
Map<String, dynamic> body = Map();
@ -194,7 +196,7 @@ class LabsService extends BaseService {
RequestSendLabReportEmail _requestSendLabReportEmail = RequestSendLabReportEmail();
Future sendLabReportEmail({PatientLabOrders patientLabOrder, AuthenticatedUser userObj}) async {
Future sendLabReportEmail({PatientLabOrders patientLabOrder, AuthenticatedUser userObj, bool isDownload = false}) async {
_requestSendLabReportEmail.projectID = patientLabOrder.projectID;
_requestSendLabReportEmail.invoiceNo = patientLabOrder.invoiceNo;
_requestSendLabReportEmail.doctorName = patientLabOrder.doctorName;
@ -208,8 +210,16 @@ class LabsService extends BaseService {
_requestSendLabReportEmail.projectName = patientLabOrder.projectName;
_requestSendLabReportEmail.setupID = patientLabOrder.setupID;
_requestSendLabReportEmail.orderNo = patientLabOrder.orderNo;
_requestSendLabReportEmail.isDownload = isDownload;
_requestSendLabReportEmail.doctorID = patientLabOrder.doctorID;
await baseAppClient.post(SEND_LAB_RESULT_EMAIL, onSuccess: (dynamic response, int statusCode) {
await baseAppClient.post(SEND_LAB_RESULT_EMAIL, onSuccess: (dynamic response, int statusCode) {}, onFailure: (String error, int statusCode) {
if(isDownload) {
labReportPDF = response['LabReportsPDFContent'];
}
}, onFailure: (String error, int statusCode) {
hasError = true;
super.error = error;
}, body: _requestSendLabReportEmail.toJson());

@ -20,6 +20,8 @@ class LabsViewModel extends BaseViewModel {
List<LabOrderResult> get labOrdersResultsList => _labsService.labOrdersResultsList;
List<TimeSeriesSales2> timeSeries = [];
String get labReportPDF => _labsService.labReportPDF;
List<PatientLabOrdersList> _patientLabOrdersListClinic = List();
List<PatientLabOrdersList> _patientLabOrdersListHospital = List();
@ -135,11 +137,16 @@ class LabsViewModel extends BaseViewModel {
}
}
sendLabReportEmail({PatientLabOrders patientLabOrder, String mes, AuthenticatedUser userObj}) async {
await _labsService.sendLabReportEmail(patientLabOrder: patientLabOrder, userObj: userObj);
sendLabReportEmail({PatientLabOrders patientLabOrder, String mes, AuthenticatedUser userObj, bool isDownload = false}) async {
await _labsService.sendLabReportEmail(patientLabOrder: patientLabOrder, userObj: userObj, isDownload: isDownload);
if (_labsService.hasError) {
error = _labsService.error;
} else
AppToast.showSuccessToast(message: mes);
} else {
if(isDownload) {
} else {
AppToast.showSuccessToast(message: mes);
}
}
}
}

@ -59,6 +59,10 @@ class _BookConfirmState extends State<BookConfirm> {
AppSharedPreferences sharedPref = new AppSharedPreferences();
bool isInsured = false;
bool isEligible = false;
bool isCash = false;
@override
void initState() {
// widget.authUser = new AuthenticatedUser();
@ -437,13 +441,40 @@ class _BookConfirmState extends State<BookConfirm> {
}
getPatientShare(context, String appointmentNo, int clinicID, int projectID, DoctorList docObject) {
String errorMsg = "";
GifLoaderDialogUtils.showMyDialog(context);
widget.service.getPatientShare(appointmentNo, clinicID, projectID, context).then((res) {
projectViewModel.selectedBodyPartList.clear();
projectViewModel.laserSelectionDuration = 0;
print(res);
widget.patientShareResponse = new PatientShareResponse.fromJson(res);
navigateToBookSuccess(context, docObject, widget.patientShareResponse);
widget.patientShareResponse = new PatientShareResponse.fromJson(res['OnlineCheckInAppointments'][0]);
isInsured = res["IsInsured"];
isEligible = res["IsEligible"];
isCash = res["IsCash"];
if (isCash) {
navigateToBookSuccess(context, docObject, widget.patientShareResponse);
} else {
if (isInsured && isEligible) {
navigateToBookSuccess(context, docObject, widget.patientShareResponse);
} else {
if (isInsured && !isEligible) {
errorMsg = TranslationBase.of(context).invalidEligibility;
} else {
errorMsg = TranslationBase.of(context).invalidInsurance;
}
ConfirmDialog dialog = new ConfirmDialog(
isDissmissable: false,
context: context,
confirmMessage: errorMsg,
okText: TranslationBase.of(context).updateInsuranceText,
cancelText: TranslationBase.of(context).continueCash,
okFunction: () => {openUpdateInsurance()},
cancelFunction: () => {continueAsCash(docObject, appointmentNo)});
dialog.showAlertDialog(context);
}
}
}).catchError((err) {
GifLoaderDialogUtils.hideDialog(context);
// AppToast.showErrorToast(message: err);

@ -79,6 +79,10 @@ class _ToDoState extends State<ToDo> with SingleTickerProviderStateMixin {
bool dataLoaded = false;
bool isInsured = false;
bool isEligible = false;
bool isCash = false;
@override
void initState() {
widget.patientShareResponse = new PatientShareResponse();
@ -889,6 +893,7 @@ class _ToDoState extends State<ToDo> with SingleTickerProviderStateMixin {
}
getPatientShare(context, AppoitmentAllHistoryResultList appo) {
String errorMsg = "";
DoctorsListService service = new DoctorsListService();
if (appo.isLiveCareAppointment) {
getLiveCareAppointmentPatientShare(context, service, appo);
@ -896,8 +901,33 @@ class _ToDoState extends State<ToDo> with SingleTickerProviderStateMixin {
GifLoaderDialogUtils.showMyDialog(context);
service.getPatientShare(appo.appointmentNo.toString(), appo.clinicID, appo.projectID, context).then((res) {
GifLoaderDialogUtils.hideDialog(context);
widget.patientShareResponse = new PatientShareResponse.fromJson(res);
openPaymentDialog(appo, widget.patientShareResponse);
widget.patientShareResponse = new PatientShareResponse.fromJson(res['OnlineCheckInAppointments'][0]);
isInsured = res["IsInsured"];
isEligible = res["IsEligible"];
isCash = res["IsCash"];
if (isCash) {
openPaymentDialog(appo, widget.patientShareResponse);
} else {
if (isInsured && isEligible) {
openPaymentDialog(appo, widget.patientShareResponse);
} else {
if (isInsured && !isEligible) {
errorMsg = TranslationBase.of(context).invalidEligibility;
} else {
errorMsg = TranslationBase.of(context).invalidInsurance;
}
ConfirmDialog dialog = new ConfirmDialog(
isDissmissable: false,
context: context,
confirmMessage: errorMsg,
okText: TranslationBase.of(context).updateInsuranceText,
cancelText: TranslationBase.of(context).continueCash,
okFunction: () => {openUpdateInsurance()},
cancelFunction: () => {continueAsCash(appo)});
dialog.showAlertDialog(context);
}
}
}).catchError((err) {
GifLoaderDialogUtils.hideDialog(context);
AppToast.showErrorToast(message: err);

@ -132,24 +132,31 @@ class _AttachInsuranceCardImageDialogState extends State<AttachInsuranceCardImag
),
Padding(
padding: const EdgeInsets.all(18.0),
child: DefaultButton(
TranslationBase.of(context).updateInsuranceManually,
() {
Navigator.pop(context);
Navigator.push(
context,
FadePage(
page: UpdateInsuranceManually(
patientIdentificationNo: widget.identificationNo,
patientMobileNumber: widget.mobileNo,
patientID: num.parse(widget.fileNo),
),
),
);
},
textColor: Colors.white,
color: CustomColors.accentColor,
child: Text(TranslationBase.of(context).habibCallCenter,
style: TextStyle(
fontSize: 16,
letterSpacing: -0.48,
fontWeight: FontWeight.w600,
),
),
// DefaultButton(
// TranslationBase.of(context).updateInsuranceManually,
// () {
// Navigator.pop(context);
// Navigator.push(
// context,
// FadePage(
// page: UpdateInsuranceManually(
// patientIdentificationNo: widget.identificationNo,
// patientMobileNumber: widget.mobileNo,
// patientID: num.parse(widget.fileNo),
// ),
// ),
// );
// },
// textColor: Colors.white,
// color: CustomColors.accentColor,
// ),
),
SizedBox(
height: 25.0,

@ -184,19 +184,21 @@ class InsurancePage extends StatelessWidget {
void updateManually(BuildContext context, String errorMsg, String patientIdentificationID, int patientID, String mobileNumber) {
ConfirmDialog dialog = new ConfirmDialog(
context: context,
confirmMessage: errorMsg + ". " + TranslationBase.of(context).updateInsuranceManuallyDialog,
okText: TranslationBase.of(context).yes,
cancelText: TranslationBase.of(context).no,
confirmMessage: errorMsg + ". " + TranslationBase.of(context).habibCallCenter,
okText: TranslationBase.of(context).ok,
cancelText: TranslationBase.of(context).cancel,
okFunction: () => {
Navigator.pop(context),
Navigator.push(
context,
FadePage(
page: UpdateInsuranceManually(
context,
FadePage(
page: UpdateInsuranceManually(
patientIdentificationNo: patientIdentificationID,
patientID: patientID,
patientMobileNumber: mobileNumber,
)))
),
),
),
},
cancelFunction: () => {});
dialog.showAlertDialog(context);

@ -1,13 +1,21 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:diplomaticquarterapp/core/model/labs/patient_lab_orders.dart';
import 'package:diplomaticquarterapp/core/viewModels/medical/labs_view_model.dart';
import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart';
import 'package:diplomaticquarterapp/pages/base/base_view.dart';
import 'package:diplomaticquarterapp/uitl/app_toast.dart';
import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart';
import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart';
import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart';
import 'package:diplomaticquarterapp/widgets/data_display/medical/LabResult/laboratory_result_widget.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:open_filex/open_filex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
class LaboratoryResultPage extends StatefulWidget {
@ -37,27 +45,73 @@ class _LaboratoryResultPageState extends State<LaboratoryResultPage> {
showNewAppBar: true,
showNewAppBarTitle: true,
backgroundColor: Color(0xffF8F8F8),
body: ListView.builder(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.only(bottom: 12),
itemBuilder: (context, index) => LaboratoryResultWidget(
onTap: () async {
GifLoaderDialogUtils.showMyDialog(context);
await model.sendLabReportEmail(patientLabOrder: widget.patientLabOrders, mes: TranslationBase.of(context).sendSuc, userObj: projectViewModel.user);
GifLoaderDialogUtils.hideDialog(context);
},
billNo: widget.patientLabOrders.invoiceNo,
// details: model.patientLabSpecialResult[index].resultDataHTML,
details: model.patientLabSpecialResult.isEmpty ? null : getSpecialResults(model),
orderNo: widget.patientLabOrders.orderNo,
patientLabOrder: widget.patientLabOrders,
body: SingleChildScrollView(
child: Column(
children: [
ListView.builder(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.only(bottom: 12),
itemBuilder: (context, index) => LaboratoryResultWidget(
onTap: () async {
GifLoaderDialogUtils.showMyDialog(context);
await model.sendLabReportEmail(patientLabOrder: widget.patientLabOrders, mes: TranslationBase.of(context).sendSuc, userObj: projectViewModel.user);
GifLoaderDialogUtils.hideDialog(context);
},
billNo: widget.patientLabOrders.invoiceNo,
// details: model.patientLabSpecialResult[index].resultDataHTML,
details: model.patientLabSpecialResult.isEmpty ? null : getSpecialResults(model),
orderNo: widget.patientLabOrders.orderNo,
patientLabOrder: widget.patientLabOrders,
),
itemCount: 1,
),
SizedBox(
height: 120.0,
)
],
),
),
bottomSheet: Container(
color: Colors.white,
height: MediaQuery.of(context).size.height * 0.081,
width: double.infinity,
padding: EdgeInsets.all(12.0),
child: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width * 0.9,
child: DefaultButton(
TranslationBase.of(context).downloadReport,
() async {
GifLoaderDialogUtils.showMyDialog(context);
await model.sendLabReportEmail(patientLabOrder: widget.patientLabOrders, mes: TranslationBase.of(context).sendSuc, userObj: projectViewModel.user, isDownload: true);
GifLoaderDialogUtils.hideDialog(context);
try {
String path = await _createFileFromString(model.labReportPDF, "pdf");
OpenFilex.open(path);
} catch (ex) {
AppToast.showErrorToast(message: "Cannot open file.");
}
},
textColor: Colors.white,
),
),
],
),
itemCount: 1,
),
),
);
}
Future<String> _createFileFromString(String encodedStr, String ext) async {
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getApplicationDocumentsDirectory()).path;
File file = File("$dir/" + DateTime.now().millisecondsSinceEpoch.toString() + "." + ext);
await file.writeAsBytes(bytes);
return file.path;
}
String getSpecialResults(LabsViewModel model) {
String labResults = "";
model.patientLabSpecialResult.forEach((element) {

@ -104,40 +104,39 @@ class _HomeReportPageState extends State<HomeReportPage> with SingleTickerProvid
),
if (model.user != null)
Expanded(
child:
TabBarView(
physics: BouncingScrollPhysics(),
controller: _tabController_new,
children: <Widget>[
child: TabBarView(
physics: BouncingScrollPhysics(),
controller: _tabController_new,
children: <Widget>[
Container(
child: Column(
children: [
Padding(
padding: EdgeInsets.all(21),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
myRadioButton(TranslationBase.of(context).requested, 0),
myRadioButton(TranslationBase.of(context).ready, 1),
myRadioButton(TranslationBase.of(context).cancelled, 2),
],
),
child: Column(
children: [
Padding(
padding: EdgeInsets.all(21),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
myRadioButton(TranslationBase.of(context).requested, 0),
myRadioButton(TranslationBase.of(context).ready, 1),
myRadioButton(TranslationBase.of(context).cancelled, 2),
],
),
),
Expanded(
child: IndexedStack(
index: _currentPage,
children: [
ReportListWidget(reportList: model.reportsOrderRequestList, emailAddress: model.user.emailAddress),
ReportListWidget(reportList: model.reportsOrderReadyList, emailAddress: model.user.emailAddress),
ReportListWidget(reportList: model.reportsOrderCanceledList, emailAddress: model.user.emailAddress),
],
),
)
],
),
Expanded(
child: IndexedStack(
index: _currentPage,
children: [
ReportListWidget(reportList: model.reportsOrderRequestList, emailAddress: model.user.emailAddress),
ReportListWidget(reportList: model.reportsOrderReadyList, emailAddress: model.user.emailAddress),
ReportListWidget(reportList: model.reportsOrderCanceledList, emailAddress: model.user.emailAddress),
],
),
)
],
),
),
// InPatient Medical Reports
Container(
),
// InPatient Medical Reports
Container(
child: model.admissionsMedicalReportList.isNotEmpty
? Column(
children: [
@ -249,20 +248,21 @@ class _HomeReportPageState extends State<HomeReportPage> with SingleTickerProvid
if (projectViewModel.havePrivilege(21)
// && _tabController_new.index == 0
)
Padding(
padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21),
child: DefaultButton(
TranslationBase.of(context).requestMedicalReport.toLowerCase().capitalizeFirstofEach,
() => Navigator.push(
context,
FadePage(
page: MedicalReports(),
),
).then((value) {
model.getReports();
}),
),
)
if (_tabController_new.index == 0)
Padding(
padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21),
child: DefaultButton(
TranslationBase.of(context).requestMedicalReport.toLowerCase().capitalizeFirstofEach,
() => Navigator.push(
context,
FadePage(
page: MedicalReports(),
),
).then((value) {
model.getReports();
}),
),
)
],
),
),

@ -484,7 +484,7 @@ class DoctorsListService extends BaseService {
dynamic localRes;
await baseAppClient.post(GET_PATIENT_SHARE, onSuccess: (response, statusCode) async {
localRes = response['OnlineCheckInAppointments'][0];
localRes = response;
}, onFailure: (String error, int statusCode) {
throw error;
}, body: request);

@ -2903,6 +2903,12 @@ class TranslationBase {
String get dischargeDate => localizedValues["dischargeDate"][locale.languageCode];
String get selectAdmissionText => localizedValues["selectAdmissionText"][locale.languageCode];
String get spirometer => localizedValues["spirometer"][locale.languageCode];
String get invalidEligibility => localizedValues["invalidEligibility"][locale.languageCode];
String get invalidInsurance => localizedValues["invalidInsurance"][locale.languageCode];
String get continueCash => localizedValues["continueCash"][locale.languageCode];
String get updateInsuranceText => localizedValues["updateInsurance"][locale.languageCode];
String get downloadReport => localizedValues["downloadReport"][locale.languageCode];
String get habibCallCenter => localizedValues["habibCallCenter"][locale.languageCode];
}

@ -207,7 +207,7 @@ dependencies:
sms_otp_auto_verify: ^2.1.0
flutter_ios_voip_kit: ^0.0.5
google_api_availability: ^3.0.1
# open_file: ^3.2.1
# open_file: ^3.2.1
open_filex: ^4.3.2
path_provider: ^2.0.8
flutter_blue_plus: 1.15.7

Loading…
Cancel
Save