INFRA: Set Up Project.

This commit is contained in:
2025-11-28 11:10:49 +05:00
commit c798279f7d
609 changed files with 77436 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/advertisement_list_controller.dart';
import 'package:customer/models/advertisement_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../../widget/video_widget.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class AllAdvertisementScreen extends StatelessWidget {
const AllAdvertisementScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: AdvertisementListController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
"Highlights for you".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
body:
controller.isLoading.value
? Constant.loader()
: controller.advertisementList.isEmpty
? Constant.showEmptyView(message: "Highlights for you not found.".tr)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView.builder(
physics: const BouncingScrollPhysics(),
itemCount: controller.advertisementList.length,
padding: EdgeInsets.all(0),
itemBuilder: (BuildContext context, int index) {
return AdvertisementCard(controller: controller, model: controller.advertisementList[index]);
},
),
),
);
},
);
}
}
class AdvertisementCard extends StatelessWidget {
final AdvertisementModel model;
final AdvertisementListController controller;
const AdvertisementCard({super.key, required this.controller, required this.model});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
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(bottom: 16),
width: Responsive.width(80, 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: 150, width: double.infinity, fit: BoxFit.cover),
)
: VideoAdvWidget(url: model.video ?? '', height: 150, 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.showRating == true && model.showReview == true ? ' ' : ''}${model.showReview == true ? '(${vendorModel.reviewsCount!.toStringAsFixed(0)})' : ''}",
style: TextStyle(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: 16, fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
),
Text(
model.description ?? '',
style: TextStyle(fontSize: 14, fontFamily: AppThemeData.medium, color: isDark ? AppThemeData.grey400 : AppThemeData.grey600),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
],
),
),
model.type == 'restaurant_promotion'
? Obx(
() => IconButton(
icon:
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);
}
},
),
)
: 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)),
),
],
),
),
],
),
),
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/cart_controller.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/text_field_widget.dart';
import 'package:customer/widget/my_separator.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
class CouponListScreen extends StatelessWidget {
const CouponListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: CartController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Coupon Code".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(55),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextFieldWidget(
hintText: 'Enter coupon code'.tr,
controller: controller.couponCodeController.value,
suffix: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: InkWell(
onTap: () {
if (controller.couponCodeController.value.text.isEmpty) {
ShowToastDialog.showToast("Please enter coupon code".tr);
return;
}
CouponModel? matchedCoupon = controller.couponList.firstWhereOrNull((coupon) => coupon.code!.toLowerCase() == controller.couponCodeController.value.text.toLowerCase());
if (matchedCoupon != null) {
double couponAmount = Constant.calculateDiscount(amount: controller.subTotal.value.toString(), offerModel: matchedCoupon);
if (couponAmount < controller.subTotal.value) {
controller.selectedCouponModel.value = matchedCoupon;
controller.calculatePrice();
Get.back();
} else {
ShowToastDialog.showToast("Coupon code not applied".tr);
}
} else {
ShowToastDialog.showToast("Invalid Coupon".tr);
}
},
child: Text(
"Apply".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
),
),
),
),
),
),
body: ListView.builder(
shrinkWrap: true,
itemCount: controller.couponList.length,
itemBuilder: (context, index) {
CouponModel couponModel = controller.couponList[index];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Container(
height: Responsive.height(16, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))),
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
child: Stack(
children: [
Image.asset("assets/images/ic_coupon_image.png", height: Responsive.height(16, context), fit: BoxFit.fill),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Align(
alignment: Alignment.center,
child: RotatedBox(
quarterTurns: -1,
child: Text(
"${couponModel.discountType == "Fix Price" ? Constant.amountShow(amount: couponModel.discount) : "${couponModel.discount}%"} ${'Off'.tr}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50),
),
),
),
),
],
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
DottedBorder(
options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(6), color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
"${couponModel.code}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
),
),
const Expanded(child: SizedBox(height: 10)),
InkWell(
onTap: () {
double couponAmount = Constant.calculateDiscount(amount: controller.subTotal.value.toString(), offerModel: couponModel);
if (couponAmount < controller.subTotal.value) {
controller.selectedCouponModel.value = couponModel;
controller.calculatePrice();
Get.back();
} else {
ShowToastDialog.showToast("Coupon code not applied".tr);
}
},
child: Text(
"Tap To Apply".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
),
],
),
const SizedBox(height: 20),
MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
const SizedBox(height: 20),
Text(
"${couponModel.description}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
],
),
),
),
],
),
),
);
},
),
);
},
);
}
}

View File

@@ -0,0 +1,228 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dash_board_controller.dart';
import 'package:customer/controllers/dash_board_ecommarce_controller.dart';
import 'package:customer/controllers/order_placing_controller.dart';
import 'package:customer/models/cart_product_model.dart';
import 'package:customer/screen_ui/ecommarce/dash_board_e_commerce_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../dash_board_screens/dash_board_screen.dart';
class OrderPlacingScreen extends StatelessWidget {
const OrderPlacingScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: OrderPlacingController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, centerTitle: false, titleSpacing: 0),
body:
controller.isLoading.value
? Constant.loader()
: controller.isPlacing.value
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Order Placed".tr,
textAlign: TextAlign.start,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey900, fontSize: 34, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w400),
),
Text(
"Hang tight — your items are being delivered quickly and safely!".tr,
textAlign: TextAlign.start,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontSize: 16, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
const SizedBox(height: 40),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_location.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 10),
Expanded(
child: Text(
"Order ID".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontSize: 16),
),
),
],
),
const SizedBox(height: 5),
Text(
controller.orderModel.value.id.toString(),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
],
),
),
),
const SizedBox(height: 10),
],
),
)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(child: Image.asset("assets/images/ic_timer.gif", height: 140)),
const SizedBox(height: 20),
Text(
"Placing your order".tr,
textAlign: TextAlign.start,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey900, fontSize: 34, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w400),
),
Text(
"Take a moment to review your order before proceeding to checkout.".tr,
textAlign: TextAlign.start,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontSize: 16, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
const SizedBox(height: 40),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_location.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 10),
Expanded(
child: Text(
"Delivery Address".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontSize: 16),
),
),
],
),
const SizedBox(height: 5),
Text(
controller.orderModel.value.address!.getFullAddress(),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
],
),
),
),
const SizedBox(height: 10),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_book.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn), height: 22),
const SizedBox(width: 10),
Expanded(
child: Text(
"Order Summary".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontSize: 16),
),
),
],
),
const SizedBox(height: 5),
ListView.builder(
shrinkWrap: true,
itemCount: controller.orderModel.value.products!.length,
itemBuilder: (context, index) {
CartProductModel cartProductModel = controller.orderModel.value.products![index];
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${cartProductModel.quantity} x".tr,
textAlign: TextAlign.start,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey900, fontSize: 14, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
Text(
"${cartProductModel.name}".tr,
textAlign: TextAlign.start,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey900, fontSize: 14, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
],
);
},
),
],
),
),
),
],
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child:
controller.isPlacing.value
? RoundedButtonFill(
title: "Track Order".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
if (Constant.sectionConstantModel!.serviceTypeFlag == "ecommerce-service") {
Get.offAll(const DashBoardEcommerceScreen());
DashBoardEcommerceController controller = Get.put(DashBoardEcommerceController());
controller.selectedIndex.value = 3;
} else {
Get.offAll(const DashBoardScreen());
DashBoardController controller = Get.put(DashBoardController());
controller.selectedIndex.value = 3;
}
},
)
: RoundedButtonFill(
title: "Track Order".tr,
height: 5.5,
color: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
textColor: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
fontSizes: 16,
onPress: () async {},
),
),
),
);
},
);
}
}

View File

@@ -0,0 +1,284 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/cart_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../wallet_screen/wallet_screen.dart';
class SelectPaymentScreen extends StatelessWidget {
const SelectPaymentScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: CartController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
"Payment Option".tr,
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.medium,
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Preferred Payment".tr,
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
const SizedBox(
height: 10,
),
if (controller.walletSettingModel.value.isEnabled == true || controller.cashOnDeliverySettingModel.value.isEnabled == true)
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
shadows: const [
BoxShadow(
color: Color(0x07000000),
blurRadius: 20,
offset: Offset(0, 0),
spreadRadius: 0,
)
],
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Visibility(
visible: controller.walletSettingModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.wallet, isDark, "assets/images/ic_wallet.png"),
),
Visibility(
visible: controller.cashOnDeliverySettingModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.cod, isDark, "assets/images/ic_cash.png"),
),
],
),
),
),
if (controller.walletSettingModel.value.isEnabled == true || controller.cashOnDeliverySettingModel.value.isEnabled == true)
Column(
children: [
const SizedBox(
height: 10,
),
Text(
"Other Payment Options".tr,
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
const SizedBox(
height: 10,
),
],
),
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
shadows: const [
BoxShadow(
color: Color(0x07000000),
blurRadius: 20,
offset: Offset(0, 0),
spreadRadius: 0,
)
],
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Visibility(
visible: controller.stripeModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.stripe, isDark, "assets/images/stripe.png"),
),
Visibility(
visible: controller.payPalModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.paypal, isDark, "assets/images/paypal.png"),
),
Visibility(
visible: controller.payStackModel.value.isEnable == true,
child: cardDecoration(controller, PaymentGateway.payStack, isDark, "assets/images/paystack.png"),
),
Visibility(
visible: controller.mercadoPagoModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.mercadoPago, isDark, "assets/images/mercado-pago.png"),
),
Visibility(
visible: controller.flutterWaveModel.value.isEnable == true,
child: cardDecoration(controller, PaymentGateway.flutterWave, isDark, "assets/images/flutterwave_logo.png"),
),
Visibility(
visible: controller.payFastModel.value.isEnable == true,
child: cardDecoration(controller, PaymentGateway.payFast, isDark, "assets/images/payfast.png"),
),
Visibility(
visible: controller.razorPayModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.razorpay, isDark, "assets/images/razorpay.png"),
),
Visibility(
visible: controller.midTransModel.value.enable == true,
child: cardDecoration(controller, PaymentGateway.midTrans, isDark, "assets/images/midtrans.png"),
),
Visibility(
visible: controller.orangeMoneyModel.value.enable == true,
child: cardDecoration(controller, PaymentGateway.orangeMoney, isDark, "assets/images/orange_money.png"),
),
Visibility(
visible: controller.xenditModel.value.enable == true,
child: cardDecoration(controller, PaymentGateway.xendit, isDark, "assets/images/xendit.png"),
),
],
),
),
)
],
),
),
),
bottomNavigationBar: Container(
decoration: BoxDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20))),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "${'Pay Now'.tr} | ${Constant.amountShow(amount: controller.totalAmount.value.toString())}".tr,
height: 5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
Get.back();
},
),
),
),
);
},
);
}
Obx cardDecoration(CartController controller, PaymentGateway value, isDark, String image) {
return Obx(
() => Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Column(
children: [
InkWell(
onTap: () {
controller.selectedPaymentMethod.value = value.name;
},
child: Row(
children: [
Container(
width: 50,
height: 50,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: const BorderSide(width: 1, color: Color(0xFFE5E7EB)),
borderRadius: BorderRadius.circular(8),
),
),
child: Padding(
padding: EdgeInsets.all(value.name == "payFast" ? 0 : 8.0),
child: Image.asset(
image,
),
),
),
const SizedBox(
width: 10,
),
value.name == "wallet"
? Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
value.name.capitalizeString(),
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.medium,
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
Text(
Constant.amountShow(amount: controller.userModel.value.walletAmount == null ? '0.0' : controller.userModel.value.walletAmount.toString()),
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
fontSize: 16,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
),
),
],
),
)
: Expanded(
child: Text(
value.name.capitalizeString(),
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.medium,
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
),
const Expanded(
child: SizedBox(),
),
Radio(
value: value.name,
groupValue: controller.selectedPaymentMethod.value,
activeColor: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
onChanged: (value) {
controller.selectedPaymentMethod.value = value.toString();
},
)
],
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/cashback_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
class CashbackOffersListScreen extends StatelessWidget {
const CashbackOffersListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: CashbackController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
title: Text("Cashback Offers".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
),
body:
controller.isLoading.value
? Constant.loader()
: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16),
itemCount: controller.cashbackList.length,
itemBuilder: (BuildContext context, int index) {
return Container(
padding: const EdgeInsets.all(16),
margin: const EdgeInsets.symmetric(vertical: 6),
decoration: BoxDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
borderRadius: BorderRadius.circular(12),
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 6, offset: const Offset(0, 3))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
controller.cashbackList[index].title ?? '',
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
Text(
controller.cashbackList[index].cashbackType == 'Percent'
? "${controller.cashbackList[index].cashbackAmount}%"
: Constant.amountShow(amount: "${controller.cashbackList[index].cashbackAmount}"),
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
],
),
const SizedBox(height: 6),
Text(
"${"Min spent".tr} ${Constant.amountShow(amount: "${controller.cashbackList[index].minimumPurchaseAmount ?? 0.0}")} | ${"Valid till".tr} ${Constant.timestampToDateTime2(controller.cashbackList[index].endDate!)}",
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontSize: 14),
),
Text(
"${"Maximum cashback up to".tr} ${Constant.amountShow(amount: "${controller.cashbackList[index].maximumDiscount ?? 0.0}")}",
style: TextStyle(color: isDark ? AppThemeData.primary200 : AppThemeData.primary300, fontFamily: AppThemeData.regular, fontSize: 14),
),
],
),
);
},
),
);
},
);
}
}

View File

@@ -0,0 +1,100 @@
import 'dart:convert';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/change_language_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:customer/utils/preferences.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/localization_service.dart';
class ChangeLanguageScreen extends StatelessWidget {
const ChangeLanguageScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ChangeLanguageController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, centerTitle: false, titleSpacing: 0),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Change Language".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"Select your preferred language for a personalized app experience.".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
const SizedBox(height: 20),
Expanded(
child: GridView.count(
crossAxisCount: 2,
childAspectRatio: (1.1 / 1),
crossAxisSpacing: 5,
mainAxisSpacing: 1,
children:
controller.languageList
.map(
(data) => Obx(
() => GestureDetector(
onTap: () {
LocalizationService().changeLocale(data.slug.toString());
Preferences.setString(Preferences.languageCodeKey, jsonEncode(data));
controller.selectedLanguage.value = data;
},
child: Container(
padding: const EdgeInsets.all(16),
child: Column(
children: [
NetworkImageWidget(imageUrl: data.image.toString(), height: 80, width: 80),
// SvgPicture.network(
// data.image.toString(),
// height: 80,
// width: 80,
// fit: BoxFit.contain,
// placeholderBuilder: (context) => const Center(child: CircularProgressIndicator(strokeWidth: 1.5)),
// ),
const SizedBox(height: 5),
Text(
"${data.title}",
style: TextStyle(
fontSize: 16,
color:
controller.selectedLanguage.value.slug == data.slug
? AppThemeData.primary300
: isDark
? AppThemeData.grey400
: AppThemeData.grey500,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w400,
),
),
],
),
),
),
),
)
.toList(),
),
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,9 @@
import 'package:customer/models/conversation_model.dart';
class ChatVideoContainer {
Url videoUrl;
String thumbnailUrl;
ChatVideoContainer({required this.videoUrl, required this.thumbnailUrl});
}

View File

@@ -0,0 +1,310 @@
import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/chat_controller.dart';
import 'package:customer/models/conversation_model.dart';
import 'package:customer/themes/app_them_data.dart';
import '../../../controllers/theme_controller.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import '../../../service/fire_store_utils.dart';
import '../../../widget/firebase_pagination/src/fireStore_pagination.dart';
import '../../../widget/firebase_pagination/src/models/view_type.dart';
import 'ChatVideoContainer.dart';
import 'full_screen_image_viewer.dart';
import 'full_screen_video_viewer.dart';
class ChatScreen extends StatelessWidget {
const ChatScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ChatController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
controller.restaurantName.value,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
body: Column(
children: [
Expanded(
child: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: FirestorePagination(
controller: controller.scrollController,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, documentSnapshots, index) {
ConversationModel inboxModel = ConversationModel.fromJson(documentSnapshots[index].data() as Map<String, dynamic>);
return chatItemView(isDark, inboxModel.senderId == FireStoreUtils.getCurrentUid(), inboxModel);
},
onEmpty: Constant.showEmptyView(message: "No Conversion found".tr),
// orderBy is compulsory to enable pagination
query: FirebaseFirestore.instance
.collection(
controller.chatType.value == "Driver"
? 'chat_driver'
: controller.chatType.value == "Provider" || controller.chatType.value == "provider"
? 'chat_provider'
: controller.chatType.value == "worker" || controller.chatType.value == "Worker"
? 'chat_worker'
: 'chat_store',
)
.doc(controller.orderId.value)
.collection("thread")
.orderBy('createdAt', descending: false),
isLive: true,
viewType: ViewType.list,
),
),
),
Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () {
onCameraClick(context, controller);
},
child: SvgPicture.asset("assets/icons/ic_picture_one.svg"),
),
Flexible(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: TextField(
textInputAction: TextInputAction.send,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
controller: controller.messageController.value,
decoration: InputDecoration(
contentPadding: const EdgeInsets.only(top: 3, left: 10),
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
hintText: 'Type message here....'.tr,
),
onSubmitted: (value) async {
if (controller.messageController.value.text.isNotEmpty) {
controller.sendMessage(controller.messageController.value.text, null, '', 'text');
Timer(const Duration(milliseconds: 500), () => controller.scrollController.jumpTo(controller.scrollController.position.maxScrollExtent));
controller.messageController.value.clear();
}
},
),
),
),
InkWell(
onTap: () {
if (controller.messageController.value.text.isNotEmpty) {
controller.sendMessage(controller.messageController.value.text, null, '', 'text');
controller.messageController.value.clear();
// Timer(const Duration(milliseconds: 500), () => controller.scrollController.jumpTo(controller.scrollController.position.maxScrollExtent));
}
},
child: Container(
margin: const EdgeInsets.only(left: 10),
decoration: BoxDecoration(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200, borderRadius: BorderRadius.circular(30)),
child: Padding(padding: const EdgeInsets.all(10), child: SvgPicture.asset("assets/icons/ic_send.svg")),
),
),
],
),
const SizedBox(height: 20),
],
),
),
),
],
),
);
},
);
}
Widget chatItemView(isDark, bool isMe, ConversationModel data) {
return Container(
padding: const EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 10),
child:
isMe
? Align(
alignment: Alignment.topRight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
data.messageType == "text"
? Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomLeft: Radius.circular(12)),
color: AppThemeData.primary300,
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(data.message.toString(), style: const TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: AppThemeData.grey50)),
)
: data.messageType == "image"
? ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomLeft: Radius.circular(12)),
child: Stack(
alignment: Alignment.center,
children: [
GestureDetector(
onTap: () {
Get.to(FullScreenImageViewer(imageUrl: data.url!.url));
},
child: Hero(tag: data.url!.url, child: NetworkImageWidget(imageUrl: data.url!.url, height: 100, width: 100, fit: BoxFit.cover)),
),
],
),
)
: FloatingActionButton(
mini: true,
heroTag: data.id,
backgroundColor: AppThemeData.primary300,
onPressed: () {
Get.to(FullScreenVideoViewer(heroTag: data.id.toString(), videoUrl: data.url!.url));
},
child: const Icon(Icons.play_arrow, color: Colors.white),
),
const SizedBox(height: 5),
Text(
DateFormat('MMM d, yyyy hh:mm aa').format(DateTime.fromMillisecondsSinceEpoch(data.createdAt!.millisecondsSinceEpoch)),
style: const TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
data.messageType == "text"
? Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomRight: Radius.circular(12)),
color: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(data.message.toString(), style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey100 : AppThemeData.grey800)),
)
: data.messageType == "image"
? ConstrainedBox(
constraints: const BoxConstraints(minWidth: 50, maxWidth: 200),
child: ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12), bottomRight: Radius.circular(12)),
child: Stack(
alignment: Alignment.center,
children: [
GestureDetector(
onTap: () {
Get.to(FullScreenImageViewer(imageUrl: data.url!.url));
},
child: Hero(tag: data.url!.url, child: NetworkImageWidget(imageUrl: data.url!.url)),
),
],
),
),
)
: FloatingActionButton(
mini: true,
heroTag: data.id,
backgroundColor: AppThemeData.primary300,
onPressed: () {
Get.to(FullScreenVideoViewer(heroTag: data.id.toString(), videoUrl: data.url!.url));
},
child: const Icon(Icons.play_arrow, color: Colors.white),
),
const SizedBox(height: 5),
Text(
DateFormat('MMM d, yyyy hh:mm aa').format(DateTime.fromMillisecondsSinceEpoch(data.createdAt!.millisecondsSinceEpoch)),
style: const TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
);
}
void onCameraClick(BuildContext context, ChatController controller) {
final action = CupertinoActionSheet(
message: Text('Send Media'.tr, style: const TextStyle(fontSize: 15.0)),
actions: <Widget>[
CupertinoActionSheetAction(
isDefaultAction: false,
onPressed: () async {
Get.back();
XFile? image = await controller.imagePicker.pickImage(source: ImageSource.gallery);
if (image != null) {
Url url = await FireStoreUtils.uploadChatImageToFireStorage(File(image.path), context);
controller.sendMessage('', url, '', 'image');
}
},
child: Text("Choose image from gallery".tr),
),
CupertinoActionSheetAction(
isDefaultAction: false,
onPressed: () async {
Get.back();
XFile? galleryVideo = await controller.imagePicker.pickVideo(source: ImageSource.gallery);
if (galleryVideo != null) {
ChatVideoContainer? videoContainer = await FireStoreUtils.uploadChatVideoToFireStorage(context, File(galleryVideo.path));
if (videoContainer != null) {
controller.sendMessage('', videoContainer.videoUrl, videoContainer.thumbnailUrl, 'video');
}
}
},
child: Text("Choose video from gallery".tr),
),
CupertinoActionSheetAction(
isDestructiveAction: false,
onPressed: () async {
Get.back();
XFile? image = await controller.imagePicker.pickImage(source: ImageSource.camera);
if (image != null) {
Url url = await FireStoreUtils.uploadChatImageToFireStorage(File(image.path), context);
controller.sendMessage('', url, '', 'image');
}
},
child: Text("Take a picture".tr),
),
// CupertinoActionSheetAction(
// isDestructiveAction: false,
// onPressed: () async {
// Get.back();
// XFile? recordedVideo = await controller.imagePicker.pickVideo(source: ImageSource.camera);
// if (recordedVideo != null) {
// ChatVideoContainer videoContainer = await FireStoreUtils.uploadChatVideoToFireStorage(File(recordedVideo.path), context);
// controller.sendMessage('', videoContainer.videoUrl, videoContainer.thumbnailUrl, 'video');
// }
// },
// child: Text("Record video".tr),
// )
],
cancelButton: CupertinoActionSheetAction(
child: Text('Cancel'.tr),
onPressed: () {
Get.back();
},
),
);
showCupertinoModalPopup(context: context, builder: (context) => action);
}
}

