From 5919554eaa07b802fb9ade5e996ec64785065585 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sun, 3 Nov 2024 13:39:16 +0200 Subject: [PATCH] 11/3/1 --- android/app/src/main/AndroidManifest.xml | 37 +- ios/Podfile.lock | 82 ++++ lib/constant/links.dart | 6 + lib/controller/admin/static_controller.dart | 16 +- lib/controller/drivers/driverthebest.dart | 27 ++ .../employee_controller.dart | 76 +++ lib/controller/functions/crud.dart | 3 +- lib/controller/functions/launch.dart | 8 + .../functions/upload_image copy.dart | 446 ++++++++++++++++++ lib/controller/functions/wallet.dart | 64 +++ lib/controller/notification_controller.dart | 6 +- lib/views/admin/admin_home_page.dart | 23 +- lib/views/admin/drivers/driver_the_best.dart | 61 +++ lib/views/admin/employee/employee_page.dart | 185 ++++++++ lib/views/admin/packages.dart | 107 +++++ lib/views/admin/static/static.dart | 12 + lib/views/widgets/my_textField.dart | 2 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 48 ++ pubspec.yaml | 1 + 20 files changed, 1188 insertions(+), 24 deletions(-) create mode 100644 lib/controller/drivers/driverthebest.dart create mode 100644 lib/controller/employee_controller/employee_controller.dart create mode 100644 lib/controller/functions/upload_image copy.dart create mode 100644 lib/controller/functions/wallet.dart create mode 100644 lib/views/admin/drivers/driver_the_best.dart create mode 100644 lib/views/admin/employee/employee_page.dart create mode 100644 lib/views/admin/packages.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 062d3fd..51e8fc2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,29 +1,32 @@ - - - - + + + - - + + - + + + + - - - + + - + \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b269a90..ee46282 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -7,12 +7,19 @@ PODS: - AppAuth/Core - Firebase/CoreOnly (10.25.0): - FirebaseCore (= 10.25.0) + - Firebase/Crashlytics (10.25.0): + - Firebase/CoreOnly + - FirebaseCrashlytics (~> 10.25.0) - Firebase/Messaging (10.25.0): - Firebase/CoreOnly - FirebaseMessaging (~> 10.25.0) - firebase_core (2.32.0): - Firebase/CoreOnly (= 10.25.0) - Flutter + - firebase_crashlytics (3.5.7): + - Firebase/Crashlytics (= 10.25.0) + - firebase_core + - Flutter - firebase_messaging (14.9.4): - Firebase/Messaging (= 10.25.0) - firebase_core @@ -21,8 +28,19 @@ PODS: - FirebaseCoreInternal (~> 10.0) - GoogleUtilities/Environment (~> 7.12) - GoogleUtilities/Logger (~> 7.12) + - FirebaseCoreExtension (10.29.0): + - FirebaseCore (~> 10.0) - FirebaseCoreInternal (10.27.0): - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseCrashlytics (10.25.0): + - FirebaseCore (~> 10.5) + - FirebaseInstallations (~> 10.0) + - FirebaseRemoteConfigInterop (~> 10.23) + - FirebaseSessions (~> 10.5) + - GoogleDataTransport (~> 9.2) + - GoogleUtilities/Environment (~> 7.8) + - nanopb (< 2.30911.0, >= 2.30908.0) + - PromisesObjC (~> 2.1) - FirebaseInstallations (10.27.0): - FirebaseCore (~> 10.0) - GoogleUtilities/Environment (~> 7.8) @@ -37,7 +55,22 @@ PODS: - GoogleUtilities/Reachability (~> 7.8) - GoogleUtilities/UserDefaults (~> 7.8) - nanopb (< 2.30911.0, >= 2.30908.0) + - FirebaseRemoteConfigInterop (10.29.0) + - FirebaseSessions (10.29.0): + - FirebaseCore (~> 10.5) + - FirebaseCoreExtension (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleDataTransport (~> 9.2) + - GoogleUtilities/Environment (~> 7.13) + - GoogleUtilities/UserDefaults (~> 7.13) + - nanopb (< 2.30911.0, >= 2.30908.0) + - PromisesSwift (~> 2.1) - Flutter (1.0.0) + - flutter_image_compress_common (1.0.0): + - Flutter + - Mantle + - SDWebImage + - SDWebImageWebPCoder - flutter_secure_storage (6.0.0): - Flutter - google_sign_in_ios (0.0.1): @@ -92,8 +125,23 @@ PODS: - TOCropViewController (~> 2.7.4) - image_picker_ios (0.0.1): - Flutter + - libwebp (1.3.2): + - libwebp/demux (= 1.3.2) + - libwebp/mux (= 1.3.2) + - libwebp/sharpyuv (= 1.3.2) + - libwebp/webp (= 1.3.2) + - libwebp/demux (1.3.2): + - libwebp/webp + - libwebp/mux (1.3.2): + - libwebp/demux + - libwebp/sharpyuv (1.3.2) + - libwebp/webp (1.3.2): + - libwebp/sharpyuv - local_auth_darwin (0.0.1): - Flutter + - Mantle (2.2.0): + - Mantle/extobjc (= 2.2.0) + - Mantle/extobjc (2.2.0) - nanopb (2.30910.0): - nanopb/decode (= 2.30910.0) - nanopb/encode (= 2.30910.0) @@ -103,6 +151,14 @@ PODS: - Flutter - FlutterMacOS - PromisesObjC (2.4.0) + - PromisesSwift (2.4.0): + - PromisesObjC (= 2.4.0) + - SDWebImage (5.19.7): + - SDWebImage/Core (= 5.19.7) + - SDWebImage/Core (5.19.7) + - SDWebImageWebPCoder (0.14.6): + - libwebp (~> 1.0) + - SDWebImage/Core (~> 5.17) - sqflite (0.0.3): - Flutter - FlutterMacOS @@ -112,8 +168,10 @@ PODS: DEPENDENCIES: - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - Flutter (from `Flutter`) + - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - google_sign_in_ios (from `.symlinks/plugins/google_sign_in_ios/darwin`) - image_cropper (from `.symlinks/plugins/image_cropper/ios`) @@ -128,25 +186,38 @@ SPEC REPOS: - AppAuth - Firebase - FirebaseCore + - FirebaseCoreExtension - FirebaseCoreInternal + - FirebaseCrashlytics - FirebaseInstallations - FirebaseMessaging + - FirebaseRemoteConfigInterop + - FirebaseSessions - GoogleDataTransport - GoogleSignIn - GoogleUtilities - GTMAppAuth - GTMSessionFetcher + - libwebp + - Mantle - nanopb - PromisesObjC + - PromisesSwift + - SDWebImage + - SDWebImageWebPCoder - TOCropViewController EXTERNAL SOURCES: firebase_core: :path: ".symlinks/plugins/firebase_core/ios" + firebase_crashlytics: + :path: ".symlinks/plugins/firebase_crashlytics/ios" firebase_messaging: :path: ".symlinks/plugins/firebase_messaging/ios" Flutter: :path: Flutter + flutter_image_compress_common: + :path: ".symlinks/plugins/flutter_image_compress_common/ios" flutter_secure_storage: :path: ".symlinks/plugins/flutter_secure_storage/ios" google_sign_in_ios: @@ -168,12 +239,18 @@ SPEC CHECKSUMS: AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa Firebase: 0312a2352584f782ea56f66d91606891d4607f06 firebase_core: a626d00494efa398e7c54f25f1454a64c8abf197 + firebase_crashlytics: 17e856fabec68d993662abaf2f6fe2413f0abece firebase_messaging: 06391e8f35dc65a00c56580266285263d2861f10 FirebaseCore: 7ec4d0484817f12c3373955bc87762d96842d483 + FirebaseCoreExtension: 705ca5b14bf71d2564a0ddc677df1fc86ffa600f FirebaseCoreInternal: 4b297a2d56063dbea2c1d0d04222d44a8d058862 + FirebaseCrashlytics: 4b96efb0ce73b38b2a85e8b8bd1bd8f63f09d015 FirebaseInstallations: 766dabca09fd94aef922538aaf144cc4a6fb6869 FirebaseMessaging: 88950ba9485052891ebe26f6c43a52bb62248952 + FirebaseRemoteConfigInterop: 6efda51fb5e2f15b16585197e26eaa09574e8a4d + FirebaseSessions: dbd14adac65ce996228652c1fc3a3f576bdf3ecc Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 google_sign_in_ios: 07375bfbf2620bc93a602c0e27160d6afc6ead38 GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a @@ -183,10 +260,15 @@ SPEC CHECKSUMS: GTMSessionFetcher: 8000756fc1c19d2e5697b90311f7832d2e33f6cd image_cropper: 37d40f62177c101ff4c164906d259ea2c3aa70cf image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 local_auth_darwin: 4d56c90c2683319835a61274b57620df9c4520ab + Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d nanopb: 438bc412db1928dac798aa6fd75726007be04262 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 + SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3 + SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe diff --git a/lib/constant/links.dart b/lib/constant/links.dart index 5f22229..bd1f829 100644 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -104,6 +104,7 @@ class AppLink { "$ride/notificationPassenger/get.php"; static String updateNotificationPassenger = "$ride/notificationPassenger/update.php"; + //-----------------Driver NotificationCaptain------------------ static String addNotificationCaptain = "$ride/notificationCaptain/add.php"; static String addWaitingRide = "$ride/notificationCaptain/addWaitingRide.php"; @@ -229,6 +230,9 @@ class AppLink { static String getPassengerbyEmail = "$server/Admin/getPassengerbyEmail.php"; static String addAdminUser = "$server/Admin/adminUser/add.php"; static String getdashbord = "$server/Admin/dashbord.php"; + static String getEmployee = "$server/Admin/employee/get.php"; + static String getBestDriver = "$server/Admin/driver/getBestDriver.php"; + static String addEmployee = "$server/Admin/employee/add.php"; static String getdashbordPayment = "$server/Admin/dashbordPayment.php"; static String getAdminUser = "$server/Admin/adminUser/get.php"; static String getCaptainDetailsByEmailOrIDOrPhone = @@ -263,6 +267,8 @@ class AppLink { static String addNotesDriver = "$serviceApp/addNotesDriver.php"; static String getCarPlateNotEdit = "$serviceApp/getCarPlateNotEdit.php"; static String addNotesPassenger = "$serviceApp/addNotesPassenger.php"; + static String getPackages = "$serviceApp/getPackages.php"; + static String updatePackages = "$serviceApp/updatePackages.php"; ////// static String sendSms = "https://sms.kazumi.me/api/sms/send-sms"; static String senddlr = "https://sms.kazumi.me/api/sms/send-dlr"; diff --git a/lib/controller/admin/static_controller.dart b/lib/controller/admin/static_controller.dart index 8ca0e54..da49c9f 100644 --- a/lib/controller/admin/static_controller.dart +++ b/lib/controller/admin/static_controller.dart @@ -20,6 +20,7 @@ class StaticController extends GetxController { var chartDataEmployeeMaryam; var chartDataEmployeeRawda; var chartDataEmployeeMena; + var chartDataEmployeeSefer4; var chartDataDriversMatchingNotes; bool isLoading = false; String totalMonthlyPassengers = ''; @@ -111,6 +112,7 @@ class StaticController extends GetxController { List chartDataMaryam = []; List chartDataRawda = []; List chartDataMena = []; + List chartDataSefer4 = []; for (int day = 1; day <= DateTime.now().day; day++) { chartDataMaryam.add(FlSpot( @@ -124,10 +126,10 @@ class StaticController extends GetxController { 0)); chartDataRawda.add(FlSpot( day.toDouble(), - employeeDataMap['rawda'] + employeeDataMap['yasmine'] ?.firstWhere((e) => e.day == day, orElse: () => MonthlyEmployeeData( - day: day, totalEmployees: 0, name: 'rawda')) + day: day, totalEmployees: 0, name: 'yasmine')) .totalEmployees .toDouble() ?? 0)); @@ -140,12 +142,22 @@ class StaticController extends GetxController { .totalEmployees .toDouble() ?? 0)); + chartDataSefer4.add(FlSpot( + day.toDouble(), + employeeDataMap['ashjan'] + ?.firstWhere((e) => e.day == day, + orElse: () => MonthlyEmployeeData( + day: day, totalEmployees: 0, name: 'ashjan')) + .totalEmployees + .toDouble() ?? + 0)); } // Combine spots into a single list if needed or keep them separate chartDataEmployeeMaryam = chartDataMaryam; chartDataEmployeeRawda = chartDataRawda; chartDataEmployeeMena = chartDataMena; + chartDataEmployeeSefer4 = chartDataSefer4; update(); } diff --git a/lib/controller/drivers/driverthebest.dart b/lib/controller/drivers/driverthebest.dart new file mode 100644 index 0000000..15fa16b --- /dev/null +++ b/lib/controller/drivers/driverthebest.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; + +import 'package:get/get.dart'; + +import '../../constant/colors.dart'; +import '../../constant/links.dart'; +import '../functions/crud.dart'; + +class Driverthebest extends GetxController { + bool isLoading = false; + List driver = []; + getBestDriver() async { + var res = await CRUD().get(link: AppLink.getBestDriver, payload: {}); + if (res != 'failure') { + driver = jsonDecode(res)['message']; + update(); + } else { + Get.snackbar('error', '', backgroundColor: AppColor.redColor); + } + } + + @override + void onInit() { + getBestDriver(); + super.onInit(); + } +} diff --git a/lib/controller/employee_controller/employee_controller.dart b/lib/controller/employee_controller/employee_controller.dart new file mode 100644 index 0000000..d9c61dc --- /dev/null +++ b/lib/controller/employee_controller/employee_controller.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'package:flutter/widgets.dart'; +import 'package:get/get.dart'; +import 'package:sefer_admin1/constant/colors.dart'; +import 'package:sefer_admin1/constant/links.dart'; +import 'package:sefer_admin1/controller/functions/crud.dart'; + +class EmployeeController extends GetxController { + List employee = []; + final name = TextEditingController(); + final education = TextEditingController(); + final site = TextEditingController(); + final phone = TextEditingController(); + final status = TextEditingController(); + final formKey = GlobalKey(); + + fetchEmployee() async { + var res = await CRUD().get(link: AppLink.getEmployee, payload: {}); + if (res != 'failure') { + employee = jsonDecode(res)['message']; + update(); + } else { + Get.snackbar('error', '', backgroundColor: AppColor.redColor); + } + } + + late String id; + String generateRandomId(int length) { + const String chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + Random random = Random(); + + return String.fromCharCodes(Iterable.generate( + length, + (_) => chars.codeUnitAt(random.nextInt(chars.length)), + )); + } + + addEmployee() async { + // Create the payload with the employee data + var res = await CRUD().post(link: AppLink.addEmployee, payload: { + "id": id, + "name": name.text, + "education": education.text, + "site": site.text, + "phone": phone.text, + "status": status.text, + }); + + // Check the response from the API + if (res != 'failure') { + // You can handle the success case here, such as showing a success message + + Get.back(); + Get.snackbar('Success', 'Employee added successfully', + backgroundColor: AppColor.greenColor); + name.clear(); + education.clear(); + site.clear(); + phone.clear(); + status.clear(); + fetchEmployee(); + } else { + // Handle the error case by showing a snackbar with an error message + Get.snackbar('Error', 'Failed to add employee', + backgroundColor: AppColor.redColor); + } + } + + @override + void onInit() { + fetchEmployee(); + super.onInit(); + } +} diff --git a/lib/controller/functions/crud.dart b/lib/controller/functions/crud.dart index 2bb3f26..a70ac87 100644 --- a/lib/controller/functions/crud.dart +++ b/lib/controller/functions/crud.dart @@ -296,8 +296,9 @@ class CRUD { 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials))}', }, ); - + Log.print('payload: ${payload}'); var jsonData = jsonDecode(response.body); + Log.print('jsonData: ${jsonData}'); if (response.statusCode == 200) { if (jsonData['status'] == 'success') { diff --git a/lib/controller/functions/launch.dart b/lib/controller/functions/launch.dart index 65b1232..72fc360 100644 --- a/lib/controller/functions/launch.dart +++ b/lib/controller/functions/launch.dart @@ -7,6 +7,14 @@ void showInBrowser(String url) async { } else {} } +Future makePhoneCall(String phoneNumber) async { + final Uri launchUri = Uri( + scheme: 'tel', + path: phoneNumber, + ); + await launchUrl(launchUri); +} + void launchCommunication( String method, String contactInfo, String message) async { String url; diff --git a/lib/controller/functions/upload_image copy.dart b/lib/controller/functions/upload_image copy.dart new file mode 100644 index 0000000..02596ec --- /dev/null +++ b/lib/controller/functions/upload_image copy.dart @@ -0,0 +1,446 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter_image_compress/flutter_image_compress.dart'; +import 'package:get/get.dart'; +import 'package:http/http.dart' as http; +import 'package:image_cropper/image_cropper.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:path/path.dart'; +import 'package:image/image.dart' as img; + +import 'package:path_provider/path_provider.dart' as path_provider; + +import '../../constant/api_key.dart'; +import '../../constant/box_name.dart'; +import '../../constant/colors.dart'; +import '../../main.dart'; +import '../../print.dart'; + +class ImageController extends GetxController { + File? myImage; + bool isloading = false; + CroppedFile? croppedFile; + final picker = ImagePicker(); + var image; + + Future detectAndCropDocument(File imageFile) async { + img.Image? image = img.decodeImage(await imageFile.readAsBytes()); + if (image == null) throw Exception('Unable to decode image'); + + int left = image.width, top = image.height, right = 0, bottom = 0; + + // Threshold for considering a pixel as part of the document (adjust as needed) + const int threshold = 240; + + for (int y = 0; y < image.height; y++) { + for (int x = 0; x < image.width; x++) { + final pixel = image.getPixel(x, y); + final luminance = img.getLuminance(pixel); + + if (luminance < threshold) { + left = x < left ? x : left; + top = y < top ? y : top; + right = x > right ? x : right; + bottom = y > bottom ? y : bottom; + } + } + } + + // Add a small padding + left = (left - 5).clamp(0, image.width); + top = (top - 5).clamp(0, image.height); + right = (right + 5).clamp(0, image.width); + bottom = (bottom + 5).clamp(0, image.height); + + return img.copyCrop(image, + x: left, y: top, width: right - left, height: bottom - top); + } + + Future rotateImageIfNeeded(File imageFile) async { + img.Image croppedDoc = await detectAndCropDocument(imageFile); + + // Check if the document is in portrait orientation + bool isPortrait = croppedDoc.height > croppedDoc.width; + + img.Image processedImage; + if (isPortrait) { + // Rotate the image by 90 degrees clockwise + processedImage = img.copyRotate(croppedDoc, angle: 90); + } else { + processedImage = croppedDoc; + } + + // Get temporary directory + final tempDir = await path_provider.getTemporaryDirectory(); + final tempPath = tempDir.path; + + // Create the processed image file + File processedFile = File('$tempPath/processed_image.jpg'); + await processedFile.writeAsBytes(img.encodeJpg(processedImage)); + + return processedFile; + } + + Future rotateImage(File imageFile) async { + // Read the image file + img.Image? image = img.decodeImage(await imageFile.readAsBytes()); + + if (image == null) return imageFile; + + // Rotate the image by 90 degrees clockwise + img.Image rotatedImage = img.copyRotate(image, angle: 90); + + // Get temporary directory + final tempDir = await path_provider.getTemporaryDirectory(); + final tempPath = tempDir.path; + + // Create the rotated image file + File rotatedFile = File('$tempPath/rotated_image.jpg'); + await rotatedFile.writeAsBytes(img.encodeJpg(rotatedImage)); + + return rotatedFile; + } + + choosImage(String link, String imageType, String id) async { + try { + final pickedImage = await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.rear, + ); + + if (pickedImage == null) return; + + image = File(pickedImage.path); + + croppedFile = await ImageCropper().cropImage( + sourcePath: image!.path, + uiSettings: [ + AndroidUiSettings( + toolbarTitle: 'Cropper'.tr, + toolbarColor: AppColor.blueColor, + toolbarWidgetColor: AppColor.yellowColor, + initAspectRatio: CropAspectRatioPreset.original, + lockAspectRatio: false, + ), + IOSUiSettings( + title: 'Cropper'.tr, + ), + ], + ); + + if (croppedFile == null) return; + + myImage = File(croppedFile!.path); + isloading = true; + update(); + + // Rotate the compressed image + File processedImage = await rotateImageIfNeeded(File(croppedFile!.path)); + File compressedImage = await compressImage(processedImage); + + print('link =$link'); + Log.print('link: ${link}'); + + await uploadImage( + compressedImage, + { + 'driverID': id, + 'imageType': imageType, + }, + link, + ); + } catch (e) { + print('Error in choosImage: $e'); + Get.snackbar('Image Upload Failed'.tr, e.toString(), + backgroundColor: AppColor.primaryColor); + } finally { + isloading = false; + update(); + } + } + + // choosFaceFromDriverLicense(String link, String imageType) async { + // final pickedImage = await picker.pickImage( + // source: ImageSource.camera, + // preferredCameraDevice: CameraDevice.rear, + // ); + + // if (pickedImage == null) return; + + // image = File(pickedImage.path); + + // File? processedImage; + + // // For face images, use face detection and cropping + // processedImage = await detectAndCropFace(image!); + // if (processedImage == null) { + // Get.snackbar('Face Detection Failed', 'No face detected in the image.'); + // return; + // } + + // isloading = true; + // update(); + + // File compressedImage = await compressImage(processedImage); + + // try { + // await uploadImage( + // compressedImage, + // { + // 'driverID': box.read(BoxName.driverID).toString(), + // 'imageType': imageType + // }, + // link, + // ); + // } catch (e) { + // Get.snackbar('Image Upload Failed'.tr, e.toString(), + // backgroundColor: AppColor.redColor); + // } finally { + // isloading = false; + // update(); + // } + // } + + choosFace(String link, String imageType) async { + final pickedImage = await picker.pickImage( + source: ImageSource.camera, + preferredCameraDevice: CameraDevice.front, + ); + if (pickedImage != null) { + image = File(pickedImage.path); + isloading = true; + update(); +// Compress the image + File compressedImage = await compressImage(File(pickedImage.path)); + + // Save the picked image directly + // File savedImage = File(pickedImage.path); + print('link =$link'); + try { + await uploadImage( + compressedImage, + { + 'driverID': + box.read(BoxName.driverID) ?? box.read(BoxName.passengerID), + 'imageType': imageType + }, + link, + ); + } catch (e) { + Get.snackbar('Image Upload Failed'.tr, e.toString(), + backgroundColor: AppColor.redColor); + } finally { + isloading = false; + update(); + } + } + } + + uploadImage(File file, Map data, String link) async { + var request = http.MultipartRequest( + 'POST', + Uri.parse(link), + ); + + var length = await file.length(); + var stream = http.ByteStream(file.openRead()); + var multipartFile = http.MultipartFile( + 'image', + stream, + length, + filename: basename(file.path), + ); + request.headers.addAll({ + 'Authorization': + 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}', + }); + // Set the file name to the driverID + request.files.add( + http.MultipartFile( + 'image', + stream, + length, + filename: '${box.read(BoxName.driverID)}.jpg', + ), + ); + data.forEach((key, value) { + request.fields[key] = value; + }); + var myrequest = await request.send(); + var res = await http.Response.fromStream(myrequest); + if (res.statusCode == 200) { + Log.print('jsonDecode(res.body): ${jsonDecode(res.body)}'); + + Get.snackbar('title', 'message', backgroundColor: AppColor.greenColor); + return jsonDecode(res.body); + } else { + throw Exception( + 'Failed to upload image: ${res.statusCode} - ${res.body}'); + } + } + + choosImagePicture(String link, String imageType) async { + final pickedImage = await picker.pickImage( + source: ImageSource.gallery, + // preferredCameraDevice: CameraDevice.rear, + // maxHeight: Get.height * .3, + // maxWidth: Get.width * .9, + // imageQuality: 100, + ); + image = File(pickedImage!.path); + + croppedFile = await ImageCropper().cropImage( + sourcePath: image!.path, + uiSettings: [ + AndroidUiSettings( + toolbarTitle: 'Cropper'.tr, + toolbarColor: AppColor.blueColor, + toolbarWidgetColor: AppColor.yellowColor, + initAspectRatio: CropAspectRatioPreset.original, + lockAspectRatio: false), + IOSUiSettings( + title: 'Cropper'.tr, + ), + ], + ); + myImage = File(pickedImage.path); + isloading = true; + update(); +// Save the cropped image + // File savedCroppedImage = File(croppedFile!.path); + File compressedImage = await compressImage(File(croppedFile!.path)); + print('link =$link'); + try { + await uploadImage( + compressedImage, + { + 'driverID': + box.read(BoxName.driverID) ?? box.read(BoxName.passengerID), + 'imageType': imageType + }, + link, + ); + } catch (e) { + Get.snackbar('Image Upload Failed'.tr, e.toString(), + backgroundColor: AppColor.redColor); + } finally { + isloading = false; + update(); + } + } + + uploadImagePicture(File file, Map data, String link) async { + var request = http.MultipartRequest( + 'POST', + Uri.parse(link), //'https://ride.mobile-app.store/uploadImage1.php' + ); + + var length = await file.length(); + var stream = http.ByteStream(file.openRead()); + var multipartFile = http.MultipartFile( + 'image', + stream, + length, + filename: basename(file.path), + ); + request.headers.addAll({ + 'Authorization': + 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}', + }); + // Set the file name to the driverID + request.files.add( + http.MultipartFile( + 'image', + stream, + length, + filename: '${box.read(BoxName.driverID)}.jpg', + ), + ); + data.forEach((key, value) { + request.fields[key] = value; + }); + var myrequest = await request.send(); + var res = await http.Response.fromStream(myrequest); + if (res.statusCode == 200) { + return jsonDecode(res.body); + } else { + throw Exception( + 'Failed to upload image: ${res.statusCode} - ${res.body}'); + } + } +} + +Future compressImage(File file) async { + final dir = await path_provider.getTemporaryDirectory(); + final targetPath = "${dir.absolute.path}/temp.jpg"; + + var result = await FlutterImageCompress.compressAndGetFile( + file.absolute.path, + targetPath, + quality: 70, + minWidth: 1024, + minHeight: 1024, + ); + + return File(result!.path); +} + +// Future detectAndCropFace(File imageFile) async { +// final inputImage = InputImage.fromFilePath(imageFile.path); +// final options = FaceDetectorOptions( +// enableClassification: false, +// enableLandmarks: false, +// enableTracking: false, +// minFaceSize: 0.15, +// performanceMode: FaceDetectorMode.accurate, +// ); +// final faceDetector = FaceDetector(options: options); + +// try { +// final List faces = await faceDetector.processImage(inputImage); +// final image = img.decodeImage(await imageFile.readAsBytes()); + +// if (image == null) throw Exception('Unable to decode image'); + +// int left, top, width, height; + +// if (faces.isNotEmpty) { +// // Face detected, crop around the face +// final face = faces[0]; +// double padding = 0.2; // 20% padding +// int paddingX = (face.boundingBox.width * padding).round(); +// int paddingY = (face.boundingBox.height * padding).round(); + +// left = (face.boundingBox.left - paddingX).round(); +// top = (face.boundingBox.top - paddingY).round(); +// width = (face.boundingBox.width + 2 * paddingX).round(); +// height = (face.boundingBox.height + 2 * paddingY).round(); +// } else { +// // No face detected, crop the center of the image +// int size = min(image.width, image.height); +// left = (image.width - size) ~/ 2; +// top = (image.height - size) ~/ 2; +// width = size; +// height = size; +// } + +// // Ensure dimensions are within image bounds +// left = left.clamp(0, image.width - 1); +// top = top.clamp(0, image.height - 1); +// width = width.clamp(1, image.width - left); +// height = height.clamp(1, image.height - top); + +// final croppedImage = +// img.copyCrop(image, x: left, y: top, width: width, height: height); + +// // Save the cropped image +// final tempDir = await path_provider.getTemporaryDirectory(); +// final tempPath = tempDir.path; +// final croppedFile = File('$tempPath/cropped_image.jpg'); +// await croppedFile.writeAsBytes(img.encodeJpg(croppedImage, quality: 100)); + +// return croppedFile; +// } finally { +// faceDetector.close(); +// } +// } diff --git a/lib/controller/functions/wallet.dart b/lib/controller/functions/wallet.dart new file mode 100644 index 0000000..de99327 --- /dev/null +++ b/lib/controller/functions/wallet.dart @@ -0,0 +1,64 @@ +import 'dart:convert'; + +import 'package:get/get.dart'; +import 'package:sefer_admin1/constant/colors.dart'; + +import '../../constant/links.dart'; +import '../firebase/firbase_messge.dart'; +import 'crud.dart'; + +class WalletController extends GetxController { + String paymentToken = ''; + Future generateTokenDriver(String amount, driverID) async { + var res = await CRUD().post(link: AppLink.addPaymentTokenDriver, payload: { + 'driverID': driverID.toString(), + 'amount': amount.toString(), + }); + var d = jsonDecode(res); + return d['message']; + } + + addPaymentToDriver(String amount, driverID, token) async { + paymentToken = await generateTokenDriver(amount.toString(), driverID); + var res = await CRUD().post(link: AppLink.addDrivePayment, payload: { + 'rideId': 'gift$driverID', + 'amount': amount, + 'payment_method': 'visaRide', + 'passengerID': 'passengerId', + 'token': paymentToken, + 'driverID': driverID.toString(), + }); + if (res != 'failure') { + FirebaseMessagesController().sendNotificationToAnyWithoutData( + 'you have gift from SEFER'.tr, + '', + token, // Access token correctly + 'ding.wav', + ); + Get.snackbar('success', 'addPaymentToDriver', + backgroundColor: AppColor.greenColor); + } else { + Get.snackbar('error', 'addPaymentToDriver', + backgroundColor: AppColor.redColor); + } + } + + Future addSeferWallet(String point, driverID) async { + var amount = (int.parse(point) * -1).toStringAsFixed(0); + var seferToken = await generateTokenDriver(amount, driverID); + var res = await CRUD().post(link: AppLink.addSeferWallet, payload: { + 'amount': amount.toString(), + 'paymentMethod': 'visaRide', + 'passengerId': 'gift$driverID', + 'token': seferToken, + 'driverId': driverID.toString(), + }); + if (res != 'failure') { + Get.snackbar('success', 'addSeferWallet', + backgroundColor: AppColor.greenColor); + } else { + Get.snackbar('error', 'addSeferWallet', + backgroundColor: AppColor.redColor); + } + } +} diff --git a/lib/controller/notification_controller.dart b/lib/controller/notification_controller.dart index a8a693e..aa062ac 100644 --- a/lib/controller/notification_controller.dart +++ b/lib/controller/notification_controller.dart @@ -74,7 +74,8 @@ class NotificationController extends GetxController { "title": title.text, "body": body.text, }); - Log.print('res: ${res}'); + Log.print( + 'res: ${res}for ${box.read(BoxName.tokensDrivers)['message'][i]['id']}'); // Log.print('tokensDriver[i]: ${tokensDriver[i]}'); Future.delayed(const Duration(microseconds: 50)); @@ -138,7 +139,8 @@ class NotificationController extends GetxController { if (formKey.currentState!.validate()) { var res = await CRUD() .post(link: AppLink.addNotificationPassenger, payload: { - "passenger_id": tokensPassengersData[i]['id'].toString(), + "passenger_id": + tokensPassengersData[i]['passengerID'].toString(), "title": title.text, "body": body.text, }); diff --git a/lib/views/admin/admin_home_page.dart b/lib/views/admin/admin_home_page.dart index 08d3ab3..d012b7c 100644 --- a/lib/views/admin/admin_home_page.dart +++ b/lib/views/admin/admin_home_page.dart @@ -8,6 +8,7 @@ import 'package:sefer_admin1/controller/admin/static_controller.dart'; import 'package:sefer_admin1/controller/notification_controller.dart'; import 'package:sefer_admin1/views/admin/captain/drivers_cant_registe.dart'; import 'package:sefer_admin1/views/admin/captain/register_captain.dart'; +import 'package:sefer_admin1/views/admin/employee/employee_page.dart'; import 'package:sefer_admin1/views/widgets/elevated_btn.dart'; import 'package:sefer_admin1/views/widgets/my_textField.dart'; import 'package:sefer_admin1/views/widgets/mycircular.dart'; @@ -19,6 +20,8 @@ import '../../controller/functions/gemeni.dart'; import '../../print.dart'; import '../widgets/my_scafold.dart'; import 'captain/captain.dart'; +import 'drivers/driver_the_best.dart'; +import 'packages.dart'; import 'passenger/passenger.dart'; import 'rides/rides.dart'; import 'static/static.dart'; @@ -410,7 +413,25 @@ class AdminHomePage extends StatelessWidget { onPressed: () async { await Get.put(RegisterCaptainController()) .getDriverNotCompleteRegistration(); - Get.to(() => DriversCantRegister()); + Get.to(() => const DriversCantRegister()); + }, + ), + AdminWidgetsDashBoard( + title: 'update packages'.tr, + onPressed: () async { + Get.to(() => PackageUpdateScreen()); + }, + ), + AdminWidgetsDashBoard( + title: 'Employee'.tr, + onPressed: () async { + Get.to(() => EmployeePage()); + }, + ), + AdminWidgetsDashBoard( + title: 'Drivers the best'.tr, + onPressed: () async { + Get.to(() => DriverTheBest()); }, ), diff --git a/lib/views/admin/drivers/driver_the_best.dart b/lib/views/admin/drivers/driver_the_best.dart new file mode 100644 index 0000000..e600db8 --- /dev/null +++ b/lib/views/admin/drivers/driver_the_best.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:sefer_admin1/controller/functions/wallet.dart'; +import 'package:sefer_admin1/views/admin/wallet/wallet.dart'; +import 'package:sefer_admin1/views/widgets/my_scafold.dart'; + +import '../../../controller/drivers/driverthebest.dart'; + +class DriverTheBest extends StatelessWidget { + const DriverTheBest({super.key}); + + @override + Widget build(BuildContext context) { + Get.put(Driverthebest()); + return MyScafolld( + title: 'Best Drivers'.tr, + body: [ + GetBuilder(builder: (driverthebest) { + return driverthebest.driver.isNotEmpty + ? ListView.builder( + itemCount: driverthebest.driver.length, + itemBuilder: (context, index) { + final driver = driverthebest.driver[index]; + return ListTile( + leading: CircleAvatar( + child: Text( + ((driver['driver_count'] * 5) / 3600) + .toStringAsFixed(0), + ), + ), + title: Text(driver['name_arabic'] ?? 'Unknown Name'), + subtitle: Text('Phone: ${driver['phone'] ?? 'N/A'}'), + trailing: IconButton( + onPressed: () async { + Get.defaultDialog( + title: + 'are you sure to pay to this driver gift'.tr, + middleText: '', + onConfirm: () async { + final wallet = Get.put(WalletController()); + await wallet.addPaymentToDriver('200', + driver['id'].toString(), driver['token']); + await wallet.addSeferWallet( + '200', driver['id'].toString()); + }, + onCancel: () => Get.back()); + }, + icon: const Icon(Icons.wallet_giftcard_rounded), + ), + ); + }, + ) + : const Center( + child: Text('No drivers available.'), + ); + }) + ], + isleading: true, + ); + } +} diff --git a/lib/views/admin/employee/employee_page.dart b/lib/views/admin/employee/employee_page.dart new file mode 100644 index 0000000..80971dc --- /dev/null +++ b/lib/views/admin/employee/employee_page.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:sefer_admin1/constant/colors.dart'; +import 'package:sefer_admin1/constant/style.dart'; +import 'package:sefer_admin1/controller/employee_controller/employee_controller.dart'; +import 'package:sefer_admin1/controller/functions/launch.dart'; +import 'package:sefer_admin1/views/widgets/elevated_btn.dart'; +import 'package:sefer_admin1/views/widgets/my_scafold.dart'; +import 'package:sefer_admin1/views/widgets/my_textField.dart'; + +import '../../../constant/links.dart'; +import '../../../controller/functions/upload_image copy.dart'; + +class EmployeePage extends StatelessWidget { + const EmployeePage({super.key}); + + @override + Widget build(BuildContext context) { + Get.put(EmployeeController()); + return GetBuilder(builder: (employeeController) { + return Scaffold( + appBar: AppBar( + title: Text('Employee Page'.tr), + ), + body: ListView.builder( + itemCount: employeeController + .employee.length, // Set the item count based on the employee list + itemBuilder: (context, index) { + // Get the employee data for the current index + var employee = employeeController.employee[index]; + + // Return a widget to display the employee information + return Padding( + padding: const EdgeInsets.all(3.0), + child: Container( + decoration: AppStyle.boxDecoration1, + child: ListTile( + trailing: IconButton( + onPressed: () { + Get.to(() => EmployeeDetails( + index: index, + )); + }, + icon: Icon( + Icons.shop_two, + color: employee['status'].toString().contains('ممتاز') + ? AppColor.greenColor + : AppColor.accentColor, + ), + ), + title: Column( + children: [ + Text(employee['name']), + Text( + 'Phone: ${employee['phone']}\nEducation: ${employee['education']}'), + Text('Status: ${employee['status']}'), + ], + ), // Display employee name + onTap: () { + // Add any action you want when the employee is tapped + }, + leading: IconButton( + onPressed: () { + makePhoneCall(employee['phone'].toString()); + // launchCommunication( + // 'phone', employee['phone'].toString(), ''); + }, + icon: const Icon(Icons.phone), + ), + ), + ), + ); + }, + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + employeeController.id = employeeController.generateRandomId(8); + Get.to( + employeeFields(employeeController), + ); + }, // Icon to display + backgroundColor: Colors.blue, // Button color (optional) + tooltip: 'Add Employee', + child: const Icon(Icons.add), // Tooltip text when long-pressed + ), + ); + }); + } + + Scaffold employeeFields(EmployeeController employeeController) { + return Scaffold( + appBar: AppBar(), + body: Form( + key: employeeController.formKey, + child: SizedBox( + height: 500, + child: ListView( + children: [ + MyElevatedButton( + title: 'front id', + onPressed: () async { + await ImageController().choosImage(AppLink.uploadEgypt, + 'idFrontEmployee', employeeController.id); + }), + MyElevatedButton( + title: 'back id', + onPressed: () async { + await ImageController().choosImage(AppLink.uploadEgypt, + 'idbackEmployee', employeeController.id); + }), + MyTextForm( + controller: employeeController.name, + label: 'name', + hint: 'name', + type: TextInputType.name), + MyTextForm( + controller: employeeController.education, + label: 'education', + hint: 'education', + type: TextInputType.name), + MyTextForm( + controller: employeeController.site, + label: 'site', + hint: 'site', + type: TextInputType.name), + MyTextForm( + controller: employeeController.phone, + label: 'phone', + hint: 'phone', + type: TextInputType.phone), + MyTextForm( + controller: employeeController.status, + label: 'status', + hint: 'status', + type: TextInputType.name), + ], + ), + ), + ), + bottomNavigationBar: MyElevatedButton( + title: 'upload', + onPressed: () async { + if (employeeController.formKey.currentState!.validate()) { + await employeeController.addEmployee(); + } + }, + ), + ); + } +} + +class EmployeeDetails extends StatelessWidget { + const EmployeeDetails({super.key, required this.index}); + final int index; + @override + Widget build(BuildContext context) { + return MyScafolld( + title: 'Details', + isleading: true, + body: [ + GetBuilder(builder: (employeeController) { + return Column( + children: [ + SizedBox( + height: 200, + width: 400, + child: Image.network( + 'https://sefer.click/sefer/card_image/idFrontEmployee-${employeeController.employee[index]['id']}.jpg'), + ), + const SizedBox( + height: 10, + ), + SizedBox( + height: 200, + width: 400, + child: Image.network( + 'https://sefer.click/sefer/card_image/idFrontEmployee-${employeeController.employee[index]['id']}.jpg'), + ) + ], + ); + }) + ], + ); + } +} diff --git a/lib/views/admin/packages.dart b/lib/views/admin/packages.dart new file mode 100644 index 0000000..c695b8e --- /dev/null +++ b/lib/views/admin/packages.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'dart:convert'; +import 'package:http/http.dart' as http; +import 'package:sefer_admin1/constant/links.dart'; +import 'package:sefer_admin1/controller/functions/crud.dart'; +import 'package:sefer_admin1/views/widgets/my_textField.dart'; + +import '../../print.dart'; + +class PackageUpdateScreen extends StatelessWidget { + final PackageController packageController = Get.put(PackageController()); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Package Update'), + ), + body: GetBuilder(builder: (packageController) { + return Center( + child: ListView.builder( + itemCount: packageController.packages.length, + itemBuilder: (context, index) { + var package = packageController.packages[index]; + return ListTile( + title: Text(package['appName']), + subtitle: Text( + 'Platform: ${package['platform']} \nVersion: ${package['version']}'), + trailing: const Icon(Icons.update), + onTap: () { + Get.defaultDialog( + title: 'Update', + middleText: '', + content: Column( + children: [ + Text(package['appName']), + Text(package['platform']), + Text(package['version']), + MyTextForm( + controller: packageController.versionController, + label: package['version'].toString(), + hint: package['version'].toString(), + type: const TextInputType.numberWithOptions( + decimal: true), + ), + ], + ), + onConfirm: () async { + await packageController.updatePackages( + package['id'].toString(), + packageController.versionController.text.toString(), + ); + }, + onCancel: () {}, + ); + }, + ); + }, + ), + ); + }), + ); + } +} + +class PackageController extends GetxController { + List packages = []; // Observable list to hold package info + var isLoading = false.obs; + final versionController = TextEditingController(); + final formKey = GlobalKey(); + + @override + void onInit() { + super.onInit(); + fetchPackages(); + } + + // Method to fetch package data from API + fetchPackages() async { + var response = await CRUD().get(link: AppLink.getPackages, payload: {}); + + if (response != 'failure') { + var jsonData = jsonDecode(response); + packages = jsonData['message']; + update(); + Log.print('jsonData: ${jsonData}'); + } + } + + updatePackages(String id, version) async { + var response = await CRUD().post( + link: AppLink.updatePackages, + payload: { + "id": id, + "version": version, + }, + ); + Log.print('response: ${response}'); + if (response != 'failure') { + Get.back(); + fetchPackages(); + } else { + Get.snackbar('error', 'message'); + } + } +} diff --git a/lib/views/admin/static/static.dart b/lib/views/admin/static/static.dart index 850f56b..f7212db 100644 --- a/lib/views/admin/static/static.dart +++ b/lib/views/admin/static/static.dart @@ -354,6 +354,18 @@ class StaticDash extends StatelessWidget { show: true, color: Colors.green.withOpacity(0.3)), ), + LineChartBarData( + spots: staticController + .chartDataEmployeeSefer4, + isCurved: true, + color: + Colors.yellow, // Custom color for Mena + barWidth: 3, + dotData: const FlDotData(show: true), + belowBarData: BarAreaData( + show: true, + color: Colors.yellow.withOpacity(0.3)), + ), ], titlesData: const FlTitlesData( bottomTitles: AxisTitles( diff --git a/lib/views/widgets/my_textField.dart b/lib/views/widgets/my_textField.dart index 54f2bf0..2d4168b 100644 --- a/lib/views/widgets/my_textField.dart +++ b/lib/views/widgets/my_textField.dart @@ -51,7 +51,7 @@ class MyTextForm extends StatelessWidget { return 'Please enter a valid email.'.tr; } } else if (type == TextInputType.phone) { - if (value.length != 10) { + if (value.length != 11) { return 'Please enter a valid phone number.'.tr; } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 92f8ef3..7994270 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,7 @@ import file_selector_macos import firebase_core import firebase_crashlytics import firebase_messaging +import flutter_image_compress_macos import flutter_secure_storage_macos import google_sign_in_ios import path_provider_foundation @@ -20,6 +21,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) + FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index f7b2fa2..2c7f543 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -374,6 +374,54 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_image_compress: + dependency: "direct main" + description: + name: flutter_image_compress + sha256: "45a3071868092a61b11044c70422b04d39d4d9f2ef536f3c5b11fb65a1e7dd90" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + flutter_image_compress_common: + dependency: transitive + description: + name: flutter_image_compress_common + sha256: "7f79bc6c8a363063620b4e372fa86bc691e1cb28e58048cd38e030692fbd99ee" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + flutter_image_compress_macos: + dependency: transitive + description: + name: flutter_image_compress_macos + sha256: "26df6385512e92b3789dc76b613b54b55c457a7f1532e59078b04bf189782d47" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + flutter_image_compress_ohos: + dependency: transitive + description: + name: flutter_image_compress_ohos + sha256: e76b92bbc830ee08f5b05962fc78a532011fcd2041f620b5400a593e96da3f51 + url: "https://pub.dev" + source: hosted + version: "0.0.3" + flutter_image_compress_platform_interface: + dependency: transitive + description: + name: flutter_image_compress_platform_interface + sha256: "579cb3947fd4309103afe6442a01ca01e1e6f93dc53bb4cbd090e8ce34a41889" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + flutter_image_compress_web: + dependency: transitive + description: + name: flutter_image_compress_web + sha256: f02fe352b17f82b72f481de45add240db062a2585850bea1667e82cc4cd6c311 + url: "https://pub.dev" + source: hosted + version: "0.1.4+1" flutter_launcher_icons: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index bbb7e24..cff4a64 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -62,6 +62,7 @@ dependencies: # webview_flutter: ^4.7.0 googleapis_auth: ^1.6.0 firebase_crashlytics: ^3.5.7 + flutter_image_compress: ^2.3.0 dev_dependencies: flutter_test: