import 'package:badges/badges.dart' as badges; import 'package:customer/constant/constant.dart'; import 'package:customer/controllers/home_e_commerce_controller.dart'; import 'package:customer/controllers/theme_controller.dart'; import 'package:customer/models/advertisement_model.dart'; import 'package:customer/models/banner_model.dart'; import 'package:customer/models/brands_model.dart'; import 'package:customer/models/favourite_model.dart'; import 'package:customer/models/product_model.dart'; import 'package:customer/models/user_model.dart'; import 'package:customer/models/vendor_category_model.dart'; import 'package:customer/models/vendor_model.dart'; import 'package:customer/screen_ui/auth_screens/login_screen.dart'; import 'package:customer/screen_ui/ecommarce/all_brand_product_screen.dart'; import 'package:customer/screen_ui/ecommarce/all_category_product_screen.dart'; import 'package:customer/screen_ui/location_enable_screens/address_list_screen.dart'; import 'package:customer/screen_ui/multi_vendor_service/advertisement_screens/all_advertisement_screen.dart'; import 'package:customer/screen_ui/multi_vendor_service/cart_screen/cart_screen.dart'; import 'package:customer/screen_ui/multi_vendor_service/home_screen/category_restaurant_screen.dart'; import 'package:customer/screen_ui/multi_vendor_service/home_screen/restaurant_list_screen.dart' show RestaurantListScreen; import 'package:customer/screen_ui/multi_vendor_service/home_screen/view_all_category_screen.dart'; import 'package:customer/screen_ui/multi_vendor_service/restaurant_details_screen/restaurant_details_screen.dart'; import 'package:customer/screen_ui/multi_vendor_service/search_screen/search_screen.dart'; import 'package:customer/service/fire_store_utils.dart'; import 'package:customer/themes/app_them_data.dart'; import 'package:customer/themes/responsive.dart'; import 'package:customer/themes/round_button_border.dart'; import 'package:customer/themes/show_toast_dialog.dart'; import 'package:customer/themes/text_field_widget.dart'; import 'package:customer/utils/network_image_widget.dart'; import 'package:customer/widget/osm_map/map_picker_page.dart'; import 'package:customer/widget/place_picker/location_picker_screen.dart'; import 'package:customer/widget/place_picker/selected_location_model.dart'; import 'package:customer/widget/video_widget.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart' hide Trans; import 'package:url_launcher/url_launcher.dart'; class HomeECommerceScreen extends StatelessWidget { const HomeECommerceScreen({super.key}); @override Widget build(BuildContext context) { final themeController = Get.find(); final isDark = themeController.isDark.value; return GetX( init: HomeECommerceController(), builder: (controller) { return Scaffold( backgroundColor: isDark ? AppThemeData.grey900 : AppThemeData.grey50, appBar: AppBar( backgroundColor: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300, titleSpacing: 0, leading: InkWell( onTap: () { Get.back(); }, child: Icon( Icons.arrow_back, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, size: 20, ), ), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Constant.userModel == null ? InkWell( onTap: () { Get.offAll(const LoginScreen()); }, child: Text( "Login".tr(), textAlign: TextAlign.center, style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontSize: 12, ), ), ) : Text( Constant.userModel!.fullName(), textAlign: TextAlign.center, style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontSize: 12, ), ), InkWell( onTap: () async { if (Constant.userModel != null) { Get.to(AddressListScreen())!.then((value) { if (value != null) { ShippingAddress shippingAddress = value; Constant.selectedLocation = shippingAddress; controller.getData(); } }); } else { Constant.checkPermission( onTap: () async { ShowToastDialog.showLoader("Please wait...".tr()); // ✅ declare it once here! ShippingAddress shippingAddress = ShippingAddress(); try { await Geolocator.requestPermission(); await Geolocator.getCurrentPosition(); ShowToastDialog.closeLoader(); if (Constant.selectedMapType == 'osm') { final result = await Get.to( () => MapPickerPage(), ); if (result != null) { final firstPlace = result; final lat = firstPlace.coordinates.latitude; final lng = firstPlace.coordinates.longitude; final address = firstPlace.address; shippingAddress.addressAs = "Home"; shippingAddress.locality = address.toString(); shippingAddress.location = UserLocation( latitude: lat, longitude: lng, ); Constant.selectedLocation = shippingAddress; controller.getData(); Get.back(); } } else { Get.to(LocationPickerScreen())!.then(( value, ) async { if (value != null) { SelectedLocationModel selectedLocationModel = value; shippingAddress.addressAs = "Home"; shippingAddress.location = UserLocation( latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude, ); shippingAddress.locality = "Picked from Map"; // You can reverse-geocode Constant.selectedLocation = shippingAddress; controller.getData(); } }); } } catch (e) { await placemarkFromCoordinates( 19.228825, 72.854118, ).then((valuePlaceMaker) { Placemark placeMark = valuePlaceMaker[0]; shippingAddress.location = UserLocation( latitude: 19.228825, longitude: 72.854118, ); String currentLocation = "${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}"; shippingAddress.locality = currentLocation; }); Constant.selectedLocation = shippingAddress; ShowToastDialog.closeLoader(); controller.getData(); } }, context: context, ); } }, child: Text.rich( maxLines: 1, overflow: TextOverflow.ellipsis, TextSpan( children: [ TextSpan( text: Constant.selectedLocation.getFullAddress(), style: AppThemeData.boldTextStyle( color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontSize: 14, ), ), WidgetSpan( child: SvgPicture.asset( "assets/icons/ic_down.svg", colorFilter: ColorFilter.mode( AppThemeData.grey50, BlendMode.srcIn, ), ), ), ], ), ), ), ], ), actions: [ Obx( () => Padding( padding: const EdgeInsets.only(right: 15.0, left: 10), child: badges.Badge( showBadge: true, badgeContent: Text( "${cartItem.length}", style: TextStyle( fontSize: 14, overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, ), ), badgeStyle: badges.BadgeStyle( shape: badges.BadgeShape.circle, badgeColor: AppThemeData.info300, ), child: InkWell( onTap: () async { (await Get.to(const CartScreen())); controller.getCartData(); }, child: ClipOval( child: Container( width: 30, height: 30, decoration: ShapeDecoration( shape: RoundedRectangleBorder( side: BorderSide( width: 1, color: isDark ? AppThemeData.grey700 : AppThemeData.grey200, ), borderRadius: BorderRadius.circular(120), ), ), child: Padding( padding: const EdgeInsets.all(8.0), child: SvgPicture.asset( "assets/icons/ic_shoping_cart.svg", colorFilter: ColorFilter.mode( isDark ? AppThemeData.grey50 : AppThemeData.grey50, BlendMode.srcIn, ), ), ), ), ), ), ), ), ), ], bottom: PreferredSize( preferredSize: Size.fromHeight( 50.0, ), // height of the bottom widget child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), child: InkWell( onTap: () { Get.to( const SearchScreen(), arguments: { "vendorList": controller.allNearestRestaurant, }, ); }, child: TextFieldWidget( hintText: 'Search the store, item and more...'.tr(), controller: null, enable: false, backgroundColor: AppThemeData.grey50, hintColor: isDark ? AppThemeData.grey400 : AppThemeData.grey400, prefix: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SvgPicture.asset( "assets/icons/ic_search.svg", colorFilter: ColorFilter.mode( isDark ? AppThemeData.grey400 : AppThemeData.grey400, BlendMode.srcIn, ), ), ), ), ), ), ), ), body: controller.isLoading.value ? Constant.loader() : SingleChildScrollView( child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ Expanded( child: Text( "Category".tr(), textAlign: TextAlign.start, style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16, ), ), ), InkWell( onTap: () { Get.to(const ViewAllCategoryScreen()); }, child: Text( "View all".tr(), textAlign: TextAlign.start, style: AppThemeData.semiBoldTextStyle( decoration: TextDecoration.underline, color: isDark ? AppThemeData.multiVendorDark300 : AppThemeData.multiVendor300, fontSize: 14, ), ), ), ], ), ), SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox( height: 100, child: ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: controller.vendorCategoryModel.length, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { VendorCategoryModel vendorCategoryModel = controller.vendorCategoryModel[index]; return InkWell( onTap: () { Get.to( const CategoryRestaurantScreen(), arguments: { "vendorCategoryModel": vendorCategoryModel, "dineIn": false, }, ); }, child: Padding( padding: const EdgeInsets.only(right: 18), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ NetworkImageWidget( imageUrl: vendorCategoryModel.photo .toString(), height: 60, width: 60, fit: BoxFit.cover, ), const SizedBox(height: 5), Text( vendorCategoryModel.title .toString(), textAlign: TextAlign.center, style: AppThemeData.mediumTextStyle( color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800, fontSize: 14, ), ), ], ), ), ); }, ), ), ), SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: controller.bannerModel.isEmpty ? const SizedBox() : BannerView(controller: controller), ), Visibility( visible: (Constant.isEnableAdsFeature == true && controller.advertisementList.isNotEmpty), child: const SizedBox(height: 20), ), Visibility( visible: Constant.isEnableAdsFeature == true, child: controller.advertisementList.isEmpty ? const SizedBox() : Container( color: AppThemeData.primary300.withAlpha( 40, ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 16, ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( "Highlights for you".tr(), textAlign: TextAlign.start, style: TextStyle( fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData .grey50 : AppThemeData .grey900, ), ), ), InkWell( onTap: () { Get.to( AllAdvertisementScreen(), )?.then((value) { controller .getFavouriteRestaurant(); }); }, child: Text( "View all".tr(), textAlign: TextAlign.center, style: TextStyle( fontFamily: AppThemeData.regular, color: isDark ? AppThemeData .primary300 : AppThemeData .primary300, ), ), ), ], ), const SizedBox(height: 16), SizedBox( height: 220, child: ListView.builder( physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, itemCount: controller .advertisementList .length >= 10 ? 10 : controller .advertisementList .length, padding: EdgeInsets.all(0), itemBuilder: ( BuildContext context, int index, ) { return AdvertisementHomeCard( controller: controller, model: controller .advertisementList[index], ); }, ), ), ], ), ), ), ), const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( "New Arrivals".tr(), textAlign: TextAlign.start, style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16, ), ), ), SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox( height: 380, child: GridView.count( crossAxisCount: 2, // 2 columns mainAxisSpacing: 0, crossAxisSpacing: 20, childAspectRatio: 1 / 1.1, padding: EdgeInsets.zero, physics: NeverScrollableScrollPhysics(), children: controller.newArrivalRestaurantList .take(4) .map( (item) => NewArrivalCard(item: item), ) .toList(), ), ), ), SizedBox(height: 5), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: RoundedButtonBorder( radius: 10, color: isDark ? AppThemeData.greyDark100 : AppThemeData.grey100, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, title: 'View All Arrivals'.tr(), onPress: () { Get.to( RestaurantListScreen(), arguments: { "vendorList": controller.newArrivalRestaurantList, "title": "New Arrivals".tr(), }, ); }, ), ), SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Top Brands".tr(), textAlign: TextAlign.start, style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16, ), ), SizedBox(height: 10), GridView.builder( padding: EdgeInsets.zero, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, childAspectRatio: 4.5 / 6, crossAxisSpacing: 2, ), itemCount: controller.brandList.length, physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { BrandsModel brandModel = controller.brandList[index]; return InkWell( onTap: () { Get.to( AllBrandProductScreen(), arguments: {"brandModel": brandModel}, ); }, child: Column( children: [ Container( width: 80, height: 80, decoration: ShapeDecoration( color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder( side: BorderSide( width: 1, strokeAlign: BorderSide .strokeAlignOutside, color: isDark ? AppThemeData.grey800 : AppThemeData .grey100, ), borderRadius: BorderRadius.circular(10), ), ), child: Padding( padding: const EdgeInsets.all(10), child: ClipOval( child: NetworkImageWidget( imageUrl: brandModel.photo .toString(), fit: BoxFit.cover, ), ), ), ), SizedBox(height: 5), Text( '${brandModel.title}', textAlign: TextAlign.center, maxLines: 2, style: AppThemeData.mediumTextStyle( color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, ), ), ], ), ); }, ), ], ), ), SizedBox(height: 10), ListView.builder( shrinkWrap: true, physics: const BouncingScrollPhysics(), padding: EdgeInsets.zero, itemCount: controller.categoryWiseProductList.length, itemBuilder: (context, index) { VendorCategoryModel item = controller.categoryWiseProductList[index]; String imagePath = [ "assets/images/ic_product_bg_1.png", "assets/images/ic_product_bg_2.png", "assets/images/ic_product_bg_3.png", ][index % ["", "", ""].length]; return Container( width: Responsive.width(100, context), decoration: BoxDecoration( image: DecorationImage( image: AssetImage(imagePath), fit: BoxFit.fill, ), ), child: Padding( padding: const EdgeInsets.only( left: 16, right: 16, top: 10, bottom: 20, ), child: FutureBuilder>( future: FireStoreUtils.getProductListByCategoryId( item.id.toString(), ), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center( child: CircularProgressIndicator.adaptive( valueColor: AlwaysStoppedAnimation( AppThemeData.primary300, ), ), ); } else if ((snapshot.hasData || (snapshot.data?.isNotEmpty ?? false))) { List productList = snapshot.data!; return snapshot.data!.isEmpty ? Container() : Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( item.title.toString(), textAlign: TextAlign.start, style: AppThemeData.boldTextStyle( color: AppThemeData .grey900, fontSize: 18, ), ), Text( "Style up with the latest fits, now at unbeatable prices." .tr(), textAlign: TextAlign.start, style: AppThemeData.regularTextStyle( color: AppThemeData .grey900, fontSize: 12, ), ), SizedBox(height: 20), GridView.builder( shrinkWrap: true, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 3.5 / 6, crossAxisSpacing: 10, ), padding: EdgeInsets.zero, physics: NeverScrollableScrollPhysics(), itemCount: productList.length > 6 ? 6 : productList.length, itemBuilder: ( context, index, ) { ProductModel productModel = productList[index]; return FutureBuilder( future: FireStoreUtils.getVendorById( productModel .vendorID .toString(), ), builder: ( context, vendorSnapshot, ) { if (!vendorSnapshot .hasData || vendorSnapshot .connectionState == ConnectionState .waiting) { return const SizedBox(); // Show placeholder or loader } VendorModel? vendorModel = vendorSnapshot.data; String price = "0.0"; String disPrice = "0.0"; List selectedVariants = []; List selectedIndexVariants = []; List selectedIndexArray = []; if (productModel .itemAttribute != null) { if (productModel .itemAttribute! .attributes! .isNotEmpty) { for (var element in productModel .itemAttribute! .attributes!) { if (element .attributeOptions! .isNotEmpty) { selectedVariants.add( productModel .itemAttribute! .attributes![productModel .itemAttribute! .attributes! .indexOf( element, )] .attributeOptions![0] .toString(), ); selectedIndexVariants .add( '${productModel.itemAttribute!.attributes!.indexOf(element)} _${productModel.itemAttribute!.attributes![0].attributeOptions![0].toString()}', ); selectedIndexArray .add( '${productModel.itemAttribute!.attributes!.indexOf(element)}_0', ); } } } if (productModel .itemAttribute! .variants! .where( (element) => element .variantSku == selectedVariants .join( '-', ), ) .isNotEmpty) { price = Constant.productCommissionPrice( vendorModel!, productModel .itemAttribute! .variants! .where( ( element, ) => element.variantSku == selectedVariants.join( '-', ), ) .first .variantPrice ?? '0', ); disPrice = "0"; } } else { price = Constant.productCommissionPrice( vendorModel!, productModel .price .toString(), ); disPrice = double.parse( productModel .disPrice .toString(), ) <= 0 ? "0" : Constant.productCommissionPrice( vendorModel, productModel .disPrice .toString(), ); } return GestureDetector( onTap: () async { Get.to( const RestaurantDetailsScreen(), arguments: { "vendorModel": vendorModel, }, ); }, child: Column( crossAxisAlignment: CrossAxisAlignment .start, children: [ ClipRRect( borderRadius: BorderRadius.circular( 10, ), child: SizedBox( height: 90, width: Responsive.width( 100, context, ), child: NetworkImageWidget( imageUrl: productModel .photo .toString(), fit: BoxFit .cover, ), ), ), Column( mainAxisAlignment: MainAxisAlignment .start, crossAxisAlignment: CrossAxisAlignment .start, children: [ Text( productModel .name! .capitalizeString(), textAlign: TextAlign .start, maxLines: 1, style: AppThemeData.semiBoldTextStyle( fontSize: 18, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600, ), ), disPrice == "" || disPrice == "0" ? Text( Constant.amountShow( amount: price, ), style: AppThemeData.semiBoldTextStyle( fontSize: 16, color: AppThemeData.primary300, ), ) : Column( children: [ Text( Constant.amountShow( amount: price, ), style: AppThemeData.semiBoldTextStyle( fontSize: 14, color: Colors.grey, decoration: TextDecoration.lineThrough, ), ), const SizedBox( width: 5, ), Text( Constant.amountShow( amount: disPrice, ), style: AppThemeData.semiBoldTextStyle( fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, ), ), ], ), Container( decoration: BoxDecoration( color: isDark ? AppThemeData.warning50 : AppThemeData.warning50, borderRadius: BorderRadius.circular( 30, ), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.star, size: 18, color: AppThemeData.warning400, ), Text( "${Constant.calculateReview(reviewCount: productModel.reviewsCount.toString(), reviewSum: productModel.reviewsSum.toString())} (${productModel.reviewsSum})", style: AppThemeData.semiBoldTextStyle( fontSize: 12, color: AppThemeData.warning400, ), ), ], ), ), ), ], ), ], ), ); }, ); }, ), RoundedButtonBorder( radius: 10, color: isDark ? AppThemeData .greyDark100 : AppThemeData .grey100, borderColor: isDark ? AppThemeData .greyDark200 : AppThemeData .grey200, title: 'View All Products', onPress: () { Get.to( AllCategoryProductScreen(), arguments: { "categoryModel": item, }, ); }, ), ], ); } else { return SizedBox(); } }, ), ), ); }, ), SizedBox(height: 10), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: controller.bannerModel.isEmpty ? const SizedBox() : BannerBottomView(controller: controller), ), // Visibility( // visible: (Constant.isEnableAdsFeature == true && controller.advertisementList.isNotEmpty), // child: const SizedBox(height: 20), // ), // Visibility( // visible: Constant.isEnableAdsFeature == true, // child: // controller.advertisementList.isEmpty // ? const SizedBox() // : Container( // color: AppThemeData.primary300.withAlpha(40), // child: Padding( // padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), // child: Column( // mainAxisAlignment: MainAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Row( // children: [ // Expanded( // child: Text( // "Highlights for you".tr(), // textAlign: TextAlign.start, // style: TextStyle( // fontFamily: AppThemeData.semiBold, // fontSize: 16, // color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, // ), // ), // ), // InkWell( // onTap: () { // Get.to(AllAdvertisementScreen())?.then((value) { // controller.getFavouriteRestaurant(); // }); // }, // child: Text( // "View all".tr(), // textAlign: TextAlign.center, // style: TextStyle( // fontFamily: AppThemeData.regular, // color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, // ), // ), // ), // ], // ), // const SizedBox(height: 16), // SizedBox( // height: 220, // child: ListView.builder( // physics: const BouncingScrollPhysics(), // scrollDirection: Axis.horizontal, // itemCount: // controller.advertisementList.length >= 10 // ? 10 // : controller.advertisementList.length, // padding: EdgeInsets.all(0), // itemBuilder: (BuildContext context, int index) { // return AdvertisementHomeCard( // controller: controller, // model: controller.advertisementList[index], // ); // }, // ), // ), // ], // ), // ), // ), // ), const SizedBox(height: 20), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "All Store".tr(), textAlign: TextAlign.start, style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16, ), ), SizedBox(height: 10), ListView.builder( padding: EdgeInsets.zero, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: controller.allNearestRestaurant.length > 8 ? 8 : controller .allNearestRestaurant .length, itemBuilder: (context, index) { VendorModel item = controller.allNearestRestaurant[index]; return InkWell( onTap: () { Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": item}, ); }, child: Padding( padding: const EdgeInsets.only( bottom: 20, ), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(10), child: NetworkImageWidget( imageUrl: item.photo.toString(), height: 80, width: 130, fit: BoxFit.cover, ), ), SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item.title.toString(), style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData .greyDark900 : AppThemeData .grey900, fontSize: 16, ), ), Row( children: [ Icon( Icons.location_on, size: 14, color: Colors.grey, ), SizedBox(width: 4), Expanded( child: Text( item.location .toString(), style: AppThemeData.semiBoldTextStyle( fontSize: 12, color: isDark ? AppThemeData .greyDark500 : AppThemeData .grey500, ), overflow: TextOverflow .ellipsis, ), ), ], ), Container( decoration: BoxDecoration( color: isDark ? AppThemeData .warning50 : AppThemeData .warning50, borderRadius: BorderRadius.circular( 30, ), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 6, ), child: Row( mainAxisAlignment: MainAxisAlignment .center, crossAxisAlignment: CrossAxisAlignment .center, mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.star, size: 18, color: AppThemeData .warning400, ), Text( "${Constant.calculateReview(reviewCount: item.reviewsCount.toString(), reviewSum: item.reviewsSum.toString())} (${item.reviewsSum})", style: AppThemeData.semiBoldTextStyle( fontSize: 12, color: AppThemeData .warning400, ), ), ], ), ), ), ], ), ), ], ), ), ); }, ), RoundedButtonBorder( radius: 10, color: isDark ? AppThemeData.greyDark100 : AppThemeData.grey100, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, title: 'View All Stores'.tr(), onPress: () { Get.to( const RestaurantListScreen(), arguments: { "vendorList": controller.allNearestRestaurant, }, ); }, ), ], ), ), ], ), ), ), ); }, ); } } class NewArrivalCard extends StatelessWidget { final VendorModel item; const NewArrivalCard({super.key, required this.item}); @override Widget build(BuildContext context) { final themeController = Get.find(); final isDark = themeController.isDark.value; return InkWell( onTap: () { Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": item}, ); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(10), child: NetworkImageWidget( height: 100, width: double.infinity, fit: BoxFit.cover, imageUrl: item.photo != null && item.photo!.isNotEmpty ? item.photo.toString() : Constant.placeHolderImage.toString(), ), ), SizedBox(height: 5), Text( item.title.toString(), style: AppThemeData.semiBoldTextStyle( color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 14, ), ), Row( children: [ Icon(Icons.location_on, size: 14, color: Colors.grey), SizedBox(width: 4), Expanded( child: Text( item.location.toString(), style: AppThemeData.semiBoldTextStyle( fontSize: 12, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, ), overflow: TextOverflow.ellipsis, ), ), ], ), Container( decoration: BoxDecoration( color: isDark ? AppThemeData.warning50 : AppThemeData.warning50, borderRadius: BorderRadius.circular(30), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.star, size: 18, color: AppThemeData.warning400), Text( "${Constant.calculateReview(reviewCount: item.reviewsCount.toString(), reviewSum: item.reviewsSum.toString())} (${item.reviewsSum})", style: AppThemeData.semiBoldTextStyle( fontSize: 12, color: AppThemeData.warning400, ), ), ], ), ), ), ], ), ); } } class BannerView extends StatelessWidget { final HomeECommerceController controller; const BannerView({super.key, required this.controller}); @override Widget build(BuildContext context) { return Column( children: [ SizedBox( height: 160, child: PageView.builder( physics: const BouncingScrollPhysics(), controller: controller.pageController.value, scrollDirection: Axis.horizontal, itemCount: controller.bannerModel.length, padEnds: false, pageSnapping: true, allowImplicitScrolling: true, onPageChanged: (value) { controller.currentPage.value = value; }, itemBuilder: (BuildContext context, int index) { BannerModel bannerModel = controller.bannerModel[index]; return InkWell( onTap: () async { if (bannerModel.redirect_type == "store") { ShowToastDialog.showLoader("Please wait...".tr()); VendorModel? vendorModel = await FireStoreUtils.getVendorById( bannerModel.redirect_id.toString(), ); ShowToastDialog.closeLoader(); Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel}, ); } else if (bannerModel.redirect_type == "product") { ShowToastDialog.showLoader("Please wait...".tr()); ProductModel? productModel = await FireStoreUtils.getProductById( bannerModel.redirect_id.toString(), ); VendorModel? vendorModel = await FireStoreUtils.getVendorById( productModel!.vendorID.toString(), ); ShowToastDialog.closeLoader(); Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel}, ); } else if (bannerModel.redirect_type == "external_link") { final uri = Uri.parse(bannerModel.redirect_id.toString()); if (await canLaunchUrl(uri)) { await launchUrl(uri); } else { ShowToastDialog.showToast("Could not launch".tr()); } } }, child: Padding( padding: const EdgeInsets.only(right: 14), child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(12)), child: NetworkImageWidget( imageUrl: bannerModel.photo.toString(), fit: BoxFit.cover, ), ), ), ); }, ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: List.generate(controller.bannerModel.length, (index) { return Obx( () => Container( margin: const EdgeInsets.only(right: 5), alignment: Alignment.centerLeft, height: 9, width: 9, decoration: BoxDecoration( shape: BoxShape.circle, color: controller.currentPage.value == index ? AppThemeData.primary300 : Colors.black12, ), ), ); }), ), ), ], ); } } class BannerBottomView extends StatelessWidget { final HomeECommerceController controller; const BannerBottomView({super.key, required this.controller}); @override Widget build(BuildContext context) { return Column( children: [ SizedBox( height: 150, child: PageView.builder( physics: const BouncingScrollPhysics(), controller: controller.pageBottomController.value, scrollDirection: Axis.horizontal, itemCount: controller.bannerBottomModel.length, padEnds: false, pageSnapping: true, allowImplicitScrolling: true, onPageChanged: (value) { controller.currentBottomPage.value = value; }, itemBuilder: (BuildContext context, int index) { BannerModel bannerModel = controller.bannerBottomModel[index]; return InkWell( onTap: () async { if (bannerModel.redirect_type == "store") { ShowToastDialog.showLoader("Please wait...".tr()); VendorModel? vendorModel = await FireStoreUtils.getVendorById( bannerModel.redirect_id.toString(), ); ShowToastDialog.closeLoader(); Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel}, ); } else if (bannerModel.redirect_type == "product") { ShowToastDialog.showLoader("Please wait...".tr()); ProductModel? productModel = await FireStoreUtils.getProductById( bannerModel.redirect_id.toString(), ); VendorModel? vendorModel = await FireStoreUtils.getVendorById( productModel!.vendorID.toString(), ); ShowToastDialog.closeLoader(); Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel}, ); } else if (bannerModel.redirect_type == "external_link") { final uri = Uri.parse(bannerModel.redirect_id.toString()); if (await canLaunchUrl(uri)) { await launchUrl(uri); } else { ShowToastDialog.showToast("Could not launch".tr()); } } }, child: Padding( padding: const EdgeInsets.only(right: 14), child: ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(12)), child: NetworkImageWidget( imageUrl: bannerModel.photo.toString(), fit: BoxFit.cover, ), ), ), ); }, ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: List.generate(controller.bannerBottomModel.length, ( index, ) { return Obx( () => Container( margin: const EdgeInsets.only(right: 5), alignment: Alignment.centerLeft, height: 9, width: 9, decoration: BoxDecoration( shape: BoxShape.circle, color: controller.currentBottomPage.value == index ? AppThemeData.primary300 : Colors.black12, ), ), ); }), ), ), ], ); } } class AdvertisementHomeCard extends StatelessWidget { final AdvertisementModel model; final HomeECommerceController controller; const AdvertisementHomeCard({ super.key, required this.controller, required this.model, }); @override Widget build(BuildContext context) { final themeController = Get.find(); final isDark = themeController.isDark.value; return InkWell( onTap: () async { ShowToastDialog.showLoader("Please wait...".tr()); VendorModel? vendorModel = await FireStoreUtils.getVendorById( model.vendorId!, ); ShowToastDialog.closeLoader(); Get.to( const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel}, ); }, child: Container( margin: EdgeInsets.only(right: 16), width: Responsive.width(70, context), decoration: BoxDecoration( color: isDark ? AppThemeData.info600 : AppThemeData.surface, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: isDark ? 6 : 2, spreadRadius: 0, offset: Offset(0, isDark ? 3 : 1), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack( children: [ model.type == 'restaurant_promotion' ? ClipRRect( borderRadius: BorderRadius.vertical( top: Radius.circular(16), ), child: NetworkImageWidget( imageUrl: model.coverImage ?? '', height: 135, width: double.infinity, fit: BoxFit.cover, ), ) : VideoAdvWidget( url: model.video ?? '', height: 135, width: double.infinity, ), if (model.type != 'video_promotion' && model.vendorId != null && (model.showRating == true || model.showReview == true)) Positioned( bottom: 8, right: 8, child: FutureBuilder( future: FireStoreUtils.getVendorById(model.vendorId!), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const SizedBox(); } else { if (snapshot.hasError) { return const SizedBox(); } else if (snapshot.data == null) { return const SizedBox(); } else { VendorModel vendorModel = snapshot.data!; return Container( decoration: ShapeDecoration( color: isDark ? AppThemeData.primary600 : AppThemeData.primary50, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(120), ), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8, ), child: Row( children: [ if (model.showRating == true) SvgPicture.asset( "assets/icons/ic_star.svg", colorFilter: ColorFilter.mode( AppThemeData.primary300, BlendMode.srcIn, ), ), if (model.showRating == true) const SizedBox(width: 5), Text( "${model.showRating == true ? Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString()) : ''} ${model.showReview == true ? '(${vendorModel.reviewsCount!.toStringAsFixed(0)})' : ''}", style: TextStyle( fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, ), ), ], ), ), ); } } }, ), ), ], ), Padding( padding: EdgeInsets.all(12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (model.type == 'restaurant_promotion') ClipRRect( borderRadius: BorderRadius.circular(30), child: NetworkImageWidget( imageUrl: model.profileImage ?? '', height: 50, width: 50, fit: BoxFit.cover, ), ), SizedBox(width: 8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( model.title ?? '', style: TextStyle( color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 14, fontWeight: FontWeight.bold, ), overflow: TextOverflow.ellipsis, ), Text( model.description ?? '', style: TextStyle( fontSize: 12, fontFamily: AppThemeData.medium, color: isDark ? AppThemeData.grey400 : AppThemeData.grey600, ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ], ), ), model.type == 'restaurant_promotion' ? IconButton( icon: Obx( () => controller.favouriteList .where( (p0) => p0.restaurantId == model.vendorId, ) .isNotEmpty ? SvgPicture.asset( "assets/icons/ic_like_fill.svg", ) : SvgPicture.asset( "assets/icons/ic_like.svg", colorFilter: ColorFilter.mode( isDark ? AppThemeData.grey400 : AppThemeData.grey600, BlendMode.srcIn, ), ), ), onPressed: () async { if (controller.favouriteList .where((p0) => p0.restaurantId == model.vendorId) .isNotEmpty) { FavouriteModel favouriteModel = FavouriteModel( restaurantId: model.vendorId, userId: FireStoreUtils.getCurrentUid(), ); controller.favouriteList.removeWhere( (item) => item.restaurantId == model.vendorId, ); await FireStoreUtils.removeFavouriteRestaurant( favouriteModel, ); } else { FavouriteModel favouriteModel = FavouriteModel( restaurantId: model.vendorId, userId: FireStoreUtils.getCurrentUid(), ); controller.favouriteList.add(favouriteModel); await FireStoreUtils.setFavouriteRestaurant( favouriteModel, ); } controller.update(); }, ) : Container( decoration: ShapeDecoration( color: isDark ? AppThemeData.primary600 : AppThemeData.primary50, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), child: Icon( Icons.arrow_forward, size: 20, color: AppThemeData.primary300, ), ), ), ], ), ), ], ), ), ); } }