View File

@@ -0,0 +1,127 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/inbox_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../../widget/firebase_pagination/src/fireStore_pagination.dart';
import '../../../widget/firebase_pagination/src/models/view_type.dart';
import 'chat_screen.dart';
class DriverInboxScreen extends StatelessWidget {
const DriverInboxScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Driver Inbox".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
),
body: FirestorePagination(
//item builder type is compulsory.
physics: const BouncingScrollPhysics(),
itemBuilder: (context, documentSnapshots, index) {
final data = documentSnapshots[index].data() as Map<String, dynamic>?;
InboxModel inboxModel = InboxModel.fromJson(data!);
return InkWell(
onTap: () async {
ShowToastDialog.showLoader("Please wait...".tr);
UserModel? customer = await FireStoreUtils.getUserProfile(inboxModel.customerId.toString());
UserModel? restaurantUser = await FireStoreUtils.getUserProfile(inboxModel.restaurantId.toString());
ShowToastDialog.closeLoader();
Get.to(
const ChatScreen(),
arguments: {
"customerName": customer!.fullName(),
"restaurantName": restaurantUser!.fullName(),
"orderId": inboxModel.orderId,
"restaurantId": restaurantUser.id,
"customerId": customer.id,
"customerProfileImage": customer.profilePictureURL,
"restaurantProfileImage": restaurantUser.profilePictureURL,
"token": restaurantUser.fcmToken,
"chatType": inboxModel.chatType,
},
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 5),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: NetworkImageWidget(
imageUrl: inboxModel.restaurantProfileImage.toString(),
fit: BoxFit.cover,
height: Responsive.height(6, context),
width: Responsive.width(12, context),
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"${inboxModel.restaurantName}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey100 : AppThemeData.grey800),
),
),
Text(
Constant.timestampToDate(inboxModel.createdAt!),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.regular, fontSize: 16, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
],
),
const SizedBox(height: 5),
Text(
"${inboxModel.lastMessage}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 14, color: isDark ? AppThemeData.grey200 : AppThemeData.grey700),
),
],
),
),
],
),
),
),
),
);
},
shrinkWrap: true,
onEmpty: Constant.showEmptyView(message: "No Conversion found".tr),
// orderBy is compulsory to enable pagination
query: FirebaseFirestore.instance.collection('chat_driver').where("customerId", isEqualTo: FireStoreUtils.getCurrentUid()).orderBy('createdAt', descending: true),
//Change types customerId
viewType: ViewType.list,
initialLoader: Constant.loader(),
// to fetch real-time data
isLive: true,
),
);
}
}

View File

@@ -0,0 +1,32 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:photo_view/photo_view.dart';
class FullScreenImageViewer extends StatelessWidget {
final String imageUrl;
final File? imageFile;
const FullScreenImageViewer({super.key, required this.imageUrl, this.imageFile});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.white),
systemOverlayStyle: SystemUiOverlayStyle.light,
),
body: Container(
color: Colors.black,
child: Hero(
tag: imageUrl,
child: PhotoView(
imageProvider: imageFile == null ? NetworkImage(imageUrl) : Image.file(imageFile!).image,
),
),
));
}
}

View File

@@ -0,0 +1,75 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:video_player/video_player.dart';
class FullScreenVideoViewer extends StatefulWidget {
final String videoUrl;
final String heroTag;
final File? videoFile;
const FullScreenVideoViewer({super.key, required this.videoUrl, required this.heroTag, this.videoFile});
@override
_FullScreenVideoViewerState createState() => _FullScreenVideoViewerState();
}
class _FullScreenVideoViewerState extends State<FullScreenVideoViewer> {
late VideoPlayerController _controller;
@override
void initState() {
super.initState();
_controller = widget.videoFile == null ? VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl)) : VideoPlayerController.file(widget.videoFile!)
..initialize().then((_) {
// Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
setState(() {});
});
_controller.setLooping(true);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.white),
systemOverlayStyle: SystemUiOverlayStyle.light,
),
body: Container(
color: Colors.black,
child: Hero(
tag: widget.videoUrl,
child: Center(
child: _controller.value.isInitialized
? AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
)
: Container(),
),
)),
floatingActionButton: FloatingActionButton(
heroTag: widget.heroTag,
onPressed: () {
setState(() {
_controller.value.isPlaying ? _controller.pause() : _controller.play();
});
},
child: Icon(
_controller.value.isPlaying ? CupertinoIcons.pause : CupertinoIcons.play_arrow_solid,
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}

View File

@@ -0,0 +1,129 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/models/inbox_model.dart';
import 'package:customer/models/user_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:customer/widget/firebase_pagination/src/fireStore_pagination.dart';
import 'package:customer/widget/firebase_pagination/src/models/view_type.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import 'chat_screen.dart';
class RestaurantInboxScreen extends StatelessWidget {
const RestaurantInboxScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Store Inbox".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
),
body: FirestorePagination(
//item builder type is compulsory.
physics: const BouncingScrollPhysics(),
itemBuilder: (context, documentSnapshots, index) {
final data = documentSnapshots[index].data() as Map<String, dynamic>?;
InboxModel inboxModel = InboxModel.fromJson(data!);
return InkWell(
onTap: () async {
ShowToastDialog.showLoader("Please wait...".tr);
UserModel? customer = await FireStoreUtils.getUserProfile(inboxModel.customerId.toString());
UserModel? restaurantUser = await FireStoreUtils.getUserProfile(inboxModel.restaurantId.toString());
VendorModel? vendorModel = await FireStoreUtils.getVendorById(restaurantUser!.vendorID.toString());
ShowToastDialog.closeLoader();
Get.to(
const ChatScreen(),
arguments: {
"customerName": customer!.fullName(),
"restaurantName": vendorModel!.title,
"orderId": inboxModel.orderId,
"restaurantId": restaurantUser.id,
"customerId": customer.id,
"customerProfileImage": customer.profilePictureURL,
"restaurantProfileImage": vendorModel.photo,
"token": restaurantUser.fcmToken,
"chatType": inboxModel.chatType,
},
);
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 5),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: NetworkImageWidget(
imageUrl: inboxModel.restaurantProfileImage.toString(),
fit: BoxFit.cover,
height: Responsive.height(6, context),
width: Responsive.width(12, context),
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"${inboxModel.restaurantName}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey100 : AppThemeData.grey800),
),
),
Text(
Constant.timestampToDate(inboxModel.createdAt!),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.regular, fontSize: 16, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
],
),
const SizedBox(height: 5),
Text(
"${inboxModel.lastMessage}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 14, color: isDark ? AppThemeData.grey200 : AppThemeData.grey700),
),
],
),
),
],
),
),
),
),
);
},
shrinkWrap: true,
onEmpty: Constant.showEmptyView(message: "No Conversion found".tr),
// orderBy is compulsory to enable pagination
query: FirebaseFirestore.instance.collection('chat_store').where("customerId", isEqualTo: FireStoreUtils.getCurrentUid()).orderBy('createdAt', descending: true),
//Change types customerId
viewType: ViewType.list,
initialLoader: Constant.loader(),
// to fetch real-time data
isLive: true,
),
);
}
}

View File

@@ -0,0 +1,83 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dash_board_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
class DashBoardScreen extends StatelessWidget {
const DashBoardScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
return Obx(() {
final isDark = themeController.isDark.value;
return GetX(
init: DashBoardController(),
builder: (controller) {
return Scaffold(
body: controller.pageList[controller.selectedIndex.value],
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
showUnselectedLabels: true,
showSelectedLabels: true,
selectedFontSize: 12,
selectedLabelStyle: const TextStyle(fontFamily: AppThemeData.bold),
unselectedLabelStyle: const TextStyle(fontFamily: AppThemeData.bold),
currentIndex: controller.selectedIndex.value,
backgroundColor: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
selectedItemColor: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
unselectedItemColor: isDark ? AppThemeData.grey300 : AppThemeData.grey600,
onTap: (int index) {
if (index == 0) {
Get.put(DashBoardController());
}
controller.selectedIndex.value = index;
},
items:
Constant.walletSetting == false
? [
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home.svg", label: 'Home'.tr, controller: controller),
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_fav.svg", label: 'Favourites'.tr, controller: controller),
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_orders.svg", label: 'Orders'.tr, controller: controller),
navigationBarItem(isDark, index: 3, assetIcon: "assets/icons/ic_profile.svg", label: 'Profile'.tr, controller: controller),
]
: [
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home.svg", label: 'Home'.tr, controller: controller),
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_fav.svg", label: 'Favourites'.tr, controller: controller),
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_wallet.svg", label: 'Wallet'.tr, controller: controller),
navigationBarItem(isDark, index: 3, assetIcon: "assets/icons/ic_orders.svg", label: 'Orders'.tr, controller: controller),
navigationBarItem(isDark, index: 4, assetIcon: "assets/icons/ic_profile.svg", label: 'Profile'.tr, controller: controller),
],
),
);
},
);
});
}
BottomNavigationBarItem navigationBarItem(isDark, {required int index, required String label, required String assetIcon, required DashBoardController controller}) {
return BottomNavigationBarItem(
icon: Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: SvgPicture.asset(
assetIcon,
height: 22,
width: 22,
color:
controller.selectedIndex.value == index
? isDark
? AppThemeData.primary300
: AppThemeData.primary300
: isDark
? AppThemeData.grey300
: AppThemeData.grey600,
),
),
label: label,
);
}
}

View File

@@ -0,0 +1,272 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dine_in_booking_details_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../controllers/theme_controller.dart';
class DineInBookingDetails extends StatelessWidget {
const DineInBookingDetails({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: DineInBookingDetailsController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
title: Text(
"Dine in Bookings".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${'Order'.tr} ${Constant.orderId(orderId: controller.bookingModel.value.id.toString())}",
style: TextStyle(fontSize: 18, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
Text(
"${controller.bookingModel.value.totalGuest} ${'Peoples'.tr}",
style: TextStyle(fontSize: 14, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
],
),
),
Container(
decoration: ShapeDecoration(
color: Constant.statusColor(status: controller.bookingModel.value.status),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Text(
"${controller.bookingModel.value.status}",
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
),
),
],
),
const SizedBox(height: 20),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_building.svg"),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.bookingModel.value.vendor!.title.toString(),
style: TextStyle(fontSize: 18, color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
Text(
controller.bookingModel.value.vendor!.location.toString(),
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
],
),
),
],
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () {
launchUrl(
Constant.createCoordinatesUrl(
controller.bookingModel.value.vendor!.latitude ?? 0.0,
controller.bookingModel.value.vendor!.longitude ?? 0.0,
controller.bookingModel.value.vendor!.title,
),
);
},
child: Text(
"View in Map".tr,
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
decorationColor: AppThemeData.primary300,
),
),
),
const Padding(padding: EdgeInsets.symmetric(horizontal: 16), child: SizedBox(height: 16, child: VerticalDivider(width: 1))),
InkWell(
onTap: () {
if (controller.bookingModel.value.vendor!.phonenumber!.isNotEmpty) {
final Uri launchUri = Uri(scheme: 'tel', path: controller.bookingModel.value.vendor!.phonenumber);
launchUrl(launchUri);
}
},
child: Text(
"Call Now".tr,
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
decorationColor: AppThemeData.primary300,
),
),
),
],
),
const SizedBox(height: 10),
],
),
),
),
const SizedBox(height: 20),
Text(
"Booking Details".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16),
),
const SizedBox(height: 5),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Name".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
),
Expanded(
child: Text(
"${controller.bookingModel.value.guestFirstName} ${controller.bookingModel.value.guestLastName}",
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Phone number".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
),
Expanded(
child: Text(
"${controller.bookingModel.value.guestPhone}",
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Date and Time".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
),
Expanded(
child: Text(
Constant.timestampToDateTime(controller.bookingModel.value.date!),
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Guest".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
),
Expanded(
child: Text(
"${controller.bookingModel.value.totalGuest}",
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Discount".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
),
Expanded(
child: Text(
"${controller.bookingModel.value.discount} %",
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
],
),
),
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,259 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dine_in_booking_controller.dart';
import 'package:customer/models/dine_in_booking_model.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/app_them_data.dart';
import '../../../widget/my_separator.dart';
import 'dine_in_booking_details.dart';
class DineInBookingScreen extends StatelessWidget {
const DineInBookingScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: DineInBookingController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
title: Text(
"Dine in Bookings".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
),
body:
controller.isLoading.value
? Constant.loader()
: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: InkWell(
onTap: () {
controller.isFeature.value = true;
},
child: Container(
decoration:
controller.isFeature.value == false
? null
: ShapeDecoration(color: AppThemeData.primary300, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"Upcoming".tr,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
color:
controller.isFeature.value == false
? isDark
? AppThemeData.grey400
: AppThemeData.grey500
: isDark
? AppThemeData.grey50
: AppThemeData.grey50,
),
),
),
),
),
),
Expanded(
child: InkWell(
onTap: () {
controller.isFeature.value = false;
},
child: Container(
decoration:
controller.isFeature.value == true
? null
: ShapeDecoration(color: AppThemeData.primary300, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"History".tr,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
color:
controller.isFeature.value == true
? isDark
? AppThemeData.grey400
: AppThemeData.grey500
: isDark
? AppThemeData.grey50
: AppThemeData.grey50,
),
),
),
),
),
),
],
),
),
),
),
const SizedBox(height: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child:
controller.isFeature.value
? controller.featureList.isEmpty
? Constant.showEmptyView(message: "Upcoming Booking not found.".tr)
: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: controller.featureList.length,
itemBuilder: (BuildContext context, int index) {
DineInBookingModel dineBookingModel = controller.featureList[index];
return itemView(isDark, context, dineBookingModel);
},
)
: controller.historyList.isEmpty
? Constant.showEmptyView(message: "History not found.".tr)
: ListView.builder(
itemCount: controller.historyList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
DineInBookingModel dineBookingModel = controller.historyList[index];
return itemView(isDark, context, dineBookingModel);
},
),
),
),
],
),
);
},
);
}
InkWell itemView(isDark, BuildContext context, DineInBookingModel orderModel) {
return InkWell(
onTap: () {
Get.to(const DineInBookingDetails(), arguments: {"bookingModel": orderModel});
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: Stack(
children: [
NetworkImageWidget(imageUrl: orderModel.vendor!.photo.toString(), fit: BoxFit.cover, height: Responsive.height(10, context), width: Responsive.width(20, context)),
Container(
height: Responsive.height(10, context),
width: Responsive.width(20, context),
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(0.00, 1.00), end: const Alignment(0, -1), colors: [Colors.black.withOpacity(0), AppThemeData.grey900]),
),
),
],
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
orderModel.status.toString(),
textAlign: TextAlign.right,
style: TextStyle(color: Constant.statusColor(status: orderModel.status.toString()), fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500, fontSize: 12),
),
const SizedBox(height: 5),
Text(
orderModel.vendor!.title.toString(),
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w400),
),
const SizedBox(height: 5),
Text(
Constant.timestampToDateTime(orderModel.createdAt!),
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
],
),
),
],
),
Padding(padding: const EdgeInsets.symmetric(vertical: 14), child: MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200)),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(child: Text("Name".tr, style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400))),
Expanded(
child: Text(
"${orderModel.guestFirstName} ${orderModel.guestLastName}",
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
const SizedBox(height: 5),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text("Guest Number".tr, style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400)),
),
Expanded(
child: Text(
orderModel.totalGuest.toString(),
textAlign: TextAlign.end,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
),
],
),
Padding(padding: const EdgeInsets.symmetric(vertical: 14), child: MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200)),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_location.svg"),
const SizedBox(width: 10),
Expanded(
child: Text(
orderModel.vendor!.location.toString(),
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
),
],
),
const SizedBox(height: 10),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,390 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dine_in_restaurant_details_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/text_field_widget.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../../controllers/theme_controller.dart';
class BookTableScreen extends StatelessWidget {
const BookTableScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: DineInRestaurantDetailsController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
title: Text("Book Table".tr, style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500)),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
Expanded(
child: Text(
"Numbers of Guests".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
),
Container(
height: Responsive.height(4, context),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(200), side: BorderSide(color: isDark ? AppThemeData.grey600 : AppThemeData.grey300)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkWell(
onTap: () {
if (controller.noOfQuantity.value != 1) {
controller.noOfQuantity.value -= 1;
}
},
child: const Icon(Icons.remove),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Text(
controller.noOfQuantity.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey100 : AppThemeData.grey800,
),
),
),
InkWell(
onTap: () {
controller.noOfQuantity.value += 1;
},
child: Icon(Icons.add, color: AppThemeData.primary300),
),
],
),
),
),
],
),
),
),
const SizedBox(height: 10),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"When are you visiting?".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
SizedBox(
height: 120,
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 8),
physics: const BouncingScrollPhysics(),
itemCount: controller.dateList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Stack(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 2.0, vertical: 8),
child: GestureDetector(
onTap: () {
controller.selectedDate.value = controller.dateList[index].date;
controller.timeSet(controller.dateList[index].date);
},
child: Obx(
() => Container(
width: 100,
height: 90,
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
side: BorderSide(
width: 1,
color:
controller.selectedDate.value == controller.dateList[index].date
? AppThemeData.primary300
: isDark
? AppThemeData.grey800
: AppThemeData.grey100,
),
borderRadius: BorderRadius.circular(8),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
Constant.calculateDifference(controller.dateList[index].date.toDate()) == 0
? "Today".tr
: Constant.calculateDifference(controller.dateList[index].date.toDate()) == 1
? "Tomorrow".tr
: DateFormat('EEE').format(controller.dateList[index].date.toDate()),
style: TextStyle(
fontSize: 12,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w500,
),
),
Text(
DateFormat('d MMM').format(controller.dateList[index].date.toDate()).toString(),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
),
),
Positioned(
bottom: 10,
left: 0,
right: 0,
child: Center(
child: RoundedButtonFill(
title: "${controller.dateList[index].discountPer}%".tr,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
width: 12,
height: 3,
onPress: () {},
),
),
),
],
);
},
),
),
const SizedBox(height: 10),
Text(
"Select time slot and scroll to see offers".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(side: BorderSide(width: 1, color: isDark ? AppThemeData.grey600 : AppThemeData.grey300), borderRadius: BorderRadius.circular(12)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
child: Wrap(
spacing: 5.0,
children: <Widget>[
...controller.timeSlotList.map(
(timeSlotList) => InputChip(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
side: BorderSide.none,
backgroundColor: isDark ? AppThemeData.grey800 : AppThemeData.grey100,
selectedColor: AppThemeData.primary300,
labelStyle: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800),
label: Text(
DateFormat('hh:mm a').format(timeSlotList.time!),
style: TextStyle(
color:
controller.selectedTimeSlot.value == DateFormat('hh:mm a').format(timeSlotList.time!)
? AppThemeData.grey50
: isDark
? AppThemeData.grey400
: AppThemeData.grey500,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
),
),
showCheckmark: false,
selected: controller.selectedTimeSlot.value == DateFormat('hh:mm a').format(timeSlotList.time!),
onSelected: (value) {
controller.selectedTimeSlot.value = DateFormat('hh:mm a').format(timeSlotList.time!);
controller.selectedTimeDiscount.value = timeSlotList.discountPer!;
controller.selectedTimeDiscountType.value = timeSlotList.discountType!;
},
),
),
],
),
),
),
],
),
),
),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: Text(
"Special Occasion".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
),
InkWell(
onTap: () {
controller.selectedOccasion.value = "";
},
child: Text("Clear".tr, style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500)),
),
],
),
const SizedBox(height: 10),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < controller.occasionList[i].length; i++)
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.0),
visualDensity: const VisualDensity(horizontal: 0, vertical: -4),
dense: true,
title: Text(
//'${controller.occasionList[i]}'.tr,
controller.getLocalizedOccasion(controller.occasionList[i]),
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
leading: Radio<String>(
visualDensity: const VisualDensity(horizontal: 0, vertical: -4),
value: controller.occasionList[i],
groupValue: controller.selectedOccasion.value,
activeColor: AppThemeData.primary300,
onChanged: (value) {
controller.selectedOccasion.value = controller.occasionList[i];
},
),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.0),
visualDensity: const VisualDensity(horizontal: 0, vertical: -4),
dense: true,
title: Text(
'Is this your first visit?'.tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
leading: Checkbox(
visualDensity: const VisualDensity(horizontal: 0, vertical: -4),
value: controller.firstVisit.value,
activeColor: AppThemeData.primary300,
onChanged: (value) {
controller.firstVisit.value = value!;
},
),
),
],
),
),
),
const SizedBox(height: 10),
Text(
"Personal Details".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ClipOval(
child: NetworkImageWidget(
imageUrl: Constant.userModel!.profilePictureURL.toString(),
width: 50,
height: 50,
errorWidget: Image.asset(Constant.userPlaceHolder, fit: BoxFit.cover, width: 50, height: 50),
),
),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Constant.userModel!.fullName(),
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"${Constant.userModel!.email}",
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
],
),
],
),
),
),
const SizedBox(height: 10),
Text(
"Additional Requests".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
TextFieldWidget(controller: controller.additionRequestController.value, hintText: 'Add message here....'.tr, maxLine: 5),
const SizedBox(height: 20),
],
),
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Book Now".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
controller.orderBook();
},
),
),
),
);
},
);
}
}

