diff --git a/lib/api/offers_and_discounts_api_client.dart b/lib/api/offers_and_discounts_api_client.dart new file mode 100644 index 0000000..2f5714b --- /dev/null +++ b/lib/api/offers_and_discounts_api_client.dart @@ -0,0 +1,69 @@ +import 'dart:convert'; + +import 'package:mohem_flutter_app/api/api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_categories_list.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; + +class OffersAndDiscountsApiClient { + static final OffersAndDiscountsApiClient _instance = OffersAndDiscountsApiClient._internal(); + + OffersAndDiscountsApiClient._internal(); + + factory OffersAndDiscountsApiClient() => _instance; + + Future> getSaleCategories() async { + List getSaleCategoriesList = []; + + String url = "${ApiConsts.cocRest}Mohemm_ITG_GetCategories"; + Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgPageSize": 100, "ItgPageNo": 1}; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + + GetCategoriesList getSaleCategoriesListObj = GetCategoriesList(); + getSaleCategoriesListObj.id = 0; + getSaleCategoriesListObj.categoryNameEn = "All"; + getSaleCategoriesListObj.categoryNameAr = "الجميع"; + getSaleCategoriesListObj.isActive = true; + getSaleCategoriesListObj.content = + ''; + + getSaleCategoriesList.add(getSaleCategoriesListObj); + + body['result']['data'].forEach((v) { + getSaleCategoriesList.add(GetCategoriesList.fromJson(v)); + }); + return getSaleCategoriesList; + }, + url, + postParams, + ); + } + + Future> getOffersList(int categoryID, int pageSize) async { + List getSaleCategoriesList = []; + + String url = "${ApiConsts.cocRest}GetOfferDiscountsConfigData"; + Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgPageSize": pageSize, "ItgPageNo": 1, "ItgCategoryID": categoryID}; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + + final bodyData = json.decode(body['result']['data']); + + bodyData.forEach((v) { + getSaleCategoriesList.add(OffersListModel.fromJson(v)); + }); + return getSaleCategoriesList; + }, + url, + postParams, + ); + } +} diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 6928f09..270cb48 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -36,6 +36,8 @@ import 'package:mohem_flutter_app/ui/screens/mowadhafhi/mowadhafhi_hr_request.da import 'package:mohem_flutter_app/ui/screens/mowadhafhi/request_details.dart'; import 'package:mohem_flutter_app/ui/screens/my_requests/my_requests.dart'; import 'package:mohem_flutter_app/ui/screens/my_requests/new_request.dart'; +import 'package:mohem_flutter_app/ui/screens/offers_and_discounts/offers_and_discounts_details.dart'; +import 'package:mohem_flutter_app/ui/screens/offers_and_discounts/offers_and_discounts_home.dart'; import 'package:mohem_flutter_app/ui/screens/pending_transactions/pending_transactions.dart'; import 'package:mohem_flutter_app/ui/screens/pending_transactions/pending_transactions_details.dart'; import 'package:mohem_flutter_app/ui/screens/profile/profile_screen.dart'; @@ -117,6 +119,10 @@ class AppRoutes { static const String itemsForSaleDetail = "/itemsForSaleDetail"; static const String addNewItemForSale = "/addNewItemForSale"; + // Offers & Discounts + static const String offersAndDiscounts = "/offersAndDiscounts"; + static const String offersAndDiscountsDetails = "/offersAndDiscountsDetails"; + //Pay slip static const String monthlyPaySlip = "/monthlyPaySlip"; @@ -188,6 +194,9 @@ class AppRoutes { itemsForSaleDetail: (context) => ItemForSaleDetailPage(), addNewItemForSale: (context) => AddNewItemForSale(), + // Offers & Discounts + offersAndDiscounts: (context) => OffersAndDiscountsHome(), + offersAndDiscountsDetails: (context) => OffersAndDiscountsDetails(), //pay slip monthlyPaySlip: (context) => MonthlyPaySlipScreen(), diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 869b61c..8f280b1 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -15,8 +15,9 @@ extension CapExtension on String { extension EmailValidator on String { Widget get toWidget => Text(this); - Widget toText10({Color? color, bool isBold = false}) => Text( + Widget toText10({Color? color, bool isBold = false, int maxLine = 0}) => Text( this, + maxLines: (maxLine > 0) ? maxLine : null, style: TextStyle(fontSize: 10, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4), ); @@ -49,15 +50,21 @@ extension EmailValidator on String { style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.52, decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText14({Color? color, bool isBold = false}) => Text( + Widget toText14({Color? color, bool isBold = false, int? maxlines}) => Text( this, + maxLines: maxlines, style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 14, letterSpacing: -0.48, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); - Widget toText16({Color? color, bool isBold = false, int? maxlines}) => Text( + Widget toText16({Color? color, bool isBold = false, int? maxlines, bool isUnderLine = false}) => Text( this, maxLines: maxlines, - style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 16, letterSpacing: -0.64, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), + style: TextStyle( + color: color ?? MyColors.darkTextColor, + fontSize: 16, + letterSpacing: -0.64, + fontWeight: isBold ? FontWeight.bold : FontWeight.w600, + decoration: isUnderLine ? TextDecoration.underline : TextDecoration.none), ); Widget toText17({Color? color, bool isBold = false}) => Text( diff --git a/lib/models/offers_and_discounts/get_categories_list.dart b/lib/models/offers_and_discounts/get_categories_list.dart new file mode 100644 index 0000000..502a850 --- /dev/null +++ b/lib/models/offers_and_discounts/get_categories_list.dart @@ -0,0 +1,49 @@ +class GetCategoriesList { + int? id; + String? categoryNameEn; + String? content; + String? categoryNameAr; + int? channelId; + bool? isActive; + int? pageSize; + int? pageNo; + int? languageId; + + GetCategoriesList({ + this.id, + this.categoryNameEn, + this.content, + this.categoryNameAr, + this.channelId, + this.isActive, + this.pageSize, + this.pageNo, + this.languageId, + }); + + GetCategoriesList.fromJson(Map json) { + id = json['id']; + categoryNameEn = json['categoryName_en']; + content = json['content']; + categoryNameAr = json['categoryName_ar']; + channelId = json['channelId']; + isActive = json['isActive']; + pageSize = json['pageSize']; + pageNo = json['pageNo']; + languageId = json['languageId']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['categoryName_en'] = categoryNameEn; + data['content'] = content; + data['categoryName_ar'] = categoryNameAr; + data['channelId'] = channelId; + data['isActive'] = isActive; + data['pageSize'] = pageSize; + data['pageNo'] = pageNo; + data['languageId'] = languageId; + return data; + } +} diff --git a/lib/models/offers_and_discounts/get_offers_list.dart b/lib/models/offers_and_discounts/get_offers_list.dart new file mode 100644 index 0000000..2b08ba6 --- /dev/null +++ b/lib/models/offers_and_discounts/get_offers_list.dart @@ -0,0 +1,96 @@ +class OffersListModel { + String? title; + String? titleAR; + String? description; + String? descriptionAR; + String? startDate; + String? endDate; + String? logo; + String? bannerImage; + String? discount; + String? rowID; + String? categoryNameEn; + String? categoryNameAr; + String? categoryID; + String? isHasLocation; + String? created; + String? publishedDesc; + String? published; + String? expireAfter; + String? status; + String? isActive; + String? totalItems; + + OffersListModel( + {this.title, + this.titleAR, + this.description, + this.descriptionAR, + this.startDate, + this.endDate, + this.logo, + this.bannerImage, + this.discount, + this.rowID, + this.categoryNameEn, + this.categoryNameAr, + this.categoryID, + this.isHasLocation, + this.created, + this.publishedDesc, + this.published, + this.expireAfter, + this.status, + this.isActive, + this.totalItems}); + + OffersListModel.fromJson(Map json) { + title = json['Title']; + titleAR = json['Title_AR']; + description = json['Description']; + descriptionAR = json['Description_AR']; + startDate = json['Start Date']; + endDate = json['End Date']; + logo = json['Logo']; + bannerImage = json['Banner_Image']; + discount = json['Discount']; + rowID = json['rowID']; + categoryNameEn = json['categoryName_en']; + categoryNameAr = json['categoryName_ar']; + categoryID = json['categoryID']; + isHasLocation = json['IsHasLocation']; + created = json['created']; + publishedDesc = json['PublishedDesc']; + published = json['Published']; + expireAfter = json['ExpireAfter']; + status = json['Status']; + isActive = json['IsActive']; + totalItems = json['TotalItems']; + } + + Map toJson() { + final Map data = new Map(); + data['Title'] = this.title; + data['Title_AR'] = this.titleAR; + data['Description'] = this.description; + data['Description_AR'] = this.descriptionAR; + data['Start Date'] = this.startDate; + data['End Date'] = this.endDate; + data['Logo'] = this.logo; + data['Banner_Image'] = this.bannerImage; + data['Discount'] = this.discount; + data['rowID'] = this.rowID; + data['categoryName_en'] = this.categoryNameEn; + data['categoryName_ar'] = this.categoryNameAr; + data['categoryID'] = this.categoryID; + data['IsHasLocation'] = this.isHasLocation; + data['created'] = this.created; + data['PublishedDesc'] = this.publishedDesc; + data['Published'] = this.published; + data['ExpireAfter'] = this.expireAfter; + data['Status'] = this.status; + data['IsActive'] = this.isActive; + data['TotalItems'] = this.totalItems; + return data; + } +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 511fc03..ddb245d 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/main.dart'; @@ -13,6 +14,7 @@ import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/dashboard/menus.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; /// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool // ignore: prefer_mixin @@ -41,6 +43,9 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List? homeMenus; List? getMenuEntriesList; + //Offers And Discounts + List getOffersList = []; + //Attendance Tracking API's & Methods Future fetchAttendanceTracking(context) async { try { @@ -175,6 +180,18 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } + void getCategoryOffersListAPI(BuildContext context) async { + try { + // Utils.showLoading(context); + getOffersList = await OffersAndDiscountsApiClient().getOffersList(0, 6); + notifyListeners(); + } catch (ex) { + // Utils.hideLoading(context); + notifyListeners(); + Utils.handleException(ex, context, null); + } + } + List parseMenus(List getMenuEntriesList) { List menus = []; for (int i = 0; i < getMenuEntriesList.length; i++) { diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index d5acce6..4d2cc38 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -13,11 +13,13 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/landing/widget/app_drawer.dart'; import 'package:mohem_flutter_app/ui/landing/widget/menus_widget.dart'; import 'package:mohem_flutter_app/ui/landing/widget/services_widget.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/offers_shimmer_widget.dart'; import 'package:provider/provider.dart'; class DashboardScreen extends StatefulWidget { @@ -44,6 +46,7 @@ class _DashboardScreenState extends State { data.fetchMissingSwipe(context); data.fetchLeaveTicketBalance(context); data.fetchMenuEntries(); + data.getCategoryOffersListAPI(context); } @override @@ -267,7 +270,11 @@ class _DashboardScreenState extends State { ], ), ), - LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true), + InkWell( + onTap: () { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }, + child: LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true)), ], ).paddingOnly(left: 21, right: 21), SizedBox( @@ -278,37 +285,50 @@ class _DashboardScreenState extends State { padding: const EdgeInsets.only(left: 21, right: 21, top: 13), scrollDirection: Axis.horizontal, itemBuilder: (cxt, index) { - return SizedBox( - width: 73, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 73, - height: 73, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(100), - ), - border: Border.all(color: MyColors.lightGreyEDColor, width: 1), - ), - child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(50), - ), - child: Image.network( - "https://play-lh.googleusercontent.com/NPo88ojmhah4HDiposucJmfQIop4z4xc8kqJK9ITO9o-yCab2zxIp7PPB_XPj2iUojo", - fit: BoxFit.cover, + return data.getOffersList.isNotEmpty + ? InkWell( + onTap: () { + navigateToDetails(data.getOffersList[index]); + }, + child: SizedBox( + width: 73, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 73, + height: 73, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), + border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + ), + child: ClipRRect( + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + child: Hero( + tag: "ItemImage" + data.getOffersList[index].rowID!, + transitionOnUserGestures: true, + child: Image.network( + data.getOffersList[index].bannerImage!, + fit: BoxFit.contain, + ), + ), + ), + ), + 4.height, + Expanded( + child: AppState().isArabic(context) + ? data.getOffersList[index].titleAR!.toText12(isCenter: true, maxLine: 1) + : data.getOffersList[index].title!.toText12(isCenter: true, maxLine: 1), + ), + ], ), ), - ), - 4.height, - Expanded( - child: namesD[6 % (index + 1)].toText12(isCenter: true, maxLine: 2), - ), - ], - ), - ); + ) + : const OffersShimmerWidget(); }, separatorBuilder: (cxt, index) => 8.width, itemCount: 6), @@ -397,4 +417,23 @@ class _DashboardScreenState extends State { ), ); } + + void navigateToDetails(OffersListModel offersListModelObj) { + List getOffersDetailList = []; + getOffersDetailList.clear(); + int counter = 1; + + getOffersDetailList.add(offersListModelObj); + + data.getOffersList.forEach((element) { + if (counter <= 4) { + if (element.rowID != offersListModelObj.rowID) { + getOffersDetailList.add(element); + counter++; + } + } + }); + + Navigator.pushNamed(context, AppRoutes.offersAndDiscountsDetails, arguments: getOffersDetailList); + } } diff --git a/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart b/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart index ffb45e0..93ea8e9 100644 --- a/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart +++ b/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart @@ -195,7 +195,6 @@ class _ItemsForSaleFragmentState extends State { ), 10.height, getItemsForSaleList.title!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1), - 10.height, getItemsForSaleList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), 16.height, getItemsForSaleList.status!.toText14(isBold: true, color: getItemsForSaleList.status == 'Approved' ? MyColors.greenColor : MyColors.yellowColor), diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart new file mode 100644 index 0000000..e5b7f11 --- /dev/null +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart @@ -0,0 +1,239 @@ +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui' as ui; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_html/flutter_html.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share/share.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class OffersAndDiscountsDetails extends StatefulWidget { + const OffersAndDiscountsDetails({Key? key}) : super(key: key); + + @override + State createState() => _OffersAndDiscountsDetailsState(); +} + +class _OffersAndDiscountsDetailsState extends State { + // late OffersListModel getOffersList; + late List getOffersList; + late ScrollController _scrollController; + + GlobalKey _globalKey = GlobalKey(); + + @override + void initState() { + _scrollController = ScrollController(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + getOffersList = ModalRoute.of(context)?.settings.arguments as List; + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget( + context, + // title: LocaleKeys.mowadhafhiRequest.tr(), + title: "Offers & Discounts", + showHomeButton: true, + ), + body: SingleChildScrollView( + controller: _scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Hero( + tag: "ItemImage" + getOffersList[0].rowID!, + // transitionOnUserGestures: true, + child: RepaintBoundary( + key: _globalKey, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.network( + getOffersList[0].bannerImage!, + fit: BoxFit.contain, + ), + ).paddingAll(12), + ), + ), + 8.height, + AppState().isArabic(context) + ? getOffersList[0].titleAR!.toText22(isBold: true, color: const Color(0xff2B353E)).center + : getOffersList[0].title!.toText22(isBold: true, color: const Color(0xff2B353E)).center, + Html( + data: AppState().isArabic(context) ? getOffersList[0].descriptionAR! : getOffersList[0].description ?? "", + onLinkTap: (String? url, RenderContext context, Map attributes, _) { + launchUrl(Uri.parse(url!)); + }, + ), + checkDate(getOffersList[0].endDate!).paddingOnly(left: 8), + 10.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + getOffersList[0].discount!.toText16(isBold: true), + InkWell( + onTap: () { + _shareOfferAsImage(); + }, + child: const Icon(Icons.share, color: MyColors.darkIconColor).paddingOnly(bottom: 4)) + ], + ).paddingOnly(left: 8, right: 8), + getOffersList[0].isHasLocation == "true" + ? InkWell( + onTap: () {}, + child: Row( + children: [const Icon(Icons.map_sharp, color: MyColors.darkIconColor).paddingOnly(bottom: 4), "Offer Location".toText16(isUnderLine: true).paddingOnly(left: 8)], + ).paddingOnly(left: 8, right: 8, top: 8), + ) + : 12.height, + ], + ), + ).paddingOnly(left: 21, right: 21, top: 21), + "Related Offers".toText22(isBold: true, color: const Color(0xff2B353E)).paddingAll(21.0), + GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 21), + shrinkWrap: true, + primary: false, + physics: const ScrollPhysics(), + children: getItemsForSaleWidgets(), + ), + 50.height, + ], + ), + ), + ); + } + + void _shareOfferAsImage() async { + try { + RenderRepaintBoundary? boundary = _globalKey.currentContext?.findRenderObject() as RenderRepaintBoundary; + ui.Image? image = await boundary.toImage(pixelRatio: 3.0); + ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); + Uint8List pngBytes = byteData!.buffer.asUint8List(); + + Directory tempDir = await getTemporaryDirectory(); + File file = await File('${tempDir.path}/${DateTime.now().toString()}.png').create(); + await file.writeAsBytes(pngBytes); + await Share.shareFiles([(file.path)], text: AppState().isArabic(context) ? getOffersList[0].titleAR : getOffersList[0].title); + } catch (ex) { + debugPrint(ex.toString()); + } + } + + void _scrollToTop() { + _scrollController.animateTo( + 0, + duration: const Duration(milliseconds: 500), + curve: Curves.linear, + ); + } + + List getItemsForSaleWidgets() { + List itemsList = []; + for (int i = 1; i < 5; i++) { + itemsList.add(getItemCard(getOffersList[i])); + } + return itemsList; + } + + Widget getItemCard(OffersListModel getOffersList) { + return InkWell( + onTap: () { + this.getOffersList[0] = getOffersList; + _scrollToTop(); + setState(() {}); + }, + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Hero( + tag: "ItemImage" + getOffersList.rowID!, + transitionOnUserGestures: true, + child: AspectRatio( + aspectRatio: 148 / 127, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.network( + getOffersList.bannerImage!, + fit: BoxFit.contain, + ), + ), + ), + ), + 5.height, + getOffersList.title!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1), + // Html( + // data: AppState().isArabic(context) ? getOffersList.descriptionAR! : getOffersList.description ?? "", + // // onLinkTap: (String? url, RenderContext context, Map attributes, _) { + // // launchUrl(Uri.parse(url!)); + // // } + // ), + getOffersList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), + 16.height, + getOffersList.discount!.toText14(isBold: true, maxlines: 1), + 10.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [checkDate(getOffersList.endDate!), SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)], + ), + ], + ), + ), + ); + } + + void getOfferLocation() {} + + Widget checkDate(String endDate) { + DateTime endDateObj = DateFormat("yyyy-MM-dd").parse(endDate); + if (endDateObj.isAfter(DateTime.now())) { + return "Offer Valid".toText16(isBold: true, color: MyColors.greenColor); + } else { + return "Offer Expired".toText16(isBold: true, color: MyColors.redColor); + } + } +} diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart new file mode 100644 index 0000000..bdf6ed5 --- /dev/null +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_home.dart @@ -0,0 +1,271 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_categories_list.dart'; +import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; + +class OffersAndDiscountsHome extends StatefulWidget { + const OffersAndDiscountsHome({Key? key}) : super(key: key); + + @override + State createState() => _OffersAndDiscountsHomeState(); +} + +class _OffersAndDiscountsHomeState extends State { + List getCategoriesList = []; + List getOffersList = []; + + int currentCategoryID = 0; + + @override + void initState() { + getCategoriesListAPI(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget( + context, + // title: LocaleKeys.mowadhafhiRequest.tr(), + title: "Offers & Discounts", + showHomeButton: true, + ), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + DynamicTextFieldWidget( + "Search", + "Search Items", + isEnable: true, + suffixIconData: Icons.search, + isPopup: false, + lines: 1, + isInputTypeNum: false, + isReadOnly: false, + onChange: (String value) { + // _runFilter(value); + }, + ).paddingOnly(left: 21, right: 21, top: 21, bottom: 18), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Browse Categories".toText17(), + IconButton( + icon: const Icon(Icons.filter_alt_sharp, color: MyColors.darkIconColor, size: 28.0), + onPressed: () => Navigator.pop(context), + ), + ], + ).paddingOnly(left: 21, right: 21), + SizedBox( + height: 110.0, + child: getCategoriesList.isNotEmpty + ? ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 1 / 1, + child: InkWell( + onTap: () { + setState(() { + currentCategoryID = getCategoriesList[index].id!; + // getItemsForSaleList.clear(); + // currentPageNo = 1; + // getItemsForSale(currentPageNo, currentCategoryID); + }); + }, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.string( + getCategoriesList[index].content!, + fit: BoxFit.contain, + width: 25, + height: 25, + ), + currentCategoryID == getCategoriesList[index].id ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(), + ], + ).expanded, + AppState().isArabic(context) ? getCategoriesList[index].categoryNameAr!.toText10(maxLine: 1) : getCategoriesList[index].categoryNameEn!.toText10(maxLine: 1) + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ), + ), + ); + }, + separatorBuilder: (cxt, index) => 12.width, + itemCount: getCategoriesList.length, + ) + : Container(), + ), + getOffersList.isNotEmpty + ? GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 21), + shrinkWrap: true, + primary: false, + physics: const ScrollPhysics(), + children: getItemsForSaleWidgets(), + ) + : Utils.getNoDataWidget(context).paddingOnly(top: 50), + ], + ), + ), + ); + } + + List getItemsForSaleWidgets() { + List itemsList = []; + + for (var element in getOffersList) { + itemsList.add(getItemCard(element)); + } + + return itemsList; + } + + Widget getItemCard(OffersListModel getOffersList) { + return InkWell( + onTap: () { + navigateToDetails(getOffersList); + }, + child: Container( + padding: const EdgeInsets.all(10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Hero( + tag: "ItemImage" + getOffersList.rowID!, + transitionOnUserGestures: true, + child: AspectRatio( + aspectRatio: 148 / 127, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.network( + getOffersList.bannerImage!, + fit: BoxFit.contain, + ), + ), + ), + ), + 10.height, + AppState().isArabic(context) ? getOffersList.titleAR!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1) : getOffersList.title!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1), + // Html( + // data: AppState().isArabic(context) ? getOffersList.descriptionAR! : getOffersList.description ?? "", + // // onLinkTap: (String? url, RenderContext context, Map attributes, _) { + // // launchUrl(Uri.parse(url!)); + // // } + // ), + getOffersList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), + 16.height, + getOffersList.discount!.toText14(isBold: true, maxlines: 1), + 10.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [checkDate(getOffersList.endDate!), SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)], + ), + ], + ), + ), + ); + } + + void navigateToDetails(OffersListModel offersListModelObj) { + List getOffersDetailList = []; + getOffersDetailList.clear(); + int counter = 1; + + getOffersDetailList.add(offersListModelObj); + + getOffersList.forEach((element) { + if(counter <= 4) { + if(element.rowID != offersListModelObj.rowID) { + getOffersDetailList.add(element); + counter++; + } + } + }); + + Navigator.pushNamed(context, AppRoutes.offersAndDiscountsDetails, arguments: getOffersDetailList); + } + + Widget checkDate(String endDate) { + DateTime endDateObj = DateFormat("yyyy-MM-dd").parse(endDate); + if (endDateObj.isAfter(DateTime.now())) { + return "Offer Valid".toText14(isBold: true, color: MyColors.greenColor); + } else { + return "Offer Expired".toText14(isBold: true, color: MyColors.redColor); + } + } + + void getCategoriesListAPI() async { + try { + Utils.showLoading(context); + getCategoriesList = await OffersAndDiscountsApiClient().getSaleCategories(); + Utils.hideLoading(context); + setState(() {}); + getCategoryOffersListAPI(); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + void getCategoryOffersListAPI() async { + try { + Utils.showLoading(context); + getOffersList = await OffersAndDiscountsApiClient().getOffersList(currentCategoryID, 100); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/widgets/shimmer/offers_shimmer_widget.dart b/lib/widgets/shimmer/offers_shimmer_widget.dart new file mode 100644 index 0000000..8e090d1 --- /dev/null +++ b/lib/widgets/shimmer/offers_shimmer_widget.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; + +class OffersShimmerWidget extends StatelessWidget { + const OffersShimmerWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + child: SizedBox( + width: 73, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 73, + height: 73, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(100), + ), + border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + ), + child: ClipRRect( + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + child: Image.network( + "https://play-lh.googleusercontent.com/NPo88ojmhah4HDiposucJmfQIop4z4xc8kqJK9ITO9o-yCab2zxIp7PPB_XPj2iUojo", + fit: BoxFit.cover, + ).toShimmer(), + ), + ), + 8.height, + Container( + child: "Offers And Discounts".toText12(isCenter: true, maxLine: 1).toShimmer(), + ), + ], + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 8346c5c..fcd8742 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,12 +71,8 @@ dependencies: # flutter_barcode_scanner: ^2.0.0 qr_code_scanner: ^1.0.0 qr_flutter: ^4.0.0 - - - - - url_launcher: ^6.0.15 + share: 2.0.4 dev_dependencies: flutter_test: