From f410ce20caf1f4c64caa679924de53548da43a0e Mon Sep 17 00:00:00 2001 From: Zohaib Iqbal Kambrani <> Date: Wed, 10 Mar 2021 18:00:53 +0300 Subject: [PATCH] Packages and Offers in Progress --- assets/images/discount_ar.png | Bin 0 -> 4615 bytes assets/images/discount_en.png | Bin 0 -> 5958 bytes assets/images/svg/discount_ar.svg | 37 + assets/images/svg/discount_en.svg | 43 + ios/Flutter/.last_build_id | 2 +- ios/Podfile.lock | 786 +++++++++++++++++- lib/config/config.dart | 4 +- lib/config/localized_values.dart | 1 + .../AddProductToCartRequestModel.dart | 26 + .../requests/CreateCustomerRequestModel.dart | 21 + .../OffersCategoriesRequestModel.dart | 8 +- .../requests/OffersProductsRequestModel.dart | 20 +- .../PackagesCartItemsResponseModel.dart | 132 +++ ...t => PackagesCategoriesResponseModel.dart} | 16 +- .../PackagesCustomerResponseModel.dart | 199 +++++ ...eModel.dart => PackagesResponseModel.dart} | 15 +- lib/core/service/client/base_app_client.dart | 107 ++- .../PackagesOffersServices.dart | 275 +++++- .../PackagesOffersViewModel.dart | 17 +- .../OfferCategoriesResponseModel_helper.dart | 8 +- .../OfferProductsResponseModel_helper.dart | 8 +- .../json/base/json_convert_content.dart | 36 +- lib/locator.dart | 9 +- lib/main.dart | 3 +- .../ClinicOfferAndPackagesPage.dart | 80 ++ .../OfferAndPackageDetailPage.dart | 228 +++++ .../OfferAndPackagesCartPage.dart | 376 +++++++++ .../packages_offers/OfferAndPackagesPage.dart | 416 +++++++++ lib/routes.dart | 9 +- lib/uitl/gif_loader_dialog_utils.dart | 6 +- lib/uitl/translations_delegate_base.dart | 5 + lib/uitl/utils.dart | 38 +- lib/widgets/CounterView.dart | 188 +++++ .../carousel_indicator.dart | 197 +++++ .../offers_packages/PackagesCartItemCard.dart | 217 +++++ .../offers_packages/PackagesOfferCard.dart | 157 ++++ .../offers_packages/offers_packages.dart | 1 - lib/widgets/others/StarRating.dart | 38 +- lib/widgets/others/app_scaffold_widget.dart | 44 +- pubspec.yaml | 7 +- 40 files changed, 3687 insertions(+), 93 deletions(-) create mode 100644 assets/images/discount_ar.png create mode 100644 assets/images/discount_en.png create mode 100644 assets/images/svg/discount_ar.svg create mode 100644 assets/images/svg/discount_en.svg create mode 100644 lib/core/model/packages_offers/requests/AddProductToCartRequestModel.dart create mode 100644 lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart create mode 100644 lib/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart rename lib/core/model/packages_offers/responses/{OfferCategoriesResponseModel.dart => PackagesCategoriesResponseModel.dart} (82%) create mode 100644 lib/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart rename lib/core/model/packages_offers/responses/{OfferProductsResponseModel.dart => PackagesResponseModel.dart} (95%) create mode 100644 lib/pages/packages_offers/ClinicOfferAndPackagesPage.dart create mode 100644 lib/pages/packages_offers/OfferAndPackageDetailPage.dart create mode 100644 lib/pages/packages_offers/OfferAndPackagesCartPage.dart create mode 100644 lib/pages/packages_offers/OfferAndPackagesPage.dart create mode 100644 lib/widgets/CounterView.dart create mode 100644 lib/widgets/carousel_indicator/carousel_indicator.dart create mode 100644 lib/widgets/offers_packages/PackagesCartItemCard.dart create mode 100644 lib/widgets/offers_packages/PackagesOfferCard.dart diff --git a/assets/images/discount_ar.png b/assets/images/discount_ar.png new file mode 100644 index 0000000000000000000000000000000000000000..854d1c1f903e8e0d59bc54d20ec4dcaf2512208b GIT binary patch literal 4615 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 literal 0 HcmV?d00001 diff --git a/assets/images/svg/discount_ar.svg b/assets/images/svg/discount_ar.svg new file mode 100644 index 00000000..d9d6ec27 --- /dev/null +++ b/assets/images/svg/discount_ar.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + مصخ + + + + diff --git a/assets/images/svg/discount_en.svg b/assets/images/svg/discount_en.svg new file mode 100644 index 00000000..294cf19c --- /dev/null +++ b/assets/images/svg/discount_en.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id index b8024672..3b4ea73f 100644 --- a/ios/Flutter/.last_build_id +++ b/ios/Flutter/.last_build_id @@ -1 +1 @@ -59a6c452ee075b50114918f17f1ad8f5 \ No newline at end of file +45a7d43b55124e1b314759554f9a14d3 \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1df50aa4..0d6f024b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,25 +1,809 @@ PODS: + - abseil/algorithm (0.20200225.0): + - abseil/algorithm/algorithm (= 0.20200225.0) + - abseil/algorithm/container (= 0.20200225.0) + - abseil/algorithm/algorithm (0.20200225.0): + - abseil/base/config + - abseil/algorithm/container (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/base (0.20200225.0): + - abseil/base/atomic_hook (= 0.20200225.0) + - abseil/base/base (= 0.20200225.0) + - abseil/base/base_internal (= 0.20200225.0) + - abseil/base/bits (= 0.20200225.0) + - abseil/base/config (= 0.20200225.0) + - abseil/base/core_headers (= 0.20200225.0) + - abseil/base/dynamic_annotations (= 0.20200225.0) + - abseil/base/endian (= 0.20200225.0) + - abseil/base/errno_saver (= 0.20200225.0) + - abseil/base/exponential_biased (= 0.20200225.0) + - abseil/base/log_severity (= 0.20200225.0) + - abseil/base/malloc_internal (= 0.20200225.0) + - abseil/base/periodic_sampler (= 0.20200225.0) + - abseil/base/pretty_function (= 0.20200225.0) + - abseil/base/raw_logging_internal (= 0.20200225.0) + - abseil/base/spinlock_wait (= 0.20200225.0) + - abseil/base/throw_delegate (= 0.20200225.0) + - abseil/base/atomic_hook (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/base (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/base/base_internal (0.20200225.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/base/bits (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/config (0.20200225.0) + - abseil/base/core_headers (0.20200225.0): + - abseil/base/config + - abseil/base/dynamic_annotations (0.20200225.0) + - abseil/base/endian (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver (0.20200225.0): + - abseil/base/config + - abseil/base/exponential_biased (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal (0.20200225.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/base/periodic_sampler (0.20200225.0): + - abseil/base/core_headers + - abseil/base/exponential_biased + - abseil/base/pretty_function (0.20200225.0) + - abseil/base/raw_logging_internal (0.20200225.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/base/spinlock_wait (0.20200225.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/throw_delegate (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/container/compressed_tuple (0.20200225.0): + - abseil/utility/utility + - abseil/container/inlined_vector (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/container/inlined_vector_internal (0.20200225.0): + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/memory (0.20200225.0): + - abseil/memory/memory (= 0.20200225.0) + - abseil/memory/memory (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/meta (0.20200225.0): + - abseil/meta/type_traits (= 0.20200225.0) + - abseil/meta/type_traits (0.20200225.0): + - abseil/base/config + - abseil/numeric/int128 (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/strings/str_format (0.20200225.0): + - abseil/strings/str_format_internal + - abseil/strings/str_format_internal (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/span + - abseil/strings/strings (0.20200225.0): + - abseil/base/base + - abseil/base/bits + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/strings/internal + - abseil/time (0.20200225.0): + - abseil/time/internal (= 0.20200225.0) + - abseil/time/time (= 0.20200225.0) + - abseil/time/internal (0.20200225.0): + - abseil/time/internal/cctz (= 0.20200225.0) + - abseil/time/internal/cctz (0.20200225.0): + - abseil/time/internal/cctz/civil_time (= 0.20200225.0) + - abseil/time/internal/cctz/time_zone (= 0.20200225.0) + - abseil/time/internal/cctz/civil_time (0.20200225.0): + - abseil/base/config + - abseil/time/internal/cctz/time_zone (0.20200225.0): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/time/time (0.20200225.0): + - abseil/base/base + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types (0.20200225.0): + - abseil/types/any (= 0.20200225.0) + - abseil/types/bad_any_cast (= 0.20200225.0) + - abseil/types/bad_any_cast_impl (= 0.20200225.0) + - abseil/types/bad_optional_access (= 0.20200225.0) + - abseil/types/bad_variant_access (= 0.20200225.0) + - abseil/types/compare (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - abseil/types/span (= 0.20200225.0) + - abseil/types/variant (= 0.20200225.0) + - abseil/types/any (0.20200225.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_any_cast + - abseil/utility/utility + - abseil/types/bad_any_cast (0.20200225.0): + - abseil/base/config + - abseil/types/bad_any_cast_impl + - abseil/types/bad_any_cast_impl (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_optional_access (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/bad_variant_access (0.20200225.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/types/compare (0.20200225.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/optional (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/types/span (0.20200225.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/types/variant (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/utility/utility (0.20200225.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - android_intent (0.0.1): + - Flutter + - barcode_scan_fix (0.0.1): + - Flutter + - MTBBarcodeScanner + - BoringSSL-GRPC (0.0.7): + - BoringSSL-GRPC/Implementation (= 0.0.7) + - BoringSSL-GRPC/Interface (= 0.0.7) + - BoringSSL-GRPC/Implementation (0.0.7): + - BoringSSL-GRPC/Interface (= 0.0.7) + - BoringSSL-GRPC/Interface (0.0.7) + - cloud_firestore (0.14.4): + - Firebase/CoreOnly (~> 6.33.0) + - Firebase/Firestore (~> 6.33.0) + - firebase_core + - Flutter + - cloud_firestore_web (0.1.0): + - Flutter + - connectivity (0.0.1): + - Flutter + - Reachability + - connectivity_for_web (0.1.0): + - Flutter + - connectivity_macos (0.0.1): + - Flutter + - device_calendar (0.0.1): + - Flutter + - device_info (0.0.1): + - Flutter + - DKImagePickerController/Core (4.3.2): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.2) + - DKImagePickerController/PhotoGallery (4.3.2): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.2) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - file_picker_web (0.0.1): + - Flutter + - Firebase/CoreOnly (6.33.0): + - FirebaseCore (= 6.10.3) + - Firebase/Firestore (6.33.0): + - Firebase/CoreOnly + - FirebaseFirestore (~> 1.18.0) + - Firebase/Messaging (6.33.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 4.7.0) + - firebase_core (0.5.3): + - Firebase/CoreOnly (~> 6.33.0) + - Flutter + - firebase_core_web (0.1.0): + - Flutter + - firebase_messaging (7.0.3): + - Firebase/CoreOnly (~> 6.33.0) + - Firebase/Messaging (~> 6.33.0) + - firebase_core + - Flutter + - FirebaseCore (6.10.3): + - FirebaseCoreDiagnostics (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - FirebaseCoreDiagnostics (1.7.0): + - GoogleDataTransport (~> 7.4) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Logger (~> 6.7) + - nanopb (~> 1.30906.0) + - FirebaseFirestore (1.18.0): + - abseil/algorithm (= 0.20200225.0) + - abseil/base (= 0.20200225.0) + - abseil/memory (= 0.20200225.0) + - abseil/meta (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/time (= 0.20200225.0) + - abseil/types (= 0.20200225.0) + - FirebaseCore (~> 6.10) + - "gRPC-C++ (~> 1.28.0)" + - leveldb-library (~> 1.22) + - nanopb (~> 1.30906.0) + - FirebaseInstallations (1.7.0): + - FirebaseCore (~> 6.10) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - PromisesObjC (~> 1.2) + - FirebaseInstanceID (4.8.0): + - FirebaseCore (~> 6.10) + - FirebaseInstallations (~> 1.6) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - FirebaseMessaging (4.7.1): + - FirebaseCore (~> 6.10) + - FirebaseInstanceID (~> 4.7) + - GoogleUtilities/AppDelegateSwizzler (~> 6.7) + - GoogleUtilities/Environment (~> 6.7) + - GoogleUtilities/Reachability (~> 6.7) + - GoogleUtilities/UserDefaults (~> 6.7) + - Protobuf (>= 3.9.2, ~> 3.9) - Flutter (1.0.0) + - flutter_email_sender (0.0.1): + - Flutter + - flutter_flexible_toast (0.0.1): + - Flutter + - flutter_inappwebview (0.0.1): + - Flutter + - flutter_local_notifications (0.0.1): + - Flutter + - flutter_plugin_android_lifecycle (0.0.1): + - Flutter + - flutter_tts (0.0.1): + - Flutter + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) + - geolocator (6.2.0): + - Flutter + - google_maps_flutter (0.0.1): + - Flutter + - GoogleMaps (< 3.10) + - GoogleDataTransport (7.5.1): + - nanopb (~> 1.30906.0) + - GoogleMaps (3.9.0): + - GoogleMaps/Maps (= 3.9.0) + - GoogleMaps/Base (3.9.0) + - GoogleMaps/Maps (3.9.0): + - GoogleMaps/Base + - GoogleUtilities/AppDelegateSwizzler (6.7.2): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (6.7.2): + - PromisesObjC (~> 1.2) + - GoogleUtilities/Logger (6.7.2): + - GoogleUtilities/Environment + - GoogleUtilities/Network (6.7.2): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (6.7.2)" + - GoogleUtilities/Reachability (6.7.2): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (6.7.2): + - GoogleUtilities/Logger + - "gRPC-C++ (1.28.2)": + - "gRPC-C++/Implementation (= 1.28.2)" + - "gRPC-C++/Interface (= 1.28.2)" + - "gRPC-C++/Implementation (1.28.2)": + - abseil/container/inlined_vector (= 0.20200225.0) + - abseil/memory/memory (= 0.20200225.0) + - abseil/strings/str_format (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - "gRPC-C++/Interface (= 1.28.2)" + - gRPC-Core (= 1.28.2) + - "gRPC-C++/Interface (1.28.2)" + - gRPC-Core (1.28.2): + - gRPC-Core/Implementation (= 1.28.2) + - gRPC-Core/Interface (= 1.28.2) + - gRPC-Core/Implementation (1.28.2): + - abseil/container/inlined_vector (= 0.20200225.0) + - abseil/memory/memory (= 0.20200225.0) + - abseil/strings/str_format (= 0.20200225.0) + - abseil/strings/strings (= 0.20200225.0) + - abseil/types/optional (= 0.20200225.0) + - BoringSSL-GRPC (= 0.0.7) + - gRPC-Core/Interface (= 1.28.2) + - gRPC-Core/Interface (1.28.2) + - hexcolor (0.0.1): + - Flutter + - image_cropper (0.0.3): + - Flutter + - TOCropViewController (~> 2.5.4) + - image_picker (0.0.1): + - Flutter + - just_audio (0.0.1): + - Flutter + - leveldb-library (1.22) + - local_auth (0.0.1): + - Flutter + - location (0.0.1): + - Flutter + - manage_calendar_events (0.0.1): + - Flutter + - map_launcher (0.0.1): + - Flutter + - maps_launcher (0.0.1): + - Flutter + - MTBBarcodeScanner (5.0.11) + - nanopb (1.30906.0): + - nanopb/decode (= 1.30906.0) + - nanopb/encode (= 1.30906.0) + - nanopb/decode (1.30906.0) + - nanopb/encode (1.30906.0) + - native_device_orientation (0.0.1): + - Flutter - NVActivityIndicatorView (5.1.1): - NVActivityIndicatorView/Base (= 5.1.1) - NVActivityIndicatorView/Base (5.1.1) + - path_provider (0.0.1): + - Flutter + - path_provider_linux (0.0.1): + - Flutter + - path_provider_macos (0.0.1): + - Flutter + - path_provider_windows (0.0.1): + - Flutter + - "permission_handler (5.1.0+2)": + - Flutter + - PromisesObjC (1.2.12) + - Protobuf (3.14.0) + - Reachability (3.2) + - screen (0.0.1): + - Flutter + - SDWebImage (5.10.4): + - SDWebImage/Core (= 5.10.4) + - SDWebImage/Core (5.10.4) + - shared_preferences (0.0.1): + - Flutter + - shared_preferences_linux (0.0.1): + - Flutter + - shared_preferences_macos (0.0.1): + - Flutter + - shared_preferences_web (0.0.1): + - Flutter + - shared_preferences_windows (0.0.1): + - Flutter + - speech_to_text (0.0.1): + - Flutter + - Try + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - SwiftyGif (5.4.0) + - TOCropViewController (2.5.5) + - Try (2.1.1) + - "twilio_programmable_video (0.5.0+4)": + - Flutter + - TwilioVideo (~> 3.4) + - TwilioVideo (3.8.0) + - url_launcher (0.0.1): + - Flutter + - url_launcher_linux (0.0.1): + - Flutter + - url_launcher_macos (0.0.1): + - Flutter + - url_launcher_web (0.0.1): + - Flutter + - url_launcher_windows (0.0.1): + - Flutter + - vibration (1.7.3): + - Flutter + - vibration_web (1.6.2): + - Flutter + - video_player (0.0.1): + - Flutter + - video_player_web (0.0.1): + - Flutter + - wakelock (0.0.1): + - Flutter + - webview_flutter (0.0.1): + - Flutter + - wifi (0.0.1): + - Flutter DEPENDENCIES: + - android_intent (from `.symlinks/plugins/android_intent/ios`) + - barcode_scan_fix (from `.symlinks/plugins/barcode_scan_fix/ios`) + - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) + - cloud_firestore_web (from `.symlinks/plugins/cloud_firestore_web/ios`) + - connectivity (from `.symlinks/plugins/connectivity/ios`) + - connectivity_for_web (from `.symlinks/plugins/connectivity_for_web/ios`) + - connectivity_macos (from `.symlinks/plugins/connectivity_macos/ios`) + - device_calendar (from `.symlinks/plugins/device_calendar/ios`) + - device_info (from `.symlinks/plugins/device_info/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - file_picker_web (from `.symlinks/plugins/file_picker_web/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_core_web (from `.symlinks/plugins/firebase_core_web/ios`) + - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - Flutter (from `Flutter`) + - flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`) + - flutter_flexible_toast (from `.symlinks/plugins/flutter_flexible_toast/ios`) + - flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`) + - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) + - flutter_plugin_android_lifecycle (from `.symlinks/plugins/flutter_plugin_android_lifecycle/ios`) + - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) + - geolocator (from `.symlinks/plugins/geolocator/ios`) + - google_maps_flutter (from `.symlinks/plugins/google_maps_flutter/ios`) + - hexcolor (from `.symlinks/plugins/hexcolor/ios`) + - image_cropper (from `.symlinks/plugins/image_cropper/ios`) + - image_picker (from `.symlinks/plugins/image_picker/ios`) + - just_audio (from `.symlinks/plugins/just_audio/ios`) + - local_auth (from `.symlinks/plugins/local_auth/ios`) + - location (from `.symlinks/plugins/location/ios`) + - manage_calendar_events (from `.symlinks/plugins/manage_calendar_events/ios`) + - map_launcher (from `.symlinks/plugins/map_launcher/ios`) + - maps_launcher (from `.symlinks/plugins/maps_launcher/ios`) + - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`) - NVActivityIndicatorView + - path_provider (from `.symlinks/plugins/path_provider/ios`) + - path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`) + - path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`) + - path_provider_windows (from `.symlinks/plugins/path_provider_windows/ios`) + - permission_handler (from `.symlinks/plugins/permission_handler/ios`) + - screen (from `.symlinks/plugins/screen/ios`) + - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - shared_preferences_linux (from `.symlinks/plugins/shared_preferences_linux/ios`) + - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) + - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) + - shared_preferences_windows (from `.symlinks/plugins/shared_preferences_windows/ios`) + - speech_to_text (from `.symlinks/plugins/speech_to_text/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + - twilio_programmable_video (from `.symlinks/plugins/twilio_programmable_video/ios`) + - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - url_launcher_linux (from `.symlinks/plugins/url_launcher_linux/ios`) + - url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`) + - url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`) + - url_launcher_windows (from `.symlinks/plugins/url_launcher_windows/ios`) + - vibration (from `.symlinks/plugins/vibration/ios`) + - vibration_web (from `.symlinks/plugins/vibration_web/ios`) + - video_player (from `.symlinks/plugins/video_player/ios`) + - video_player_web (from `.symlinks/plugins/video_player_web/ios`) + - wakelock (from `.symlinks/plugins/wakelock/ios`) + - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) + - wifi (from `.symlinks/plugins/wifi/ios`) SPEC REPOS: trunk: + - abseil + - BoringSSL-GRPC + - DKImagePickerController + - DKPhotoGallery + - Firebase + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseFirestore + - FirebaseInstallations + - FirebaseInstanceID + - FirebaseMessaging + - FMDB + - GoogleDataTransport + - GoogleMaps + - GoogleUtilities + - "gRPC-C++" + - gRPC-Core + - leveldb-library + - MTBBarcodeScanner + - nanopb - NVActivityIndicatorView + - PromisesObjC + - Protobuf + - Reachability + - SDWebImage + - SwiftyGif + - TOCropViewController + - Try + - TwilioVideo EXTERNAL SOURCES: + android_intent: + :path: ".symlinks/plugins/android_intent/ios" + barcode_scan_fix: + :path: ".symlinks/plugins/barcode_scan_fix/ios" + cloud_firestore: + :path: ".symlinks/plugins/cloud_firestore/ios" + cloud_firestore_web: + :path: ".symlinks/plugins/cloud_firestore_web/ios" + connectivity: + :path: ".symlinks/plugins/connectivity/ios" + connectivity_for_web: + :path: ".symlinks/plugins/connectivity_for_web/ios" + connectivity_macos: + :path: ".symlinks/plugins/connectivity_macos/ios" + device_calendar: + :path: ".symlinks/plugins/device_calendar/ios" + device_info: + :path: ".symlinks/plugins/device_info/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + file_picker_web: + :path: ".symlinks/plugins/file_picker_web/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_core_web: + :path: ".symlinks/plugins/firebase_core_web/ios" + firebase_messaging: + :path: ".symlinks/plugins/firebase_messaging/ios" Flutter: :path: Flutter + flutter_email_sender: + :path: ".symlinks/plugins/flutter_email_sender/ios" + flutter_flexible_toast: + :path: ".symlinks/plugins/flutter_flexible_toast/ios" + flutter_inappwebview: + :path: ".symlinks/plugins/flutter_inappwebview/ios" + flutter_local_notifications: + :path: ".symlinks/plugins/flutter_local_notifications/ios" + flutter_plugin_android_lifecycle: + :path: ".symlinks/plugins/flutter_plugin_android_lifecycle/ios" + flutter_tts: + :path: ".symlinks/plugins/flutter_tts/ios" + geolocator: + :path: ".symlinks/plugins/geolocator/ios" + google_maps_flutter: + :path: ".symlinks/plugins/google_maps_flutter/ios" + hexcolor: + :path: ".symlinks/plugins/hexcolor/ios" + image_cropper: + :path: ".symlinks/plugins/image_cropper/ios" + image_picker: + :path: ".symlinks/plugins/image_picker/ios" + just_audio: + :path: ".symlinks/plugins/just_audio/ios" + local_auth: + :path: ".symlinks/plugins/local_auth/ios" + location: + :path: ".symlinks/plugins/location/ios" + manage_calendar_events: + :path: ".symlinks/plugins/manage_calendar_events/ios" + map_launcher: + :path: ".symlinks/plugins/map_launcher/ios" + maps_launcher: + :path: ".symlinks/plugins/maps_launcher/ios" + native_device_orientation: + :path: ".symlinks/plugins/native_device_orientation/ios" + path_provider: + :path: ".symlinks/plugins/path_provider/ios" + path_provider_linux: + :path: ".symlinks/plugins/path_provider_linux/ios" + path_provider_macos: + :path: ".symlinks/plugins/path_provider_macos/ios" + path_provider_windows: + :path: ".symlinks/plugins/path_provider_windows/ios" + permission_handler: + :path: ".symlinks/plugins/permission_handler/ios" + screen: + :path: ".symlinks/plugins/screen/ios" + shared_preferences: + :path: ".symlinks/plugins/shared_preferences/ios" + shared_preferences_linux: + :path: ".symlinks/plugins/shared_preferences_linux/ios" + shared_preferences_macos: + :path: ".symlinks/plugins/shared_preferences_macos/ios" + shared_preferences_web: + :path: ".symlinks/plugins/shared_preferences_web/ios" + shared_preferences_windows: + :path: ".symlinks/plugins/shared_preferences_windows/ios" + speech_to_text: + :path: ".symlinks/plugins/speech_to_text/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + twilio_programmable_video: + :path: ".symlinks/plugins/twilio_programmable_video/ios" + url_launcher: + :path: ".symlinks/plugins/url_launcher/ios" + url_launcher_linux: + :path: ".symlinks/plugins/url_launcher_linux/ios" + url_launcher_macos: + :path: ".symlinks/plugins/url_launcher_macos/ios" + url_launcher_web: + :path: ".symlinks/plugins/url_launcher_web/ios" + url_launcher_windows: + :path: ".symlinks/plugins/url_launcher_windows/ios" + vibration: + :path: ".symlinks/plugins/vibration/ios" + vibration_web: + :path: ".symlinks/plugins/vibration_web/ios" + video_player: + :path: ".symlinks/plugins/video_player/ios" + video_player_web: + :path: ".symlinks/plugins/video_player_web/ios" + wakelock: + :path: ".symlinks/plugins/wakelock/ios" + webview_flutter: + :path: ".symlinks/plugins/webview_flutter/ios" + wifi: + :path: ".symlinks/plugins/wifi/ios" SPEC CHECKSUMS: + abseil: 6c8eb7892aefa08d929b39f9bb108e5367e3228f + android_intent: 367df2f1277a74e4a90e14a8ab3df3112d087052 + barcode_scan_fix: 80dd65de55f27eec6591dd077c8b85f2b79e31f1 + BoringSSL-GRPC: 8edf627ee524575e2f8d19d56f068b448eea3879 + cloud_firestore: b8c0e15fa49dfff87c2817d288b577e5dca2df13 + cloud_firestore_web: 9ec3dc7f5f98de5129339802d491c1204462bfec + connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467 + connectivity_for_web: 2b8584556930d4bd490d82b836bcf45067ce345b + connectivity_macos: e2e9731b6b22dda39eb1b128f6969d574460e191 + device_calendar: 23b28a5f1ab3bf77e34542fb1167e1b8b29a98f5 + device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 + DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 + file_picker_web: 37b10786e88885124fac99dc899866e78a132ef3 + Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5 + firebase_core: 5d6a02f3d85acd5f8321c2d6d62877626a670659 + firebase_core_web: d501d8b946b60c8af265428ce483b0fff5ad52d1 + firebase_messaging: 0aea2cd5885b65e19ede58ee3507f485c992cc75 + FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd + FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1 + FirebaseFirestore: adff4877869ca91a11250cc0989a6cd56bad163f + FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2 + FirebaseInstanceID: bd3ffc24367f901a43c063b36c640b345a4a5dd1 + FirebaseMessaging: 5eca4ef173de76253352511aafef774caa1cba2a Flutter: 0e3d915762c693b495b44d77113d4970485de6ec + flutter_email_sender: f787522d0e82f50e5766c1213dbffff22fdcf009 + flutter_flexible_toast: 0547e740cae0c33bb7c51bcd931233f4584e1143 + flutter_inappwebview: 69dfbac46157b336ffbec19ca6dfd4638c7bf189 + flutter_local_notifications: 9e4738ce2471c5af910d961a6b7eadcf57c50186 + flutter_plugin_android_lifecycle: dc0b544e129eebb77a6bfb1239d4d1c673a60a35 + flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a + geolocator: f5e3de65e241caba7ce3e8a618803387bda73384 + google_maps_flutter: c7f9c73576de1fbe152a227bfd6e6c4ae8088619 + GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833 + GoogleMaps: 4b5346bddfe6911bb89155d43c903020170523ac + GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3 + "gRPC-C++": 13d8ccef97d5c3c441b7e3c529ef28ebee86fad2 + gRPC-Core: 4afa11bfbedf7cdecd04de535a9e046893404ed5 + hexcolor: fdfb9c4258ad96e949c2dbcdf790a62194b8aa89 + image_cropper: c8f9b4157933c7bb965a66d1c5e6c8fd408c6eb4 + image_picker: 9c3312491f862b28d21ecd8fdf0ee14e601b3f09 + just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa + leveldb-library: 55d93ee664b4007aac644a782d11da33fba316f7 + local_auth: 25938960984c3a7f6e3253e3f8d962fdd16852bd + location: 3a2eed4dd2fab25e7b7baf2a9efefe82b512d740 + manage_calendar_events: 0338d505ea26cdfd20cd883279bc28afa11eca34 + map_launcher: e325db1261d029ff33e08e03baccffe09593ffea + maps_launcher: eae38ee13a9c3f210fa04e04bb4c073fa4c6ed92 + MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb + nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc + native_device_orientation: e24d00be281de72996640885d80e706142707660 NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 + path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4 + path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0 + path_provider_windows: a2b81600c677ac1959367280991971cb9a1edb3b + permission_handler: ccb20a9fad0ee9b1314a52b70b76b473c5f8dab0 + PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 + Protobuf: 0cde852566359049847168e51bd1c690e0f70056 + Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 + screen: abd91ca7bf3426e1cc3646d27e9b2358d6bf07b0 + SDWebImage: c666b97e1fa9c64b4909816a903322018f0a9c84 + shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + shared_preferences_linux: afefbfe8d921e207f01ede8b60373d9e3b566b78 + shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 + shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 + shared_preferences_windows: 36b76d6f54e76ead957e60b49e2f124b4cd3e6ae + speech_to_text: b43a7d99aef037bd758ed8e45d79bbac035d2dfe + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7 + TOCropViewController: da59f531f8ac8a94ef6d6c0fc34009350f9e8bfe + Try: 5ef669ae832617b3cee58cb2c6f99fb767a4ff96 + twilio_programmable_video: 6a41593640f3d86af60b22541fd457b22deaae7f + TwilioVideo: c13a51ceca375e91620eb7578d2573c90cf53b46 + url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + url_launcher_linux: ac237cb7a8058736e4aae38bdbcc748a4b394cc0 + url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313 + url_launcher_web: e5527357f037c87560776e36436bf2b0288b965c + url_launcher_windows: 683d7c283894db8d1914d3ab2223b20cc1ad95d5 + vibration: b5a33e764c3f609a975b9dca73dce20fdde627dc + vibration_web: 0ba303d92469ba34d71c612a228b315908d7fcd9 + video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e + video_player_web: da8cadb8274ed4f8dbee8d7171b420dedd437ce7 + wakelock: 0d4a70faf8950410735e3f61fb15d517c8a6efc4 + webview_flutter: d2b4d6c66968ad042ad94cbb791f5b72b4678a96 + wifi: d7d77c94109e36c4175d845f0a5964eadba71060 -PODFILE CHECKSUM: d94bd40f28772938199c67fcced06ffe96096c14 +PODFILE CHECKSUM: 5a17be3f8af73a757fa4439c77cf6ab2db29a6e7 COCOAPODS: 1.10.1 diff --git a/lib/config/config.dart b/lib/config/config.dart index cf1836ac..a167e1b5 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -8,9 +8,11 @@ import 'package:diplomaticquarterapp/widgets/mobile-no/mobile_no.dart'; const MAX_SMALL_SCREEN = 660; // PACKAGES and OFFERS -const EXA_CART_API_BASE_URL = 'https://mdlaboratories.com/exacartapi'; +const EXA_CART_API_BASE_URL = 'http://10.200.101.75:9000'; const PACKAGES_CATEGORIES = '/api/categories'; const PACKAGES_PRODUCTS = '/api/products'; +const PACKAGES_SHOPPING_CART = '/api/shopping_cart_items'; +const PACKAGES_CUSTOMER = '/api/customers'; const BASE_URL = 'https://uat.hmgwebservices.com/'; // const BASE_URL = 'https://hmgwebservices.com/'; diff --git a/lib/config/localized_values.dart b/lib/config/localized_values.dart index 6ab1a723..fa4d06f7 100644 --- a/lib/config/localized_values.dart +++ b/lib/config/localized_values.dart @@ -578,6 +578,7 @@ const Map localizedValues = { "bestSellers": {"en": "Best Sellers", "ar": "أفضل البائعين"}, "deleteAllItems": {"en": "Delete All Items", "ar": "حذف كافة العناصر"}, "total": {"en": "Total", "ar": "المجموع"}, + "totalWithColonRight": {"en": "Total:", "ar": ":المجموع"}, "selectAddress": {"en": "Select Address", "ar": "حدد العنوان"}, "shippingAddress": {"en": "SHIPPING ADDRESS", "ar": "عنوان الشحن"}, "changeAddress": {"en": "Change Address", "ar": "تغيير العنوان"}, diff --git a/lib/core/model/packages_offers/requests/AddProductToCartRequestModel.dart b/lib/core/model/packages_offers/requests/AddProductToCartRequestModel.dart new file mode 100644 index 00000000..69e56ad7 --- /dev/null +++ b/lib/core/model/packages_offers/requests/AddProductToCartRequestModel.dart @@ -0,0 +1,26 @@ +import 'package:flutter/cupertino.dart'; + +class AddProductToCartRequestModel { + int quantity; + int product_id; + String shopping_cart_type; + int customer_id; + + AddProductToCartRequestModel({@required this.product_id, this.customer_id, this.shopping_cart_type = "ShoppingCart", this.quantity = 1}); + + Map json() { + return { + "shopping_cart_item" : { + "quantity": quantity, + "product_id": product_id, + "shopping_cart_type": shopping_cart_type, + "customer_id": customer_id + } + }; + } +} + +class UpdateProductToCartRequestModel extends AddProductToCartRequestModel{ + UpdateProductToCartRequestModel({@required int product_id, @required int customer_id, String shopping_cart_type = "ShoppingCart", int quantity = 1}) + : super(customer_id: customer_id, product_id: product_id, quantity: quantity, shopping_cart_type: shopping_cart_type); +} diff --git a/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart b/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart new file mode 100644 index 00000000..116545bd --- /dev/null +++ b/lib/core/model/packages_offers/requests/CreateCustomerRequestModel.dart @@ -0,0 +1,21 @@ +import 'package:flutter/cupertino.dart'; + +class PackagesCustomerRequestModel { + + String email; + String phoneNumber; + + PackagesCustomerRequestModel({@required this.email, @required this.phoneNumber}); + + Map json() { + return { + "customer" : { + "email": email, + "addresses": [{ + "email": email, + "phone_number": phoneNumber + }] + } + }; + } +} \ No newline at end of file diff --git a/lib/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart b/lib/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart index f2f4af11..13dba945 100644 --- a/lib/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart +++ b/lib/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart @@ -6,6 +6,12 @@ class OffersCategoriesRequestModel { OffersCategoriesRequestModel({this.limit, this.page, this.sinceId}); Map toFlatMap() { - return {"limit": limit.toString(), "page": page.toString(), "sinceId": sinceId.toString()}; + return { + if(limit != null && limit > 0) + "limit": limit.toString(), + + if(page != null && page > 0) + "page": page.toString(), + }; } } diff --git a/lib/core/model/packages_offers/requests/OffersProductsRequestModel.dart b/lib/core/model/packages_offers/requests/OffersProductsRequestModel.dart index 84862568..583932bb 100644 --- a/lib/core/model/packages_offers/requests/OffersProductsRequestModel.dart +++ b/lib/core/model/packages_offers/requests/OffersProductsRequestModel.dart @@ -1,12 +1,24 @@ class OffersProductsRequestModel { final int categoryId; final int limit; - final int page; - final int sinceId; + // final int page; + int sinceId; - OffersProductsRequestModel({this.categoryId, this.limit, this.page, this.sinceId}); + OffersProductsRequestModel({this.categoryId, this.limit = 50}); Map toFlatMap() { - return {"limit": limit.toString(), "page": page.toString(), "sinceId": sinceId.toString(), "categoryId": categoryId.toString()}; + return { + + if(limit != null && limit > 0) + "limit": limit.toString(), + + // if(page != null && page > 0) + // "page": page.toString(), + + if(categoryId != null && categoryId > 0) + "categoryId": categoryId.toString(), + + "sinceId": sinceId != null ? sinceId.toString() : "0", + }; } } diff --git a/lib/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart b/lib/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart new file mode 100644 index 00000000..9514976f --- /dev/null +++ b/lib/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart @@ -0,0 +1,132 @@ + +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; + +class CartProductResponseModel { + int _quantity; + + set quantity(int value) { + _quantity = value; + } + + String _shoppingCartType; + int _productId; + PackagesResponseModel _product; + int _id; + + int get quantity => _quantity; + String get shoppingCartType => _shoppingCartType; + int get productId => _productId; + PackagesResponseModel get product => _product; + int get id => _id; + + CartProductResponseModel({ + int quantity, + String shoppingCartType, + int productId, + PackagesResponseModel product, + int id}){ + _quantity = quantity; + _shoppingCartType = shoppingCartType; + _productId = productId; + _product = product; + _id = id; +} + + CartProductResponseModel.fromJson(dynamic json) { + _quantity = json["quantity"]; + _shoppingCartType = json["shopping_cart_type"]; + _productId = json["product_id"]; + _product = json["product"] != null ? PackagesResponseModel().fromJson(json["product"]) : null; + _id = json["id"]; + } + + Map toJson() { + var map = {}; + map["quantity"] = _quantity; + map["shopping_cart_type"] = _shoppingCartType; + map["product_id"] = _productId; + if (_product != null) { + map["product"] = _product.toJson(); + } + map["id"] = _id; + return map; + } + +} + +class Images { + int _id; + int _pictureId; + int _position; + String _src; + dynamic _attachment; + + int get id => _id; + int get pictureId => _pictureId; + int get position => _position; + String get src => _src; + dynamic get attachment => _attachment; + + Images({ + int id, + int pictureId, + int position, + String src, + dynamic attachment}){ + _id = id; + _pictureId = pictureId; + _position = position; + _src = src; + _attachment = attachment; +} + + Images.fromJson(dynamic json) { + _id = json["id"]; + _pictureId = json["picture_id"]; + _position = json["position"]; + _src = json["src"]; + _attachment = json["attachment"]; + } + + Map toJson() { + var map = {}; + map["id"] = _id; + map["picture_id"] = _pictureId; + map["position"] = _position; + map["src"] = _src; + map["attachment"] = _attachment; + return map; + } + +} + +/// language_id : 1 +/// localized_name : "Dermatology testing" + +class Localized_names { + int _languageId; + String _localizedName; + + int get languageId => _languageId; + String get localizedName => _localizedName; + + Localized_names({ + int languageId, + String localizedName}){ + _languageId = languageId; + _localizedName = localizedName; +} + + Localized_names.fromJson(dynamic json) { + _languageId = json["language_id"]; + _localizedName = json["localized_name"]; + } + + Map toJson() { + var map = {}; + map["language_id"] = _languageId; + map["localized_name"] = _localizedName; + return map; + } + +} \ No newline at end of file diff --git a/lib/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart b/lib/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart similarity index 82% rename from lib/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart rename to lib/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart index 087f1961..292022c0 100644 --- a/lib/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart +++ b/lib/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart @@ -1,8 +1,8 @@ import 'package:diplomaticquarterapp/generated/json/base/json_convert_content.dart'; import 'package:diplomaticquarterapp/generated/json/base/json_field.dart'; -class OfferCategoriesResponseModel with JsonConvert { - String id; +class PackagesCategoriesResponseModel with JsonConvert { + int id; String name; String namen; @JSONField(name: "localized_names") @@ -49,6 +49,18 @@ class OfferCategoriesResponseModel with JsonConvert { diff --git a/lib/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart b/lib/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart new file mode 100644 index 00000000..314b0ad3 --- /dev/null +++ b/lib/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart @@ -0,0 +1,199 @@ +class PackagesCustomerResponseModel { + List _shoppingCartItems; + dynamic _billingAddress; + dynamic _shippingAddress; + List _addresses; + String _customerGuid; + dynamic _username; + String _email; + dynamic _firstName; + dynamic _lastName; + dynamic _languageId; + dynamic _dateOfBirth; + dynamic _gender; + dynamic _adminComment; + bool _isTaxExempt; + bool _hasShoppingCartItems; + bool _active; + bool _deleted; + bool _isSystemAccount; + dynamic _systemName; + dynamic _lastIpAddress; + String _createdOnUtc; + dynamic _lastLoginDateUtc; + String _lastActivityDateUtc; + int _registeredInStoreId; + bool _subscribedToNewsletter; + List _roleIds; + int _id; + + List get shoppingCartItems => _shoppingCartItems; + dynamic get billingAddress => _billingAddress; + dynamic get shippingAddress => _shippingAddress; + List get addresses => _addresses; + String get customerGuid => _customerGuid; + dynamic get username => _username; + String get email => _email; + dynamic get firstName => _firstName; + dynamic get lastName => _lastName; + dynamic get languageId => _languageId; + dynamic get dateOfBirth => _dateOfBirth; + dynamic get gender => _gender; + dynamic get adminComment => _adminComment; + bool get isTaxExempt => _isTaxExempt; + bool get hasShoppingCartItems => _hasShoppingCartItems; + bool get active => _active; + bool get deleted => _deleted; + bool get isSystemAccount => _isSystemAccount; + dynamic get systemName => _systemName; + dynamic get lastIpAddress => _lastIpAddress; + String get createdOnUtc => _createdOnUtc; + dynamic get lastLoginDateUtc => _lastLoginDateUtc; + String get lastActivityDateUtc => _lastActivityDateUtc; + int get registeredInStoreId => _registeredInStoreId; + bool get subscribedToNewsletter => _subscribedToNewsletter; + List get roleIds => _roleIds; + int get id => _id; + + PackagesCustomerResponseModel({ + List shoppingCartItems, + dynamic billingAddress, + dynamic shippingAddress, + List addresses, + String customerGuid, + dynamic username, + String email, + dynamic firstName, + dynamic lastName, + dynamic languageId, + dynamic dateOfBirth, + dynamic gender, + dynamic adminComment, + bool isTaxExempt, + bool hasShoppingCartItems, + bool active, + bool deleted, + bool isSystemAccount, + dynamic systemName, + dynamic lastIpAddress, + String createdOnUtc, + dynamic lastLoginDateUtc, + String lastActivityDateUtc, + int registeredInStoreId, + bool subscribedToNewsletter, + List roleIds, + int id}){ + _shoppingCartItems = shoppingCartItems; + _billingAddress = billingAddress; + _shippingAddress = shippingAddress; + _addresses = addresses; + _customerGuid = customerGuid; + _username = username; + _email = email; + _firstName = firstName; + _lastName = lastName; + _languageId = languageId; + _dateOfBirth = dateOfBirth; + _gender = gender; + _adminComment = adminComment; + _isTaxExempt = isTaxExempt; + _hasShoppingCartItems = hasShoppingCartItems; + _active = active; + _deleted = deleted; + _isSystemAccount = isSystemAccount; + _systemName = systemName; + _lastIpAddress = lastIpAddress; + _createdOnUtc = createdOnUtc; + _lastLoginDateUtc = lastLoginDateUtc; + _lastActivityDateUtc = lastActivityDateUtc; + _registeredInStoreId = registeredInStoreId; + _subscribedToNewsletter = subscribedToNewsletter; + _roleIds = roleIds; + _id = id; +} + + PackagesCustomerResponseModel.fromJson(dynamic json) { + _billingAddress = json["billing_address"]; + _shippingAddress = json["shipping_address"]; + _customerGuid = json["customer_guid"]; + _username = json["username"]; + _email = json["email"]; + _firstName = json["first_name"]; + _lastName = json["last_name"]; + _languageId = json["language_id"]; + _dateOfBirth = json["date_of_birth"]; + _gender = json["gender"]; + _adminComment = json["admin_comment"]; + _isTaxExempt = json["is_tax_exempt"]; + _hasShoppingCartItems = json["has_shopping_cart_items"]; + _active = json["active"]; + _deleted = json["deleted"]; + _isSystemAccount = json["is_system_account"]; + _systemName = json["system_name"]; + _lastIpAddress = json["last_ip_address"]; + _createdOnUtc = json["created_on_utc"]; + _lastLoginDateUtc = json["last_login_date_utc"]; + _lastActivityDateUtc = json["last_activity_date_utc"]; + _registeredInStoreId = json["registered_in_store_id"]; + _subscribedToNewsletter = json["subscribed_to_newsletter"]; + _id = json["id"]; + + // if (json["role_ids"] != null) { + // _roleIds = []; + // json["role_ids"].forEach((v) { + // _roleIds.add(dynamic.fromJson(v)); + // }); + // } + // if (json["addresses"] != null) { + // _addresses = []; + // json["addresses"].forEach((v) { + // _addresses.add(dynamic.fromJson(v)); + // }); + // } + // if (json["shopping_cart_items"] != null) { + // _shoppingCartItems = []; + // json["shopping_cart_items"].forEach((v) { + // _shoppingCartItems.add(dynamic.fromJson(v)); + // }); + // } + } + + Map toJson() { + var map = {}; + if (_shoppingCartItems != null) { + map["shopping_cart_items"] = _shoppingCartItems.map((v) => v.toJson()).toList(); + } + map["billing_address"] = _billingAddress; + map["shipping_address"] = _shippingAddress; + if (_addresses != null) { + map["addresses"] = _addresses.map((v) => v.toJson()).toList(); + } + map["customer_guid"] = _customerGuid; + map["username"] = _username; + map["email"] = _email; + map["first_name"] = _firstName; + map["last_name"] = _lastName; + map["language_id"] = _languageId; + map["date_of_birth"] = _dateOfBirth; + map["gender"] = _gender; + map["admin_comment"] = _adminComment; + map["is_tax_exempt"] = _isTaxExempt; + map["has_shopping_cart_items"] = _hasShoppingCartItems; + map["active"] = _active; + map["deleted"] = _deleted; + map["is_system_account"] = _isSystemAccount; + map["system_name"] = _systemName; + map["last_ip_address"] = _lastIpAddress; + map["created_on_utc"] = _createdOnUtc; + map["last_login_date_utc"] = _lastLoginDateUtc; + map["last_activity_date_utc"] = _lastActivityDateUtc; + map["registered_in_store_id"] = _registeredInStoreId; + map["subscribed_to_newsletter"] = _subscribedToNewsletter; + if (_roleIds != null) { + map["role_ids"] = _roleIds.map((v) => v.toJson()).toList(); + } + map["id"] = _id; + return map; + } + +} \ No newline at end of file diff --git a/lib/core/model/packages_offers/responses/OfferProductsResponseModel.dart b/lib/core/model/packages_offers/responses/PackagesResponseModel.dart similarity index 95% rename from lib/core/model/packages_offers/responses/OfferProductsResponseModel.dart rename to lib/core/model/packages_offers/responses/PackagesResponseModel.dart index 58481a8b..7297a20a 100644 --- a/lib/core/model/packages_offers/responses/OfferProductsResponseModel.dart +++ b/lib/core/model/packages_offers/responses/PackagesResponseModel.dart @@ -1,8 +1,8 @@ import 'package:diplomaticquarterapp/generated/json/base/json_convert_content.dart'; import 'package:diplomaticquarterapp/generated/json/base/json_field.dart'; -class OfferProductsResponseModel with JsonConvert { - String id; +class PackagesResponseModel with JsonConvert { + int id; @JSONField(name: "visible_individually") bool visibleIndividually; String name; @@ -205,6 +205,17 @@ class OfferProductsResponseModel with JsonConvert { int vendorId; @JSONField(name: "se_name") String seName; + + String getName() { + if(localizedNames.length == 2){ + if(localizedNames.first.languageId == 2) + return localizedNames.first.localizedName ?? name; + + else if(localizedNames.first.languageId == 1) + return localizedNames.last.localizedName ?? name; + } + return name; + } } class OfferProductsResponseModelLocalizedName with JsonConvert { diff --git a/lib/core/service/client/base_app_client.dart b/lib/core/service/client/base_app_client.dart index 429303bd..24fd202f 100644 --- a/lib/core/service/client/base_app_client.dart +++ b/lib/core/service/client/base_app_client.dart @@ -300,11 +300,45 @@ class BaseAppClient { } } + simplePost(String fullUrl, + { Map body, + Function(dynamic response, int statusCode) onSuccess, + Function(String error, int statusCode) onFailure,}) async { + + String url = fullUrl; + print("URL Query String: $url"); + + if (await Utils.checkConnection()) { + final response = await http.post( + url.trim(), + body: json.encode(body), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + ); + + final int statusCode = response.statusCode; + print("statusCode :$statusCode"); + + if (statusCode < 200 || statusCode >= 400 || json == null) { + onFailure('Error While Fetching data', statusCode); + } else { + onSuccess(response.body.toString(), statusCode); + } + } else { + onFailure('Please Check The Internet Connection', -1); + } + } + simpleGet(String fullUrl, {Function(dynamic response, int statusCode) onSuccess, - Function(String error, int statusCode) onFailure, - Map queryParams}) async { + Function(String error, int statusCode) onFailure, + Map queryParams}) async { + String url = fullUrl; + print("URL Query String: $url"); + var haveParams = (queryParams != null); if (haveParams) { String queryString = Uri(queryParameters: queryParams).query; @@ -334,6 +368,75 @@ class BaseAppClient { } } + + simplePut(String fullUrl, + { Map body, + Function(dynamic response, int statusCode) onSuccess, + Function(String error, int statusCode) onFailure}) async { + + String url = fullUrl; + print("URL Query String: $url"); + + if (await Utils.checkConnection()) { + final response = await http.put( + url.trim(), + body: json.encode(body), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + ); + + final int statusCode = response.statusCode; + print("statusCode :$statusCode"); + + if (statusCode < 200 || statusCode >= 400 || json == null) { + onFailure('Error While Fetching data', statusCode); + } else { + onSuccess(response.body.toString(), statusCode); + } + } else { + onFailure('Please Check The Internet Connection', -1); + } + } + + simpleDelete(String fullUrl, + {Function(dynamic response, int statusCode) onSuccess, + Function(String error, int statusCode) onFailure, + Map queryParams}) async { + + String url = fullUrl; + print("URL Query String: $url"); + + var haveParams = (queryParams != null); + if (haveParams) { + String queryString = Uri(queryParameters: queryParams).query; + url += '?' + queryString; + print("URL Query String: $url"); + } + + if (await Utils.checkConnection()) { + final response = await http.delete( + url.trim(), + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + ); + + final int statusCode = response.statusCode; + print("statusCode :$statusCode"); + + if (statusCode < 200 || statusCode >= 400 || json == null) { + onFailure('Error While Fetching data', statusCode); + } else { + onSuccess(response.body.toString(), statusCode); + } + } else { + onFailure('Please Check The Internet Connection', -1); + } + } + logout() async { await sharedPref.remove(LOGIN_TOKEN_ID); await sharedPref.remove(PHARMACY_CUSTOMER_ID); diff --git a/lib/core/service/packages_offers/PackagesOffersServices.dart b/lib/core/service/packages_offers/PackagesOffersServices.dart index e58c3fa3..c0c7bc94 100644 --- a/lib/core/service/packages_offers/PackagesOffersServices.dart +++ b/lib/core/service/packages_offers/PackagesOffersServices.dart @@ -1,27 +1,42 @@ import 'dart:convert'; import 'dart:developer'; +import 'dart:ui'; import 'package:diplomaticquarterapp/config/config.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/CreateCustomerRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart'; -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart'; -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferProductsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCustomerResponseModel.dart'; import 'package:diplomaticquarterapp/core/service/base_service.dart'; import 'package:diplomaticquarterapp/core/service/client/base_app_client.dart'; +import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:flutter/cupertino.dart'; import '../../../locator.dart'; class OffersAndPackagesServices extends BaseService { - List categoryList = List(); + List categoryList = List(); + List productList = List(); + List latestOffersList = List(); + List bestSellerList = List(); + List bannersList = List(); + List cartItemList = List(); + + PackagesCustomerResponseModel customer; + + Future> getAllCategories(OffersCategoriesRequestModel request) async { + Future errorThrow; - Future> getAllCategories(OffersCategoriesRequestModel request) async { - hasError = false; var url = EXA_CART_API_BASE_URL + PACKAGES_CATEGORIES; await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['categories'].forEach((json) { - categoryList.add(OfferCategoriesResponseModel().fromJson(json)); + categoryList.add(PackagesCategoriesResponseModel().fromJson(json)); }); } }, onFailure: (String error, int statusCode) { @@ -31,15 +46,18 @@ class OffersAndPackagesServices extends BaseService { return categoryList; } - List productList = List(); - Future> getAllProducts(OffersProductsRequestModel request) async { - hasError = false; + Future> getAllProducts({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { + Future errorThrow; + + request.sinceId = (productList.isNotEmpty) ? productList.last.id : 0; + + productList = List(); var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { if (statusCode == 200) { var jsonResponse = json.decode(stringResponse); jsonResponse['products'].forEach((json) { - productList.add(OfferProductsResponseModel().fromJson(json)); + productList.add(PackagesResponseModel().fromJson(json)); }); } }, onFailure: (String error, int statusCode) { @@ -48,4 +66,241 @@ class OffersAndPackagesServices extends BaseService { return productList; } + + Future> getLatestOffers({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { + + var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; + await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + if (statusCode == 200) { + var jsonResponse = json.decode(stringResponse); + jsonResponse['products'].forEach((json) { + latestOffersList.add(PackagesResponseModel().fromJson(json)); + }); + } + }, onFailure: (String error, int statusCode) { + log(error); + }, queryParams: request.toFlatMap()); + + return latestOffersList; + } + + Future> getBestSellers({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { + + var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; + await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + if (statusCode == 200) { + var jsonResponse = json.decode(stringResponse); + jsonResponse['products'].forEach((json) { + bestSellerList.add(PackagesResponseModel().fromJson(json)); + }); + } + }, onFailure: (String error, int statusCode) { + log(error); + }, queryParams: request.toFlatMap()); + + return bestSellerList; + } + + + Future> getBanners({@required OffersProductsRequestModel request, @required BuildContext context, @required bool showLoading = true}) async { + var url = EXA_CART_API_BASE_URL + PACKAGES_PRODUCTS; + await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + if (statusCode == 200) { + var jsonResponse = json.decode(stringResponse); + jsonResponse['products'].forEach((json) { + bannersList.add(PackagesResponseModel().fromJson(json)); + }); + } + }, onFailure: (String error, int statusCode) { + log(error); + }, queryParams: request.toFlatMap()); + + return bannersList; + } + + Future loadOffersPackagesDataForMainPage({@required BuildContext context, bool showLoading = true, Function completion }) async { + var finished = 0; + var totalCalls = 3; + + completedAll(){ + + finished++; + if(completion != null && finished == totalCalls) { + _hideLoading(context, showLoading); + completion(); + } + } + + _showLoading(context, showLoading); + + // Performing Parallel Request on same time + // # 1 + getBestSellers(request: OffersProductsRequestModel(), context: context, showLoading: false).then((value){ + completedAll(); + }); + + // # 2 + getLatestOffers(request: OffersProductsRequestModel(), context: context, showLoading: false).then((value){ + completedAll(); + }); + + // # 3 + getBanners(request: OffersProductsRequestModel(), context: context, showLoading: false).then((value){ + completedAll(); + }); + + } + + // -------------------- + // Create Customer + // -------------------- + Future createCustomer(PackagesCustomerRequestModel request, {@required BuildContext context, bool showLoading = true, Function(bool) completion }) async{ + if(customer != null) + return Future.value(customer); + + hasError = false; + var url = EXA_CART_API_BASE_URL + PACKAGES_CUSTOMER; + + customer = null; + _showLoading(context, showLoading); + await baseAppClient.simplePost(url, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ + _hideLoading(context, showLoading); + + var jsonResponse = json.decode(stringResponse); + var customerJson = jsonResponse['customers'].first; + customer = PackagesCustomerResponseModel.fromJson(customerJson); + + }, onFailure: (String error, int statusCode){ + _hideLoading(context, showLoading); + log(error); + }); + + return customer; + } + + + + // -------------------- + // Shopping Cart + // -------------------- + Future> cartItems({@required BuildContext context, bool showLoading = true}) async{ + Future errorThrow; + + cartItemList.clear(); + _showLoading(context, showLoading); + var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART + '/${customer.id}'; + await baseAppClient.simpleGet(url, onSuccess: (dynamic stringResponse, int statusCode) { + _hideLoading(context, showLoading); + + var jsonResponse = json.decode(stringResponse); + jsonResponse['shopping_carts'].forEach((json) { + cartItemList.add(CartProductResponseModel.fromJson(json)); + }); + + }, onFailure: (String error, int statusCode) { + _hideLoading(context, showLoading); + log(error); + errorThrow = Future.error({"error":error, "statusCode":statusCode}); + }, queryParams: null); + + return errorThrow ?? cartItemList; + } + + Future addProductToCart(AddProductToCartRequestModel request, {@required BuildContext context, bool showLoading = true}) async{ + Future errorThrow; + + request.customer_id = customer.id; + + _showLoading(context, showLoading); + var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART; + await baseAppClient.simplePost(url, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ + _hideLoading(context, showLoading); + + var jsonResponse = json.decode(stringResponse); + + }, onFailure: (String error, int statusCode){ + _hideLoading(context, showLoading); + log(error); + errorThrow = Future.error(error); + }); + + return errorThrow ?? true; + } + + Future updateProductToCart(int cartItemID, {UpdateProductToCartRequestModel request, @required BuildContext context, bool showLoading = true}) async{ + Future errorThrow; + + _showLoading(context, showLoading); + var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART + '/$cartItemID'; + await baseAppClient.simplePut(url, body: request.json(), onSuccess: (dynamic stringResponse, int statusCode){ + _hideLoading(context, showLoading); + + var jsonResponse = json.decode(stringResponse); + + }, onFailure: (String error, int statusCode){ + _hideLoading(context, showLoading); + log(error); + errorThrow = Future.error({"error":error, "statusCode":statusCode}); + }); + + return errorThrow ?? bannersList; + } + + + Future deleteProductFromCart(int cartItemID, {@required BuildContext context, bool showLoading = true}) async{ + Future errorThrow; + + _showLoading(context, showLoading); + var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART + '/$cartItemID'; + await baseAppClient.simpleDelete(url, onSuccess: (dynamic stringResponse, int statusCode){ + _hideLoading(context, showLoading); + // var jsonResponse = json.decode(stringResponse); + + }, onFailure: (String error, int statusCode){ + _hideLoading(context, showLoading); + log(error); + errorThrow = Future.error({"error":error, "statusCode":statusCode}); + }); + + return errorThrow ?? true; + } + + // -------------------- + // Place Order + // -------------------- + Future placeOrder({@required BuildContext context, bool showLoading = true}) async{ + Future errorThrow; + + var jsonBody = { + "order": { + "customer_id" : customer.id + } + }; + + _showLoading(context, showLoading); + var url = EXA_CART_API_BASE_URL + PACKAGES_SHOPPING_CART; + await baseAppClient.simplePost(url, body: jsonBody, onSuccess: (dynamic stringResponse, int statusCode){ + _hideLoading(context, showLoading); + + var jsonResponse = json.decode(stringResponse); + + }, onFailure: (String error, int statusCode){ + _hideLoading(context, showLoading); + log(error); + errorThrow = Future.error(error); + }); + + return errorThrow ?? true; + } + } + +_showLoading(BuildContext context, bool flag){ + if(flag) + GifLoaderDialogUtils.showMyDialog(context); +} + +_hideLoading(BuildContext context, bool flag){ + if(flag) + GifLoaderDialogUtils.hideDialog(context); +} \ No newline at end of file diff --git a/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart b/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart index 21df52bc..83e104dc 100644 --- a/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart +++ b/lib/core/viewModels/packages_offers/PackagesOffersViewModel.dart @@ -1,6 +1,7 @@ import 'package:diplomaticquarterapp/core/enum/viewstate.dart'; -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart'; -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferProductsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; import 'package:diplomaticquarterapp/core/service/base_service.dart'; import 'package:diplomaticquarterapp/core/service/client/base_app_client.dart'; import 'package:diplomaticquarterapp/core/service/packages_offers/PackagesOffersServices.dart'; @@ -10,10 +11,16 @@ import 'package:diplomaticquarterapp/locator.dart'; class OfferCategoriesViewModel extends BaseViewModel { OffersAndPackagesServices service = locator(); - List get list => service.categoryList; + get categoryList => service.categoryList; + get productList => service.categoryList; } -class OfferProductsViewModel extends BaseViewModel { +class PackagesViewModel extends BaseViewModel { OffersAndPackagesServices service = locator(); - List get list => service.productList; + List get categoryList => service.categoryList; + List get productList => service.productList; + List get latestOffersList => service.latestOffersList; + List get bestSellerList => service.bestSellerList; + List get bannersList => service.bannersList; + List get cartItemList => service.cartItemList; } diff --git a/lib/generated/json/OfferCategoriesResponseModel_helper.dart b/lib/generated/json/OfferCategoriesResponseModel_helper.dart index e5ebc914..1a28d179 100644 --- a/lib/generated/json/OfferCategoriesResponseModel_helper.dart +++ b/lib/generated/json/OfferCategoriesResponseModel_helper.dart @@ -1,8 +1,8 @@ -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart'; -offerCategoriesResponseModelFromJson(OfferCategoriesResponseModel data, Map json) { +offerCategoriesResponseModelFromJson(PackagesCategoriesResponseModel data, Map json) { if (json['id'] != null) { - data.id = json['id']?.toString(); + data.id = json['id']; } if (json['name'] != null) { data.name = json['name']?.toString(); @@ -91,7 +91,7 @@ offerCategoriesResponseModelFromJson(OfferCategoriesResponseModel data, Map offerCategoriesResponseModelToJson(OfferCategoriesResponseModel entity) { +Map offerCategoriesResponseModelToJson(PackagesCategoriesResponseModel entity) { final Map data = new Map(); data['id'] = entity.id; data['name'] = entity.name; diff --git a/lib/generated/json/OfferProductsResponseModel_helper.dart b/lib/generated/json/OfferProductsResponseModel_helper.dart index dc0edeca..d180af67 100644 --- a/lib/generated/json/OfferProductsResponseModel_helper.dart +++ b/lib/generated/json/OfferProductsResponseModel_helper.dart @@ -1,8 +1,8 @@ -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferProductsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; -offerProductsResponseModelFromJson(OfferProductsResponseModel data, Map json) { +offerProductsResponseModelFromJson(PackagesResponseModel data, Map json) { if (json['id'] != null) { - data.id = json['id']?.toString(); + data.id = json['id']; } if (json['visible_individually'] != null) { data.visibleIndividually = json['visible_individually']; @@ -353,7 +353,7 @@ offerProductsResponseModelFromJson(OfferProductsResponseModel data, Map offerProductsResponseModelToJson(OfferProductsResponseModel entity) { +Map offerProductsResponseModelToJson(PackagesResponseModel entity) { final Map data = new Map(); data['id'] = entity.id; data['visible_individually'] = entity.visibleIndividually; diff --git a/lib/generated/json/base/json_convert_content.dart b/lib/generated/json/base/json_convert_content.dart index 9fab946e..ae4eadeb 100644 --- a/lib/generated/json/base/json_convert_content.dart +++ b/lib/generated/json/base/json_convert_content.dart @@ -3,11 +3,11 @@ // ignore_for_file: prefer_single_quotes // This file is automatically generated. DO NOT EDIT, all your changes would be lost. -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferCategoriesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart'; import 'package:diplomaticquarterapp/generated/json/OfferCategoriesResponseModel_helper.dart'; import 'package:diplomaticquarterapp/core/model/geofencing/responses/LogGeoZoneResponseModel.dart'; import 'package:diplomaticquarterapp/generated/json/log_geo_zone_response_model_entity_helper.dart'; -import 'package:diplomaticquarterapp/core/model/packages_offers/responses/OfferProductsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; import 'package:diplomaticquarterapp/generated/json/OfferProductsResponseModel_helper.dart'; import 'package:diplomaticquarterapp/core/model/geofencing/responses/GeoZonesResponseModel.dart'; import 'package:diplomaticquarterapp/generated/json/GeoZonesResponseModel_helper.dart'; @@ -23,16 +23,16 @@ class JsonConvert { static _getFromJson(Type type, data, json) { switch (type) { - case OfferCategoriesResponseModel: - return offerCategoriesResponseModelFromJson(data as OfferCategoriesResponseModel, json) as T; + case PackagesCategoriesResponseModel: + return offerCategoriesResponseModelFromJson(data as PackagesCategoriesResponseModel, json) as T; case OfferCategoriesResponseModelLocalizedName: return offerCategoriesResponseModelLocalizedNameFromJson(data as OfferCategoriesResponseModelLocalizedName, json) as T; case OfferCategoriesResponseModelImage: return offerCategoriesResponseModelImageFromJson(data as OfferCategoriesResponseModelImage, json) as T; case LogGeoZoneResponseModel: return logGeoZoneResponseModelEntityFromJson(data as LogGeoZoneResponseModel, json) as T; - case OfferProductsResponseModel: - return offerProductsResponseModelFromJson(data as OfferProductsResponseModel, json) as T; + case PackagesResponseModel: + return offerProductsResponseModelFromJson(data as PackagesResponseModel, json) as T; case OfferProductsResponseModelLocalizedName: return offerProductsResponseModelLocalizedNameFromJson(data as OfferProductsResponseModelLocalizedName, json) as T; case OfferProductsResponseModelImage: @@ -47,16 +47,16 @@ class JsonConvert { static _getToJson(Type type, data) { switch (type) { - case OfferCategoriesResponseModel: - return offerCategoriesResponseModelToJson(data as OfferCategoriesResponseModel); + case PackagesCategoriesResponseModel: + return offerCategoriesResponseModelToJson(data as PackagesCategoriesResponseModel); case OfferCategoriesResponseModelLocalizedName: return offerCategoriesResponseModelLocalizedNameToJson(data as OfferCategoriesResponseModelLocalizedName); case OfferCategoriesResponseModelImage: return offerCategoriesResponseModelImageToJson(data as OfferCategoriesResponseModelImage); case LogGeoZoneResponseModel: return logGeoZoneResponseModelEntityToJson(data as LogGeoZoneResponseModel); - case OfferProductsResponseModel: - return offerProductsResponseModelToJson(data as OfferProductsResponseModel); + case PackagesResponseModel: + return offerProductsResponseModelToJson(data as PackagesResponseModel); case OfferProductsResponseModelLocalizedName: return offerProductsResponseModelLocalizedNameToJson(data as OfferProductsResponseModelLocalizedName); case OfferProductsResponseModelImage: @@ -72,16 +72,16 @@ class JsonConvert { //Go back to a single instance by type static _fromJsonSingle(json) { String type = M.toString(); - if (type == (OfferCategoriesResponseModel).toString()) { - return OfferCategoriesResponseModel().fromJson(json); + if (type == (PackagesCategoriesResponseModel).toString()) { + return PackagesCategoriesResponseModel().fromJson(json); } else if (type == (OfferCategoriesResponseModelLocalizedName).toString()) { return OfferCategoriesResponseModelLocalizedName().fromJson(json); } else if (type == (OfferCategoriesResponseModelImage).toString()) { return OfferCategoriesResponseModelImage().fromJson(json); } else if (type == (LogGeoZoneResponseModel).toString()) { return LogGeoZoneResponseModel().fromJson(json); - } else if (type == (OfferProductsResponseModel).toString()) { - return OfferProductsResponseModel().fromJson(json); + } else if (type == (PackagesResponseModel).toString()) { + return PackagesResponseModel().fromJson(json); } else if (type == (OfferProductsResponseModelLocalizedName).toString()) { return OfferProductsResponseModelLocalizedName().fromJson(json); } else if (type == (OfferProductsResponseModelImage).toString()) { @@ -96,16 +96,16 @@ class JsonConvert { //list is returned by type static M _getListChildType(List data) { - if (List() is M) { - return data.map((e) => OfferCategoriesResponseModel().fromJson(e)).toList() as M; + if (List() is M) { + return data.map((e) => PackagesCategoriesResponseModel().fromJson(e)).toList() as M; } else if (List() is M) { return data.map((e) => OfferCategoriesResponseModelLocalizedName().fromJson(e)).toList() as M; } else if (List() is M) { return data.map((e) => OfferCategoriesResponseModelImage().fromJson(e)).toList() as M; } else if (List() is M) { return data.map((e) => LogGeoZoneResponseModel().fromJson(e)).toList() as M; - } else if (List() is M) { - return data.map((e) => OfferProductsResponseModel().fromJson(e)).toList() as M; + } else if (List() is M) { + return data.map((e) => PackagesResponseModel().fromJson(e)).toList() as M; } else if (List() is M) { return data.map((e) => OfferProductsResponseModelLocalizedName().fromJson(e)).toList() as M; } else if (List() is M) { diff --git a/lib/locator.dart b/lib/locator.dart index 4d7f1e50..bf97537d 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -282,12 +282,9 @@ void setupLocator() { // Offer And Packages //---------------------- - locator.registerLazySingleton( - () => OffersAndPackagesServices()); // offerPackagesServices Service - locator.registerFactory( - () => OfferCategoriesViewModel()); // Categories View Model - locator - .registerFactory(() => OfferProductsViewModel()); // Products View Model + locator.registerLazySingleton(() => OffersAndPackagesServices()); // offerPackagesServices Service + locator.registerFactory(() => OfferCategoriesViewModel()); // Categories View Model + locator.registerFactory(() => PackagesViewModel()); // Products View Model // Geofencing // --------------------- diff --git a/lib/main.dart b/lib/main.dart index e0444a32..05615a68 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -115,7 +115,8 @@ class MyApp extends StatelessWidget { ), ), ), - initialRoute: SPLASH, + // initialRoute: SPLASH, + initialRoute: PACKAGES_OFFERS, routes: routes, debugShowCheckedModeBanner: false, ), diff --git a/lib/pages/packages_offers/ClinicOfferAndPackagesPage.dart b/lib/pages/packages_offers/ClinicOfferAndPackagesPage.dart new file mode 100644 index 00000000..b1e447ef --- /dev/null +++ b/lib/pages/packages_offers/ClinicOfferAndPackagesPage.dart @@ -0,0 +1,80 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart'; +import 'package:diplomaticquarterapp/locator.dart'; +import 'package:diplomaticquarterapp/pages/base/base_view.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart'; +import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart' as utils; +import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart'; +import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_material_pickers/flutter_material_pickers.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; + +dynamic languageID; + +class ClinicPackagesPage extends StatefulWidget { + List products; + ClinicPackagesPage({@required this.products}); + + @override + _ClinicPackagesPageState createState() => _ClinicPackagesPageState(); + + +} + +class _ClinicPackagesPageState extends State { + List get _products => widget.products; + + PackagesViewModel viewModel; + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + + return BaseView( + allowAny: true, + onModelReady: (model){ + viewModel = model; + }, + builder: (_, model, wi) => AppScaffold( + appBarTitle: TranslationBase.of(context).offerAndPackages, + isShowAppBar: true, + isPharmacy: false, + showPharmacyCart: false, + showHomeAppBarIcon: false, + isOfferPackages: true, + showOfferPackagesCart: true, + isShowDecPage: false, + body: Padding( + padding: const EdgeInsets.all(5), + child: StaggeredGridView.countBuilder( + crossAxisCount:4, + itemCount: _products.length, + itemBuilder: (BuildContext context, int index) => new Container( + color: Colors.transparent, + child: PackagesItemCard( itemContentPadding: 10,itemModel: _products[index],) + ), + staggeredTileBuilder: (int index) => StaggeredTile.fit(2), + mainAxisSpacing: 20, + crossAxisSpacing: 10, + ) + ), + ), + ); + } + +} diff --git a/lib/pages/packages_offers/OfferAndPackageDetailPage.dart b/lib/pages/packages_offers/OfferAndPackageDetailPage.dart new file mode 100644 index 00000000..a6711ded --- /dev/null +++ b/lib/pages/packages_offers/OfferAndPackageDetailPage.dart @@ -0,0 +1,228 @@ +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCategoriesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart'; +import 'package:diplomaticquarterapp/pages/base/base_view.dart'; +import 'package:diplomaticquarterapp/pages/pharmacies/ProductCheckTypeWidget.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart' as utils; +import 'package:diplomaticquarterapp/widgets/others/StarRating.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import 'ClinicOfferAndPackagesPage.dart'; + +class OfferAndPackagesDetail extends StatefulWidget{ + final dynamic model; + + const OfferAndPackagesDetail({@required this.model, Key key}) : super(key: key); + + @override + State createState() => OfferAndPackagesDetailState(); +} + + +class OfferAndPackagesDetailState extends State{ + + PackagesViewModel viewModel; + + @override + Widget build(BuildContext context) { + getLanguageID(); + + return BaseView( + onModelReady: (model){ + viewModel = model; + }, + builder: (_, model, wi) => AppScaffold( + appBarTitle: TranslationBase.of(context).offerAndPackages, + isShowAppBar: true, + isPharmacy: false, + showPharmacyCart: false, + showHomeAppBarIcon: false, + isOfferPackages: true, + showOfferPackagesCart: true, + isShowDecPage: false, + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 60), + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(10), + child: AspectRatio( + aspectRatio: 1/1, + child: utils.applyShadow( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: utils.Utils.loadNetworkImage(url: "https://wallpaperaccess.com/full/30103.jpg",) + ), + ) + ), + ), + Align( + alignment: Alignment.topLeft, + child: Image.asset( + 'assets/images/discount_${'en'}.png', + height: 70, + width: 70, + ), + ), + + ], + + ), + ), + + Padding( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Child Dental Offer", + style: TextStyle( + fontWeight: FontWeight.normal, + color: Colors.black, + fontSize: 25 + ) + ), + + Stack( + children: [ + Text( + "200 SAR", + style: TextStyle( + fontWeight: FontWeight.normal, + decoration: TextDecoration.lineThrough, + color: Colors.grey, + fontSize: 12 + ) + ), + Padding( + padding: const EdgeInsets.only(top: 15), + child: Text( + "894.99 SAR", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.green, + fontSize: 18 + ) + ), + ), + ], + ), + + StarRating( + size: 20, + totalCount: null, + totalAverage: 5, + forceStars: true), + + + SizedBox(height: 20,), + + + Text( + "Details", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.grey, + fontSize: 20 + ) + ), + + + + AspectRatio( + aspectRatio: 2/1, + child: Container( + color: Colors.grey[300], + child: Padding( + padding: const EdgeInsets.all(10), + child: Text("Detail of offers written here ss"), + ) + ), + ), + + + SizedBox(height: 10,), + ], + ), + ), + ], + ), + ), + + Padding( + padding: const EdgeInsets.all(10), + child: Align( + alignment: Alignment.bottomRight, + child: Row( + children: [ + + Expanded( + child: RaisedButton.icon( + padding: EdgeInsets.only(top: 5, bottom: 5, left: 0, right: 0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + side: BorderSide(color: Colors.red, width: 0.5) + ), + color: Colors.red, + icon: Icon( + Icons.add_shopping_cart_outlined, + size: 25, + color: Colors.white, + ), + label: Text( + "Add to Cart", + style: TextStyle(fontSize: 17, color: Colors.white, fontWeight: FontWeight.normal), + ), + onPressed: (){},), + ), + + SizedBox(width: 15,), + + Expanded( + child: OutlineButton.icon( + padding: EdgeInsets.only(top: 5, bottom: 5, left: 0, right: 0), + borderSide: BorderSide(width: 1.0, color: Colors.red), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + color: Colors.white, + icon: Icon( + Icons.favorite_rounded, + size: 25, + color: Colors.red, + ), + label: Text( + "Add to Favorites", + style: TextStyle(fontSize: 17, color: Colors.red, fontWeight: FontWeight.normal), + ), + onPressed: (){ + + },), + ), + + ], + ), + ), + ) + + + + ], + ) + ), + ); + } +} + diff --git a/lib/pages/packages_offers/OfferAndPackagesCartPage.dart b/lib/pages/packages_offers/OfferAndPackagesCartPage.dart new file mode 100644 index 00000000..98f237f6 --- /dev/null +++ b/lib/pages/packages_offers/OfferAndPackagesCartPage.dart @@ -0,0 +1,376 @@ +import 'package:after_layout/after_layout.dart'; +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart'; +import 'package:diplomaticquarterapp/locator.dart'; +import 'package:diplomaticquarterapp/pages/base/base_view.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/ClinicOfferAndPackagesPage.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart'; +import 'package:diplomaticquarterapp/pages/pharmacies/screens/pharmacy-terms-conditions-page.dart'; +import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart' as utils; +import 'package:diplomaticquarterapp/widgets/Loader/gif_loader_container.dart'; +import 'package:diplomaticquarterapp/widgets/carousel_indicator/carousel_indicator.dart'; +import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart'; +import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesCartItemCard.dart'; +import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_material_pickers/flutter_material_pickers.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +dynamic languageID; +const _columnCount = 1; + +AnimationController _animationController; + +class PackagesCartPage extends StatefulWidget { + PackagesCartPage(); + + @override + _PackagesCartPageState createState() => _PackagesCartPageState(); +} + +class _PackagesCartPageState extends State with AfterLayoutMixin, SingleTickerProviderStateMixin { + getLanguageID() async { + languageID = await sharedPref.getString(APP_LANGUAGE); + } + + @override + void initState() { + _agreeTerms = false; + _selectedPaymentMethod = null; + _animationController = AnimationController(vsync: this, duration: Duration(seconds: 500)); + super.initState(); + } + + @override + void dispose() { + _animationController.dispose(); + viewModel.cartItemList.clear(); + super.dispose(); + } + + PackagesViewModel viewModel; + + bool loadWidgets = false; + + onTermsClick(bool isAgree) { + setState(() => _agreeTerms = isAgree); + } + + onTermsInfoClick() { + Navigator.push(context, FadePage(page: PharmacyTermsConditions())); + } + + onPayNowClick() async{ + await viewModel.service.placeOrder(context: context); + } + + @override + void afterFirstLayout(BuildContext context) { + fetchData(); + } + + @override + Widget build(BuildContext context) { + return BaseView( + allowAny: true, + onModelReady: (model) => viewModel = model, + builder: (_, model, wi) { + return AppScaffold( + appBarTitle: TranslationBase.of(context).offerAndPackages, + isShowAppBar: true, + isPharmacy: false, + showPharmacyCart: false, + showHomeAppBarIcon: false, + isOfferPackages: true, + showOfferPackagesCart: false, + isShowDecPage: false, + body: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(5), + child: StaggeredGridView.countBuilder( + crossAxisCount: (_columnCount * _columnCount), + itemCount: viewModel.cartItemList.length, + itemBuilder: (BuildContext context, int index) { + + var item = viewModel.cartItemList[index]; + return Dismissible( + key: Key(index.toString()), + direction: DismissDirection.startToEnd, + background: _cartItemDeleteContainer(), + secondaryBackground: _cartItemDeleteContainer(), + confirmDismiss: (direction) async { + bool status = await viewModel.service.deleteProductFromCart(item.id, context: context, showLoading: false); + return status; + }, + onDismissed: (direction) { + debugPrint('Index: $index'); + viewModel.cartItemList.removeAt(index); + }, + child: PackagesCartItemCard( + itemModel: item, + shouldStepperChangeApply: (apply,total) async{ + var request = AddProductToCartRequestModel(product_id: item.productId, quantity: apply); + bool success = await viewModel.service.addProductToCart(request, context: context, showLoading: false).catchError((error){ + utils.Utils.showErrorToast(error); + }); + return success ?? false; + }, + ) + ); + }, + staggeredTileBuilder: (int index) => StaggeredTile.fit(_columnCount), + mainAxisSpacing: 0, + crossAxisSpacing: 10, + )), + ), + Container( + height: 0.25, + color: Theme.of(context).primaryColor, + ), + Container( + color: Colors.white, + child: Column( + children: [ + Text( + TranslationBase.of(context).selectPaymentOption, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold + ), + ), + + Container(height: 0.25, width: 100, color: Colors.grey[300],), + + _paymentOptions(context, (paymentMethod) { + setState(() => _selectedPaymentMethod = paymentMethod); + }), + + Container(height: 0.25, color: Colors.grey[300],), + + Container(height: 40, + child: _termsAndCondition(context, onSelected: onTermsClick, onInfoClick: onTermsInfoClick) + ), + + Container(height: 0.25, color: Colors.grey[300],), + _payNow(context, onPayNowClick: onPayNowClick) + ], + ), + ) + ], + ), + ); + }); + } + + fetchData() async { + await viewModel.service.cartItems(context: context).catchError((error) {}); + setState((){}); + } +} + +// /* Payment Footer Widgets */ +// --------------------------- +String _selectedPaymentMethod; +Widget _paymentOptions(BuildContext context, Function(String) onSelected) { + double height = 22; + + Widget buttonContent(bool isSelected, String imageName) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: isSelected ? Colors.green[50] : Colors.grey[200], + blurRadius: 1, + spreadRadius: 2, + ), + ], + borderRadius: BorderRadius.all(Radius.circular(5)), + border: Border.all(color: isSelected ? Colors.green : Colors.grey, width: isSelected ? 1 : 0.5) + ), + child: Padding( + padding: const EdgeInsets.all(4), + child: Image.asset('assets/images/new-design/$imageName'), + ) + ); + } + + return Padding( + padding: const EdgeInsets.all(5), + child: Container( + height: height, + color: Colors.transparent, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + InkWell( + child: buttonContent(_selectedPaymentMethod == "mada", 'mada.png'), + onTap: () { + onSelected("mada"); + }, + ), + SizedBox( + width: 5, + ), + InkWell( + child: buttonContent(_selectedPaymentMethod == "visa", 'visa.png'), + onTap: () { + onSelected("visa"); + }, + ), + SizedBox( + width: 5, + ), + InkWell( + child: buttonContent(_selectedPaymentMethod == "mastercard", 'mastercard.png'), + onTap: () { + onSelected("mastercard"); + }, + ), + SizedBox( + width: 5, + ), + InkWell( + child: buttonContent(_selectedPaymentMethod == "installment", 'installment.png'), + onTap: () { + onSelected("installment"); + }, + ), + ], + ), + ), + ); +} + +bool _agreeTerms = false; +Widget _termsAndCondition(BuildContext context, {@required Function(bool) onSelected, @required VoidCallback onInfoClick}) { + return Padding( + padding: const EdgeInsets.all(5), + child: Row( + children: [ + InkWell( + child: Icon( + _agreeTerms ? Icons.check_circle : Icons.radio_button_unchecked_sharp, + size: 20, + color: _agreeTerms ? Colors.green[600] : Colors.grey[400], + ), + onTap: () { + onSelected(!_agreeTerms); + }, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text( + TranslationBase.of(context).pharmacyServiceTermsCondition, + style: TextStyle(height: 1, fontWeight: FontWeight.normal, fontSize: 13), + ), + )), + InkWell( + child: Icon( + Icons.info, + size: 20, + color: Colors.grey[600], + ), + onTap: () { + onInfoClick(); + }, + ), + ], + ), + ); +} + +Widget _payNow(BuildContext context, {@required VoidCallback onPayNowClick}) { + bool isPayNowAQctive = (_agreeTerms && (_selectedPaymentMethod != null)); + + return Padding( + padding: const EdgeInsets.all(5), + child: Container( + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all(5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('${TranslationBase.of(context).subtotal}: ${'999.9'} ${TranslationBase.of(context).sar}', + style: TextStyle(height: 1.5, fontWeight: FontWeight.bold, color: Colors.grey, fontSize: 8)), + Text('${TranslationBase.of(context).vat}: ${'14.9'} ${TranslationBase.of(context).sar}', style: TextStyle(height: 1.5, fontWeight: FontWeight.bold, color: Colors.grey, fontSize: 8)), + Padding( + padding: const EdgeInsets.all(3), + child: Container( + height: 0.25, + width: 120, + color: Colors.grey[300], + ), + ), + Text(' ${TranslationBase.of(context).total}: 999.99 ${TranslationBase.of(context).sar}', + style: TextStyle(height: 1.5, fontWeight: FontWeight.bold, color: Colors.black54, fontSize: 15)) + ], + ), + ), + Expanded(child: Container()), + RaisedButton( + child: Text( + TranslationBase.of(context).payNow, + style: TextStyle(fontSize: 15, color: Colors.white, fontWeight: FontWeight.bold), + ), + padding: EdgeInsets.only(top: 5, bottom: 5, left: 0, right: 0), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5), side: BorderSide(color: Theme.of(context).primaryColor, width: 0.5)), + color: Theme.of(context).primaryColor, + onPressed: isPayNowAQctive ? onPayNowClick : null, + ), + ], + )), + ); +} +// ------------------- + +Widget _cartItemDeleteContainer() { + _animationController.duration = Duration(milliseconds: 500); + _animationController.repeat(reverse: true); + return FadeTransition( + opacity: _animationController, + child: Padding( + padding: const EdgeInsets.all(5), + child: Container( + decoration: BoxDecoration( + color: Colors.red, + boxShadow: [ + BoxShadow( + color: Colors.grey[500], + blurRadius: 2, + spreadRadius: 1, + ), + ], + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + child: Center( + child: Text( + "Deleting...", + style: TextStyle( + fontWeight: FontWeight.normal, + fontSize: 15, + color: Colors.white, + ), + )), + ), + ), + ); +} diff --git a/lib/pages/packages_offers/OfferAndPackagesPage.dart b/lib/pages/packages_offers/OfferAndPackagesPage.dart new file mode 100644 index 00000000..0a83c683 --- /dev/null +++ b/lib/pages/packages_offers/OfferAndPackagesPage.dart @@ -0,0 +1,416 @@ +import 'package:after_layout/after_layout.dart'; +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/AddProductToCartRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/CreateCustomerRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/packages_offers/PackagesOffersViewModel.dart'; +import 'package:diplomaticquarterapp/core/viewModels/pharmacyModule/order_model_view_model.dart'; +import 'package:diplomaticquarterapp/locator.dart'; +import 'package:diplomaticquarterapp/pages/base/base_view.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/ClinicOfferAndPackagesPage.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackageDetailPage.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesCartPage.dart'; +import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart' as utils; +import 'package:diplomaticquarterapp/widgets/Loader/gif_loader_container.dart'; +import 'package:diplomaticquarterapp/widgets/carousel_indicator/carousel_indicator.dart'; +import 'package:diplomaticquarterapp/widgets/loadings/ShimmerLoading.dart'; +import 'package:diplomaticquarterapp/widgets/offers_packages/PackagesOfferCard.dart'; +import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_material_pickers/flutter_material_pickers.dart'; + +dynamic languageID; + +class PackagesHomePage extends StatefulWidget { + dynamic offersModel; + PackagesHomePage({@required this.offersModel}); + + @override + _PackagesHomePageState createState() => _PackagesHomePageState(); + +} + +class _PackagesHomePageState extends State with AfterLayoutMixin{ + getLanguageID() async { + languageID = await sharedPref.getString(APP_LANGUAGE); + } + + @override + void initState() { + super.initState(); + getLanguageID(); + } + + @override + void afterFirstLayout(BuildContext context) async{ + viewModel.service.loadOffersPackagesDataForMainPage(context: context, completion: (){ + setState((){}); + }); + } + + // Controllers + var _searchTextController = TextEditingController(); + var _filterTextController = TextEditingController(); + var _carouselController = CarouselController(); + + + int carouselIndicatorIndex = 0; + CarouselSlider _bannerCarousel; + TextField _textFieldSearch; + TextField _textFieldFilterSelection; + + ListView _listViewLatestOffers; + ListView _listViewBestSeller; + + PackagesViewModel viewModel; + + onCartClick(){ + if (viewModel.service.customer == null){ + utils.Utils.showErrorToast("Cart is empty for your current session"); + return; + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => PackagesCartPage() + ) + ); + } + + onProductCartClick(PackagesResponseModel product) async { + if(viewModel.service.customer == null) + await viewModel.service.createCustomer(PackagesCustomerRequestModel(email: "zikambrani@gmail.com", phoneNumber: "0500409598"), context: context); + + if(viewModel.service.customer != null){ + var request = AddProductToCartRequestModel(product_id: product.id, customer_id: viewModel.service.customer.id); + await viewModel.service.addProductToCart(request, context: context).catchError((error) { + utils.Utils.showErrorToast(error); + }); + } + } + + @override + Widget build(BuildContext context) { + + return BaseView( + allowAny: true, + onModelReady: (model) => viewModel = model, + builder: (_, model, wi){ + return + AppScaffold( + appBarTitle: TranslationBase.of(context).offerAndPackages, + isShowAppBar: true, + isPharmacy: false, + showPharmacyCart: false, + showHomeAppBarIcon: false, + isOfferPackages: true, + showOfferPackagesCart: true, + isShowDecPage: false, + body: ListView( + children: [ + + // Top Banner Carousel + AspectRatio( + aspectRatio: 2.2/1, + child: bannerCarousel() + ), + + Center( + child: CarouselIndicator( + activeColor: Theme.of(context).appBarTheme.color, + color: Colors.grey[300], + cornerRadius: 15, + width: 15, height: 15, + count: _bannerCarousel.itemCount, + index: carouselIndicatorIndex, + onClick: (index){ + debugPrint('onClick at ${index}'); + }, + ), + ), + + SizedBox(height: 10,), + + Padding( + padding: const EdgeInsets.all(15), + child: Column( + children: [ + // Search Textfield + searchTextField(), + + SizedBox(height: 10,), + + // Filter Selection + filterOptionSelection(), + + SizedBox(height: 20,), + + // Horizontal Scrollable Cards + Text( + "Latest offers", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87, + fontSize: 20 + ), + ), + + // Latest Offers Horizontal Scrollable List + AspectRatio( + aspectRatio: 1.3/1, + child: LayoutBuilder(builder: (context, constraints){ + double itemContentPadding = 10; + double itemWidth = (constraints.maxWidth/2) - (itemContentPadding*2); + return latestOfferListView(itemWidth: itemWidth, itemContentPadding: itemContentPadding); + }), + ), + + SizedBox(height: 10,), + + Text( + "Best sellers", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.black87, + fontSize: 20 + ), + ), + + + // Best Seller Horizontal Scrollable List + AspectRatio( + aspectRatio: 1.3/1, + child: LayoutBuilder(builder: (context, constraints){ + double itemContentPadding = 10; // 10 is content padding in each item + double itemWidth = (constraints.maxWidth/2) - (itemContentPadding*2 /* 2 = LeftRight */); + return bestSellerListView(itemWidth: itemWidth, itemContentPadding: itemContentPadding); + }), + ) + + ],), + ), + ], + ), + ) + .setOnAppBarCartClick(onCartClick); + } + ); + } + + + + + showClinicSelectionList() async { + var clinics = viewModel.service.categoryList; + if(clinics.isEmpty) { + GifLoaderDialogUtils.showMyDialog(context); + clinics = await viewModel.service.getAllCategories(OffersCategoriesRequestModel()); + GifLoaderDialogUtils.hideDialog(context); + } + + List options = clinics.map((e) => e.toString()).toList(); + + showMaterialSelectionPicker( + context: context, + title: "Select Clinic", + items: options, + selectedItem: options.first, + onChanged: (value) async { + var selectedClinic = clinics.firstWhere((element) => element.toString() == value); + var clinicProducts = await viewModel.service.getAllProducts(request: OffersProductsRequestModel(categoryId: selectedClinic.id), context: context, showLoading: true); + if(clinicProducts.isNotEmpty) + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => ClinicPackagesPage(products: clinicProducts) + ) + ); + else + utils.Utils.showErrorToast("No offers available for this clinic"); + }, + ); + } + + //---------------------------------- + // Main Widgets of Page + //---------------------------------- + + CarouselSlider bannerCarousel(){ + _bannerCarousel = CarouselSlider.builder( + carouselController: _carouselController, + itemCount: 10, + itemBuilder: (BuildContext context, int itemIndex) { + return Padding( + padding: const EdgeInsets.only(top: 10, bottom: 10, left: 15, right: 15), + child: FractionallySizedBox( + widthFactor: 1, + heightFactor: 1, + child: utils.applyShadow( + spreadRadius: 1, + blurRadius: 5, + child: InkWell( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: utils.Utils.loadNetworkImage(url: "https://wallpaperaccess.com/full/30103.jpg",) + ), + onTap: (){ + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => OfferAndPackagesDetail(model: "",) + ) + ); + }, + ) + ), + ), + ); + }, + options: CarouselOptions( + autoPlayInterval: Duration(milliseconds: 3500), + enlargeStrategy: CenterPageEnlargeStrategy.scale, + enlargeCenterPage: true, + autoPlay: false, + autoPlayCurve: Curves.fastOutSlowIn, + enableInfiniteScroll: true, + autoPlayAnimationDuration: Duration(milliseconds: 1500), + viewportFraction: 1, + onPageChanged: (page, reason){ + setState(() { + carouselIndicatorIndex = page; + }); + }, + ), + ); + return _bannerCarousel; + } + + TextField searchTextField(){ + return _textFieldSearch = + TextField( + controller: _searchTextController, + decoration: InputDecoration( + contentPadding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 10, right: 10), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( width: 0.5, color: Colors.grey), + borderRadius: const BorderRadius.all( + const Radius.circular(10.0), + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( width: 1, color: Colors.grey), + borderRadius: const BorderRadius.all( + const Radius.circular(10.0), + ), + ), + filled: true, + fillColor: Colors.white, + hintText: "Search", + hintStyle: TextStyle(color: Colors.grey[350], fontWeight: FontWeight.bold), + suffixIcon: IconButton( + onPressed: (){ + // viewModel.search(text: _searchTextController.text); + }, + icon: Icon(Icons.search_rounded, size: 35,), + ), + ), + ); + + } + + Widget filterOptionSelection(){ + _textFieldFilterSelection = + TextField( + enabled: false, + controller: _searchTextController, + decoration: InputDecoration( + contentPadding: EdgeInsets.only(top: 0.0, bottom: 0.0, left: 10, right: 10), + border: OutlineInputBorder( + borderSide: BorderSide(color: Colors.grey, width: 1), + borderRadius: const BorderRadius.all( + const Radius.circular(10.0), + ), + ), + disabledBorder: OutlineInputBorder( + borderSide: BorderSide( width: 0.5, color: Colors.grey), + borderRadius: const BorderRadius.all( + const Radius.circular(10.0), + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: const BorderRadius.all( + const Radius.circular(10.0), + ), + ), + filled: true, + fillColor: Colors.white, + hintText: "Browse offers by Clinic", + hintStyle: TextStyle(color: Colors.grey[350], fontWeight: FontWeight.bold), + suffixIcon: IconButton( + onPressed: (){ + showClinicSelectionList(); + }, + icon: Icon(Icons.keyboard_arrow_down_rounded, size: 35, color: Colors.grey,), + ), + ), + ); + + return InkWell( + child: _textFieldFilterSelection, + onTap: (){ + showClinicSelectionList(); + }, + ); + + } + + Widget latestOfferListView({@required double itemWidth, @required double itemContentPadding}){ + return _listViewLatestOffers = + ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: viewModel.bestSellerList.length, + itemBuilder: (BuildContext context, int index) { + return PackagesItemCard(itemWidth: itemWidth, itemContentPadding: itemContentPadding, itemModel: viewModel.bestSellerList[index], onCartClick: onProductCartClick,); + }, + separatorBuilder: separator, + ); + + } + + Widget bestSellerListView({@required double itemWidth, @required double itemContentPadding}){ + return _listViewLatestOffers = + ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: viewModel.bestSellerList.length, + itemBuilder: (BuildContext context, int index) { + return PackagesItemCard(itemWidth: itemWidth, itemContentPadding: itemContentPadding, itemModel: viewModel.bestSellerList[index], onCartClick: onProductCartClick,); + }, + separatorBuilder: separator, + ); + + } + + + Widget separator(BuildContext context, int index){ + return Container( + width: 1, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment(-1.0, -2.0), + end: Alignment(1.0, 4.0), + colors: [ + Colors.grey, + Colors.grey[100], + Colors.grey[200], + Colors.grey[300], + Colors.grey[400], + Colors.grey[500] + ] + )), + ); + } +} diff --git a/lib/routes.dart b/lib/routes.dart index b11025c2..57452796 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -10,6 +10,8 @@ import 'package:diplomaticquarterapp/pages/login/welcome.dart'; import 'package:diplomaticquarterapp/pages/login/login-type.dart'; import 'package:diplomaticquarterapp/pages/login/login.dart'; import 'package:diplomaticquarterapp/pages/login/register.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesCartPage.dart'; +import 'package:diplomaticquarterapp/pages/packages_offers/OfferAndPackagesPage.dart'; import 'package:diplomaticquarterapp/pages/settings/settings.dart'; import 'package:diplomaticquarterapp/pages/symptom-checker/info.dart'; import 'package:diplomaticquarterapp/pages/symptom-checker/select-gender.dart'; @@ -35,6 +37,9 @@ const String SYMPTOM_CHECKER = 'symptom-checker'; const String SYMPTOM_CHECKER_INFO = 'symptom-checker-info'; const String SELECT_GENDER = 'select-gender'; const String SETTINGS = 'settings'; +const String PACKAGES_OFFERS = 'packages-offers'; +const String PACKAGES_OFFERS_CART = 'packages-offers-cart'; + var routes = { SPLASH: (_) => SplashScreen(), HOME: (_) => LandingPage(), @@ -52,5 +57,7 @@ var routes = { SYMPTOM_CHECKER: (_) => SymptomChecker(), SYMPTOM_CHECKER_INFO: (_) => SymptomInfo(), SELECT_GENDER: (_) => SelectGender(), - SETTINGS: (_) => Settings() + SETTINGS: (_) => Settings(), + PACKAGES_OFFERS: (_) => PackagesHomePage(), + PACKAGES_OFFERS_CART: (_) => PackagesCartPage(), }; diff --git a/lib/uitl/gif_loader_dialog_utils.dart b/lib/uitl/gif_loader_dialog_utils.dart index ac37cdfb..785a6c6c 100644 --- a/lib/uitl/gif_loader_dialog_utils.dart +++ b/lib/uitl/gif_loader_dialog_utils.dart @@ -8,6 +8,10 @@ class GifLoaderDialogUtils { } static hideDialog(BuildContext context) { - Navigator.of(context).pop(); + try{ + Navigator.of(context).pop(); + }catch(error){ + Future.delayed(Duration(milliseconds: 250)).then((value) => Navigator.of(context).pop()); + } } } diff --git a/lib/uitl/translations_delegate_base.dart b/lib/uitl/translations_delegate_base.dart index 9ed7b478..97399f31 100644 --- a/lib/uitl/translations_delegate_base.dart +++ b/lib/uitl/translations_delegate_base.dart @@ -1110,6 +1110,11 @@ class TranslationBase { String get submitncontinue => localizedValues["submitncontinue"][locale.languageCode]; String get areyousure => localizedValues["areyousure"][locale.languageCode]; String get preferredunit => localizedValues["preferredunit"][locale.languageCode]; + + + // Offer And Packahes + String get subT=> localizedValues['OffersAndPackages'][locale.languageCode]; + String get totalWithColonRight => localizedValues['totalWithColonRight'][locale.languageCode]; } class TranslationBaseDelegate extends LocalizationsDelegate { diff --git a/lib/uitl/utils.dart b/lib/uitl/utils.dart index 85e97429..4a7eaead 100644 --- a/lib/uitl/utils.dart +++ b/lib/uitl/utils.dart @@ -3,6 +3,7 @@ import 'dart:core'; import 'dart:typed_data'; import 'package:badges/badges.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:connectivity/connectivity.dart'; import 'package:diplomaticquarterapp/config/shared_pref_kay.dart'; import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; @@ -35,7 +36,6 @@ import 'package:diplomaticquarterapp/widgets/dialogs/alert_dialog.dart'; import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import '../Constants.dart'; import 'app_shared_preferences.dart'; @@ -534,8 +534,44 @@ class Utils { return medical; } + + static Widget loadNetworkImage({@required String url, BoxFit fitting = BoxFit.cover}){ + return CachedNetworkImage( + placeholderFadeInDuration: Duration(milliseconds: 250), + fit: fitting, + imageUrl: url, + placeholder: (context, url) => Container( + child: Center( + child: CircularProgressIndicator() + ) + ), + errorWidget: (context, url, error){ + return Icon(Icons.error, color: Colors.red, size: 50,); + } + ); + } } + + + +Widget applyShadow({ Color color = Colors.grey, double shadowOpacity = 0.5, double spreadRadius = 2, double blurRadius = 7, Offset offset = const Offset(2, 2), @required Widget child}){ + return Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: color.withOpacity(shadowOpacity), + spreadRadius: spreadRadius, + blurRadius: blurRadius, + offset: offset, // changes position of shadow + ), + ], + ), + child: child, + ); +} + + Future userData() async { var userData = AuthenticatedUser.fromJson(await AppSharedPreferences().getObject(MAIN_USER)); return userData; diff --git a/lib/widgets/CounterView.dart b/lib/widgets/CounterView.dart new file mode 100644 index 00000000..6ba77c15 --- /dev/null +++ b/lib/widgets/CounterView.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; + +typedef StepperCallbackFuture = Future Function(int apply, int total); + +class StepperView extends StatefulWidget { + final double height; + final Color foregroundColor; + final Color backgroundColor; + final double buttonPadding; + + final int initialNumber; + final int maxNumber; + final int minNumber; + final StepperCallbackFuture counterCallback; + final Function increaseCallback; + final Function decreaseCallback; + + StepperView({this.initialNumber = 1, this.minNumber = 1, this.maxNumber, @required this.counterCallback, this.increaseCallback, this.decreaseCallback, this.height = 25, @required this.foregroundColor, @required this.backgroundColor, this.buttonPadding = 1}){ + assert((this.initialNumber >= this.minNumber && this.initialNumber <= this.maxNumber)); + } + @override + _StepperViewState createState() => _StepperViewState(); +} + +class _StepperViewState extends State { + int _currentCount; + StepperCallbackFuture _counterCallback; + Function _increaseCallback; + Function _decreaseCallback; + + @override + void initState() { + _currentCount = widget.initialNumber ?? 1; + _counterCallback = widget.counterCallback; + _increaseCallback = widget.increaseCallback ?? () {}; + _decreaseCallback = widget.decreaseCallback ?? () {}; + super.initState(); + } + + bool loadingInc = false; + bool loadingDec = false; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.all(widget.buttonPadding), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular((widget.height/2) + (widget.buttonPadding*2)), + color: widget.backgroundColor, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _createDecrementButton( + (_currentCount > widget.minNumber) ? () => _decrement() : null, + ), + Container( + width: 25, + child: Center( + child: Text( + _currentCount.toString(), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + height: 1.5, + color: Colors.black, + ) + ), + ) + ), + _createIncrementButton( + (_currentCount < widget.maxNumber) ? () => _increment() : null + ), + ], + ), + ); + } + + void _increment() async{ + doInc({@required bool can}){ + if(can) + setState(() { + _currentCount++; + _increaseCallback(); + }); + } + + if (_currentCount < widget.maxNumber){ + if(_counterCallback == null) + doInc(can: true); + else { + setState(() => loadingInc = true); + var result = (await _counterCallback(1,_currentCount)); + doInc(can: result); + setState(() => loadingInc = false); + } + } + } + + void _decrement() async{ + doDec({@required bool can}){ + if(can) + setState(() { + _currentCount--; + _decreaseCallback(); + }); + } + if (_currentCount > widget.minNumber) { + if(_counterCallback == null) + doDec(can: true); + else { + setState(() => loadingDec = true); + var result = (await _counterCallback(-1,_currentCount)); + doDec(can: result); + setState(() => loadingDec = false); + } + } + } + + Widget _createIncrementButton(Function onPressed,) { + return Stack( + children: [ + RawMaterialButton( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + constraints: BoxConstraints(minWidth: widget.height, minHeight: widget.height), + onPressed: onPressed, + elevation: 2.0, + fillColor: widget.foregroundColor, + child: + Icon( + Icons.add, + color: (_currentCount < widget.maxNumber) ? Colors.grey[700] : Colors.grey[400], + size: 12.0, + ), + shape: CircleBorder(), + ), + + if(loadingInc) + Container( + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.circular(widget.height/2), + ), + height: widget.height, + width: widget.height, + child: CircularProgressIndicator( + strokeWidth: 3, + valueColor: AlwaysStoppedAnimation(Colors.green) + ) + ) + ], + ); + } + + Widget _createDecrementButton(Function onPressed) { + return Stack( + children: [ + RawMaterialButton( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + constraints: BoxConstraints(minWidth: widget.height, minHeight: widget.height), + onPressed: onPressed, + elevation: 2.0, + fillColor: widget.foregroundColor, + child: Icon( + Icons.remove, + color: (_currentCount > widget.minNumber) ? Colors.grey[700] : Colors.grey[400], + size: 12.0, + ), + shape: CircleBorder(), + ), + + if(loadingDec) + Container( + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.8), + borderRadius: BorderRadius.circular(widget.height/2), + ), + height: widget.height, + width: widget.height, + child: CircularProgressIndicator( + strokeWidth: 3, + valueColor: AlwaysStoppedAnimation(Colors.red) + ) + ) + ], + ); + } +} diff --git a/lib/widgets/carousel_indicator/carousel_indicator.dart b/lib/widgets/carousel_indicator/carousel_indicator.dart new file mode 100644 index 00000000..020c44d8 --- /dev/null +++ b/lib/widgets/carousel_indicator/carousel_indicator.dart @@ -0,0 +1,197 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + + +class CarouselIndicator extends StatefulWidget { + /// width of the indicator + final double width; + + /// height of the indicator + final double height; + + /// space between indicators. + final double space; + + /// count of indicator + final int count; + + /// active color + final Color activeColor; + + /// normal color + final Color color; + + /// use this to give some radius to the corner indicator + final double cornerRadius; + + /// duration for slide animation + final int animationDuration; + + final int index; + + final Function(int index) onClick; + + CarouselIndicator({ + Key key, + this.width: 20.0, + this.height: 6, + this.space: 5.0, + this.count, + this.cornerRadius: 6, + this.animationDuration: 300, + this.color: Colors.white30, + this.index, + this.activeColor: Colors.white, + this.onClick + }) : assert(count != null && count != 0), + assert(index != null && index >= 0), + super(key: key); + + @override + State createState() { + return new _CarouselIndicatorState(); + } +} + +class _CarouselIndicatorState extends State + with TickerProviderStateMixin { + /// [Tween] object of type double + Tween _tween; + + /// [AnimationController] object + AnimationController _animationController; + + /// [Aniamtion] object + Animation _animation; + + /// [Paint] object to paint our indicator + Paint _paint = new Paint(); + + /// Method to initilize [BasePainter] to paint indicators. + BasePainter _createPainer() { + return SlidePainter(widget, _animation.value, _paint); + } + + @override + Widget build(BuildContext context) { + Widget child = new SizedBox( + width: widget.count * widget.width + (widget.count - 1) * widget.space, + height: widget.height, + child: CustomPaint( + painter: _createPainer(), + ), + ); + + return InkWell( + child: child, + onTap: (){ + if(widget.onClick != null) + widget.onClick(0); + }, + ); + } + + @override + void initState() { + /// for initial index=0 we do not want to change any value so setting [_tween] to (0.0,0.0), + createAnimation(0.0, 0.0); + super.initState(); + } + + @override + void didUpdateWidget(CarouselIndicator oldWidget) { + if (widget.index != oldWidget.index) { + if (widget.index != 0) { + _animationController.reset(); + + /// for each new index we want to change value so setting [_tween] to (oldWidget.index,widget.index) so animation tween from old position to new position rather not start from 0.0 again and again. + createAnimation(oldWidget.index.toDouble(), widget.index.toDouble()); + _animationController.forward(); + } else { + _animationController.reset(); + createAnimation(oldWidget.index.toDouble(), 0.0); + _animationController.forward(); + } + } + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + void createAnimation(double begin, double end) { + _tween = Tween(begin: begin, end: end); + _animationController = AnimationController( + vsync: this, + duration: Duration(milliseconds: widget.animationDuration)); + _animation = _tween.animate(_animationController) + ..addListener(() { + setState(() {}); + }); + } +} + + +/// Base Painter class to draw indicator +abstract class BasePainter extends CustomPainter { + final CarouselIndicator widget; + final double page; + final Paint _paint; + + BasePainter(this.widget, this.page, this._paint); + + /// This method will get body to class extending [BasePainter] and this method will draw the sliding indicator which slide over changing index. + void draw(Canvas canvas, double space, double width, double height, + double radius, double cornerRadius); + + @override + void paint(Canvas canvas, Size size) { + _paint.color = widget.color; + double space = widget.space; + double width = widget.width; + double height = widget.height; + double distance = width + space; + double radius = width / 2; + for (int i = 0, c = widget.count; i < c; ++i) { + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromCenter( + center: Offset((i * distance) + radius, radius), + width: width, + height: height), + Radius.circular(widget.cornerRadius)), + _paint); + } + + _paint.color = widget.activeColor; + draw(canvas, space, width, height, radius, widget.cornerRadius); + } + + @override + bool shouldRepaint(BasePainter oldDelegate) { + return oldDelegate.page != page; + } +} + +/// This class we draw the indicator which slides. +class SlidePainter extends BasePainter { + SlidePainter(CarouselIndicator widget, double page, Paint paint) + : super(widget, page, paint); + + @override + void draw(Canvas canvas, double space, double width, double height, + double radius, double cornerRadius) { + canvas.drawRRect( + RRect.fromRectAndRadius( + Rect.fromCenter( + center: Offset(radius + (page * (width + space)), radius), + width: width, + height: height), + Radius.circular(cornerRadius)), + _paint); + } +} + diff --git a/lib/widgets/offers_packages/PackagesCartItemCard.dart b/lib/widgets/offers_packages/PackagesCartItemCard.dart new file mode 100644 index 00000000..72ce7475 --- /dev/null +++ b/lib/widgets/offers_packages/PackagesCartItemCard.dart @@ -0,0 +1,217 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesCartItemsResponseModel.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; +import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; +import 'package:diplomaticquarterapp/widgets/CounterView.dart'; +import 'package:diplomaticquarterapp/widgets/others/StarRating.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + + +bool wide = true; + +class PackagesCartItemCard extends StatefulWidget { + final CartProductResponseModel itemModel; + final StepperCallbackFuture shouldStepperChangeApply ; + + const PackagesCartItemCard( + { + @required this.itemModel, + @required this.shouldStepperChangeApply, + Key key}) + : super(key: key); + + @override + State createState() => PackagesCartItemCardState(); +} + +class PackagesCartItemCardState extends State { + + @override + Widget build(BuildContext context) { + + wide = !wide; + return Container( + color: Colors.transparent, + child: Card( + elevation: 3, + shadowColor: Colors.grey[100], + color: Colors.white, + child: Stack( + children: [ + Container( + height: 100, + child: Row( + children: [ + _image(widget.itemModel.product), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _itemName(widget.itemModel.product.getName()), + Row( + children: [ + _itemPrice(widget.itemModel.product.price, context: context), + _priceSeperator(), + _itemOldPrice(widget.itemModel.product.oldPrice, context: context), + ], + ), + Row( + children: [ + _itemCounter( + widget.itemModel.quantity, + minQuantity: widget.itemModel.product.orderMinimumQuantity, + maxQuantity: widget.itemModel.product.orderMaximumQuantity, + shouldStepperChangeApply: (apply,total) async{ + bool success = await widget.shouldStepperChangeApply(apply,total); + if(success == true) + setState(() => widget.itemModel.quantity = total); + return success; + } + ), + ], + ), + ], + ) + ], + ), + ), + + Positioned( + bottom: 8, + left: 10, + child: Row( + children: [ + _totalPrice((widget.itemModel.product.price * widget.itemModel.quantity), context: context), + _totalLabel(context: context), + ], + ), + ) + ], + ) + ) + ); + } +} + + +// -------------------- +// Product Image +// -------------------- +Widget _image(PackagesResponseModel model) => AspectRatio( + aspectRatio: 1/1, + child: Padding( + padding: const EdgeInsets.all(10), + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey[300], width: 0.25), + boxShadow: [ + BoxShadow(color: Colors.grey[200], blurRadius: 2.0, spreadRadius: 1, offset: Offset(1,1.5)) + ], + borderRadius: BorderRadius.circular(8), + color: Colors.white, + shape: BoxShape.rectangle, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: (model.images.isNotEmpty) + ? Utils.loadNetworkImage(url: model.images.first.src, fitting:BoxFit.fill) + : Container(color: Colors.grey[200]) + ), + ), + ), +); + +// -------------------- +// Product Name +// -------------------- +Widget _itemName(String name) => Padding( + padding: const EdgeInsets.all(0), + child: Text( + name, + style: TextStyle( + fontWeight: FontWeight.normal, + color: Colors.black, + fontSize: 15)) +); + + +Widget _itemPrice(double price, {@required BuildContext context}) => Padding( + padding: const EdgeInsets.all(0), + child: Text( + '${price} ${TranslationBase.of(context).sar}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.green, + fontSize: 15)) +); + + +// -------------------- +// Price Seperator +// -------------------- +Widget _priceSeperator() => Padding( + padding: const EdgeInsets.only(left: 3, right: 3), + child: Container(height: 0.5, width: 5, color: Colors.grey[100],), +); + + +// -------------------- +// Product Price +// -------------------- +Widget _itemOldPrice(double oldPrice, {@required BuildContext context}) => Padding( + padding: const EdgeInsets.all(0), + child: Text( + '${oldPrice} ${TranslationBase.of(context).sar}', + style: TextStyle( + fontWeight: FontWeight.normal, + decoration: TextDecoration.lineThrough, + color: Colors.grey, + fontSize: 10 + ) + ) +); + +// -------------------- +// Product Price +// -------------------- +Widget _itemCounter(int quantity, {int minQuantity, int maxQuantity, StepperCallbackFuture shouldStepperChangeApply}) => Padding( + padding: const EdgeInsets.all(0), + child: StepperView( + height: 25, + backgroundColor: Colors.grey[300], + foregroundColor: Colors.grey[200], + initialNumber: quantity, + minNumber: minQuantity, + maxNumber: maxQuantity, + counterCallback: shouldStepperChangeApply, + decreaseCallback: (){}, + increaseCallback: (){}, + ) +); + + +Widget _totalLabel({@required BuildContext context}) => Padding( + padding: const EdgeInsets.all(0), + child: Text( + '${TranslationBase.of(context).totalWithColonRight}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.grey[600], + fontSize: 13 + ) + ) +); + + +Widget _totalPrice(double totalPrice, {@required BuildContext context}) => Padding( + padding: const EdgeInsets.all(0), + child: Text( + '${totalPrice.toStringAsFixed(2)} ${TranslationBase.of(context).sar}', + style: TextStyle( + fontWeight: FontWeight.normal, + color: Colors.green, + fontSize: 12)) +); + diff --git a/lib/widgets/offers_packages/PackagesOfferCard.dart b/lib/widgets/offers_packages/PackagesOfferCard.dart new file mode 100644 index 00000000..af0341c5 --- /dev/null +++ b/lib/widgets/offers_packages/PackagesOfferCard.dart @@ -0,0 +1,157 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:diplomaticquarterapp/core/model/packages_offers/responses/PackagesResponseModel.dart'; +import 'package:diplomaticquarterapp/uitl/utils.dart'; +import 'package:diplomaticquarterapp/widgets/others/StarRating.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + + +bool wide = true; + +class PackagesItemCard extends StatefulWidget { + final double itemWidth; + final double itemHeight; + final double itemContentPadding; + final PackagesResponseModel itemModel; + final Function(PackagesResponseModel product) onCartClick; + + const PackagesItemCard( + { + this.itemWidth, + this.itemHeight, + @required this.itemModel, + @required this.itemContentPadding, + @required this.onCartClick, + Key key}) + : super(key: key); + + @override + State createState() => PackagesItemCardState(); +} + +class PackagesItemCardState extends State { + + @override + Widget build(BuildContext context) { + wide = !wide; + return Directionality( + textDirection: TextDirection.rtl, + child: Stack( + children: [ + Padding( + padding: EdgeInsets.only( + left: widget.itemContentPadding, + right: widget.itemContentPadding, + top: widget.itemContentPadding + 5), + child: Container( + width: widget.itemWidth, + color: Colors.transparent, + child: Stack( + children: [ + Column( + mainAxisSize: MainAxisSize.max, + children: [ + AspectRatio( + aspectRatio:1 / 1, + child: applyShadow( + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: Utils.loadNetworkImage( + url: + "https://wallpaperaccess.com/full/30103.jpg", + )), + )), + Text( + widget.itemModel.getName(), + style: TextStyle( + fontWeight: FontWeight.normal, + color: Colors.black, + fontSize: 15)), + Padding( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [ + Stack( + children: [ + Text( + '${widget.itemModel.oldPrice} ${'SAR'}', + style: TextStyle( + fontWeight: FontWeight.normal, + decoration: TextDecoration.lineThrough, + color: Colors.grey, + fontSize: 12)), + Padding( + padding: const EdgeInsets.only(top: 8), + child: Text( + '${widget.itemModel.price} ${'SAR'}', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Colors.green, + fontSize: 18)), + ), + Padding( + padding: const EdgeInsets.only(top: 35), + child: StarRating( + size: 15, + totalCount: null, + totalAverage: widget.itemModel.approvedRatingSum.toDouble(), + forceStars: true), + ) + ], + ), + Spacer( + flex: 1, + ), + InkWell( + child: Icon( + Icons.add_shopping_cart_rounded, + size: 30.0, + color: Colors.grey, + ), + onTap: () { + widget.onCartClick(widget.itemModel); + }, + ), + ], + ), + ), + ], + ), + ], + ), + ), + ), + Positioned( + top: 0, + right: 0, + child: Visibility( + visible: false, + child: InkWell( + child: Icon( + Icons.favorite, + size: 40.0, + color: Colors.red, + ), + onTap: () { + + }, + ), + ), + ), + + Positioned( + top: 7, + left: 2, + child: Image.asset( + 'assets/images/discount_${'en'}.png', + height: 60, + width: 60, + ), + ), + ], + ), + ); + } +} diff --git a/lib/widgets/offers_packages/offers_packages.dart b/lib/widgets/offers_packages/offers_packages.dart index 6834d516..8fae69b4 100644 --- a/lib/widgets/offers_packages/offers_packages.dart +++ b/lib/widgets/offers_packages/offers_packages.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:carousel_pro/carousel_pro.dart'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersCategoriesRequestModel.dart'; import 'package:diplomaticquarterapp/core/model/packages_offers/requests/OffersProductsRequestModel.dart'; diff --git a/lib/widgets/others/StarRating.dart b/lib/widgets/others/StarRating.dart index 2eaa7f91..5620b21a 100644 --- a/lib/widgets/others/StarRating.dart +++ b/lib/widgets/others/StarRating.dart @@ -14,26 +14,26 @@ class StarRating extends StatelessWidget { @override Widget build(BuildContext context) { return Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - if (!forceStars && (totalAverage==null || totalAverage==0)) - Texts("New", style: "caption"), - if (forceStars || (totalAverage!=null && totalAverage>0)) - ...List.generate(5, (index) => - Padding( - padding: EdgeInsets.only(right: 1.0), - child: Icon( - (index+1) <= (totalAverage ?? 0) ? EvaIcons.star : EvaIcons.starOutline, - size: size, - color: (index+1) <= (totalAverage ?? 0) ? Color.fromRGBO(255, 186, 0, 1.0) : Theme.of(context).hintColor + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (!forceStars && (totalAverage==null || totalAverage==0)) + Texts("New", style: "caption"), + if (forceStars || (totalAverage!=null && totalAverage>0)) + ...List.generate(5, (index) => + Padding( + padding: EdgeInsets.only(right: 1.0), + child: Icon( + (index+1) <= (totalAverage ?? 0) ? EvaIcons.star : EvaIcons.starOutline, + size: size, + color: (index+1) <= (totalAverage ?? 0) ? Color.fromRGBO(255, 186, 0, 1.0) : Theme.of(context).hintColor + ), + ) ), - ) - ), - if (totalCount!=null) - SizedBox(width: 9.0), - if (totalCount!=null) - Texts("("+totalCount.toString()+")", style: "overline", color: Colors.grey[400],) - ] + if (totalCount!=null) + SizedBox(width: 9.0), + if (totalCount!=null) + Texts("("+totalCount.toString()+")", style: "overline", color: Colors.grey[400],) + ] ); } } \ No newline at end of file diff --git a/lib/widgets/others/app_scaffold_widget.dart b/lib/widgets/others/app_scaffold_widget.dart index ca91efe2..af61052e 100644 --- a/lib/widgets/others/app_scaffold_widget.dart +++ b/lib/widgets/others/app_scaffold_widget.dart @@ -27,6 +27,8 @@ import 'arrow_back.dart'; import 'network_base_view.dart'; import 'not_auh_page.dart'; +VoidCallback _onCartClick; + class AppScaffold extends StatelessWidget { final String appBarTitle; final Widget body; @@ -38,7 +40,9 @@ class AppScaffold extends StatelessWidget { final bool isBottomBar; final Widget floatingActionButton; final bool isPharmacy; + final bool isOfferPackages; final bool showPharmacyCart; + final bool showOfferPackagesCart; final String title; final String description; final bool isShowDecPage; @@ -63,6 +67,8 @@ class AppScaffold extends StatelessWidget { this.floatingActionButton, this.isPharmacy = false, this.showPharmacyCart = true, + this.isOfferPackages = false, + this.showOfferPackagesCart = false, this.title, this.description, this.isShowDecPage = true, @@ -74,6 +80,11 @@ class AppScaffold extends StatelessWidget { this.infoList, this.imagesInfo}); + AppScaffold setOnAppBarCartClick(VoidCallback onClick){ + _onCartClick = onClick; + return this; + } + @override Widget build(BuildContext context) { AppGlobal.context = context; @@ -87,6 +98,8 @@ class AppScaffold extends StatelessWidget { showHomeAppBarIcon: showHomeAppBarIcon, isPharmacy: isPharmacy, showPharmacyCart: showPharmacyCart, + isOfferPackages: isOfferPackages, + showOfferPackagesCart: showOfferPackagesCart, isShowDecPage: isShowDecPage, ) : null, @@ -122,7 +135,9 @@ class AppBarWidget extends StatelessWidget with PreferredSizeWidget { final bool showHomeAppBarIcon; final List appBarIcons; final bool isPharmacy; + final bool isOfferPackages; final bool showPharmacyCart; + final bool showOfferPackagesCart; final bool isShowDecPage; AppBarWidget( @@ -131,6 +146,8 @@ class AppBarWidget extends StatelessWidget with PreferredSizeWidget { this.appBarIcons, this.isPharmacy = true, this.showPharmacyCart = true, + this.isOfferPackages = false, + this.showOfferPackagesCart = false, this.isShowDecPage = true}); @override @@ -164,12 +181,25 @@ class AppBarWidget extends StatelessWidget with PreferredSizeWidget { actions: [ (isPharmacy && showPharmacyCart) ? IconButton( - icon: Icon(Icons.shopping_cart), - color: Colors.white, - onPressed: () { - Navigator.of(context).popUntil(ModalRoute.withName('/')); - }) + icon: Icon(Icons.shopping_cart), + color: Colors.white, + onPressed: () { + Navigator.of(context).popUntil(ModalRoute.withName('/')); + }) : Container(), + + (isOfferPackages && showOfferPackagesCart) + ? IconButton( + icon: Icon(Icons.shopping_cart), + color: Colors.white, + onPressed: () { + // Cart Click Event + if(_onCartClick != null) + _onCartClick(); + + }) + : Container(), + if (showHomeAppBarIcon) IconButton( icon: Icon(FontAwesomeIcons.home), @@ -179,6 +209,10 @@ class AppBarWidget extends StatelessWidget with PreferredSizeWidget { context, MaterialPageRoute(builder: (context) => LandingPage()), (Route r) => false); + + // Cart Click Event + if(_onCartClick != null) + _onCartClick(); }, ), if (appBarIcons != null) ...appBarIcons diff --git a/pubspec.yaml b/pubspec.yaml index 3b44f01f..54d08a71 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -101,9 +101,6 @@ dependencies: # Location Helper map_launcher: ^0.8.1 - #carousel slider - carousel_slider: ^2.3.1 - #Calendar Events manage_calendar_events: ^1.0.2 @@ -170,6 +167,10 @@ dependencies: # Dep by Zohaib shimmer: ^1.1.2 + cached_network_image: ^2.5.0 + carousel_slider: ^2.3.1 + flutter_material_pickers: 1.7.4 + flutter_staggered_grid_view: 0.3.4 # Marker Animation flutter_animarker: ^1.0.0