View File

@@ -0,0 +1,768 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dine_in_restaurant_details_controller.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../chat_screens/full_screen_image_viewer.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher.dart';
import '../review_list_screen/review_list_screen.dart';
import 'book_table_screen.dart';
class DineInDetailsScreen extends StatelessWidget {
const DineInDetailsScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: DineInRestaurantDetailsController(),
builder: (controller) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: Responsive.height(30, context),
floating: true,
pinned: true,
automaticallyImplyLeading: false,
backgroundColor: AppThemeData.primary300,
title: Row(
children: [
InkWell(
onTap: () {
Get.back();
},
child: Icon(Icons.arrow_back, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50),
),
const Expanded(child: SizedBox()),
InkWell(
onTap: () async {
if (controller.favouriteList.where((p0) => p0.restaurantId == controller.vendorModel.value.id).isNotEmpty) {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: controller.vendorModel.value.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.removeWhere((item) => item.restaurantId == controller.vendorModel.value.id);
await FireStoreUtils.removeFavouriteRestaurant(favouriteModel);
} else {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: controller.vendorModel.value.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.add(favouriteModel);
await FireStoreUtils.setFavouriteRestaurant(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteList.where((p0) => p0.restaurantId == controller.vendorModel.value.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg", colorFilter: const ColorFilter.mode(AppThemeData.grey50, BlendMode.srcIn))
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
],
),
flexibleSpace: FlexibleSpaceBar(
background: Stack(
children: [
controller.vendorModel.value.photos == null || controller.vendorModel.value.photos!.isEmpty
? Stack(
children: [
NetworkImageWidget(
imageUrl: controller.vendorModel.value.photo.toString(),
fit: BoxFit.cover,
width: Responsive.width(100, context),
height: Responsive.height(40, context),
),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(0.00, -1.00), end: const Alignment(0, 1), colors: [Colors.black.withOpacity(0), Colors.black]),
),
),
],
)
: PageView.builder(
physics: const BouncingScrollPhysics(),
controller: controller.pageController.value,
scrollDirection: Axis.horizontal,
itemCount: controller.vendorModel.value.photos!.length,
padEnds: false,
pageSnapping: true,
onPageChanged: (value) {
controller.currentPage.value = value;
},
itemBuilder: (BuildContext context, int index) {
String image = controller.vendorModel.value.photos![index];
return Stack(
children: [
NetworkImageWidget(imageUrl: image.toString(), fit: BoxFit.cover, width: Responsive.width(100, context), height: Responsive.height(40, context)),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(0.00, -1.00), end: const Alignment(0, 1), colors: [Colors.black.withOpacity(0), Colors.black]),
),
),
],
);
},
),
Positioned(
bottom: 10,
right: 0,
left: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: List.generate(controller.vendorModel.value.photos!.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 : AppThemeData.grey300),
),
);
}),
),
),
],
),
),
),
];
},
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.vendorModel.value.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 22,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
SizedBox(
width: Responsive.width(78, context),
child: Text(
controller.vendorModel.value.location.toString(),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500, color: isDark ? AppThemeData.grey400 : AppThemeData.grey400),
),
),
],
),
),
Column(
children: [
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.primary600 : AppThemeData.primary50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
Constant.calculateReview(
reviewCount: controller.vendorModel.value.reviewsCount!.toStringAsFixed(0),
reviewSum: controller.vendorModel.value.reviewsSum.toString(),
),
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
),
const SizedBox(height: 5),
InkWell(
onTap: () {
Get.to(const ReviewListScreen(), arguments: {"vendorModel": controller.vendorModel.value});
},
child: Text(
"${controller.vendorModel.value.reviewsCount} ${'Ratings'.tr}",
style: TextStyle(decoration: TextDecoration.underline, color: isDark ? AppThemeData.grey200 : AppThemeData.grey700, fontFamily: AppThemeData.regular),
),
),
],
),
],
),
Row(
children: [
Text(
controller.isOpen.value ? "Open".tr : "Close".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 14,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: controller.isOpen.value ? AppThemeData.success400 : AppThemeData.danger300,
),
),
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Icon(Icons.circle, size: 5, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500)),
InkWell(
onTap: () {
timeShowBottomSheet(context, controller);
},
child: Text(
"View Timings".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 14,
decoration: TextDecoration.underline,
decorationColor: AppThemeData.ecommerce300,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
),
),
),
Padding(padding: const EdgeInsets.symmetric(horizontal: 10), child: Icon(Icons.circle, size: 5, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500)),
Text(
"${Constant.amountShow(amount: controller.vendorModel.value.restaurantCost)} ${'for two'.tr}".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 14,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
"Also applicable on food delivery".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
const SizedBox(height: 10),
InkWell(
onTap: () {
if (Constant.userModel == null) {
ShowToastDialog.showToast("Please log in to the application. You are not logged in.".tr);
} else {
Get.to(const BookTableScreen(), arguments: {"vendorModel": controller.vendorModel.value});
}
},
child: Container(
height: 80,
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: isDark ? AppThemeData.grey900 : AppThemeData.grey50),
borderRadius: BorderRadius.circular(16),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey800 : AppThemeData.grey100,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: Image.asset("assets/images/ic_table.gif"),
),
const SizedBox(width: 10),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Table Booking".tr,
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
Text(
"Quick Conformations".tr,
style: TextStyle(
fontSize: 12,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
),
),
],
),
),
const Icon(Icons.chevron_right),
],
),
),
),
),
const SizedBox(height: 10),
InkWell(
onTap: () {
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": controller.vendorModel.value});
},
child: Container(
height: 80,
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1, color: isDark ? AppThemeData.grey900 : AppThemeData.grey50),
borderRadius: BorderRadius.circular(16),
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey800 : AppThemeData.grey100,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: Padding(padding: const EdgeInsets.all(4), child: Image.asset("assets/images/food_delivery.gif")),
),
const SizedBox(width: 10),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Available food delivery".tr,
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
Text(
"in 30-45 mins.".tr,
style: TextStyle(
fontSize: 12,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
),
),
],
),
),
const Icon(Icons.chevron_right),
],
),
),
),
),
],
),
controller.vendorModel.value.restaurantMenuPhotos == null || controller.vendorModel.value.restaurantMenuPhotos!.isEmpty
? const SizedBox()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
"Menu".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
SizedBox(
height: Responsive.height(12, context),
child: ListView.builder(
itemCount: controller.vendorModel.value.restaurantMenuPhotos!.length,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Get.to(FullScreenImageViewer(imageUrl: controller.vendorModel.value.restaurantMenuPhotos![index]));
},
child: Padding(
padding: const EdgeInsets.all(6.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: NetworkImageWidget(
imageUrl: controller.vendorModel.value.restaurantMenuPhotos![index],
height: Responsive.height(12, context),
width: Responsive.height(12, context),
fit: BoxFit.fill,
),
),
),
);
},
),
),
],
),
const SizedBox(height: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Location, Timing & Costs".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
const SizedBox(height: 20),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_location.svg"),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
controller.vendorModel.value.location.toString(),
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w400,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
),
),
InkWell(
onTap: () {
launchUrl(
Constant.createCoordinatesUrl(
controller.vendorModel.value.latitude ?? 0.0,
controller.vendorModel.value.longitude ?? 0.0,
controller.vendorModel.value.title,
),
);
},
child: Text(
"View on Map".tr,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
),
),
),
],
),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset("assets/icons/ic_alarm_clock.svg", height: 20),
const SizedBox(width: 14),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Timing".tr,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w400,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
),
),
InkWell(
onTap: () {},
child: Text(
"${controller.vendorModel.value.openDineTime == '' ? "10:00 AM" : controller.vendorModel.value.openDineTime.toString()} ${"To".tr} ${controller.vendorModel.value.closeDineTime == '' ? "10:00 PM" : controller.vendorModel.value.closeDineTime.toString()}",
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
),
),
),
],
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
Constant.currencyModel!.symbol.toString(),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w400, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
const SizedBox(width: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cost for Two".tr,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w400,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey500,
),
),
Text(
"${Constant.amountShow(amount: controller.vendorModel.value.restaurantCost ?? "0.0")} ${'(approx)'.tr}",
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 16,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
),
),
],
),
],
),
],
),
const SizedBox(height: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Cuisines".tr,
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
const SizedBox(height: 10),
Wrap(
spacing: 5.0,
children: <Widget>[
...controller.tags.map(
(tag) => FilterChip(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
side: BorderSide.none,
backgroundColor: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
labelStyle: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800),
label: Text("$tag"),
onSelected: (bool value) {},
),
),
],
),
],
),
],
),
),
],
),
),
),
),
);
},
);
}
Future timeShowBottomSheet(BuildContext context, DineInRestaurantDetailsController productModel) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(30))),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder:
(context) => FractionallySizedBox(
heightFactor: 0.70,
child: StatefulBuilder(
builder: (context1, setState) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Center(
child: Container(
width: 134,
height: 5,
margin: const EdgeInsets.only(bottom: 6),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey50 : AppThemeData.grey800, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3))),
),
),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemCount: productModel.vendorModel.value.workingHours!.length,
itemBuilder: (context, dayIndex) {
WorkingHours workingHours = productModel.vendorModel.value.workingHours![dayIndex];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${workingHours.day}",
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
const SizedBox(height: 10),
workingHours.timeslot == null || workingHours.timeslot!.isEmpty
? const SizedBox()
: ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: workingHours.timeslot!.length,
itemBuilder: (context, timeIndex) {
Timeslot timeSlotModel = workingHours.timeslot![timeIndex];
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(12)),
border: Border.all(color: isDark ? AppThemeData.grey400 : AppThemeData.grey200),
),
child: Center(
child: Text(
timeSlotModel.from.toString(),
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 14, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
),
),
),
const SizedBox(width: 10),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(12)),
border: Border.all(color: isDark ? AppThemeData.grey400 : AppThemeData.grey200),
),
child: Center(
child: Text(
timeSlotModel.to.toString(),
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 14, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
),
),
),
],
),
);
},
),
],
),
);
},
),
),
],
),
),
);
},
),
),
);
}
}

View File

@@ -0,0 +1,356 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/restaurant_list_controller.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/fire_store_utils.dart';
import '../../../widget/restaurant_image_view.dart';
import 'dine_in_details_screen.dart';
class DineInRestaurantListScreen extends StatelessWidget {
const DineInRestaurantListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: RestaurantListController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor:
isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
controller.title.value,
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.medium,
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView.builder(
shrinkWrap: true,
itemCount: controller.vendorSearchList.length,
itemBuilder: (context, index) {
VendorModel vendorModel =
controller.vendorSearchList[index];
return InkWell(
onTap: () {
Get.to(
const DineInDetailsScreen(),
arguments: {"vendorModel": vendorModel},
);
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(
color:
isDark
? AppThemeData.grey900
: AppThemeData.grey50,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
child: Stack(
children: [
RestaurantImageView(
vendorModel: vendorModel,
),
Container(
height: Responsive.height(
20,
context,
),
width: Responsive.width(
100,
context,
),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(
-0.00,
-1.00,
),
end: const Alignment(0, 1),
colors: [
Colors.black.withOpacity(0),
const Color(0xFF111827),
],
),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteList
.where(
(p0) =>
p0.restaurantId ==
vendorModel.id,
)
.isNotEmpty) {
FavouriteModel
favouriteModel =
FavouriteModel(
restaurantId:
vendorModel.id,
userId:
FireStoreUtils
.getCurrentUid(),
);
controller.favouriteList
.removeWhere(
(item) =>
item.restaurantId ==
vendorModel.id,
);
await FireStoreUtils
.removeFavouriteRestaurant(
favouriteModel,
);
} else {
FavouriteModel
favouriteModel =
FavouriteModel(
restaurantId:
vendorModel.id,
userId:
FireStoreUtils
.getCurrentUid(),
);
controller.favouriteList
.add(favouriteModel);
await FireStoreUtils
.setFavouriteRestaurant(
favouriteModel,
);
}
},
child: Obx(
() =>
controller.favouriteList
.where(
(p0) =>
p0.restaurantId ==
vendorModel
.id,
)
.isNotEmpty
? SvgPicture.asset(
"assets/icons/ic_like_fill.svg",
)
: SvgPicture.asset(
"assets/icons/ic_like.svg",
),
),
),
),
],
),
),
Transform.translate(
offset: Offset(
Responsive.width(-3, context),
Responsive.height(17.5, context),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
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: [
SvgPicture.asset(
"assets/icons/ic_star.svg",
colorFilter:
ColorFilter.mode(
AppThemeData
.primary300,
BlendMode.srcIn,
),
),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(
color:
isDark
? AppThemeData
.primary300
: AppThemeData
.primary300,
fontFamily:
AppThemeData
.semiBold,
fontWeight:
FontWeight.w600,
),
),
],
),
),
),
const SizedBox(width: 10),
Container(
decoration: ShapeDecoration(
color:
isDark
? AppThemeData
.ecommerce600
: AppThemeData
.ecommerce50,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(
120,
),
),
),
child: Padding(
padding:
const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
child: Row(
children: [
SvgPicture.asset(
"assets/icons/ic_map_distance.svg",
colorFilter:
ColorFilter.mode(
AppThemeData
.ecommerce300,
BlendMode.srcIn,
),
),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(
color:
isDark
? AppThemeData
.ecommerce300
: AppThemeData
.ecommerce300,
fontFamily:
AppThemeData
.semiBold,
fontWeight:
FontWeight.w600,
),
),
],
),
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 18,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
color:
isDark
? AppThemeData.grey50
: AppThemeData.grey900,
),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color:
isDark
? AppThemeData.grey400
: AppThemeData.grey400,
),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
),
),
);
},
);
}
}

View File

@@ -0,0 +1,830 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dine_in_controller.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/dine_in_screeen/view_all_category_dine_in_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../controllers/theme_controller.dart';
import '../../../models/banner_model.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../../widget/restaurant_image_view.dart';
import '../home_screen/category_restaurant_screen.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
import 'dine_in_details_screen.dart';
import 'dine_in_restaurant_list_screen.dart';
class DineInScreen extends StatelessWidget {
const DineInScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: DineInController(),
builder: (controller) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: Responsive.height(38, context),
floating: true,
pinned: true,
automaticallyImplyLeading: false,
backgroundColor: AppThemeData.primary300,
title: Row(
children: [
InkWell(
onTap: () {
Get.back();
},
child: Icon(Icons.arrow_back, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50),
),
],
),
flexibleSpace: FlexibleSpaceBar(
background: Stack(
children: [
Image.asset("assets/images/dine_in_bg.png", fit: BoxFit.fill, width: Responsive.width(100, context)),
Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"Dine-In Reservations".tr,
style: TextStyle(fontSize: 24, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, color: isDark ? AppThemeData.grey900 : AppThemeData.grey900),
),
Text(
"Book a table at your favorite restaurant and enjoy a delightful dining experience.".tr,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.grey900 : AppThemeData.grey900),
),
],
),
),
),
],
),
),
),
];
},
body:
controller.isLoading.value
? Constant.loader()
: Constant.isZoneAvailable == false || controller.allNearestRestaurant.isEmpty
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset("assets/images/location.gif", height: 120),
const SizedBox(height: 12),
Text("No Store Found in Your Area".tr, style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontSize: 22, fontFamily: AppThemeData.semiBold)),
const SizedBox(height: 5),
Text(
"Currently, there are no available store in your zone. Try changing your location to find nearby options.".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey500, fontSize: 16, fontFamily: AppThemeData.bold),
),
const SizedBox(height: 20),
RoundedButtonFill(
title: "Change Zone".tr,
width: 55,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
onPress: () async {
Get.offAll(const LocationPermissionScreen());
},
),
],
),
)
: SingleChildScrollView(
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 10),
titleView(isDark, "Explore the Categories".tr, () {
Get.to(const ViewAllCategoryDineInScreen());
}),
const SizedBox(height: 10),
CategoryView(controller: controller),
const SizedBox(height: 28),
],
),
),
controller.newArrivalRestaurantList.isEmpty
? const SizedBox()
: Container(
decoration: const BoxDecoration(image: DecorationImage(image: AssetImage("assets/images/ic_new_arrival_dinein.png"), fit: BoxFit.cover)),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
"New Arrivals".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50),
),
),
InkWell(
onTap: () {
Get.to(const DineInRestaurantListScreen(), arguments: {"vendorList": controller.newArrivalRestaurantList, "title": "New Arrival"});
},
child: Text(
"View all".tr,
textAlign: TextAlign.center,
style: TextStyle(fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
),
],
),
const SizedBox(height: 16),
NewArrival(controller: controller),
],
),
),
),
controller.bannerBottomModel.isEmpty
? const SizedBox()
: Padding(padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), child: BannerBottomView(controller: controller)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: InkWell(
onTap: () {
controller.isPopular.value = true;
},
child: Container(
decoration:
controller.isPopular.value == false
? null
: ShapeDecoration(color: AppThemeData.grey900, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"Popular Stores".tr,
textAlign: TextAlign.center,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
),
),
),
),
Expanded(
child: InkWell(
onTap: () {
controller.isPopular.value = false;
},
child: Container(
decoration:
controller.isPopular.value == true
? null
: ShapeDecoration(color: AppThemeData.grey900, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"All Stores".tr,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
color:
controller.isPopular.value == true
? isDark
? AppThemeData.grey400
: AppThemeData.grey500
: isDark
? AppThemeData.primary300
: AppThemeData.primary300,
),
),
),
),
),
),
],
),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: controller.isPopular.value ? PopularRestaurant(controller: controller) : AllRestaurant(controller: controller),
),
],
),
),
),
);
},
);
}
Row titleView(isDark, String name, Function()? onPress) {
return Row(
children: [
Expanded(child: Text(name, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.bold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900))),
InkWell(
onTap: () {
onPress!();
},
child: Text("View all".tr, textAlign: TextAlign.center, style: TextStyle(fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300)),
),
],
);
}
}
class PopularRestaurant extends StatelessWidget {
final DineInController controller;
const PopularRestaurant({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: controller.popularRestaurantList.length,
itemBuilder: (BuildContext context, int index) {
VendorModel vendorModel = controller.popularRestaurantList[index];
return InkWell(
onTap: () {
Get.to(const DineInDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
child: Stack(
children: [
RestaurantImageView(vendorModel: vendorModel),
Container(
height: Responsive.height(20, context),
width: Responsive.width(100, context),
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(-0.00, -1.00), end: const Alignment(0, 1), colors: [Colors.black.withOpacity(0), const Color(0xFF111827)]),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty) {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.removeWhere((item) => item.restaurantId == vendorModel.id);
await FireStoreUtils.removeFavouriteRestaurant(favouriteModel);
} else {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.add(favouriteModel);
await FireStoreUtils.setFavouriteRestaurant(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg")
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
),
],
),
),
Transform.translate(
offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
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: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
),
const SizedBox(width: 10),
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_map_distance.svg", colorFilter: ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(fontSize: 18, overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500, color: isDark ? AppThemeData.grey400 : AppThemeData.grey400),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
);
}
}
class AllRestaurant extends StatelessWidget {
final DineInController controller;
const AllRestaurant({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.vertical,
itemCount: controller.allNearestRestaurant.length,
itemBuilder: (BuildContext context, int index) {
VendorModel vendorModel = controller.allNearestRestaurant[index];
return InkWell(
onTap: () {
Get.to(const DineInDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
child: Stack(
children: [
RestaurantImageView(vendorModel: vendorModel),
Container(
height: Responsive.height(20, context),
width: Responsive.width(100, context),
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(-0.00, -1.00), end: const Alignment(0, 1), colors: [Colors.black.withOpacity(0), const Color(0xFF111827)]),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty) {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.removeWhere((item) => item.restaurantId == vendorModel.id);
await FireStoreUtils.removeFavouriteRestaurant(favouriteModel);
} else {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.add(favouriteModel);
await FireStoreUtils.setFavouriteRestaurant(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg")
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
),
],
),
),
Transform.translate(
offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
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: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount.toString(), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
),
const SizedBox(width: 10),
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_map_distance.svg", colorFilter: ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(fontSize: 18, overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500, color: isDark ? AppThemeData.grey400 : AppThemeData.grey400),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
);
}
}
class NewArrival extends StatelessWidget {
final DineInController controller;
const NewArrival({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return SizedBox(
height: Responsive.height(24, context),
child: ListView.builder(
physics: const BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
itemCount: controller.newArrivalRestaurantList.length >= 10 ? 10 : controller.newArrivalRestaurantList.length,
itemBuilder: (BuildContext context, int index) {
VendorModel vendorModel = controller.newArrivalRestaurantList[index];
return InkWell(
onTap: () {
Get.to(const DineInDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(right: 10),
child: SizedBox(
width: Responsive.width(55, context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Stack(
children: [
NetworkImageWidget(imageUrl: vendorModel.photo.toString(), fit: BoxFit.cover, height: Responsive.height(100, context), width: Responsive.width(100, context)),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(0.00, 1.00), end: const Alignment(0, -1), colors: [Colors.black.withOpacity(0), AppThemeData.grey900]),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty) {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.removeWhere((item) => item.restaurantId == vendorModel.id);
await FireStoreUtils.removeFavouriteRestaurant(favouriteModel);
} else {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.add(favouriteModel);
await FireStoreUtils.setFavouriteRestaurant(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg")
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
),
],
),
),
),
const SizedBox(height: 5),
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(fontSize: 16, overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50),
),
Row(
children: [
Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 10),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount.toString(), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
],
),
const SizedBox(width: 20),
Row(
children: [
SvgPicture.asset("assets/icons/ic_map_distance.svg"),
const SizedBox(width: 10),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
],
),
],
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500, color: isDark ? AppThemeData.grey400 : AppThemeData.grey400),
),
],
),
),
),
);
},
),
);
}
}
class CategoryView extends StatelessWidget {
final DineInController controller;
const CategoryView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return SizedBox(
height: 124,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
itemCount: controller.vendorCategoryModel.length,
itemBuilder: (context, index) {
VendorCategoryModel vendorCategoryModel = controller.vendorCategoryModel[index];
return InkWell(
onTap: () {
Get.to(const CategoryRestaurantScreen(), arguments: {"vendorCategoryModel": vendorCategoryModel, "dineIn": true});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: SizedBox(
width: 78,
child: Container(
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(100),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 60, height: 60, child: ClipOval(child: NetworkImageWidget(imageUrl: vendorCategoryModel.photo.toString(), fit: BoxFit.cover))),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Text(
'${vendorCategoryModel.title}',
textAlign: TextAlign.center,
maxLines: 1,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium),
),
),
],
),
),
),
),
);
},
),
);
}
}
class BannerBottomView extends StatelessWidget {
final DineInController 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,
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),
),
);
}),
),
),
],
);
}
}

View File

@@ -0,0 +1,104 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/view_all_category_controller.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../home_screen/category_restaurant_screen.dart';
class ViewAllCategoryDineInScreen extends StatelessWidget {
const ViewAllCategoryDineInScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ViewAllCategoryController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
"Categories".tr,
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
),
),
),
body: controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: GridView.builder(
padding: EdgeInsets.zero,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4, childAspectRatio: 3.5 / 6, crossAxisSpacing: 6),
itemCount: controller.vendorCategoryModel.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
VendorCategoryModel vendorCategoryModel = controller.vendorCategoryModel[index];
return InkWell(
onTap: () {
Get.to(const CategoryRestaurantScreen(), arguments: {"vendorCategoryModel": vendorCategoryModel, "dineIn": true});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
child: Container(
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(100),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
width: 60,
height: 60,
child: ClipOval(
child: NetworkImageWidget(
imageUrl: vendorCategoryModel.photo.toString(),
fit: BoxFit.cover,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Text(
'${vendorCategoryModel.title}',
textAlign: TextAlign.center,
maxLines: 2,
style: TextStyle(
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.medium,
),
),
)
],
),
),
),
);
},
),
),
);
});
}
}

View File

@@ -0,0 +1,161 @@
import 'dart:io';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/edit_profile_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/text_field_widget.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import '../../../controllers/theme_controller.dart';
class EditProfileScreen extends StatelessWidget {
const EditProfileScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: EditProfileController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(centerTitle: false, titleSpacing: 0, backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Profile Information".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"View and update your personal details, contact information, and preferences.".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
const SizedBox(height: 20),
Center(
child: Stack(
children: [
controller.profileImage.isEmpty
? ClipRRect(
borderRadius: BorderRadius.circular(60),
child: Image.asset(Constant.userPlaceHolder, height: Responsive.width(24, context), width: Responsive.width(24, context), fit: BoxFit.cover),
)
: Constant().hasValidUrl(controller.profileImage.value) == false
? ClipRRect(
borderRadius: BorderRadius.circular(60),
child: Image.file(File(controller.profileImage.value), height: Responsive.width(24, context), width: Responsive.width(24, context), fit: BoxFit.cover),
)
: ClipRRect(
borderRadius: BorderRadius.circular(60),
child: NetworkImageWidget(
fit: BoxFit.cover,
imageUrl: controller.profileImage.value,
height: Responsive.width(24, context),
width: Responsive.width(24, context),
errorWidget: Image.asset(Constant.userPlaceHolder, fit: BoxFit.cover, height: Responsive.width(24, context), width: Responsive.width(24, context)),
),
),
Positioned(
bottom: 0,
right: 0,
child: InkWell(
onTap: () {
buildBottomSheet(context, controller);
},
child: SvgPicture.asset("assets/icons/ic_edit.svg"),
),
),
],
),
),
const SizedBox(height: 20),
Row(
children: [
Expanded(child: TextFieldWidget(title: 'First Name'.tr, controller: controller.firstNameController.value, hintText: 'First Name'.tr)),
const SizedBox(width: 10),
Expanded(child: TextFieldWidget(title: 'Last Name'.tr, controller: controller.lastNameController.value, hintText: 'Last Name'.tr)),
],
),
TextFieldWidget(title: 'Email'.tr, textInputType: TextInputType.emailAddress, controller: controller.emailController.value, hintText: 'Email'.tr, enable: false),
TextFieldWidget(title: 'Phone Number'.tr, textInputType: TextInputType.emailAddress, controller: controller.phoneNumberController.value, hintText: 'Phone Number'.tr, enable: false),
],
),
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Save Details".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
controller.saveData();
},
),
),
),
);
},
);
}
Future buildBottomSheet(BuildContext context, EditProfileController controller) {
return showModalBottomSheet(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return SizedBox(
height: Responsive.height(22, context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(padding: const EdgeInsets.only(top: 15), child: Text("please select".tr, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600))),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () => controller.pickFile(source: ImageSource.camera), icon: const Icon(Icons.camera_alt, size: 32)),
Padding(padding: const EdgeInsets.only(top: 3), child: Text("camera".tr, style: const TextStyle())),
],
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () => controller.pickFile(source: ImageSource.gallery), icon: const Icon(Icons.photo_library_sharp, size: 32)),
Padding(padding: const EdgeInsets.only(top: 3), child: Text("gallery".tr, style: const TextStyle())),
],
),
),
],
),
],
),
);
},
);
},
);
}
}

View File

@@ -0,0 +1,629 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/favourite_controller.dart';
import 'package:customer/models/favourite_item_model.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import '../../../controllers/theme_controller.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../../widget/restaurant_image_view.dart';
import '../../auth_screens/login_screen.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class FavouriteScreen extends StatelessWidget {
const FavouriteScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: FavouriteController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top),
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: Text(
"Your Favourites, All in One Place".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
),
//SvgPicture.asset("assets/images/ic_favourite.svg"),
],
),
),
const SizedBox(height: 20),
Expanded(
child:
Constant.userModel == null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset("assets/images/login.gif", height: 120),
const SizedBox(height: 12),
Text(
"Please Log In to Continue".tr,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontSize: 22, fontFamily: AppThemeData.semiBold),
),
const SizedBox(height: 5),
Text(
"Youre not logged in. Please sign in to access your account and explore all features.".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey500, fontSize: 16, fontFamily: AppThemeData.bold),
),
const SizedBox(height: 20),
RoundedButtonFill(
title: "Log in".tr,
width: 55,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
onPress: () async {
Get.offAll(const LoginScreen());
},
),
],
),
)
: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: InkWell(
onTap: () {
controller.favouriteRestaurant.value = true;
},
child: Container(
decoration:
controller.favouriteRestaurant.value == false
? null
: ShapeDecoration(color: AppThemeData.grey900, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"Favourite Store".tr,
textAlign: TextAlign.center,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
),
),
),
),
Expanded(
child: InkWell(
onTap: () {
controller.favouriteRestaurant.value = false;
},
child: Container(
decoration:
controller.favouriteRestaurant.value == true
? null
: ShapeDecoration(color: AppThemeData.grey900, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"Favourite Item".tr,
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: AppThemeData.semiBold,
color:
controller.favouriteRestaurant.value == true
? isDark
? AppThemeData.grey400
: AppThemeData.grey500
: isDark
? AppThemeData.primary300
: AppThemeData.primary300,
),
),
),
),
),
),
],
),
),
),
),
const SizedBox(height: 20),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18),
child:
controller.favouriteRestaurant.value
? controller.favouriteVendorList.isEmpty
? Constant.showEmptyView(message: "Favourite Store not found.".tr)
: ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
scrollDirection: Axis.vertical,
itemCount: controller.favouriteVendorList.length,
itemBuilder: (BuildContext context, int index) {
VendorModel vendorModel = controller.favouriteVendorList[index];
return InkWell(
onTap: () {
ShowToastDialog.closeLoader();
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
// Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
child: Stack(
children: [
RestaurantImageView(vendorModel: vendorModel),
Container(
height: Responsive.height(20, context),
width: Responsive.width(100, context),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(-0.00, -1.00),
end: const Alignment(0, 1),
colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty) {
FavouriteModel favouriteModel = FavouriteModel(
restaurantId: vendorModel.id,
userId: FireStoreUtils.getCurrentUid(),
);
controller.favouriteList.removeWhere((item) => item.restaurantId == vendorModel.id);
controller.favouriteVendorList.removeAt(index);
await FireStoreUtils.removeFavouriteRestaurant(favouriteModel);
} else {
FavouriteModel favouriteModel = FavouriteModel(
restaurantId: vendorModel.id,
userId: FireStoreUtils.getCurrentUid(),
);
controller.favouriteList.add(favouriteModel);
await FireStoreUtils.setFavouriteRestaurant(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg")
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
),
],
),
),
Transform.translate(
offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Visibility(
visible: (vendorModel.isSelfDelivery == true && Constant.isSelfDeliveryFeature == true),
child: Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: BoxDecoration(
color: AppThemeData.success300,
borderRadius: BorderRadius.circular(120), // Optional
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_free_delivery.svg"),
const SizedBox(width: 5),
Text(
"Free Delivery".tr,
style: TextStyle(
fontSize: 14,
color: AppThemeData.success600,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(width: 6),
],
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.primary600 : AppThemeData.primary50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Row(
children: [
SvgPicture.asset(
"assets/icons/ic_star.svg",
colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn),
),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Row(
children: [
SvgPicture.asset(
"assets/icons/ic_map_distance.svg",
colorFilter: ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn),
),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 18,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
)
: controller.favouriteFoodList.isEmpty
? Constant.showEmptyView(message: "Favourite Item not found.".tr)
: ListView.builder(
itemCount: controller.favouriteFoodList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
ProductModel productModel = controller.favouriteFoodList[index];
return FutureBuilder(
future: getPrice(productModel),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Constant.loader();
} else {
if (snapshot.hasError) {
return Center(child: Text('${"error".tr}: ${snapshot.error}'));
} else if (snapshot.data == null) {
return const SizedBox();
} else {
Map<String, dynamic> map = snapshot.data!;
String price = map['price'];
String disPrice = map['disPrice'];
return InkWell(
onTap: () async {
await FireStoreUtils.getVendorById(productModel.vendorID.toString()).then((value) {
if (value != null) {
ShowToastDialog.closeLoader();
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": value});
// Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": value});
}
});
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
productModel.nonveg == true
? SvgPicture.asset("assets/icons/ic_nonveg.svg")
: SvgPicture.asset("assets/icons/ic_veg.svg"),
const SizedBox(width: 5),
Text(
productModel.nonveg == true ? "Non Veg.".tr : "Pure veg.".tr,
style: TextStyle(
color: productModel.nonveg == true ? AppThemeData.danger300 : AppThemeData.success400,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
const SizedBox(height: 5),
Text(
productModel.name.toString(),
style: TextStyle(
fontSize: 18,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
double.parse(disPrice) <= 0
? Text(
Constant.amountShow(amount: price),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
)
: Row(
children: [
Text(
Constant.amountShow(amount: disPrice),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 5),
Text(
Constant.amountShow(amount: price),
style: TextStyle(
fontSize: 14,
decoration: TextDecoration.lineThrough,
decorationColor: isDark ? AppThemeData.grey500 : AppThemeData.grey400,
color: isDark ? AppThemeData.grey500 : AppThemeData.grey400,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
Row(
children: [
SvgPicture.asset(
"assets/icons/ic_star.svg",
colorFilter: const ColorFilter.mode(AppThemeData.warning300, BlendMode.srcIn),
),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: productModel.reviewsCount!.toStringAsFixed(0), reviewSum: productModel.reviewsSum.toString())} (${productModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w500,
),
),
],
),
Text(
"${productModel.description}",
maxLines: 2,
style: TextStyle(
overflow: TextOverflow.ellipsis,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w400,
),
),
],
),
),
const SizedBox(width: 6),
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: Stack(
children: [
NetworkImageWidget(
imageUrl: productModel.photo.toString(),
fit: BoxFit.cover,
height: Responsive.height(16, context),
width: Responsive.width(34, context),
),
Container(
height: Responsive.height(16, context),
width: Responsive.width(34, context),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(-0.00, -1.00),
end: const Alignment(0, 1),
colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteItemList.where((p0) => p0.productId == productModel.id).isNotEmpty) {
FavouriteItemModel favouriteModel = FavouriteItemModel(
productId: productModel.id,
storeId: productModel.vendorID,
userId: FireStoreUtils.getCurrentUid(),
);
controller.favouriteItemList.removeWhere((item) => item.productId == productModel.id);
controller.favouriteFoodList.removeAt(index);
await FireStoreUtils.removeFavouriteItem(favouriteModel);
} else {
FavouriteItemModel favouriteModel = FavouriteItemModel(
productId: productModel.id,
storeId: productModel.vendorID,
userId: FireStoreUtils.getCurrentUid(),
);
controller.favouriteItemList.add(favouriteModel);
await FireStoreUtils.setFavouriteItem(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteItemList.where((p0) => p0.productId == productModel.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg")
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
),
],
),
),
],
),
),
),
),
);
}
}
},
);
},
),
),
),
],
),
),
],
),
),
);
},
);
}
Future<Map<String, dynamic>> getPrice(ProductModel productModel) async {
String price = "0.0";
String disPrice = "0.0";
List<String> selectedVariants = [];
List<String> selectedIndexVariants = [];
List<String> selectedIndexArray = [];
print("=======>");
print(productModel.price);
print(productModel.disPrice);
VendorModel? vendorModel = await FireStoreUtils.getVendorById(productModel.vendorID.toString());
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 = Constant.productCommissionPrice(vendorModel, '0');
}
} else {
price = Constant.productCommissionPrice(vendorModel!, productModel.price.toString());
disPrice = Constant.productCommissionPrice(vendorModel, productModel.disPrice.toString());
}
return {'price': price, 'disPrice': disPrice};
}
}

View File

@@ -0,0 +1,61 @@
import 'package:customer/controllers/forgot_password_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/text_field_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
class ForgotPasswordScreen extends StatelessWidget {
const ForgotPasswordScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ForgotPasswordController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Forgot Password".tr, style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 22, fontFamily: AppThemeData.semiBold)),
Text("No worries!! Well send you reset instructions".tr, style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey500, fontSize: 16, fontFamily: AppThemeData.regular)),
const SizedBox(height: 32),
TextFieldWidget(
title: 'Email Address'.tr,
controller: controller.emailEditingController.value,
hintText: 'Enter email address'.tr,
prefix: Padding(
padding: const EdgeInsets.all(12),
child: SvgPicture.asset("assets/icons/ic_mail.svg", colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn)),
),
),
const SizedBox(height: 32),
RoundedButtonFill(
title: "Forgot Password".tr,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
onPress: () async {
if (controller.emailEditingController.value.text.trim().isEmpty) {
ShowToastDialog.showToast("Please enter valid email".tr);
} else {
controller.forgotPassword();
}
},
),
],
),
),
);
},
);
}
}

View File

@@ -0,0 +1,330 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/gift_card_controller.dart';
import 'package:customer/models/gift_cards_model.dart';
import 'package:customer/screen_ui/multi_vendor_service/gift_card/redeem_gift_card_screen.dart';
import 'package:customer/screen_ui/multi_vendor_service/gift_card/select_gift_payment_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/text_field_widget.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
import 'history_gift_card.dart';
class GiftCardScreen extends StatelessWidget {
const GiftCardScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: GiftCardController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
"Customize Gift Card".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
actions: [
InkWell(
onTap: () {
Get.to(const HistoryGiftCard());
},
child: SvgPicture.asset("assets/icons/ic_history.svg"),
),
const SizedBox(width: 10),
InkWell(
onTap: () {
Get.to(const RedeemGiftCardScreen());
},
child: SvgPicture.asset("assets/icons/ic_redeem.svg"),
),
const SizedBox(width: 10),
],
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: Responsive.height(22, context),
child: PageView.builder(
itemCount: controller.giftCardList.length,
onPageChanged: (value) {
controller.selectedPageIndex.value = value;
controller.selectedGiftCard.value = controller.giftCardList[controller.selectedPageIndex.value];
controller.messageController.value.text = controller.giftCardList[controller.selectedPageIndex.value].message.toString();
},
scrollDirection: Axis.horizontal,
controller: controller.pageController,
itemBuilder: (context, index) {
GiftCardsModel giftCardModel = controller.giftCardList[index];
return InkWell(
onTap: () {
controller.selectedGiftCard.value = giftCardModel;
controller.messageController.value.text = controller.selectedGiftCard.value.message.toString();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Container(
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), border: Border.all(color: AppThemeData.primary300)),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: NetworkImageWidget(imageUrl: giftCardModel.image.toString(), width: Responsive.width(80, context), fit: BoxFit.cover),
),
),
),
);
},
),
),
const SizedBox(height: 20),
TextFieldWidget(
title: 'Choose an amount'.tr,
controller: controller.amountController.value,
hintText: 'Enter gift card amount'.tr,
textInputType: const TextInputType.numberWithOptions(signed: true, decimal: true),
textInputAction: TextInputAction.done,
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]'))],
prefix: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Text(
Constant.currencyModel!.symbol.tr,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontSize: 18),
),
),
onchange: (value) {
controller.selectedAmount.value = value;
},
),
SizedBox(
height: 40,
child: ListView.builder(
itemCount: controller.amountList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Obx(
() => InkWell(
onTap: () {
controller.selectedAmount.value = controller.amountList[index];
controller.amountController.value.text = controller.amountList[index];
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(40)),
border: Border.all(
color:
controller.selectedAmount == controller.amountList[index]
? AppThemeData.primary300
: isDark
? AppThemeData.grey400
: AppThemeData.grey200,
),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Center(
child: Text(
Constant.amountShow(amount: controller.amountList[index]),
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 14, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500),
),
),
),
),
),
),
);
},
),
),
const SizedBox(height: 40),
TextFieldWidget(title: 'Add Message (Optional)'.tr, controller: controller.messageController.value, hintText: 'Add message here....'.tr, maxLine: 6),
],
),
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Continue".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
if (controller.amountController.value.text.isNotEmpty) {
if (Constant.userModel == null) {
ShowToastDialog.showToast("Please log in to the application. You are not logged in.".tr);
} else {
giftCardBottomSheet(context, controller);
}
} else {
ShowToastDialog.showToast("Please enter Amount".tr);
}
},
),
),
),
);
},
);
}
Future giftCardBottomSheet(BuildContext context, GiftCardController controller) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(30))),
clipBehavior: Clip.antiAliasWithSaveLayer,
builder:
(context) => FractionallySizedBox(
heightFactor: 0.7,
child: StatefulBuilder(
builder: (context1, setState) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return Obx(
() => Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 20),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: NetworkImageWidget(imageUrl: controller.selectedGiftCard.value.image.toString(), height: Responsive.height(20, context), width: Responsive.width(100, context)),
),
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Container(
padding: const EdgeInsets.all(8),
decoration: ShapeDecoration(color: AppThemeData.ecommerce50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
child: Text(
'Complete payment and share this e-gift card with loved ones using any app'.tr,
style: TextStyle(color: AppThemeData.ecommerce300, fontSize: 14, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Bill Details".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16),
),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 14),
child: Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Sub Total".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontSize: 16),
),
),
Text(
Constant.amountShow(amount: controller.amountController.value.text),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
"Grand Total".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontSize: 16),
),
),
Text(
Constant.amountShow(amount: controller.amountController.value.text),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.regular, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16),
),
],
),
],
),
),
),
],
),
const SizedBox(height: 20),
Center(
child: Text(
"${'Gift Card expire'.tr} ${controller.selectedGiftCard.value.expiryDay} ${'days after purchase'.tr}".tr,
textAlign: TextAlign.center,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey500 : AppThemeData.grey400),
),
),
],
),
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "${'Pay'.tr} ${Constant.amountShow(amount: controller.amountController.value.text)}",
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
Get.off(const SelectGiftPaymentScreen());
},
),
),
),
),
);
},
),
),
);
}
}

View File

@@ -0,0 +1,198 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/history_gift_card_controller.dart';
import 'package:customer/models/gift_cards_order_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../widget/my_separator.dart';
class HistoryGiftCard extends StatelessWidget {
const HistoryGiftCard({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: HistoryGiftCardController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, centerTitle: false, titleSpacing: 0),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child:
controller.giftCardsOrderList.isEmpty
? Constant.showEmptyView(message: "Purchased Gift card not found".tr)
: ListView.builder(
itemCount: controller.giftCardsOrderList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
GiftCardsOrderModel giftCardOrderModel = controller.giftCardsOrderList[index];
return Container(
margin: const EdgeInsets.symmetric(vertical: 4),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
giftCardOrderModel.giftTitle.toString(),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
),
Text(
Constant.amountShow(amount: giftCardOrderModel.price.toString()),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 10),
MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
const SizedBox(height: 10),
Row(
children: [
Expanded(
child: Text(
"Gift Code".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
),
Text(
giftCardOrderModel.giftCode.toString().replaceAllMapped(RegExp(r".{4}"), (match) => "${match.group(0)} "),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 5),
Row(
children: [
Expanded(
child: Text(
"Gift Pin".tr,
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
),
giftCardOrderModel.isPasswordShow == true
? Text(
giftCardOrderModel.giftPin.toString(),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
)
: Text(
"****",
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 10),
giftCardOrderModel.isPasswordShow == true
? InkWell(
onTap: () {
controller.updateList(index);
controller.update();
},
child: const Icon(Icons.visibility_off),
)
: InkWell(
onTap: () {
controller.updateList(index);
controller.update();
},
child: const Icon(Icons.remove_red_eye),
),
],
),
const SizedBox(height: 10),
MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
const SizedBox(height: 10),
Row(
children: [
InkWell(
onTap: () {
controller.share(
giftCardOrderModel.giftCode.toString(),
giftCardOrderModel.giftPin.toString(),
giftCardOrderModel.message.toString(),
giftCardOrderModel.price.toString(),
giftCardOrderModel.expireDate!,
);
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Share'.tr,
style: TextStyle(
color: isDark ? AppThemeData.grey300 : AppThemeData.grey600,
fontSize: 14,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
height: 0.11,
),
),
const SizedBox(width: 4),
const Icon(Icons.share),
],
),
),
),
const Expanded(child: SizedBox()),
Text(
giftCardOrderModel.redeem == true ? "Redeemed".tr : "Not Redeem".tr,
style: TextStyle(
fontSize: 16,
color: giftCardOrderModel.redeem == true ? AppThemeData.success400 : AppThemeData.danger300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
],
),
],
),
),
);
},
),
),
);
},
);
}
}

View File

@@ -0,0 +1,143 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/dash_board_controller.dart';
import 'package:customer/controllers/redeem_gift_card_controller.dart';
import 'package:customer/models/gift_cards_order_model.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/text_field_widget.dart';
import '../../../controllers/theme_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../dash_board_screens/dash_board_screen.dart';
class RedeemGiftCardScreen extends StatelessWidget {
const RedeemGiftCardScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: RedeemGiftCardController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, centerTitle: false, titleSpacing: 0),
body: InkWell(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Redeem Gift Card".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"Enter your gift card code to enjoy discounts and special offers on your orders.".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
const SizedBox(height: 20),
TextFieldWidget(
title: 'Gift Code'.tr,
controller: controller.giftCodeController.value,
hintText: 'Enter gift code'.tr,
textInputType: TextInputType.number,
prefix: Padding(padding: const EdgeInsets.all(10), child: SvgPicture.asset("assets/icons/ic_gift_code.svg")),
),
TextFieldWidget(
title: 'Gift Pin'.tr,
controller: controller.giftPinController.value,
hintText: 'Enter gift pin'.tr,
textInputType: TextInputType.number,
prefix: Padding(padding: const EdgeInsets.all(10), child: SvgPicture.asset("assets/icons/ic_gift_pin.svg")),
),
],
),
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Redeem".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
if (controller.giftCodeController.value.text.isEmpty) {
ShowToastDialog.showToast("Please Enter Gift Code".tr);
} else if (controller.giftPinController.value.text.isEmpty) {
ShowToastDialog.showToast("Please Enter Gift Pin".tr);
} else {
ShowToastDialog.showLoader("Please wait...".tr);
await FireStoreUtils.checkRedeemCode(controller.giftCodeController.value.text.replaceAll(" ", "")).then((value) async {
if (value != null) {
GiftCardsOrderModel giftCodeModel = value;
if (giftCodeModel.redeem == true) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Gift voucher already redeemed".tr);
} else if (giftCodeModel.giftPin != controller.giftPinController.value.text) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Gift Pin Invalid".tr);
} else if (giftCodeModel.expireDate!.toDate().isBefore(DateTime.now())) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Gift Voucher expire".tr);
} else {
giftCodeModel.redeem = true;
WalletTransactionModel transactionModel = WalletTransactionModel(
id: Constant.getUuid(),
amount: double.parse(giftCodeModel.price.toString()),
date: Timestamp.now(),
paymentMethod: "Wallet",
transactionUser: "user",
userId: FireStoreUtils.getCurrentUid(),
isTopup: true,
note: "Gift Voucher",
paymentStatus: "success",
);
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
if (value == true) {
await FireStoreUtils.updateUserWallet(amount: giftCodeModel.price.toString(), userId: FireStoreUtils.getCurrentUid()).then((value) async {
await FireStoreUtils.sendTopUpMail(paymentMethod: "Gift Voucher", amount: giftCodeModel.price.toString(), tractionId: transactionModel.id.toString());
await FireStoreUtils.placeGiftCardOrder(giftCodeModel).then((value) {
ShowToastDialog.closeLoader();
if (Constant.walletSetting == true) {
Get.offAll(const DashBoardScreen());
DashBoardController controller = Get.put(DashBoardController());
controller.selectedIndex.value = 2;
}
ShowToastDialog.showToast("Voucher redeem successfully".tr);
});
});
}
});
}
} else {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Invalid Gift Code".tr);
}
});
}
},
),
),
),
);
},
);
}
}

View File

@@ -0,0 +1,222 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/gift_card_controller.dart';
import 'package:customer/payment/createRazorPayOrderModel.dart';
import 'package:customer/payment/rozorpayConroller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
import '../wallet_screen/wallet_screen.dart';
class SelectGiftPaymentScreen extends StatelessWidget {
const SelectGiftPaymentScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: GiftCardController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Payment Option".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Preferred Payment".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
const SizedBox(height: 10),
if (controller.walletSettingModel.value.isEnabled == true)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
shadows: const [BoxShadow(color: Color(0x07000000), blurRadius: 20, offset: Offset(0, 0), spreadRadius: 0)],
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Visibility(
visible: controller.walletSettingModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.wallet, isDark, "assets/images/ic_wallet.png"),
),
],
),
),
),
const SizedBox(height: 10),
Text(
"Other Payment Options".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
const SizedBox(height: 10),
],
),
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
shadows: const [BoxShadow(color: Color(0x07000000), blurRadius: 20, offset: Offset(0, 0), spreadRadius: 0)],
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Visibility(visible: controller.flutterWaveModel.value.isEnable == true, child: cardDecoration(controller, PaymentGateway.stripe, isDark, "assets/images/stripe.png")),
Visibility(visible: controller.paytmModel.value.isEnabled == true, child: cardDecoration(controller, PaymentGateway.paypal, isDark, "assets/images/paypal.png")),
Visibility(visible: controller.payStackModel.value.isEnable == true, child: cardDecoration(controller, PaymentGateway.payStack, isDark, "assets/images/paystack.png")),
Visibility(
visible: controller.mercadoPagoModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.mercadoPago, isDark, "assets/images/mercado-pago.png"),
),
Visibility(
visible: controller.flutterWaveModel.value.isEnable == true,
child: cardDecoration(controller, PaymentGateway.flutterWave, isDark, "assets/images/flutterwave_logo.png"),
),
Visibility(visible: controller.payFastModel.value.isEnable == true, child: cardDecoration(controller, PaymentGateway.payFast, isDark, "assets/images/payfast.png")),
Visibility(visible: controller.razorPayModel.value.isEnabled == true, child: cardDecoration(controller, PaymentGateway.razorpay, isDark, "assets/images/razorpay.png")),
Visibility(visible: controller.midTransModel.value.enable == true, child: cardDecoration(controller, PaymentGateway.midTrans, isDark, "assets/images/midtrans.png")),
Visibility(
visible: controller.orangeMoneyModel.value.enable == true,
child: cardDecoration(controller, PaymentGateway.orangeMoney, isDark, "assets/images/orange_money.png"),
),
Visibility(visible: controller.xenditModel.value.enable == true, child: cardDecoration(controller, PaymentGateway.xendit, isDark, "assets/images/xendit.png")),
],
),
),
),
],
),
),
),
bottomNavigationBar: Container(
decoration: BoxDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20))),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Pay Now".tr,
height: 5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
if (controller.selectedPaymentMethod.value == PaymentGateway.stripe.name) {
controller.stripeMakePayment(amount: controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.paypal.name) {
controller.paypalPaymentSheet(controller.amountController.value.text, context);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payStack.name) {
controller.payStackPayment(controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.mercadoPago.name) {
controller.mercadoPagoMakePayment(context: context, amount: controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.flutterWave.name) {
controller.flutterWaveInitiatePayment(context: context, amount: controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payFast.name) {
controller.payFastPayment(context: context, amount: controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.midTrans.name) {
controller.midtransMakePayment(context: context, amount: controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.orangeMoney.name) {
controller.orangeMakePayment(context: context, amount: controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.xendit.name) {
controller.xenditPayment(context, controller.amountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.wallet.name) {
controller.placeOrder();
} else if (controller.selectedPaymentMethod.value == PaymentGateway.razorpay.name) {
RazorPayController().createOrderRazorPay(amount: double.parse(controller.amountController.value.text), razorpayModel: controller.razorPayModel.value).then((value) {
if (value == null) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
CreateRazorPayOrderModel result = value;
controller.openCheckout(amount: controller.amountController.value.text, orderId: result.id);
}
});
} else {
ShowToastDialog.showToast("Please select payment method".tr);
}
},
),
),
),
);
},
);
}
Obx cardDecoration(GiftCardController controller, PaymentGateway value, isDark, String image) {
return Obx(
() => Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: InkWell(
onTap: () {
controller.selectedPaymentMethod.value = value.name;
},
child: Row(
children: [
Container(
width: 50,
height: 50,
decoration: ShapeDecoration(shape: RoundedRectangleBorder(side: const BorderSide(width: 1, color: Color(0xFFE5E7EB)), borderRadius: BorderRadius.circular(8))),
child: Padding(padding: EdgeInsets.all(value.name == "payFast" ? 0 : 8.0), child: Image.asset(image)),
),
const SizedBox(width: 10),
value.name == "wallet"
? Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
value.name.capitalizeString(),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
Text(
Constant.amountShow(amount: controller.userModel.value.walletAmount == null ? '0.0' : controller.userModel.value.walletAmount.toString()),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
],
),
)
: Expanded(
child: Text(
value.name.capitalizeString(),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
const Expanded(child: SizedBox()),
Radio(
value: value.name,
groupValue: controller.selectedPaymentMethod.value,
activeColor: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
onChanged: (value) {
controller.selectedPaymentMethod.value = value.toString();
},
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,198 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/category_restaurant_controller.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../widget/restaurant_image_view.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class CategoryRestaurantScreen extends StatelessWidget {
const CategoryRestaurantScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: CategoryRestaurantController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, centerTitle: false, titleSpacing: 0),
body:
controller.isLoading.value
? Constant.loader()
: controller.allNearestRestaurant.isEmpty
? Constant.showEmptyView(message: "No Restaurant found".tr)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView.builder(
shrinkWrap: true,
itemCount: controller.allNearestRestaurant.length,
itemBuilder: (context, index) {
VendorModel vendorModel = controller.allNearestRestaurant[index];
return InkWell(
onTap: () {
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
child: Stack(
children: [
RestaurantImageView(vendorModel: vendorModel),
Container(
height: Responsive.height(20, context),
width: Responsive.width(100, context),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(-0.00, -1.00),
end: const Alignment(0, 1),
colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
),
),
),
],
),
),
Transform.translate(
offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Visibility(
visible: (vendorModel.isSelfDelivery == true && Constant.isSelfDeliveryFeature == true),
child: Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: BoxDecoration(
color: AppThemeData.success300,
borderRadius: BorderRadius.circular(120), // Optional
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_free_delivery.svg"),
const SizedBox(width: 5),
Text(
"Free Delivery".tr,
style: TextStyle(fontSize: 14, color: AppThemeData.success600, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
const SizedBox(width: 6),
],
),
),
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.primary600 : AppThemeData.primary50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
const SizedBox(width: 6),
Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_map_distance.svg", colorFilter: ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 18,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
),
),
);
},
);
}
}

View File

@@ -0,0 +1,320 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/discount_restaurant_list_controller.dart';
import 'package:customer/models/coupon_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class DiscountRestaurantListScreen extends StatelessWidget {
const DiscountRestaurantListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: DiscountRestaurantListController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
controller.title.value,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView.builder(
shrinkWrap: true,
itemCount: controller.vendorList.length,
itemBuilder: (context, index) {
VendorModel vendorModel = controller.vendorList[index];
CouponModel offerModel = controller.couponList[index];
return InkWell(
onTap: () {
Get.to(RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), bottomLeft: Radius.circular(16)),
child: Stack(
children: [
NetworkImageWidget(imageUrl: vendorModel.photo.toString(), fit: BoxFit.cover, height: Responsive.height(16, context), width: Responsive.width(28, context)),
Container(
height: Responsive.height(16, context),
width: Responsive.width(28, context),
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(-0.00, -1.00), end: const Alignment(0, 1), colors: [Colors.black.withOpacity(0), const Color(0xFF111827)]),
),
),
Positioned(
top: 10,
left: 10,
child: Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text(
"${offerModel.discountType == "Fix Price" ? Constant.currencyModel!.symbol : ""}${offerModel.discount}${offerModel.discountType == "Percentage" ? "% off".toUpperCase().tr : " off".toUpperCase().tr}",
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50),
),
),
),
),
],
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 18,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
),
Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
],
),
const SizedBox(height: 5),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.location_on, size: 18, color: isDark ? AppThemeData.grey300 : AppThemeData.grey600),
const SizedBox(width: 5),
Expanded(
child: Text(
vendorModel.location.toString(),
style: TextStyle(
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
fontSize: 12,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
),
],
),
const SizedBox(height: 5),
Container(
color: isDark ? AppThemeData.primary600 : AppThemeData.primary50,
child: DottedBorder(
options: RoundedRectDottedBorderOptions(
radius: const Radius.circular(6),
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
strokeWidth: 1,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
child: Text(
"${offerModel.code}",
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
),
),
),
),
],
),
),
),
],
),
),
),
);
},
),
),
);
},
);
}
// vhhv(){
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Stack(
// children: [
// ClipRRect(
// borderRadius: const BorderRadius.only(topLeft: Radius.circular(16),topRight: Radius.circular(16)),
// child: Stack(
// children: [
// RestaurantImageView(
// vendorModel: vendorModel,
// ),
// Container(
// height: Responsive.height(20, context),
// width: Responsive.width(100, context),
// decoration: BoxDecoration(
// gradient: LinearGradient(
// begin: const Alignment(-0.00, -1.00),
// end: const Alignment(0, 1),
// colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
// ),
// ),
// ),
// ],
// ),
// ),
// Transform.translate(
// offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
// crossAxisAlignment: CrossAxisAlignment.end,
// children: [
// 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: [
// SvgPicture.asset(
// "assets/icons/ic_star.svg",
// colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn),
// ),
// const SizedBox(
// width: 5,
// ),
// Text(
// "${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
// style: TextStyle(
// color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
// fontFamily: AppThemeData.semiBold,
// fontWeight: FontWeight.w600,
// ),
// ),
// ],
// ),
// ),
// ),
// const SizedBox(
// width: 10,
// ),
// Container(
// decoration: ShapeDecoration(
// color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
// ),
// child: Padding(
// padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
// child: Row(
// children: [
// SvgPicture.asset(
// "assets/icons/ic_map_distance.svg",
// colorFilter: const ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn),
// ),
// const SizedBox(
// width: 5,
// ),
// Text(
// "${Constant.getDistance(
// lat1: vendorModel.latitude.toString(),
// lng1: vendorModel.longitude.toString(),
// lat2: Constant.selectedLocation.location!.latitude.toString(),
// lng2: Constant.selectedLocation.location!.longitude.toString(),
// )} ${Constant.distanceType}",
// style: TextStyle(
// color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
// fontFamily: AppThemeData.semiBold,
// fontWeight: FontWeight.w600,
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// )
// ],
// ),
// const SizedBox(
// height: 15,
// ),
// Padding(
// padding: const EdgeInsets.symmetric(horizontal: 16),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// vendorModel.title.toString(),
// textAlign: TextAlign.start,
// maxLines: 1,
// style: TextStyle(
// fontSize: 18,
// overflow: TextOverflow.ellipsis,
// fontFamily: AppThemeData.semiBold,
// color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
// ),
// ),
// Text(
// vendorModel.location.toString(),
// textAlign: TextAlign.start,
// maxLines: 1,
// style: TextStyle(
// overflow: TextOverflow.ellipsis,
// fontFamily: AppThemeData.medium,
// fontWeight: FontWeight.w500,
// color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
// ),
// )
// ],
// ),
// ),
// const SizedBox(
// height: 10,
// ),
// ],
// );
// }
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,229 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/restaurant_list_controller.dart';
import 'package:customer/models/favourite_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import '../../../controllers/theme_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../service/fire_store_utils.dart';
import '../../../widget/restaurant_image_view.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class RestaurantListScreen extends StatelessWidget {
const RestaurantListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: RestaurantListController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
controller.title.value,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: ListView.builder(
shrinkWrap: true,
itemCount: controller.vendorSearchList.length,
itemBuilder: (context, index) {
VendorModel vendorModel = controller.vendorSearchList[index];
return InkWell(
onTap: () {
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel})?.then((v) {
controller.getFavouriteRestaurant();
});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
child: Stack(
children: [
RestaurantImageView(vendorModel: vendorModel),
Container(
height: Responsive.height(20, context),
width: Responsive.width(100, context),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(-0.00, -1.00),
end: const Alignment(0, 1),
colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
),
),
),
Positioned(
right: 10,
top: 10,
child: InkWell(
onTap: () async {
if (controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty) {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.removeWhere((item) => item.restaurantId == vendorModel.id);
await FireStoreUtils.removeFavouriteRestaurant(favouriteModel);
} else {
FavouriteModel favouriteModel = FavouriteModel(restaurantId: vendorModel.id, userId: FireStoreUtils.getCurrentUid());
controller.favouriteList.add(favouriteModel);
await FireStoreUtils.setFavouriteRestaurant(favouriteModel);
}
},
child: Obx(
() =>
controller.favouriteList.where((p0) => p0.restaurantId == vendorModel.id).isNotEmpty
? SvgPicture.asset("assets/icons/ic_like_fill.svg")
: SvgPicture.asset("assets/icons/ic_like.svg"),
),
),
),
],
),
),
Transform.translate(
offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Visibility(
visible: (vendorModel.isSelfDelivery == true && Constant.isSelfDeliveryFeature == true),
child: Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: BoxDecoration(
color: AppThemeData.success300,
borderRadius: BorderRadius.circular(120), // Optional
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_free_delivery.svg"),
const SizedBox(width: 5),
Text(
"Free Delivery".tr,
style: TextStyle(fontSize: 14, color: AppThemeData.carRent600, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
const SizedBox(width: 6),
],
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.primary600 : AppThemeData.primary50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_map_distance.svg", colorFilter: ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 18,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
),
),
);
},
);
}
}

View File

@@ -0,0 +1,171 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/models/story_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:customer/widget/story_view/controller/story_controller.dart';
import 'package:customer/widget/story_view/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../service/fire_store_utils.dart';
import '../../../widget/story_view/widgets/story_view.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
// ignore: must_be_immutable
class MoreStories extends StatefulWidget {
final List<StoryModel> storyList;
int index;
MoreStories({super.key, required this.index, required this.storyList});
@override
MoreStoriesState createState() => MoreStoriesState();
}
class MoreStoriesState extends State<MoreStories> {
StoryController storyController = StoryController();
@override
void dispose() {
storyController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onHorizontalDragEnd: (details) {
// Swipe to next story
if (details.primaryVelocity != null && details.primaryVelocity! < 0) {
if (widget.index < widget.storyList.length - 1) {
setState(() {
storyController.dispose();
storyController = StoryController();
});
setState(() {
widget.index++;
});
} else {
Navigator.pop(context);
}
}
// Swipe to previous story
if (details.primaryVelocity != null && details.primaryVelocity! > 0) {
if (widget.index > 0) {
setState(() {
storyController.dispose();
storyController = StoryController();
});
setState(() {
widget.index--;
});
}
}
},
child: Stack(
children: [
StoryView(
key: ValueKey(widget.index),
storyItems:
List.generate(widget.storyList[widget.index].videoUrl.length, (i) {
return StoryItem.pageVideo(widget.storyList[widget.index].videoUrl[i], controller: storyController);
}).toList(),
onComplete: () {
debugPrint("--------->");
debugPrint(widget.storyList.length.toString());
debugPrint(widget.index.toString());
if (widget.storyList.length - 1 != widget.index) {
setState(() {
widget.index = widget.index + 1;
});
} else {
Navigator.pop(context);
}
},
progressPosition: ProgressPosition.top,
repeat: true,
controller: storyController,
onVerticalSwipeComplete: (direction) {
if (direction == Direction.down) {
Navigator.pop(context);
}
},
),
Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top + 30, left: 16, right: 16),
child: FutureBuilder(
future: FireStoreUtils.getVendorById(widget.storyList[widget.index].vendorID.toString()),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return SizedBox();
} else {
if (snapshot.hasError) {
return Center(child: Text('${"Error".tr}: ${snapshot.error}'));
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.data == null) {
return const SizedBox();
} else {
VendorModel vendorModel = snapshot.data!;
return InkWell(
onTap: () {
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipOval(child: NetworkImageWidget(imageUrl: vendorModel.photo.toString(), width: 50, height: 50, fit: BoxFit.cover)),
const SizedBox(width: 10),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.center,
maxLines: 1,
style: const TextStyle(color: Colors.white, fontSize: 16, overflow: TextOverflow.ellipsis, fontWeight: FontWeight.w700),
),
Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg"),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount.toString(), reviewSum: vendorModel.reviewsSum.toString())} ${'reviews'.tr}",
textAlign: TextAlign.center,
maxLines: 1,
style: const TextStyle(color: AppThemeData.warning300, fontSize: 12, overflow: TextOverflow.ellipsis, fontWeight: FontWeight.w700),
),
],
),
],
),
),
InkWell(
onTap: () async {
Get.back();
},
child: Container(
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(30), color: Colors.grey),
child: SvgPicture.asset("assets/icons/ic_close.svg", colorFilter: ColorFilter.mode(AppThemeData.grey800, BlendMode.srcIn)),
),
),
],
),
);
}
}
},
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,82 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/view_all_category_controller.dart';
import 'package:customer/models/vendor_category_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import 'category_restaurant_screen.dart';
class ViewAllCategoryScreen extends StatelessWidget {
const ViewAllCategoryScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ViewAllCategoryController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Categories".tr, style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500)),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: GridView.builder(
padding: EdgeInsets.zero,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 4, childAspectRatio: 3.5 / 6, crossAxisSpacing: 6),
itemCount: controller.vendorCategoryModel.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
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.symmetric(horizontal: 4, vertical: 6),
child: Container(
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(100),
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(width: 60, height: 60, child: ClipOval(child: NetworkImageWidget(imageUrl: vendorCategoryModel.photo.toString(), fit: BoxFit.cover))),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Text(
'${vendorCategoryModel.title}',
textAlign: TextAlign.center,
maxLines: 2,
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 12),
),
),
],
),
),
),
);
},
),
),
);
},
);
}
}

View File

@@ -0,0 +1,56 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/live_tracking_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart' as flutterMap;
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmap;
import '../../../controllers/theme_controller.dart';
class LiveTrackingScreen extends StatelessWidget {
const LiveTrackingScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX<LiveTrackingController>(
init: LiveTrackingController(),
builder: (controller) {
if (controller.isLoading.value) {
return Scaffold(body: Constant.loader());
}
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, title: Text("Live Tracking".tr), centerTitle: false),
body:
Constant.selectedMapType == 'osm'
? flutterMap.FlutterMap(
mapController: controller.osmMapController,
options: flutterMap.MapOptions(initialCenter: controller.driverCurrent.value, initialZoom: 14),
children: [
flutterMap.TileLayer(urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.emart.customer'),
if (controller.routePoints.isNotEmpty) flutterMap.PolylineLayer(polylines: [flutterMap.Polyline(points: controller.routePoints, strokeWidth: 5.0, color: Colors.blue)]),
flutterMap.MarkerLayer(markers: controller.orderModel.value.id == null ? [] : controller.osmMarkers),
],
)
: gmap.GoogleMap(
onMapCreated: (gmap.GoogleMapController mapController) {
controller.mapController = mapController;
},
myLocationEnabled: true,
zoomControlsEnabled: false,
polylines: Set<gmap.Polyline>.of(controller.polyLines.values),
markers: Set<gmap.Marker>.of(controller.markers.values),
initialCameraPosition: gmap.CameraPosition(
zoom: 14,
target: gmap.LatLng(controller.driverUserModel.value.location?.latitude ?? 0.0, controller.driverUserModel.value.location?.longitude ?? 0.0),
),
),
);
},
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,345 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/order_controller.dart';
import 'package:customer/models/cart_product_model.dart';
import 'package:customer/models/order_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../../widget/my_separator.dart';
import '../../auth_screens/login_screen.dart';
import 'live_tracking_screen.dart';
import 'order_details_screen.dart';
class OrderScreen extends StatelessWidget {
const OrderScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: OrderController(),
builder: (controller) {
return Scaffold(
body: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top),
child:
controller.isLoading.value
? Constant.loader()
: Constant.userModel == null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset("assets/images/login.gif", height: 120),
const SizedBox(height: 12),
Text("Please Log In to Continue".tr, style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontSize: 22, fontFamily: AppThemeData.semiBold)),
const SizedBox(height: 5),
Text(
"Youre not logged in. Please sign in to access your account and explore all features.".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey500, fontSize: 16, fontFamily: AppThemeData.bold),
),
const SizedBox(height: 20),
RoundedButtonFill(
title: "Log in".tr,
width: 55,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
onPress: () async {
Get.offAll(const LoginScreen());
},
),
],
),
)
: DefaultTabController(
length: 5,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"My Order".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"Keep track your delivered, In Progress and Rejected item all in just one place.".tr,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
],
),
),
],
),
),
const SizedBox(height: 10),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 10),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey800 : AppThemeData.grey100, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120))),
child: TabBar(
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50), // Creates border
color: AppThemeData.primary300,
),
labelColor: AppThemeData.grey50,
isScrollable: true,
tabAlignment: TabAlignment.start,
indicatorWeight: 0.5,
unselectedLabelColor: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
dividerColor: Colors.transparent,
indicatorSize: TabBarIndicatorSize.tab,
tabs: [
Padding(padding: const EdgeInsets.symmetric(horizontal: 18), child: Tab(text: 'All'.tr)),
Tab(text: 'In Progress'.tr),
Tab(text: 'Delivered'.tr),
Tab(text: 'Cancelled'.tr),
Tab(text: 'Rejected'.tr),
],
),
),
const SizedBox(height: 10),
Expanded(
child: TabBarView(
children: [
controller.allList.isEmpty
? Constant.showEmptyView(message: "Order Not Found".tr)
: RefreshIndicator(
onRefresh: () => controller.getOrder(),
child: ListView.builder(
itemCount: controller.allList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
OrderModel orderModel = controller.allList[index];
return itemView(isDark, context, orderModel, controller);
},
),
),
controller.inProgressList.isEmpty
? Constant.showEmptyView(message: "Order Not Found".tr)
: RefreshIndicator(
onRefresh: () => controller.getOrder(),
child: ListView.builder(
itemCount: controller.inProgressList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
OrderModel orderModel = controller.inProgressList[index];
return itemView(isDark, context, orderModel, controller);
},
),
),
controller.deliveredList.isEmpty
? Constant.showEmptyView(message: "Order Not Found".tr)
: RefreshIndicator(
onRefresh: () => controller.getOrder(),
child: ListView.builder(
itemCount: controller.deliveredList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
OrderModel orderModel = controller.deliveredList[index];
return itemView(isDark, context, orderModel, controller);
},
),
),
controller.cancelledList.isEmpty
? Constant.showEmptyView(message: "Order Not Found".tr)
: RefreshIndicator(
onRefresh: () => controller.getOrder(),
child: ListView.builder(
itemCount: controller.cancelledList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
OrderModel orderModel = controller.cancelledList[index];
return itemView(isDark, context, orderModel, controller);
},
),
),
controller.rejectedList.isEmpty
? Constant.showEmptyView(message: "Order Not Found".tr)
: RefreshIndicator(
onRefresh: () => controller.getOrder(),
child: ListView.builder(
itemCount: controller.rejectedList.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
OrderModel orderModel = controller.rejectedList[index];
return itemView(isDark, context, orderModel, controller);
},
),
),
],
),
),
],
),
),
),
],
),
),
),
);
},
);
}
Padding itemView(isDark, BuildContext context, OrderModel orderModel, OrderController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: Stack(
children: [
NetworkImageWidget(imageUrl: orderModel.vendor!.photo.toString(), fit: BoxFit.cover, height: Responsive.height(10, context), width: Responsive.width(20, context)),
Container(
height: Responsive.height(10, context),
width: Responsive.width(20, context),
decoration: BoxDecoration(
gradient: LinearGradient(begin: const Alignment(0.00, 1.00), end: const Alignment(0, -1), colors: [Colors.black.withOpacity(0), AppThemeData.grey900]),
),
),
],
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
orderModel.status.toString(),
textAlign: TextAlign.right,
style: TextStyle(color: Constant.statusColor(status: orderModel.status.toString()), fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500, fontSize: 12),
),
const SizedBox(height: 5),
Text(
orderModel.vendor!.title.toString(),
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w400),
),
const SizedBox(height: 5),
Text(
Constant.timestampToDateTime(orderModel.createdAt!),
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500),
),
],
),
),
],
),
const SizedBox(height: 10),
ListView.builder(
itemCount: orderModel.products!.length,
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
CartProductModel cartProduct = orderModel.products![index];
return Row(
children: [
Expanded(
child: Text(
"${cartProduct.quantity} x ${cartProduct.name.toString()}",
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
),
Text(
Constant.amountShow(
amount:
double.parse(cartProduct.discountPrice.toString()) <= 0
? (double.parse('${cartProduct.price ?? 0}') * double.parse('${cartProduct.quantity ?? 0}')).toString()
: (double.parse('${cartProduct.discountPrice ?? 0}') * double.parse('${cartProduct.quantity ?? 0}')).toString(),
),
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
],
);
},
),
Padding(padding: const EdgeInsets.symmetric(vertical: 14), child: MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200)),
Row(
children: [
orderModel.status == Constant.orderCompleted
? Expanded(
child: InkWell(
onTap: () {
for (var element in orderModel.products!) {
controller.addToCart(cartProductModel: element);
ShowToastDialog.showToast("Item Added In a cart".tr);
}
},
child: Text(
"Reorder".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, fontSize: 16),
),
),
)
: orderModel.status == Constant.orderShipped || orderModel.status == Constant.orderInTransit
? Expanded(
child: InkWell(
onTap: () {
Get.to(const LiveTrackingScreen(), arguments: {"orderModel": orderModel});
},
child: Text(
"Track Order".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.primary300 : AppThemeData.primary300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, fontSize: 16),
),
),
)
: const SizedBox(),
Expanded(
child: InkWell(
onTap: () {
Get.to(const OrderDetailsScreen(), arguments: {"orderModel": orderModel});
// Get.off(const OrderPlacingScreen(), arguments: {"orderModel": orderModel});
},
child: Text(
"View Details".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, fontSize: 16),
),
),
),
],
),
const SizedBox(height: 10),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,379 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/my_profile_controller.dart';
import 'package:customer/screen_ui/on_demand_service/provider_inbox_screen.dart';
import 'package:customer/screen_ui/on_demand_service/worker_inbox_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/custom_dialog_box.dart';
import 'package:customer/themes/responsive.dart';
import 'package:in_app_review/in_app_review.dart';
import '../../../controllers/theme_controller.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../auth_screens/login_screen.dart';
import '../cashback_screen/cashback_offers_list.dart';
import '../change langauge/change_language_screen.dart';
import '../chat_screens/driver_inbox_screen.dart';
import '../chat_screens/restaurant_inbox_screen.dart';
import '../dine_in_booking/dine_in_booking_screen.dart';
import '../dine_in_screeen/dine_in_screen.dart';
import '../edit_profile_screen/edit_profile_screen.dart';
import '../gift_card/gift_card_screen.dart';
import '../refer_friend_screen/refer_friend_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:share_plus/share_plus.dart';
import '../terms_and_condition/terms_and_condition_screen.dart';
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
return Scaffold(
body: Obx(() {
final isDark = themeController.isDark.value;
return GetX(
init: MyProfileController(),
builder: (controller) {
return controller.isLoading.value
? Constant.loader()
: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"My Profile".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"Manage your personal information, preferences, and settings all in one place.".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
const SizedBox(height: 20),
Text(
"General Information".tr,
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Column(
children: [
Constant.userModel == null
? const SizedBox()
: cardDecoration(isDark, controller, "assets/images/ic_profile.svg", "Profile Information".tr, () {
Get.to(const EditProfileScreen());
}),
if (Constant.sectionConstantModel!.dineInActive == true)
cardDecoration(isDark, controller, "assets/images/ic_dinin.svg", "Dine-In".tr, () {
Get.to(const DineInScreen());
}),
cardDecoration(isDark, controller, "assets/images/ic_gift.svg", "Gift Card".tr, () {
Get.to(const GiftCardScreen());
}),
if (Constant.isCashbackActive == true)
cardDecoration(isDark, controller, "assets/icons/ic_cashback_Offer.svg", "Cashback Offers".tr, () {
Get.to(const CashbackOffersListScreen());
}),
],
),
),
),
const SizedBox(height: 20),
Constant.sectionConstantModel!.dineInActive == true
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Bookings Information".tr,
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Column(
children: [
cardDecoration(isDark, controller, "assets/icons/ic_dinin_order.svg", "Dine-In Booking".tr, () {
Get.to(const DineInBookingScreen());
}),
],
),
),
),
],
)
: const SizedBox(),
const SizedBox(height: 10),
Text(
"Preferences".tr,
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Column(
children: [
cardDecoration(isDark, controller, "assets/icons/ic_change_language.svg", "Change Language".tr, () {
Get.to(const ChangeLanguageScreen());
}),
cardDecoration(isDark, controller, "assets/icons/ic_light_dark.svg", "Dark Mode".tr, () {}),
],
),
),
),
const SizedBox(height: 10),
Text(
"Social".tr,
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Column(
children: [
Constant.userModel == null
? const SizedBox()
: cardDecoration(isDark, controller, "assets/icons/ic_refer.svg", "Refer a Friend".tr, () {
Get.to(const ReferFriendScreen());
}),
cardDecoration(isDark, controller, "assets/icons/ic_share.svg", "Share app".tr, () {
Share.share(
'${'Check out Foodie, your ultimate food delivery application!'.tr} \n\n${'Google Play:'.tr} ${Constant.googlePlayLink} \n\n${'App Store:'.tr} ${Constant.appStoreLink}',
subject: 'Look what I made!'.tr,
);
}),
cardDecoration(isDark, controller, "assets/icons/ic_rate.svg", "Rate the app".tr, () {
final InAppReview inAppReview = InAppReview.instance;
inAppReview.requestReview();
}),
],
),
),
),
const SizedBox(height: 10),
Constant.userModel == null
? const SizedBox()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Communication".tr,
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Column(
children: [
cardDecoration(isDark, controller, "assets/icons/ic_restaurant_chat.svg", "Store Inbox".tr, () {
Get.to(const RestaurantInboxScreen());
}),
cardDecoration(isDark, controller, "assets/icons/ic_restaurant_driver.svg", "Driver Inbox".tr, () {
Get.to(const DriverInboxScreen());
}),
cardDecoration(isDark, controller, "assets/icons/ic_restaurant_chat.svg", "Provider Inbox".tr, () {
Get.to(const ProviderInboxScreen());
}),
cardDecoration(isDark, controller, "assets/icons/ic_restaurant_driver.svg", "Worker Inbox".tr, () {
Get.to(const WorkerInboxScreen());
}),
],
),
),
),
const SizedBox(height: 10),
],
),
Text("Legal".tr, style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500)),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
child: Column(
children: [
cardDecoration(isDark, controller, "assets/icons/ic_privacy_policy.svg", "Privacy Policy".tr, () {
Get.to(const TermsAndConditionScreen(type: "privacy"));
}),
cardDecoration(isDark, controller, "assets/icons/ic_tearm_condition.svg", "Terms and Conditions".tr, () {
Get.to(const TermsAndConditionScreen(type: "termAndCondition"));
}),
],
),
),
),
const SizedBox(height: 10),
const SizedBox(height: 10),
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
child: Column(
children: [
Constant.userModel == null
? cardDecoration(isDark, controller, "assets/icons/ic_logout.svg", "Log In".tr, () {
Get.offAll(const LoginScreen());
})
: cardDecoration(isDark, controller, "assets/icons/ic_logout.svg", "Log out".tr, () {
showDialog(
context: context,
builder: (BuildContext context) {
return CustomDialogBox(
title: "Log out".tr,
descriptions: "Are you sure you want to log out? You will need to enter your credentials to log back in.".tr,
positiveString: "Log out".tr,
negativeString: "Cancel".tr,
positiveClick: () async {
Constant.userModel!.fcmToken = "";
await FireStoreUtils.updateUser(Constant.userModel!);
Constant.userModel = null;
await FirebaseAuth.instance.signOut();
Get.offAll(const LoginScreen());
},
negativeClick: () {
Get.back();
},
img: Image.asset('assets/images/ic_logout.gif', height: 50, width: 50),
);
},
);
}),
],
),
),
),
const SizedBox(height: 10),
Constant.userModel == null
? const SizedBox()
: Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: InkWell(
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return CustomDialogBox(
title: "Delete Account".tr,
descriptions: "Are you sure you want to delete your account? This action is irreversible and will permanently remove all your data.".tr,
positiveString: "Delete".tr,
negativeString: "Cancel".tr,
positiveClick: () async {
ShowToastDialog.showLoader("Please wait...".tr);
await controller.deleteUserFromServer();
await FireStoreUtils.deleteUser().then((value) {
ShowToastDialog.closeLoader();
if (value == true) {
ShowToastDialog.showToast("Account deleted successfully".tr);
Get.offAll(const LoginScreen());
} else {
ShowToastDialog.showToast("Contact Administrator".tr);
}
});
},
negativeClick: () {
Get.back();
},
img: Image.asset('assets/icons/delete_dialog.gif', height: 50, width: 50),
);
},
);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset("assets/icons/ic_delete.svg"),
const SizedBox(width: 10),
Text(
"Delete Account".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.danger300 : AppThemeData.danger300),
),
],
),
),
),
Center(
child: Text(
"V : ${Constant.appVersion}",
textAlign: TextAlign.center,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 14, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
],
),
),
),
);
},
);
}),
);
}
Padding cardDecoration(bool isDark, MyProfileController controller, String image, String title, Function()? onPress) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: InkWell(
onTap: () {
FocusManager.instance.primaryFocus?.unfocus();
onPress?.call();
},
child: Row(
children: [
SvgPicture.asset(image, colorFilter: title == "Log In".tr || title == "Cashbacks".tr ? const ColorFilter.mode(AppThemeData.success500, BlendMode.srcIn) : null, height: 24, width: 24),
const SizedBox(width: 10),
Expanded(
child: Text(
title.tr,
textAlign: TextAlign.start,
style: TextStyle(
fontFamily: AppThemeData.medium,
fontSize: 16,
color:
title == "Log out".tr
? AppThemeData.danger300
: title == "Log In".tr
? AppThemeData.success500
: (isDark ? AppThemeData.grey100 : AppThemeData.grey800),
),
),
),
title == "Dark Mode".tr
? Transform.scale(
scale: 0.8,
child: Obx(() => CupertinoSwitch(value: controller.isDarkModeSwitch.value, activeTrackColor: AppThemeData.primary300, onChanged: controller.toggleDarkMode)),
)
: Icon(Icons.keyboard_arrow_right, color: isDark ? AppThemeData.greyDark700 : AppThemeData.grey700),
],
),
),
);
}
}

View File

@@ -0,0 +1,305 @@
import 'dart:io';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/rate_product_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import '../../../controllers/theme_controller.dart';
import '../../../widget/my_separator.dart';
class RateProductScreen extends StatelessWidget {
const RateProductScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: RateProductController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Rate the item".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: SingleChildScrollView(
child: Column(
children: [
Container(
width: Responsive.width(100, context),
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Rate for".tr, style: TextStyle(color: isDark ? AppThemeData.grey400 : AppThemeData.grey500, fontSize: 16, fontFamily: AppThemeData.medium)),
Text(
"${controller.productModel.value.name}".tr,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontSize: 18, fontFamily: AppThemeData.semiBold),
),
const SizedBox(height: 10),
RatingBar.builder(
initialRating: controller.ratings.value,
minRating: 1,
direction: Axis.horizontal,
itemCount: 5,
itemSize: 26,
unratedColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
itemPadding: const EdgeInsets.symmetric(horizontal: 6.0),
itemBuilder: (context, _) => const Icon(Icons.star, color: AppThemeData.warning300),
onRatingUpdate: (double rate) {
controller.ratings.value = rate;
},
),
Padding(padding: const EdgeInsets.symmetric(vertical: 20), child: MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200)),
ListView.builder(
itemCount: controller.reviewAttributeList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
children: [
Expanded(
child: Text(
controller.reviewAttributeList[index].title.toString(),
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16, fontFamily: AppThemeData.semiBold),
),
),
RatingBar.builder(
initialRating:
controller.ratingModel.value.id == null ? 0.0 : controller.ratingModel.value.reviewAttributes?[controller.reviewAttributeList[index].id] ?? 0.0,
minRating: 1,
direction: Axis.horizontal,
itemCount: 5,
itemSize: 18,
unratedColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
itemPadding: const EdgeInsets.symmetric(horizontal: 2.0),
itemBuilder: (context, _) => const Icon(Icons.star, color: AppThemeData.warning300),
onRatingUpdate: (double rate) {
controller.reviewAttribute.addEntries([MapEntry(controller.reviewAttributeList[index].id.toString(), rate)]);
},
),
],
),
);
},
),
const SizedBox(height: 20),
DottedBorder(
options: RoundedRectDottedBorderOptions(
radius: const Radius.circular(12),
dashPattern: const [6, 6, 6, 6],
color: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
),
child: Container(
decoration: BoxDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, borderRadius: const BorderRadius.all(Radius.circular(12))),
child: SizedBox(
height: Responsive.height(20, context),
width: Responsive.width(90, context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset('assets/icons/ic_folder.svg'),
const SizedBox(height: 10),
Text(
"Choose a image and upload here".tr,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontFamily: AppThemeData.medium, fontSize: 16),
),
const SizedBox(height: 5),
Text("JPEG, PNG".tr, style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.grey200 : AppThemeData.grey700, fontFamily: AppThemeData.regular)),
const SizedBox(height: 10),
RoundedButtonFill(
title: "Brows Image".tr,
color: AppThemeData.primary50,
width: 30,
height: 5,
textColor: AppThemeData.primary300,
onPress: () async {
buildBottomSheet(context, controller);
},
),
],
),
),
),
),
const SizedBox(height: 10),
controller.images.isEmpty
? const SizedBox()
: SizedBox(
height: 90,
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: controller.images.length,
shrinkWrap: true,
scrollDirection: Axis.horizontal,
// physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(10)),
child:
(controller.images[index] is XFile)
? Image.file(File((controller.images[index] as XFile).path), fit: BoxFit.cover, width: 80, height: 80)
: NetworkImageWidget(imageUrl: controller.images[index]?.toString() ?? '', fit: BoxFit.cover, width: 80, height: 80),
// controller.images[index].runtimeType == XFile
// ? Image.file(File(controller.images[index].path), fit: BoxFit.cover, width: 80, height: 80)
// : NetworkImageWidget(imageUrl: controller.images[index], fit: BoxFit.cover, width: 80, height: 80),
),
Positioned(
bottom: 0,
top: 0,
left: 0,
right: 0,
child: InkWell(
onTap: () {
controller.images.removeAt(index);
},
child: const Icon(Icons.remove_circle, size: 28, color: AppThemeData.danger300),
),
),
],
),
);
},
),
),
const SizedBox(height: 10),
],
),
),
DottedBorder(
options: RoundedRectDottedBorderOptions(
radius: const Radius.circular(12),
dashPattern: const [6, 6, 6, 6],
color: isDark ? AppThemeData.grey700 : AppThemeData.grey200,
),
child: TextFormField(
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
controller: controller.commentController.value,
maxLines: 4,
textInputAction: TextInputAction.done,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium),
decoration: InputDecoration(
errorStyle: const TextStyle(color: Colors.red),
filled: true,
fillColor: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
disabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
border: InputBorder.none,
hintText: "Type comment".tr,
hintStyle: TextStyle(fontSize: 14, color: isDark ? AppThemeData.grey600 : AppThemeData.grey400, fontFamily: AppThemeData.regular),
),
),
),
],
),
),
),
],
),
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Submit Review".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
onPress: () async {
controller.saveRating();
},
),
),
),
);
},
);
}
Future buildBottomSheet(BuildContext context, RateProductController controller) {
return showModalBottomSheet(
context: context,
builder: (context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return StatefulBuilder(
builder: (context, setState) {
return SizedBox(
height: Responsive.height(22, context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(top: 15),
child: Text("Please Select".tr, style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.bold, fontSize: 16)),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () => controller.pickFile(source: ImageSource.camera), icon: const Icon(Icons.camera_alt, size: 32)),
Padding(padding: const EdgeInsets.only(top: 3), child: Text("Camera".tr)),
],
),
),
Padding(
padding: const EdgeInsets.all(18.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(onPressed: () => controller.pickFile(source: ImageSource.gallery), icon: const Icon(Icons.photo_library_sharp, size: 32)),
Padding(padding: const EdgeInsets.only(top: 3), child: Text("Gallery".tr)),
],
),
),
],
),
],
),
);
},
);
},
);
}
}

View File

@@ -0,0 +1,156 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/refer_friend_controller.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:share_plus/share_plus.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
class ReferFriendScreen extends StatelessWidget {
const ReferFriendScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ReferFriendController(),
builder: (controller) {
return Scaffold(
body:
controller.isLoading.value
? Constant.loader()
: Container(
width: Responsive.width(100, context),
height: Responsive.height(100, context),
decoration: const BoxDecoration(image: DecorationImage(image: AssetImage("assets/images/refer_friend.png"), fit: BoxFit.fill)),
child: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () {
Get.back();
},
child: const Icon(Icons.arrow_back, color: AppThemeData.grey50),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 60),
Center(child: SvgPicture.asset("assets/images/referal_top.svg")),
const SizedBox(height: 10),
Text(
"Refer your friend and earn".tr,
style: TextStyle(fontSize: 22, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500),
),
const SizedBox(width: 4),
Text(
"${Constant.amountShow(amount: Constant.sectionConstantModel!.referralAmount)} ${'Each🎉'.tr}",
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
const SizedBox(height: 32),
Text(
"Invite Friends & Businesses".tr,
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.ecommerce100 : AppThemeData.ecommerceDark100,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
"${'Invite your friends to sign up with Foodie using your code, and youll earn'.tr} ${Constant.amountShow(amount: Constant.sectionConstantModel!.referralAmount)} ${'after their Success the first order! 💸🍔'.tr}"
.tr,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500),
),
const SizedBox(height: 40),
Container(
decoration: ShapeDecoration(
gradient: const LinearGradient(begin: Alignment(0.00, -1.00), end: Alignment(0, 1), colors: [Color(0xFF271366), Color(0xFF4826B2)]),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
shadows: const [BoxShadow(color: Color(0x14FFFFFF), blurRadius: 120, offset: Offset(0, 0), spreadRadius: 0)],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 80, vertical: 16),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
controller.referralModel.value.referralCode.toString(),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.ecommerce100 : AppThemeData.ecommerceDark100,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 10),
InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: controller.referralModel.value.referralCode.toString()));
ShowToastDialog.showToast("Copied".tr);
},
child: const Icon(Icons.copy, color: AppThemeData.ecommerce100),
),
],
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: Row(
children: [
Expanded(child: Divider(thickness: 1, color: isDark ? AppThemeData.ecommerce100 : AppThemeData.ecommerceDark100)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
child: Text(
"or".tr,
textAlign: TextAlign.center,
style: TextStyle(
color: isDark ? AppThemeData.ecommerce100 : AppThemeData.ecommerceDark100,
fontSize: 12,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
),
),
),
Expanded(child: Divider(color: isDark ? AppThemeData.ecommerce100 : AppThemeData.ecommerceDark100)),
],
),
),
RoundedButtonFill(
title: "Share Code".tr,
width: 55,
color: AppThemeData.ecommerce300,
textColor: AppThemeData.grey50,
onPress: () async {
await Share.share(
"${"Hey there, thanks for choosing Foodie. Hope you love our product. If you do, share it with your friends using code".tr} ${controller.referralModel.value.referralCode.toString()} ${"and get".tr}${Constant.amountShow(amount: Constant.sectionConstantModel!.referralAmount.toString())} ${"when order completed".tr}",
);
},
),
],
),
],
),
),
),
),
);
},
);
}
}

View File

@@ -0,0 +1,206 @@
import 'package:customer/constant/collection_name.dart';
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/review_list_controller.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/rating_model.dart';
import 'package:customer/models/review_attribute_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import '../../../controllers/theme_controller.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../service/fire_store_utils.dart';
import '../chat_screens/full_screen_image_viewer.dart';
class ReviewListScreen extends StatelessWidget {
const ReviewListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: ReviewListController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Reviews".tr, textAlign: TextAlign.start, style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900)),
),
body:
controller.isLoading.value
? Constant.loader()
: controller.ratingList.isEmpty
? Constant.showEmptyView(message: "No Review found".tr)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: ListView.builder(
itemCount: controller.ratingList.length,
itemBuilder: (context, index) {
RatingModel ratingModel = controller.ratingList[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Container(
decoration: ShapeDecoration(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
shape: RoundedRectangleBorder(side: BorderSide(width: 1, color: isDark ? AppThemeData.grey700 : AppThemeData.grey200), borderRadius: BorderRadius.circular(12)),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(ratingModel.uname.toString(), style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 18, fontFamily: AppThemeData.semiBold)),
Visibility(
visible: ratingModel.productId != null,
child: FutureBuilder(
future: FireStoreUtils.fireStore.collection(CollectionName.vendorProducts).doc(ratingModel.productId?.split('~').first).get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('');
} else {
if (snapshot.hasError) {
return const Text('');
} else if (snapshot.data == null) {
return const Text('');
} else if (snapshot.data != null) {
ProductModel model = ProductModel.fromJson(snapshot.data!.data()!);
return Text(
'${'Rate for'.tr} - ${model.name ?? ''}',
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 14, fontFamily: AppThemeData.semiBold),
);
} else {
return const Text('');
}
}
},
),
),
const SizedBox(height: 5),
RatingBar.builder(
ignoreGestures: true,
initialRating: ratingModel.rating ?? 0.0,
minRating: 1,
direction: Axis.horizontal,
itemCount: 5,
itemSize: 18,
itemPadding: const EdgeInsets.symmetric(horizontal: 2.0),
itemBuilder: (context, _) => const Icon(Icons.star, color: AppThemeData.warning300),
unratedColor: AppThemeData.grey400,
onRatingUpdate: (double rate) {},
),
const SizedBox(height: 5),
Visibility(
visible: ratingModel.comment != '' && ratingModel.comment != null,
child: Text(
ratingModel.comment.toString(),
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16, fontFamily: AppThemeData.medium),
),
),
const SizedBox(height: 5),
Visibility(
visible: ratingModel.reviewAttributes != null,
child: ListView.builder(
itemCount: ratingModel.reviewAttributes!.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
String key = ratingModel.reviewAttributes!.keys.elementAt(index);
dynamic value = ratingModel.reviewAttributes![key];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
children: [
FutureBuilder(
future: FireStoreUtils.fireStore.collection(CollectionName.reviewAttributes).doc(key).get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Text('');
} else {
if (snapshot.hasError) {
return const Text('');
} else if (snapshot.data == null) {
return const Text('');
} else {
ReviewAttributeModel model = ReviewAttributeModel.fromJson(snapshot.data!.data()!);
return Expanded(
child: Text(
model.title.toString(),
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontSize: 16, fontFamily: AppThemeData.semiBold),
),
);
}
}
},
),
RatingBar.builder(
ignoreGestures: true,
initialRating: value == null ? 0.0 : value ?? 0.0,
minRating: 1,
direction: Axis.horizontal,
itemCount: 5,
itemSize: 15,
unratedColor: AppThemeData.grey400,
itemPadding: const EdgeInsets.symmetric(horizontal: 2.0),
itemBuilder: (context, _) => const Icon(Icons.star, color: AppThemeData.warning300),
onRatingUpdate: (double rate) {},
),
],
),
);
},
),
),
if (ratingModel.photos?.isNotEmpty == true)
SizedBox(
height: Responsive.height(9, context),
child: ListView.builder(
itemCount: ratingModel.photos?.length,
scrollDirection: Axis.horizontal,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Get.to(FullScreenImageViewer(imageUrl: ratingModel.photos?[index]));
},
child: Padding(
padding: const EdgeInsets.all(6.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: NetworkImageWidget(
imageUrl: ratingModel.photos?[index],
height: Responsive.height(9, context),
width: Responsive.height(8, context),
fit: BoxFit.fill,
),
),
),
);
},
),
),
const SizedBox(height: 5),
Text(
Constant.timestampToDateTime(ratingModel.createdAt!),
style: TextStyle(color: isDark ? AppThemeData.grey300 : AppThemeData.grey600, fontSize: 14, fontFamily: AppThemeData.medium),
),
],
),
),
),
);
},
),
),
);
},
);
}
}

View File

@@ -0,0 +1,56 @@
import 'package:customer/controllers/scan_qr_code_controller.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:qr_code_dart_scan/qr_code_dart_scan.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class ScanQrCodeScreen extends StatelessWidget {
const ScanQrCodeScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetBuilder(
init: ScanQrCodeController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
centerTitle: false,
titleSpacing: 0,
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
title: Text("Scan QR Code".tr, style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500)),
),
body: QRCodeDartScanView(
// enable scan invert qr code ( default = false)
typeScan: TypeScan.live,
// if TypeScan.takePicture will try decode when click to take a picture(default TypeScan.live)
onCapture: (Result result) {
Get.back();
ShowToastDialog.showLoader("Please wait...".tr);
if (controller.allNearestRestaurant.isNotEmpty) {
if (controller.allNearestRestaurant.where((vendor) => vendor.id == result.text).isEmpty) {
ShowToastDialog.closeLoader();
ShowToastDialog.showToast("Store is not available".tr);
return;
}
VendorModel storeModel = controller.allNearestRestaurant.firstWhere((vendor) => vendor.id == result.text);
ShowToastDialog.closeLoader();
Get.back();
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": storeModel});
} else {
Get.back();
ShowToastDialog.showToast("Store is not available".tr);
}
},
),
);
},
);
}
}

View File

@@ -0,0 +1,447 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/search_controller.dart';
import 'package:customer/models/product_model.dart';
import 'package:customer/models/vendor_model.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/responsive.dart';
import 'package:customer/themes/text_field_widget.dart';
import '../../../controllers/theme_controller.dart';
import 'package:customer/utils/network_image_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import '../../../service/fire_store_utils.dart';
import '../../../widget/restaurant_image_view.dart';
import '../restaurant_details_screen/restaurant_details_screen.dart';
class SearchScreen extends StatelessWidget {
const SearchScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: SearchScreenController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text(
Constant.sectionConstantModel?.name?.toLowerCase().contains('restaurants') == true ? "Find your favorite products and nearby stores" : "Search Item & Store".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(55),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextFieldWidget(
hintText: Constant.sectionConstantModel?.name?.toLowerCase().contains('restaurants') == true ? 'Find your favorite products and nearby stores'.tr : 'Search the store and item'.tr,
prefix: Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: SvgPicture.asset("assets/icons/ic_search.svg")),
controller: null,
onchange: (value) {
controller.onSearchTextChanged(value);
},
),
),
),
),
body:
controller.isLoading.value
? Constant.loader()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
controller.vendorSearchList.isEmpty
? const SizedBox()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Store".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
const Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Divider()),
],
),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: controller.vendorSearchList.length,
itemBuilder: (context, index) {
VendorModel vendorModel = controller.vendorSearchList[index];
return InkWell(
onTap: () {
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Container(
decoration: ShapeDecoration(color: isDark ? AppThemeData.grey900 : AppThemeData.grey50, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
ClipRRect(
borderRadius: const BorderRadius.only(topLeft: Radius.circular(16), topRight: Radius.circular(16)),
child: Stack(
children: [
RestaurantImageView(vendorModel: vendorModel),
Container(
height: Responsive.height(20, context),
width: Responsive.width(100, context),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(-0.00, -1.00),
end: const Alignment(0, 1),
colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
),
),
),
],
),
),
Transform.translate(
offset: Offset(Responsive.width(-3, context), Responsive.height(17.5, context)),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Visibility(
visible: (vendorModel.isSelfDelivery == true && Constant.isSelfDeliveryFeature == true),
child: Row(
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: BoxDecoration(
color: AppThemeData.success300,
borderRadius: BorderRadius.circular(120), // Optional
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_free_delivery.svg"),
const SizedBox(width: 5),
Text(
"Free Delivery".tr,
style: TextStyle(fontSize: 14, color: AppThemeData.success300, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600),
),
],
),
),
const SizedBox(width: 6),
],
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.primary600 : AppThemeData.primary50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: ColorFilter.mode(AppThemeData.primary300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: vendorModel.reviewsCount!.toStringAsFixed(0), reviewSum: vendorModel.reviewsSum.toString())} (${vendorModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
decoration: ShapeDecoration(
color: isDark ? AppThemeData.ecommerce600 : AppThemeData.ecommerce50,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(120)),
),
child: Row(
children: [
SvgPicture.asset("assets/icons/ic_map_distance.svg", colorFilter: ColorFilter.mode(AppThemeData.ecommerce300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.getDistance(lat1: vendorModel.latitude.toString(), lng1: vendorModel.longitude.toString(), lat2: Constant.selectedLocation.location!.latitude.toString(), lng2: Constant.selectedLocation.location!.longitude.toString())} ${Constant.distanceType}",
style: TextStyle(
fontSize: 14,
color: isDark ? AppThemeData.ecommerce300 : AppThemeData.ecommerce300,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
vendorModel.title.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
fontSize: 18,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.semiBold,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
),
),
Text(
vendorModel.location.toString(),
textAlign: TextAlign.start,
maxLines: 1,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.medium,
fontWeight: FontWeight.w500,
color: isDark ? AppThemeData.grey400 : AppThemeData.grey400,
),
),
],
),
),
const SizedBox(height: 10),
],
),
),
),
);
},
),
controller.productSearchList.isEmpty
? const SizedBox()
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Items".tr,
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
const Padding(padding: EdgeInsets.symmetric(vertical: 10), child: Divider()),
],
),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: controller.productSearchList.length,
itemBuilder: (context, index) {
ProductModel productModel = controller.productSearchList[index];
return FutureBuilder(
future: getPrice(productModel),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Constant.loader();
} else {
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.data == null) {
return const SizedBox();
} else {
Map<String, dynamic> map = snapshot.data!;
String price = map['price'];
String disPrice = map['disPrice'];
return InkWell(
onTap: () async {
await FireStoreUtils.getVendorById(productModel.vendorID.toString()).then((value) {
if (value != null) {
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": value});
}
});
},
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Constant.sectionConstantModel!.isProductDetails == false
? SizedBox()
: productModel.nonveg == true || productModel.veg == true
? Row(
children: [
productModel.nonveg == true ? SvgPicture.asset("assets/icons/ic_nonveg.svg") : SvgPicture.asset("assets/icons/ic_veg.svg"),
const SizedBox(width: 5),
Text(
productModel.nonveg == true ? "Non Veg.".tr : "Pure veg.".tr,
style: TextStyle(
color: productModel.nonveg == true ? AppThemeData.danger300 : AppThemeData.success400,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
)
: SizedBox(),
const SizedBox(height: 5),
Text(
productModel.name.toString(),
style: TextStyle(
fontSize: 18,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
double.parse(disPrice) <= 0
? Text(
Constant.amountShow(amount: price),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
)
: Row(
children: [
Text(
Constant.amountShow(amount: disPrice),
style: TextStyle(
fontSize: 16,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: 5),
Text(
Constant.amountShow(amount: price),
style: TextStyle(
fontSize: 14,
decoration: TextDecoration.lineThrough,
decorationColor: isDark ? AppThemeData.grey500 : AppThemeData.grey400,
color: isDark ? AppThemeData.grey500 : AppThemeData.grey400,
fontFamily: AppThemeData.semiBold,
fontWeight: FontWeight.w600,
),
),
],
),
Row(
children: [
SvgPicture.asset("assets/icons/ic_star.svg", colorFilter: const ColorFilter.mode(AppThemeData.warning300, BlendMode.srcIn)),
const SizedBox(width: 5),
Text(
"${Constant.calculateReview(reviewCount: productModel.reviewsCount!.toStringAsFixed(0), reviewSum: productModel.reviewsSum.toString())} (${productModel.reviewsCount!.toStringAsFixed(0)})",
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500),
),
],
),
Text(
"${productModel.description}",
maxLines: 2,
style: TextStyle(
overflow: TextOverflow.ellipsis,
color: isDark ? AppThemeData.grey50 : AppThemeData.grey900,
fontFamily: AppThemeData.regular,
fontWeight: FontWeight.w400,
),
),
],
),
),
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)),
child: Stack(
children: [
NetworkImageWidget(
imageUrl: productModel.photo.toString(),
fit: BoxFit.cover,
height: Responsive.height(16, context),
width: Responsive.width(34, context),
),
Container(
height: Responsive.height(16, context),
width: Responsive.width(34, context),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: const Alignment(-0.00, -1.00),
end: const Alignment(0, 1),
colors: [Colors.black.withOpacity(0), const Color(0xFF111827)],
),
),
),
],
),
),
],
),
),
);
}
}
},
);
},
),
],
),
),
),
);
},
);
}
Future<Map<String, dynamic>> getPrice(ProductModel productModel) async {
String price = "0.0";
String disPrice = "0.0";
List<String> selectedVariants = [];
List<String> selectedIndexVariants = [];
List<String> selectedIndexArray = [];
print("=======>");
print(productModel.price);
print(productModel.disPrice);
VendorModel? vendorModel = await FireStoreUtils.getVendorById(productModel.vendorID.toString());
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 = Constant.productCommissionPrice(vendorModel, '0');
}
} else {
price = Constant.productCommissionPrice(vendorModel!, productModel.price.toString());
disPrice = Constant.productCommissionPrice(vendorModel, productModel.disPrice.toString());
}
return {'price': price, 'disPrice': disPrice};
}
}

View File

@@ -0,0 +1,43 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_html/flutter_html.dart';
import '../../../controllers/theme_controller.dart';
class TermsAndConditionScreen extends StatelessWidget {
final String? type;
const TermsAndConditionScreen({super.key, this.type});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return Scaffold(
backgroundColor: isDark ? AppThemeData.grey50 : AppThemeData.grey50,
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
centerTitle: false,
automaticallyImplyLeading: false,
titleSpacing: 0,
leading: InkWell(
onTap: () {
Get.back();
},
child: Icon(Icons.chevron_left_outlined, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
title: Text(
type == "privacy" ? "Privacy Policy".tr : "Terms & Conditions".tr,
style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontFamily: AppThemeData.bold, fontSize: 18),
),
elevation: 0,
bottom: PreferredSize(preferredSize: const Size.fromHeight(4.0), child: Container(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200, height: 4.0)),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
child: SingleChildScrollView(child: Html(shrinkWrap: true, data: type == "privacy" ? Constant.privacyPolicy : Constant.termsAndConditions)),
),
);
}
}

View File

@@ -0,0 +1,182 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/wallet_controller.dart';
import 'package:customer/payment/createRazorPayOrderModel.dart';
import 'package:customer/payment/rozorpayConroller.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import 'package:customer/themes/text_field_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../../controllers/theme_controller.dart';
import '../../../themes/show_toast_dialog.dart';
class PaymentListScreen extends StatelessWidget {
const PaymentListScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: WalletController(),
builder: (controller) {
return Scaffold(
appBar: AppBar(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
centerTitle: false,
titleSpacing: 0,
title: Text("Top up Wallet".tr, style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500)),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextFieldWidget(
title: 'Amount'.tr,
hintText: 'Enter Amount'.tr,
controller: controller.topUpAmountController.value,
textInputType: const TextInputType.numberWithOptions(decimal: true, signed: true),
prefix: Padding(padding: const EdgeInsets.all(12.0), child: Text(Constant.currencyModel!.symbol.toString(), style: const TextStyle(fontSize: 20, color: AppThemeData.grey800))),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]'))],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text(
"Select Top up Options".tr,
style: TextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: BoxDecoration(borderRadius: const BorderRadius.all(Radius.circular(20)), color: isDark ? AppThemeData.grey900 : AppThemeData.grey50),
child: Column(
children: [
Visibility(visible: controller.stripeModel.value.isEnabled == true, child: cardDecoration(controller, PaymentGateway.stripe, isDark, "assets/images/stripe.png")),
Visibility(visible: controller.payPalModel.value.isEnabled == true, child: cardDecoration(controller, PaymentGateway.paypal, isDark, "assets/images/paypal.png")),
Visibility(visible: controller.payStackModel.value.isEnable == true, child: cardDecoration(controller, PaymentGateway.payStack, isDark, "assets/images/paystack.png")),
Visibility(
visible: controller.mercadoPagoModel.value.isEnabled == true,
child: cardDecoration(controller, PaymentGateway.mercadoPago, isDark, "assets/images/mercado-pago.png"),
),
Visibility(
visible: controller.flutterWaveModel.value.isEnable == true,
child: cardDecoration(controller, PaymentGateway.flutterWave, isDark, "assets/images/flutterwave_logo.png"),
),
Visibility(visible: controller.payFastModel.value.isEnable == true, child: cardDecoration(controller, PaymentGateway.payFast, isDark, "assets/images/payfast.png")),
Visibility(visible: controller.razorPayModel.value.isEnabled == true, child: cardDecoration(controller, PaymentGateway.razorpay, isDark, "assets/images/razorpay.png")),
Visibility(visible: controller.midTransModel.value.enable == true, child: cardDecoration(controller, PaymentGateway.midTrans, isDark, "assets/images/midtrans.png")),
Visibility(visible: controller.orangeMoneyModel.value.enable == true, child: cardDecoration(controller, PaymentGateway.orangeMoney, isDark, "assets/images/orange_money.png")),
Visibility(visible: controller.xenditModel.value.enable == true, child: cardDecoration(controller, PaymentGateway.xendit, isDark, "assets/images/xendit.png")),
],
),
),
),
],
),
),
bottomNavigationBar: Container(
color: isDark ? AppThemeData.grey900 : AppThemeData.grey50,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Padding(
padding: const EdgeInsets.only(bottom: 20),
child: RoundedButtonFill(
title: "Top-up".tr,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
fontSizes: 16,
onPress: () async {
if (controller.topUpAmountController.value.text.isEmpty) {
ShowToastDialog.showToast("Please Enter Amount".tr);
} else {
if (double.parse(controller.topUpAmountController.value.text) >= double.parse(Constant.minimumAmountToDeposit.toString())) {
if (controller.selectedPaymentMethod.value == PaymentGateway.stripe.name) {
controller.stripeMakePayment(amount: controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.paypal.name) {
controller.paypalPaymentSheet(controller.topUpAmountController.value.text, context);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payStack.name) {
controller.payStackPayment(controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.mercadoPago.name) {
controller.mercadoPagoMakePayment(context: context, amount: controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.flutterWave.name) {
controller.flutterWaveInitiatePayment(context: context, amount: controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payFast.name) {
controller.payFastPayment(context: context, amount: controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.midTrans.name) {
controller.midtransMakePayment(context: context, amount: controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.orangeMoney.name) {
controller.orangeMakePayment(context: context, amount: controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.xendit.name) {
controller.xenditPayment(context, controller.topUpAmountController.value.text);
} else if (controller.selectedPaymentMethod.value == PaymentGateway.razorpay.name) {
RazorPayController().createOrderRazorPay(amount: double.parse(controller.topUpAmountController.value.text), razorpayModel: controller.razorPayModel.value).then((value) {
if (value == null) {
Get.back();
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
} else {
CreateRazorPayOrderModel result = value;
controller.openCheckout(amount: controller.topUpAmountController.value.text, orderId: result.id);
}
});
} else {
ShowToastDialog.showToast("Please select payment method".tr);
}
} else {
ShowToastDialog.showToast("${'Please Enter minimum amount of'.tr} ${Constant.amountShow(amount: Constant.minimumAmountToDeposit)}");
}
}
},
),
),
),
);
},
);
}
Obx cardDecoration(WalletController controller, PaymentGateway value, isDark, String image) {
return Obx(
() => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: InkWell(
onTap: () {
controller.selectedPaymentMethod.value = value.name;
},
child: Row(
children: [
Container(
width: 50,
height: 50,
decoration: ShapeDecoration(shape: RoundedRectangleBorder(side: const BorderSide(width: 1, color: Color(0xFFE5E7EB)), borderRadius: BorderRadius.circular(8))),
child: Padding(padding: EdgeInsets.all(value.name == "payFast" ? 0 : 8.0), child: Image.asset(image)),
),
const SizedBox(width: 10),
Expanded(
child: Text(
value.name.capitalizeString(),
textAlign: TextAlign.start,
style: TextStyle(fontFamily: AppThemeData.medium, fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
),
),
const Expanded(child: SizedBox()),
Radio(
value: value.name,
groupValue: controller.selectedPaymentMethod.value,
activeColor: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
onChanged: (value) {
controller.selectedPaymentMethod.value = value.toString();
},
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,269 @@
import 'package:customer/constant/constant.dart';
import 'package:customer/controllers/wallet_controller.dart';
import 'package:customer/models/wallet_transaction_model.dart';
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/payment_list_screen.dart';
import 'package:customer/themes/app_them_data.dart';
import 'package:customer/themes/round_button_fill.dart';
import '../../../constant/collection_name.dart';
import '../../../controllers/theme_controller.dart';
import '../../../models/cab_order_model.dart';
import '../../../models/onprovider_order_model.dart';
import '../../../models/order_model.dart';
import '../../../models/parcel_order_model.dart';
import '../../../models/rental_order_model.dart';
import '../../../service/fire_store_utils.dart';
import '../../../themes/show_toast_dialog.dart';
import '../../../widget/my_separator.dart';
import '../../auth_screens/login_screen.dart';
import '../../cab_service_screens/cab_order_details.dart';
import '../../on_demand_service/on_demand_order_details_screen.dart';
import '../../parcel_service/parcel_order_details.dart';
import '../../rental_service/rental_order_details_screen.dart';
import '../order_list_screen/order_details_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
class WalletScreen extends StatelessWidget {
const WalletScreen({super.key});
@override
Widget build(BuildContext context) {
final themeController = Get.find<ThemeController>();
final isDark = themeController.isDark.value;
return GetX(
init: WalletController(),
builder: (controller) {
return Scaffold(
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
body:
controller.isLoading.value
? Constant.loader()
: Constant.userModel == null
? Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset("assets/images/login.gif", height: 120),
const SizedBox(height: 12),
Text("Please Log In to Continue".tr, style: TextStyle(color: isDark ? AppThemeData.grey100 : AppThemeData.grey800, fontSize: 22, fontFamily: AppThemeData.semiBold)),
const SizedBox(height: 5),
Text(
"Youre not logged in. Please sign in to access your account and explore all features.".tr,
textAlign: TextAlign.center,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey500, fontSize: 16, fontFamily: AppThemeData.bold),
),
const SizedBox(height: 20),
RoundedButtonFill(
title: "Log in".tr,
width: 55,
height: 5.5,
color: AppThemeData.primary300,
textColor: AppThemeData.grey50,
onPress: () async {
Get.offAll(const LoginScreen());
},
),
],
),
)
: Padding(
padding: EdgeInsets.only(top: MediaQuery.of(context).viewPadding.top),
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"My Wallet".tr,
style: TextStyle(fontSize: 24, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w500),
),
Text(
"Keep track of your balance, transactions, and payment methods all in one place.".tr,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey900, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400),
),
],
),
),
],
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
image: DecorationImage(image: AssetImage("assets/images/wallet.png"), fit: BoxFit.fill),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: Column(
children: [
Text(
"My Wallet".tr,
maxLines: 1,
style: TextStyle(
color: isDark ? AppThemeData.primary100 : AppThemeData.primary100,
fontSize: 16,
overflow: TextOverflow.ellipsis,
fontFamily: AppThemeData.regular,
),
),
Text(
Constant.amountShow(amount: controller.userModel.value.walletAmount.toString()),
maxLines: 1,
style: TextStyle(color: isDark ? AppThemeData.grey50 : AppThemeData.grey50, fontSize: 40, overflow: TextOverflow.ellipsis, fontFamily: AppThemeData.bold),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 80),
child: RoundedButtonFill(
title: "Top up".tr,
color: AppThemeData.warning300,
textColor: AppThemeData.grey900,
onPress: () {
Get.to(const PaymentListScreen());
},
),
),
],
),
),
),
),
],
),
Expanded(
child:
controller.walletTransactionList.isEmpty
? Constant.showEmptyView(message: "Transaction not found".tr)
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: ListView.builder(
padding: EdgeInsets.zero,
itemCount: controller.walletTransactionList.length,
itemBuilder: (context, index) {
WalletTransactionModel walletTractionModel = controller.walletTransactionList[index];
return transactionCard(controller, isDark, walletTractionModel);
},
),
),
),
],
),
),
);
},
);
}
Column transactionCard(WalletController controller, isDark, WalletTransactionModel transactionModel) {
return Column(
children: [
InkWell(
onTap: () async {
final orderId = transactionModel.orderId.toString();
final orderData = await FireStoreUtils.getOrderByIdFromAllCollections(orderId);
if (orderData != null) {
final collection = orderData['collection_name'];
switch (collection) {
case CollectionName.parcelOrders:
Get.to(const ParcelOrderDetails(), arguments: ParcelOrderModel.fromJson(orderData));
break;
case CollectionName.providerOrders:
Get.to(const OnDemandOrderDetailsScreen(), arguments: OnProviderOrderModel.fromJson(orderData));
break;
case CollectionName.rentalOrders:
Get.to(() => RentalOrderDetailsScreen(), arguments: RentalOrderModel.fromJson(orderData));
break;
case CollectionName.rides:
Get.to(const CabOrderDetails(), arguments: {"cabOrderModel": CabOrderModel.fromJson(orderData)});
break;
case CollectionName.vendorOrders:
Get.to(const OrderDetailsScreen(), arguments: {"orderModel": OrderModel.fromJson(orderData)});
break;
default:
ShowToastDialog.showToast("Order details not available".tr);
}
}
},
// onTap: () async {
// await FireStoreUtils
// .getOrderByOrderId(transactionModel.orderId.toString())
// .then((value) {
// if (value != null) {
// Get.to(
// const OrderDetailsScreen(),
// arguments: {"orderModel": value},
// );
// }
// });
// },
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 5),
child: Row(
children: [
Container(
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(side: BorderSide(width: 1, color: isDark ? AppThemeData.grey800 : AppThemeData.grey100), borderRadius: BorderRadius.circular(8)),
),
child: Padding(
padding: const EdgeInsets.all(16),
child:
transactionModel.isTopup == false
? SvgPicture.asset("assets/icons/ic_debit.svg", height: 16, width: 16)
: SvgPicture.asset("assets/icons/ic_credit.svg", height: 16, width: 16),
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
transactionModel.note.toString(),
style: TextStyle(fontSize: 16, fontFamily: AppThemeData.semiBold, fontWeight: FontWeight.w600, color: isDark ? AppThemeData.grey100 : AppThemeData.grey800),
),
),
Text(
Constant.amountShow(amount: transactionModel.amount.toString()),
style: TextStyle(fontSize: 16, fontFamily: AppThemeData.medium, color: transactionModel.isTopup == true ? AppThemeData.success400 : AppThemeData.danger300),
),
],
),
const SizedBox(height: 2),
Text(
Constant.timestampToDateTime(transactionModel.date!),
style: TextStyle(fontSize: 12, fontFamily: AppThemeData.medium, fontWeight: FontWeight.w500, color: isDark ? AppThemeData.grey200 : AppThemeData.grey700),
),
],
),
),
],
),
),
),
Padding(padding: const EdgeInsets.symmetric(vertical: 5), child: MySeparator(color: isDark ? AppThemeData.grey700 : AppThemeData.grey200)),
],
);
}
}
enum PaymentGateway { payFast, mercadoPago, paypal, stripe, flutterWave, payStack, razorpay, cod, wallet, midTrans, orangeMoney, xendit }