INFRA: Set Up Project.
This commit is contained in:
98
lib/screen_ui/auth_screens/forgot_password_screen.dart
Normal file
98
lib/screen_ui/auth_screens/forgot_password_screen.dart
Normal file
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/forgot_password_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import 'login_screen.dart';
|
||||
|
||||
class ForgotPasswordScreen extends StatelessWidget {
|
||||
const ForgotPasswordScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetX<ForgotPasswordController>(
|
||||
init: ForgotPasswordController(),
|
||||
builder: (controller) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
style: TextButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: const Size(0, 40), tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("Skip".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
Padding(padding: const EdgeInsets.only(top: 2), child: Icon(Icons.arrow_forward_ios, size: 16, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, bottom: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Enter your registered email to receive a reset link.".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
TextFieldWidget(title: "Email Address*".tr, hintText: "jerome014@gmail.com", controller: controller.emailEditingController.value),
|
||||
const SizedBox(height: 30),
|
||||
RoundedButtonFill(
|
||||
title: "Send Link".tr,
|
||||
onPress: controller.forgotPassword,
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
textColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: "Remember Password?".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Log in".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: AppThemeData.ecommerce300, decoration: TextDecoration.underline, decorationColor: AppThemeData.ecommerce300),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Get.offAll(() => const LoginScreen());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
186
lib/screen_ui/auth_screens/login_screen.dart
Normal file
186
lib/screen_ui/auth_screens/login_screen.dart
Normal file
@@ -0,0 +1,186 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:customer/screen_ui/auth_screens/sign_up_screen.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../controllers/login_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'forgot_password_screen.dart';
|
||||
import 'mobile_login_screen.dart';
|
||||
|
||||
class LoginScreen extends StatelessWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetX<LoginController>(
|
||||
init: LoginController(),
|
||||
builder: (controller) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.to(() => LocationPermissionScreen());
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Text("Skip".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Icon(Icons.arrow_forward_ios, size: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, bottom: 15),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Log in to explore your all in one vendor app favourites and shop effortlessly.".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
TextFieldWidget(title: "Email Address*".tr, hintText: "jerome014@gmail.com", controller: controller.emailController.value, focusNode: controller.emailFocusNode),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(
|
||||
title: "Password*".tr,
|
||||
hintText: "Enter password".tr,
|
||||
controller: controller.passwordController.value,
|
||||
obscureText: controller.passwordVisible.value,
|
||||
focusNode: controller.passwordFocusNode,
|
||||
suffix: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
controller.passwordVisible.value = !controller.passwordVisible.value;
|
||||
},
|
||||
child:
|
||||
controller.passwordVisible.value
|
||||
? SvgPicture.asset("assets/icons/ic_password_show.svg", colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn))
|
||||
: SvgPicture.asset("assets/icons/ic_password_close.svg", colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () => Get.to(() => const ForgotPasswordScreen()),
|
||||
child: Text("Forgot Password".tr, style: AppThemeData.semiBoldTextStyle(color: AppThemeData.info400)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
RoundedButtonFill(
|
||||
title: "Log in".tr,
|
||||
onPress: controller.loginWithEmail,
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
textColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(width: 52, height: 1, color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey300),
|
||||
const SizedBox(width: 15),
|
||||
Text("or continue with".tr, style: AppThemeData.regularTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900.withOpacity(0.6))),
|
||||
const SizedBox(width: 15),
|
||||
Container(width: 52, height: 1, color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey300),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
RoundedButtonFill(
|
||||
title: "Mobile number".tr,
|
||||
onPress: () => Get.to(() => const MobileLoginScreen()),
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
icon: Icon(Icons.mobile_friendly_outlined, size: 20, color: isDark ? AppThemeData.greyDark900 : null),
|
||||
//Image.asset(AppAssets.icMessage, width: 20, height: 18, color: isDark ? AppThemeData.greyDark900 : null),
|
||||
color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey300,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "with Google".tr,
|
||||
textColor: isDark ? AppThemeData.grey100 : AppThemeData.grey900,
|
||||
color: isDark ? AppThemeData.grey900 : AppThemeData.grey100,
|
||||
icon: SvgPicture.asset("assets/icons/ic_google.svg"),
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
onPress: () async {
|
||||
controller.loginWithGoogle();
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Platform.isIOS
|
||||
? Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "with Apple".tr,
|
||||
isCenter: true,
|
||||
textColor: isDark ? AppThemeData.grey100 : AppThemeData.grey900,
|
||||
color: isDark ? AppThemeData.grey900 : AppThemeData.grey100,
|
||||
icon: SvgPicture.asset("assets/icons/ic_apple.svg"),
|
||||
isRight: false,
|
||||
onPress: () async {
|
||||
controller.loginWithApple();
|
||||
},
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: "Didn't have an account?".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Sign up".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: AppThemeData.ecommerce300, decoration: TextDecoration.underline),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Get.offAll(() => const SignUpScreen());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
166
lib/screen_ui/auth_screens/mobile_login_screen.dart
Normal file
166
lib/screen_ui/auth_screens/mobile_login_screen.dart
Normal file
@@ -0,0 +1,166 @@
|
||||
import 'package:country_code_picker/country_code_picker.dart';
|
||||
import 'package:customer/screen_ui/auth_screens/sign_up_screen.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/assets.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/mobile_login_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
|
||||
class MobileLoginScreen extends StatelessWidget {
|
||||
const MobileLoginScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetX<MobileLoginController>(
|
||||
init: MobileLoginController(),
|
||||
builder: (controller) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, size: 20, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.to(() => LocationPermissionScreen());
|
||||
},
|
||||
style: TextButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: const Size(0, 40), tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("Skip".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
Padding(padding: const EdgeInsets.only(top: 2, left: 4), child: Icon(Icons.arrow_forward_ios, size: 16, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Use your mobile number to Log in easily and securely.".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
TextFieldWidget(
|
||||
title: "Mobile Number*".tr,
|
||||
hintText: "Enter Mobile number".tr,
|
||||
controller: controller.mobileController.value,
|
||||
textInputType: const TextInputType.numberWithOptions(signed: true, decimal: true),
|
||||
textInputAction: TextInputAction.done,
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]')), LengthLimitingTextInputFormatter(10)],
|
||||
prefix: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CountryCodePicker(
|
||||
onChanged: (value) {
|
||||
controller.countryCodeController.value.text = value.dialCode ?? Constant.defaultCountryCode;
|
||||
},
|
||||
initialSelection: controller.countryCodeController.value.text.isNotEmpty ? controller.countryCodeController.value.text : Constant.defaultCountryCode,
|
||||
showCountryOnly: false,
|
||||
showOnlyCountryWhenClosed: false,
|
||||
alignLeft: false,
|
||||
textStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : Colors.black),
|
||||
dialogTextStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
searchStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
dialogBackgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
// const Icon(Icons.keyboard_arrow_down_rounded, size: 24, color: AppThemeData.grey400),
|
||||
Container(height: 24, width: 1, color: AppThemeData.grey400),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
RoundedButtonFill(
|
||||
title: "Send Code".tr,
|
||||
onPress: controller.sendOtp,
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
textColor: isDark ? AppThemeData.surfaceDark : Colors.white,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(width: 52, height: 1, color: isDark ? AppThemeData.greyDark300 : AppThemeData.grey300),
|
||||
const SizedBox(width: 15),
|
||||
Text("or continue with".tr, style: AppThemeData.regularTextStyle(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400)),
|
||||
const SizedBox(width: 15),
|
||||
Container(width: 52, height: 1, color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
RoundedButtonFill(
|
||||
title: "Email address".tr,
|
||||
onPress: () => Get.to(() => const SignUpScreen()),
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
icon: Image.asset(AppAssets.icMessage, width: 20, height: 18, color: isDark ? AppThemeData.greyDark900 : null),
|
||||
color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: "Didn't have an account?".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Sign up".tr,
|
||||
style: AppThemeData.mediumTextStyle(
|
||||
color: AppThemeData.ecommerce300,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: AppThemeData.ecommerce300,
|
||||
decorationStyle: TextDecorationStyle.solid,
|
||||
),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Get.offAll(() => const SignUpScreen());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
154
lib/screen_ui/auth_screens/otp_verification_screen.dart
Normal file
154
lib/screen_ui/auth_screens/otp_verification_screen.dart
Normal file
@@ -0,0 +1,154 @@
|
||||
import 'package:customer/screen_ui/auth_screens/sign_up_screen.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
import '../../constant/assets.dart';
|
||||
import '../../controllers/otp_verification_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
|
||||
class OtpVerificationScreen extends StatelessWidget {
|
||||
const OtpVerificationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetX<OtpVerifyController>(
|
||||
init: OtpVerifyController(),
|
||||
builder: (controller) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, size: 20, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Handle skip action
|
||||
},
|
||||
style: TextButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: const Size(0, 40), tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("Skip".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2, left: 4),
|
||||
child: Icon(Icons.arrow_forward_ios, size: 16, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${"Enter the OTP sent to your mobile".tr} ${controller.countryCode} ${controller.maskPhoneNumber(controller.phoneNumber.value)}",
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
|
||||
/// OTP Field
|
||||
PinCodeTextField(
|
||||
appContext: context,
|
||||
length: 6,
|
||||
controller: controller.otpController.value,
|
||||
keyboardType: TextInputType.number,
|
||||
cursorColor: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500,
|
||||
enablePinAutofill: true,
|
||||
hintCharacter: "-",
|
||||
textStyle: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
pinTheme: PinTheme(
|
||||
shape: PinCodeFieldShape.box,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
fieldHeight: 54,
|
||||
fieldWidth: 51,
|
||||
inactiveColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
inactiveFillColor: Colors.transparent,
|
||||
selectedColor: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400,
|
||||
selectedFillColor: isDark ? AppThemeData.surfaceDark : AppThemeData.grey50,
|
||||
activeColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
activeFillColor: Colors.transparent,
|
||||
errorBorderColor: AppThemeData.danger300,
|
||||
disabledColor: Colors.transparent,
|
||||
borderWidth: 1,
|
||||
),
|
||||
enableActiveFill: true,
|
||||
onCompleted: (v) {},
|
||||
onChanged: (value) {},
|
||||
),
|
||||
|
||||
/// Resend OTP
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(AppAssets.icArrowsClockwise, height: 20, width: 20),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
controller.otpController.value.clear();
|
||||
controller.sendOTP();
|
||||
},
|
||||
child: Text("Resend OTP".tr, style: AppThemeData.semiBoldTextStyle(color: AppThemeData.info400, fontSize: 16)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
/// Verify Button
|
||||
RoundedButtonFill(
|
||||
title: "Verify".tr,
|
||||
onPress: controller.verifyOtp,
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
textColor: isDark ? AppThemeData.surfaceDark : Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: "Didn't have an account?".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Sign up".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: AppThemeData.ecommerce300, decoration: TextDecoration.underline),
|
||||
recognizer: TapGestureRecognizer()..onTap = () => Get.offAll(() => const SignUpScreen()),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
230
lib/screen_ui/auth_screens/sign_up_screen.dart
Normal file
230
lib/screen_ui/auth_screens/sign_up_screen.dart
Normal file
@@ -0,0 +1,230 @@
|
||||
import 'package:country_code_picker/country_code_picker.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/sign_up_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'login_screen.dart';
|
||||
import 'mobile_login_screen.dart';
|
||||
|
||||
class SignUpScreen extends StatelessWidget {
|
||||
const SignUpScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetX<SignUpController>(
|
||||
init: SignUpController(),
|
||||
builder: (controller) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.to(() => LocationPermissionScreen());
|
||||
},
|
||||
style: TextButton.styleFrom(padding: const EdgeInsets.symmetric(horizontal: 12), minimumSize: const Size(0, 40), tapTargetSize: MaterialTapTargetSize.shrinkWrap),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("Skip".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2),
|
||||
child: Icon(Icons.arrow_forward_ios, size: 16, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15, top: 10),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Sign up to explore all our services and start shopping, riding, and more.".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: TextFieldWidget(title: "First Name*".tr, hintText: "Jerome".tr, controller: controller.firstNameController.value)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: TextFieldWidget(title: "Last Name*".tr, hintText: "Bell".tr, controller: controller.lastNameController.value)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(
|
||||
title: "Email Address*".tr,
|
||||
hintText: "jerome014@gmail.com",
|
||||
controller: controller.emailController.value,
|
||||
focusNode: controller.emailFocusNode,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(
|
||||
title: "Mobile Number*".tr,
|
||||
hintText: "Enter Mobile number".tr,
|
||||
enable: controller.type.value == "mobileNumber" ? false : true,
|
||||
controller: controller.mobileController.value,
|
||||
textInputType: const TextInputType.numberWithOptions(signed: true, decimal: true),
|
||||
textInputAction: TextInputAction.done,
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]')), LengthLimitingTextInputFormatter(10)],
|
||||
prefix: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CountryCodePicker(
|
||||
onChanged: (value) {
|
||||
controller.countryCodeController.value.text = value.dialCode ?? Constant.defaultCountryCode;
|
||||
},
|
||||
initialSelection: controller.countryCodeController.value.text.isNotEmpty ? controller.countryCodeController.value.text : Constant.defaultCountryCode,
|
||||
showCountryOnly: false,
|
||||
showOnlyCountryWhenClosed: false,
|
||||
alignLeft: false,
|
||||
enabled: controller.type.value != "mobileNumber",
|
||||
textStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : Colors.black),
|
||||
dialogTextStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
searchStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
dialogBackgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
// const Icon(Icons.keyboard_arrow_down_rounded, size: 24, color: AppThemeData.grey400),
|
||||
Container(height: 24, width: 1, color: AppThemeData.grey400),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(
|
||||
title: "Password*".tr,
|
||||
hintText: "Enter password".tr,
|
||||
controller: controller.passwordController.value,
|
||||
obscureText: controller.passwordVisible.value,
|
||||
focusNode: controller.passwordFocusNode,
|
||||
suffix: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
controller.passwordVisible.value = !controller.passwordVisible.value;
|
||||
},
|
||||
child:
|
||||
controller.passwordVisible.value
|
||||
? SvgPicture.asset(
|
||||
"assets/icons/ic_password_show.svg",
|
||||
colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn),
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
"assets/icons/ic_password_close.svg",
|
||||
colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(
|
||||
title: "Confirm Password*".tr,
|
||||
hintText: "Enter confirm password".tr,
|
||||
controller: controller.confirmPasswordController.value,
|
||||
obscureText: controller.conformPasswordVisible.value,
|
||||
suffix: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
controller.conformPasswordVisible.value = !controller.conformPasswordVisible.value;
|
||||
},
|
||||
child:
|
||||
controller.conformPasswordVisible.value
|
||||
? SvgPicture.asset(
|
||||
"assets/icons/ic_password_show.svg",
|
||||
colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn),
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
"assets/icons/ic_password_close.svg",
|
||||
colorFilter: ColorFilter.mode(isDark ? AppThemeData.grey300 : AppThemeData.grey600, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(title: "Referral Code".tr, hintText: "Enter referral code".tr, controller: controller.referralController.value),
|
||||
const SizedBox(height: 40),
|
||||
RoundedButtonFill(
|
||||
title: "Sign up".tr,
|
||||
onPress: () => controller.signUp(),
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
textColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(width: 52, height: 1, color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey300),
|
||||
const SizedBox(width: 15),
|
||||
Text("or continue with".tr, style: AppThemeData.regularTextStyle(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400)),
|
||||
const SizedBox(width: 15),
|
||||
Container(width: 52, height: 1, color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey300),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
RoundedButtonFill(
|
||||
title: "Mobile number".tr,
|
||||
onPress: () => Get.to(() => const MobileLoginScreen()),
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
icon: Icon(Icons.mobile_friendly_outlined, size: 20, color: isDark ? AppThemeData.greyDark900 : null),
|
||||
//Image.asset(AppAssets.icMessage, width: 20, height: 18, color: isDark ? AppThemeData.greyDark900 : null),
|
||||
color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Center(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: "Already have an account?".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: "Log in".tr,
|
||||
style: AppThemeData.mediumTextStyle(
|
||||
color: AppThemeData.ecommerce300,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: AppThemeData.ecommerce300,
|
||||
decorationStyle: TextDecorationStyle.solid,
|
||||
),
|
||||
recognizer:
|
||||
TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
Get.offAll(() => const LoginScreen());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
1586
lib/screen_ui/cab_service_screens/Intercity_home_screen.dart
Normal file
1586
lib/screen_ui/cab_service_screens/Intercity_home_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
1590
lib/screen_ui/cab_service_screens/cab_booking_screen.dart
Normal file
1590
lib/screen_ui/cab_service_screens/cab_booking_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
138
lib/screen_ui/cab_service_screens/cab_coupon_code_screen.dart
Normal file
138
lib/screen_ui/cab_service_screens/cab_coupon_code_screen.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/cab_coupon_code_controller.dart';
|
||||
import 'package:customer/controllers/theme_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/widget/my_separator.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class CabCouponCodeScreen extends StatelessWidget {
|
||||
const CabCouponCodeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: CabCouponCodeController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Coupon".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: controller.cabCouponList.isEmpty
|
||||
? Constant.showEmptyView(message: "Coupon not found".tr)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: controller.cabCouponList.length,
|
||||
itemBuilder: (context, index) {
|
||||
CouponModel couponModel = controller.cabCouponList[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: () {
|
||||
Get.back(result: couponModel);
|
||||
},
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
80
lib/screen_ui/cab_service_screens/cab_dashboard_screen.dart
Normal file
80
lib/screen_ui/cab_service_screens/cab_dashboard_screen.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/cab_dashboard_controller.dart';
|
||||
import 'package:customer/controllers/theme_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';
|
||||
|
||||
class CabDashboardScreen extends StatelessWidget {
|
||||
const CabDashboardScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: CabDashboardController(),
|
||||
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(CabDashboardController());
|
||||
}
|
||||
controller.selectedIndex.value = index;
|
||||
},
|
||||
items:
|
||||
Constant.walletSetting == false
|
||||
? [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_cab.svg", label: 'Home'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_booking_cab.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_profile.svg", label: 'Profile'.tr, controller: controller),
|
||||
]
|
||||
: [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_cab.svg", label: 'Home'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_booking_cab.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_wallet_cab.svg", label: 'Wallet'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 3, assetIcon: "assets/icons/ic_profile.svg", label: 'Profile'.tr, controller: controller),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
BottomNavigationBarItem navigationBarItem(isDark, {required int index, required String label, required String assetIcon, required CabDashboardController 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
285
lib/screen_ui/cab_service_screens/cab_home_screen.dart
Normal file
285
lib/screen_ui/cab_service_screens/cab_home_screen.dart
Normal file
@@ -0,0 +1,285 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/cab_home_controller.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/banner_model.dart';
|
||||
import 'package:customer/screen_ui/auth_screens/login_screen.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/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'Intercity_home_screen.dart';
|
||||
import 'cab_booking_screen.dart';
|
||||
|
||||
class CabHomeScreen extends StatelessWidget {
|
||||
const CabHomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: CabHomeController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Constant.userModel == null
|
||||
? InkWell(
|
||||
onTap: () {
|
||||
Get.offAll(const LoginScreen());
|
||||
},
|
||||
child: Text(
|
||||
"Login".tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppThemeData.boldTextStyle(color: AppThemeData.grey900, fontSize: 12),
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
Constant.userModel!.fullName(),
|
||||
textAlign: TextAlign.center,
|
||||
style: AppThemeData.boldTextStyle(color: AppThemeData.grey900, fontSize: 12),
|
||||
),
|
||||
Text(
|
||||
Constant.selectedLocation.getFullAddress(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
BannerView(bannerList: controller.bannerTopHome),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
"Where are you going for?".tr,
|
||||
style: AppThemeData.mediumTextStyle(
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Constant.sectionConstantModel!.rideType == "both" || Constant.sectionConstantModel!.rideType == "ride"
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => CabBookingScreen());
|
||||
},
|
||||
child: Container(
|
||||
width: Responsive.width(40, context),
|
||||
decoration: BoxDecoration(
|
||||
color: AppThemeData.warning50,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: AppThemeData.warning200),
|
||||
),
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_ride.svg", height: 38, width: 38),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
"Ride".tr,
|
||||
style: AppThemeData.semiBoldTextStyle(color: AppThemeData.taxiBooking500, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
"City rides, 24x7 availability".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: AppThemeData.taxiBooking600, fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(),
|
||||
SizedBox(width: 20),
|
||||
Constant.sectionConstantModel!.rideType == "both" || Constant.sectionConstantModel!.rideType == "intercity"
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => IntercityHomeScreen());
|
||||
},
|
||||
child: Container(
|
||||
width: Responsive.width(44, context),
|
||||
decoration: BoxDecoration(
|
||||
color: AppThemeData.carRent50,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: AppThemeData.carRent200),
|
||||
),
|
||||
padding: EdgeInsets.all(15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_intercity.svg", height: 38, width: 38),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
"Intercity/Outstation".tr,
|
||||
style: AppThemeData.semiBoldTextStyle(color: AppThemeData.carRent500, fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
"Long trips, prepaid options".tr,
|
||||
style: AppThemeData.mediumTextStyle(color: AppThemeData.parcelService600, fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Every Ride. Every Driver. Verified.".tr,
|
||||
style: AppThemeData.boldTextStyle(
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"All drivers go through ID checks and background verification for your safety.".tr,
|
||||
style: AppThemeData.mediumTextStyle(
|
||||
color: isDark ? AppThemeData.greyDark700 : AppThemeData.grey700,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: Image.asset("assets/images/img_ride_driver.png", height: 118, width: 68)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BannerView extends StatelessWidget {
|
||||
final List<BannerModel> bannerList;
|
||||
final RxInt currentPage = 0.obs;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
|
||||
BannerView({super.key, required this.bannerList});
|
||||
|
||||
/// Computes the visible item index from scroll offset
|
||||
void onScroll(BuildContext context) {
|
||||
if (scrollController.hasClients && bannerList.isNotEmpty) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final itemWidth = screenWidth * 0.8 + 10; // banner width + spacing
|
||||
final offset = scrollController.offset;
|
||||
final index = (offset / itemWidth).round();
|
||||
|
||||
if (index != currentPage.value && index < bannerList.length) {
|
||||
currentPage.value = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
scrollController.addListener(() {
|
||||
onScroll(context);
|
||||
});
|
||||
|
||||
return bannerList.isEmpty
|
||||
? SizedBox()
|
||||
: Column(
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: ListView.separated(
|
||||
controller: scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: bannerList.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 15),
|
||||
itemBuilder: (context, index) {
|
||||
final banner = bannerList[index];
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.8,
|
||||
child: NetworkImageWidget(imageUrl: banner.photo ?? '', fit: BoxFit.cover),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Obx(() {
|
||||
return Row(
|
||||
children: List.generate(bannerList.length, (index) {
|
||||
bool isSelected = currentPage.value == index;
|
||||
return Expanded(
|
||||
child: Container(
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppThemeData.grey300 : AppThemeData.grey100,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
497
lib/screen_ui/cab_service_screens/cab_order_details.dart
Normal file
497
lib/screen_ui/cab_service_screens/cab_order_details.dart
Normal file
@@ -0,0 +1,497 @@
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/cab_order_details_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/user_model.dart';
|
||||
import '../../service/fire_store_utils.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import 'package:flutter_map/flutter_map.dart' as fm;
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmap;
|
||||
import 'package:latlong2/latlong.dart' as osm;
|
||||
|
||||
import '../../themes/round_button_border.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/show_toast_dialog.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
import '../multi_vendor_service/chat_screens/chat_screen.dart';
|
||||
import 'cab_review_screen.dart';
|
||||
import 'complain_screen.dart';
|
||||
|
||||
class CabOrderDetails extends StatelessWidget {
|
||||
const CabOrderDetails({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: CabOrderDetailsController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Ride Details".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
"${'Order Id:'.tr} ${Constant.orderId(orderId: controller.cabOrder.value.id.toString())}".tr,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 18, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${'Booking Date:'.tr} ${controller.formatDate(controller.cabOrder.value.scheduleDateTime!)}".tr,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 18, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Icon(Icons.stop_circle_outlined, color: Colors.green),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(size.width / 2, 0)
|
||||
..lineTo(size.width / 2, size.height),
|
||||
),
|
||||
child: const SizedBox(width: 20, height: 55),
|
||||
),
|
||||
Icon(Icons.radio_button_checked, color: Colors.red),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// Source Location Name
|
||||
Expanded(
|
||||
child: Text(
|
||||
controller.cabOrder.value.sourceLocationName.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: AppThemeData.warning300, width: 1),
|
||||
color: AppThemeData.warning50,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
child: Text(
|
||||
controller.cabOrder.value.status.toString(),
|
||||
style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.warning500),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(0, size.height / 2) // start from left center
|
||||
..lineTo(size.width, size.height / 2), // draw to right center
|
||||
),
|
||||
child: const SizedBox(width: 295, height: 3),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Text(
|
||||
controller.cabOrder.value.destinationLocationName.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// map view show
|
||||
Container(
|
||||
height: 180,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child:
|
||||
Constant.selectedMapType == "osm"
|
||||
? fm.FlutterMap(
|
||||
options: fm.MapOptions(
|
||||
initialCenter: osm.LatLng(controller.cabOrder.value.sourceLocation!.latitude!, controller.cabOrder.value.sourceLocation!.longitude!),
|
||||
initialZoom: 13,
|
||||
),
|
||||
children: [
|
||||
fm.TileLayer(urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png"),
|
||||
|
||||
// Only show polyline if points exist
|
||||
if (controller.osmPolyline.isNotEmpty) fm.PolylineLayer(polylines: [fm.Polyline(points: controller.osmPolyline.toList(), color: Colors.blue, strokeWidth: 4)]),
|
||||
|
||||
fm.MarkerLayer(
|
||||
markers: [
|
||||
fm.Marker(
|
||||
point: osm.LatLng(controller.cabOrder.value.sourceLocation!.latitude!, controller.cabOrder.value.sourceLocation!.longitude!),
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: Image.asset('assets/icons/ic_cab_pickup.png', width: 10, height: 10),
|
||||
),
|
||||
fm.Marker(
|
||||
point: osm.LatLng(controller.cabOrder.value.destinationLocation!.latitude!, controller.cabOrder.value.destinationLocation!.longitude!),
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: Image.asset('assets/icons/ic_cab_destination.png', width: 10, height: 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
: gmap.GoogleMap(
|
||||
initialCameraPosition: gmap.CameraPosition(
|
||||
target: gmap.LatLng(controller.cabOrder.value.sourceLocation!.latitude!, controller.cabOrder.value.sourceLocation!.longitude!),
|
||||
zoom: 13,
|
||||
),
|
||||
polylines: controller.googlePolylines.toSet(),
|
||||
markers: controller.googleMarkers.toSet(),
|
||||
),
|
||||
),
|
||||
),
|
||||
controller.cabOrder.value.driver != null
|
||||
? Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Ride & Fare Summary".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 52,
|
||||
height: 52,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadiusGeometry.circular(10),
|
||||
child: NetworkImageWidget(imageUrl: controller.cabOrder.value.driver?.profilePictureURL ?? '', height: 70, width: 70, borderRadius: 35),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
controller.cabOrder.value.driver?.fullName() ?? '',
|
||||
style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 18),
|
||||
),
|
||||
Text(
|
||||
"${controller.cabOrder.value.driver?.vehicleType ?? ''} | ${controller.cabOrder.value.driver?.carMakes.toString()}",
|
||||
style: TextStyle(fontFamily: AppThemeData.medium, color: isDark ? AppThemeData.greyDark700 : AppThemeData.grey700, fontSize: 14),
|
||||
),
|
||||
Text(
|
||||
controller.cabOrder.value.driver?.carNumber ?? '',
|
||||
style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark700 : AppThemeData.grey700, fontSize: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
RoundedButtonBorder(
|
||||
title: controller.driverUser.value.averageRating.toStringAsFixed(1) ?? '',
|
||||
width: 20,
|
||||
height: 3.5,
|
||||
radius: 10,
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
textColor: AppThemeData.warning400,
|
||||
borderColor: AppThemeData.warning400,
|
||||
color: AppThemeData.warning50,
|
||||
icon: SvgPicture.asset("assets/icons/ic_start.svg"),
|
||||
onPress: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Visibility(
|
||||
visible: controller.cabOrder.value.status == Constant.orderCompleted ? true : false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: RoundedButtonFill(
|
||||
title: controller.ratingModel.value.id != null && controller.ratingModel.value.id!.isNotEmpty ? 'Update Review'.tr : 'Add Review'.tr,
|
||||
onPress: () async {
|
||||
final result = await Get.to(() => CabReviewScreen(), arguments: {'order': controller.cabOrder.value});
|
||||
|
||||
// If review was submitted successfully
|
||||
if (result == true) {
|
||||
await controller.fetchDriverDetails();
|
||||
}
|
||||
},
|
||||
height: 5,
|
||||
borderRadius: 15,
|
||||
color: Colors.orange,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Expanded(
|
||||
child: Visibility(
|
||||
visible: controller.cabOrder.value.status == Constant.orderCompleted ? true : false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: RoundedButtonFill(
|
||||
title: 'Complain'.tr,
|
||||
onPress: () async {
|
||||
Get.to(() => ComplainScreen(), arguments: {'order': controller.cabOrder.value});
|
||||
},
|
||||
height: 5,
|
||||
borderRadius: 15,
|
||||
color: Colors.orange,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (controller.cabOrder.value.status != Constant.orderCompleted)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Constant.makePhoneCall(controller.cabOrder.value.driver!.phoneNumber.toString());
|
||||
},
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 42,
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
|
||||
borderRadius: BorderRadius.circular(120),
|
||||
),
|
||||
),
|
||||
child: Padding(padding: const EdgeInsets.all(8.0), child: SvgPicture.asset("assets/icons/ic_phone_call.svg")),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
|
||||
UserModel? customer = await FireStoreUtils.getUserProfile(controller.cabOrder.value.authorID ?? '');
|
||||
UserModel? driverUser = await FireStoreUtils.getUserProfile(controller.cabOrder.value.driverId ?? '');
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
Get.to(
|
||||
const ChatScreen(),
|
||||
arguments: {
|
||||
"customerName": customer?.fullName(),
|
||||
"restaurantName": driverUser?.fullName(),
|
||||
"orderId": controller.cabOrder.value.id,
|
||||
"restaurantId": driverUser?.id,
|
||||
"customerId": customer?.id,
|
||||
"customerProfileImage": customer?.profilePictureURL,
|
||||
"restaurantProfileImage": driverUser?.profilePictureURL,
|
||||
"token": driverUser?.fcmToken,
|
||||
"chatType": "Driver",
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 42,
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
|
||||
borderRadius: BorderRadius.circular(120),
|
||||
),
|
||||
),
|
||||
child: Padding(padding: const EdgeInsets.all(8.0), child: SvgPicture.asset("assets/icons/ic_wechat.svg")),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: SizedBox(),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_iconTile("${double.parse(controller.cabOrder.value.distance.toString()).toStringAsFixed(2)} ${'KM'.tr}", "Distance".tr, "assets/icons/ic_distance_parcel.svg", isDark),
|
||||
_iconTile(controller.cabOrder.value.duration ?? '--', "Duration".tr, "assets/icons/ic_duration.svg", isDark),
|
||||
_iconTile(Constant.amountShow(amount: controller.cabOrder.value.subTotal), "${controller.cabOrder.value.paymentMethod}".tr, "assets/icons/ic_rate_parcel.svg", isDark),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Order Summary".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey500)),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Subtotal
|
||||
_summaryTile("Subtotal", Constant.amountShow(amount: controller.subTotal.value.toString()), isDark),
|
||||
|
||||
// Discount
|
||||
_summaryTile("Discount", Constant.amountShow(amount: controller.discount.value.toString()), isDark),
|
||||
|
||||
// Tax List
|
||||
...List.generate(controller.cabOrder.value.taxSetting!.length, (index) {
|
||||
return _summaryTile(
|
||||
"${controller.cabOrder.value.taxSetting![index].title} ${controller.cabOrder.value.taxSetting![index].type == 'fix' ? '' : '(${controller.cabOrder.value.taxSetting![index].tax}%)'}",
|
||||
Constant.amountShow(
|
||||
amount:
|
||||
Constant.getTaxValue(
|
||||
amount:
|
||||
((double.tryParse(controller.cabOrder.value.subTotal.toString()) ?? 0.0) - (double.tryParse(controller.cabOrder.value.discount.toString()) ?? 0.0))
|
||||
.toString(),
|
||||
taxModel: controller.cabOrder.value.taxSetting![index],
|
||||
).toString(),
|
||||
),
|
||||
isDark,
|
||||
);
|
||||
}),
|
||||
|
||||
const Divider(),
|
||||
|
||||
// Total
|
||||
_summaryTile("Order Total", Constant.amountShow(amount: controller.totalAmount.value.toString()), isDark),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _iconTile(String value, title, icon, bool isDark) {
|
||||
return Column(
|
||||
children: [
|
||||
// Icon(icon, color: AppThemeData.primary300),
|
||||
SvgPicture.asset(icon, height: 28, width: 28, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
const SizedBox(height: 6),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
const SizedBox(height: 6),
|
||||
Text(title, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _summaryTile(String title, String value, bool isDark) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title.tr, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: title == "Order Total" ? 18 : 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
157
lib/screen_ui/cab_service_screens/cab_review_screen.dart
Normal file
157
lib/screen_ui/cab_service_screens/cab_review_screen.dart
Normal file
@@ -0,0 +1,157 @@
|
||||
import 'package:customer/controllers/cab_review_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
|
||||
class CabReviewScreen extends StatelessWidget {
|
||||
const CabReviewScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<CabReviewController>(
|
||||
init: CabReviewController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
leading: GestureDetector(onTap: () => Get.back(), child: Icon(Icons.arrow_back_ios, color: isDark ? Colors.white : Colors.black)),
|
||||
title: Text(controller.ratingModel.value != null ? "Update Review".tr : "Add Review".tr, style: TextStyle(color: isDark ? Colors.white : Colors.black, fontSize: 16)),
|
||||
),
|
||||
body: Obx(
|
||||
() =>
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 50, bottom: 20),
|
||||
child: Card(
|
||||
elevation: 2,
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 65),
|
||||
child: Column(
|
||||
children: [
|
||||
// Driver Name
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
controller.order.value!.driver?.fullName() ?? "",
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black87, fontFamily: AppThemeData.medium, fontSize: 18),
|
||||
),
|
||||
),
|
||||
// Car info
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
controller.driverUser.value?.carNumber?.toUpperCase() ?? '',
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black87, fontFamily: AppThemeData.medium),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"${controller.driverUser.value?.carName} ${controller.driverUser.value?.carMakes}",
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black38, fontFamily: AppThemeData.medium),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const Padding(padding: EdgeInsets.symmetric(vertical: 12), child: Divider(color: Colors.grey)),
|
||||
|
||||
// Title
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: Text('How is your trip?'.tr, style: TextStyle(fontSize: 18, color: isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, letterSpacing: 2)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
'Your feedback will help us improve \n driving experience better'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black.withOpacity(0.60), letterSpacing: 0.8),
|
||||
),
|
||||
),
|
||||
|
||||
// Rating
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Text('Rate for'.tr, style: TextStyle(fontSize: 16, color: isDark ? Colors.white : Colors.black.withOpacity(0.60), letterSpacing: 0.8)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
controller.order.value!.driver?.fullName() ?? "",
|
||||
style: TextStyle(fontSize: 18, color: isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, letterSpacing: 2),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: RatingBar.builder(
|
||||
initialRating: controller.ratings.value,
|
||||
minRating: 1,
|
||||
direction: Axis.horizontal,
|
||||
allowHalfRating: true,
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, _) => const Icon(Icons.star, color: Colors.amber),
|
||||
unratedColor: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400,
|
||||
onRatingUpdate: (rating) => controller.ratings.value = rating,
|
||||
),
|
||||
),
|
||||
|
||||
// Comment
|
||||
Padding(padding: const EdgeInsets.all(20.0), child: TextFieldWidget(hintText: "Type comment....".tr, controller: controller.comment.value, maxLine: 5)),
|
||||
|
||||
// Submit Button
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: RoundedButtonFill(
|
||||
title: controller.ratingModel.value != null ? "Update Review".tr : "Add Review".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: isDark ? Colors.white : Colors.black,
|
||||
onPress: controller.submitReview,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
color: Colors.white,
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.15), blurRadius: 8, spreadRadius: 6)],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
child: NetworkImageWidget(imageUrl: controller.order.value?.driver?.profilePictureURL ?? '', fit: BoxFit.cover, height: 110, width: 110),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
65
lib/screen_ui/cab_service_screens/complain_screen.dart
Normal file
65
lib/screen_ui/cab_service_screens/complain_screen.dart
Normal file
@@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/complain_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
|
||||
class ComplainScreen extends StatelessWidget {
|
||||
const ComplainScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetBuilder<ComplainController>(
|
||||
init: ComplainController(),
|
||||
builder: (controller) {
|
||||
return Obx(
|
||||
() => Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.taxiBooking300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: const Center(child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Complain".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
Obx(() => TextFieldWidget(title: "Title".tr, hintText: "Title".tr, controller: controller.title.value)),
|
||||
const SizedBox(height: 10),
|
||||
Obx(() => TextFieldWidget(title: "Complain".tr, hintText: 'Type Description....'.tr, controller: controller.comment.value, maxLine: 8)),
|
||||
const SizedBox(height: 20),
|
||||
RoundedButtonFill(title: "Save".tr, color: AppThemeData.primary300, textColor: AppThemeData.grey50, onPress: () => controller.submitComplain()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
481
lib/screen_ui/cab_service_screens/my_cab_booking_screen.dart
Normal file
481
lib/screen_ui/cab_service_screens/my_cab_booking_screen.dart
Normal file
@@ -0,0 +1,481 @@
|
||||
import 'package:customer/models/cab_order_model.dart';
|
||||
import 'package:customer/payment/createRazorPayOrderModel.dart';
|
||||
import 'package:customer/payment/rozorpayConroller.dart';
|
||||
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
|
||||
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import 'package:customer/themes/show_toast_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/my_cab_booking_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
|
||||
import 'cab_order_details.dart';
|
||||
|
||||
class MyCabBookingScreen extends StatelessWidget {
|
||||
const MyCabBookingScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX(
|
||||
init: MyCabBookingController(),
|
||||
builder: (controller) {
|
||||
return DefaultTabController(
|
||||
// length: controller.tabTitles.length,
|
||||
// initialIndex: controller.tabTitles.indexOf(controller.selectedTab.value),
|
||||
length: controller.tabKeys.length,
|
||||
initialIndex: controller.tabKeys.indexOf(controller.selectedTab.value),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(children: [const SizedBox(width: 10), Text("Ride History".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900))]),
|
||||
),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(48),
|
||||
child: TabBar(
|
||||
isScrollable: false,
|
||||
onTap: (index) {
|
||||
controller.selectTab(controller.tabKeys[index]);
|
||||
},
|
||||
indicatorColor: AppThemeData.taxiBooking500,
|
||||
labelColor: AppThemeData.taxiBooking500,
|
||||
unselectedLabelColor: AppThemeData.taxiBooking500,
|
||||
labelStyle: AppThemeData.boldTextStyle(fontSize: 14),
|
||||
unselectedLabelStyle: AppThemeData.mediumTextStyle(fontSize: 14),
|
||||
tabs:
|
||||
controller.tabKeys
|
||||
.map(
|
||||
(key) => Tab(
|
||||
child: SizedBox.expand(
|
||||
child: Center(
|
||||
child: Text(
|
||||
controller.getLocalizedTabTitle(key),
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.visible, // 👈 show full text
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
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: [
|
||||
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(
|
||||
"You’re 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());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: TabBarView(
|
||||
children:
|
||||
controller.tabKeys.map((title) {
|
||||
final orders = controller.getOrdersForTab(title);
|
||||
|
||||
if (orders.isEmpty) {
|
||||
return Center(child: Text("No order found".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: orders.length,
|
||||
itemBuilder: (context, index) {
|
||||
CabOrderModel order = orders[index];
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => CabOrderDetails(), arguments: {"cabOrderModel": order});
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${'Booking Date:'.tr} ${controller.formatDate(order.scheduleDateTime!)}".tr,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 18, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Icon(Icons.stop_circle_outlined, color: Colors.green),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(size.width / 2, 0)
|
||||
..lineTo(size.width / 2, size.height),
|
||||
),
|
||||
child: const SizedBox(width: 20, height: 55),
|
||||
),
|
||||
Icon(Icons.radio_button_checked, color: Colors.red),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// Source Location Name
|
||||
Expanded(
|
||||
child: Text(
|
||||
order.sourceLocationName.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: AppThemeData.warning300, width: 1),
|
||||
color: AppThemeData.warning50,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
|
||||
child: Text(
|
||||
order.status.toString(),
|
||||
style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.warning500),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(0, size.height / 2) // start from left center
|
||||
..lineTo(size.width, size.height / 2), // draw to right center
|
||||
),
|
||||
child: const SizedBox(width: 295, height: 3),
|
||||
),
|
||||
SizedBox(height: 15),
|
||||
Text(
|
||||
order.destinationLocationName.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (Constant.isEnableOTPTripStart == true)
|
||||
Row(
|
||||
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text("Otp :".tr, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
SizedBox(width: 5),
|
||||
Text(order.otpCode ?? '', style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
if (order.status == Constant.orderInTransit && order.paymentStatus == false) SizedBox(height: 14),
|
||||
order.status == Constant.orderInTransit && order.paymentStatus == false
|
||||
? RoundedButtonFill(
|
||||
title: "Pay Now".tr,
|
||||
onPress: () async {
|
||||
controller.selectedPaymentMethod.value = order.paymentMethod.toString();
|
||||
controller.calculateTotalAmount(order);
|
||||
Get.bottomSheet(paymentBottomSheet(context, controller, isDark), isScrollControlled: true, backgroundColor: Colors.transparent);
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
)
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget paymentBottomSheet(BuildContext context, MyCabBookingController controller, bool isDark) {
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 0.70,
|
||||
// Start height
|
||||
minChildSize: 0.30,
|
||||
// Minimum height
|
||||
maxChildSize: 0.8,
|
||||
// Maximum height
|
||||
expand: false,
|
||||
//Prevents full-screen takeover
|
||||
builder: (context, scrollController) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.grey500 : Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(24))),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Select Payment Method".tr, style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: EdgeInsets.zero,
|
||||
controller: scrollController,
|
||||
children: [
|
||||
Text("Preferred Payment".tr, textAlign: TextAlign.start, style: AppThemeData.boldTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 10),
|
||||
if (controller.walletSettingModel.value.isEnabled == true || controller.cashOnDeliverySettingModel.value.isEnabled == true)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
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(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Other Payment Options".tr,
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
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")),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
RoundedButtonFill(
|
||||
title: "Continue".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
onPress: () async {
|
||||
if (controller.selectedPaymentMethod.value.isEmpty) {
|
||||
ShowToastDialog.showToast("Please select a payment method".tr);
|
||||
} else {
|
||||
if (controller.selectedPaymentMethod.value == PaymentGateway.stripe.name) {
|
||||
controller.stripeMakePayment(amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.paypal.name) {
|
||||
controller.paypalPaymentSheet(controller.totalAmount.value.toString(), context);
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payStack.name) {
|
||||
controller.payStackPayment(controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.mercadoPago.name) {
|
||||
controller.mercadoPagoMakePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.flutterWave.name) {
|
||||
controller.flutterWaveInitiatePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payFast.name) {
|
||||
controller.payFastPayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.cod.name) {
|
||||
controller.completeOrder();
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.wallet.name) {
|
||||
if (Constant.userModel!.walletAmount == null || Constant.userModel!.walletAmount! < controller.totalAmount.value) {
|
||||
ShowToastDialog.showToast("You do not have sufficient wallet balance".tr);
|
||||
} else {
|
||||
controller.completeOrder();
|
||||
}
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.midTrans.name) {
|
||||
controller.midtransMakePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.orangeMoney.name) {
|
||||
controller.orangeMakePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.xendit.name) {
|
||||
controller.xenditPayment(context, controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.razorpay.name) {
|
||||
RazorPayController().createOrderRazorPay(amount: double.parse(controller.totalAmount.value.toString()), 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.totalAmount.value.toString(), orderId: result.id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.showToast("Please select payment method".tr);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Obx cardDecoration(MyCabBookingController 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: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
Text(
|
||||
Constant.amountShow(amount: Constant.userModel!.walletAmount == null ? '0.0' : Constant.userModel!.walletAmount.toString()),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Text(
|
||||
value.name.capitalizeString(),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
148
lib/screen_ui/ecommarce/all_brand_product_screen.dart
Normal file
148
lib/screen_ui/ecommarce/all_brand_product_screen.dart
Normal file
@@ -0,0 +1,148 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/all_brand_product_controller.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/product_model.dart';
|
||||
import 'package:customer/models/vendor_model.dart';
|
||||
import 'package:customer/screen_ui/multi_vendor_service/restaurant_details_screen/restaurant_details_screen.dart';
|
||||
import 'package:customer/service/fire_store_utils.dart';
|
||||
import 'package:customer/themes/app_them_data.dart';
|
||||
import 'package:customer/themes/responsive.dart';
|
||||
import 'package:customer/utils/network_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class AllBrandProductScreen extends StatelessWidget {
|
||||
const AllBrandProductScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: AllBrandProductController(),
|
||||
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,vertical: 20),
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 3.5 / 6, crossAxisSpacing: 10),
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: controller.productList.length,
|
||||
itemBuilder: (context, index) {
|
||||
ProductModel productModel = controller.productList[index];
|
||||
return FutureBuilder(
|
||||
future: FireStoreUtils.getVendorById(productModel.vendorID.toString()),
|
||||
builder: (context, vendorSnapshot) {
|
||||
if (!vendorSnapshot.hasData || vendorSnapshot.connectionState == ConnectionState.waiting) {
|
||||
return const SizedBox(); // Show placeholder or loader
|
||||
}
|
||||
VendorModel? vendorModel = vendorSnapshot.data;
|
||||
String price = "0.0";
|
||||
String disPrice = "0.0";
|
||||
List<String> selectedVariants = [];
|
||||
List<String> selectedIndexVariants = [];
|
||||
List<String> selectedIndexArray = [];
|
||||
if (productModel.itemAttribute != null) {
|
||||
if (productModel.itemAttribute!.attributes!.isNotEmpty) {
|
||||
for (var element in productModel.itemAttribute!.attributes!) {
|
||||
if (element.attributeOptions!.isNotEmpty) {
|
||||
selectedVariants.add(
|
||||
productModel.itemAttribute!.attributes![productModel.itemAttribute!.attributes!.indexOf(element)].attributeOptions![0].toString(),
|
||||
);
|
||||
selectedIndexVariants.add(
|
||||
'${productModel.itemAttribute!.attributes!.indexOf(element)} _${productModel.itemAttribute!.attributes![0].attributeOptions![0].toString()}',
|
||||
);
|
||||
selectedIndexArray.add('${productModel.itemAttribute!.attributes!.indexOf(element)}_0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (productModel.itemAttribute!.variants!.where((element) => element.variantSku == selectedVariants.join('-')).isNotEmpty) {
|
||||
price = Constant.productCommissionPrice(
|
||||
vendorModel!,
|
||||
productModel.itemAttribute!.variants!.where((element) => element.variantSku == selectedVariants.join('-')).first.variantPrice ?? '0',
|
||||
);
|
||||
disPrice = "0";
|
||||
}
|
||||
} else {
|
||||
price = Constant.productCommissionPrice(vendorModel!, productModel.price.toString());
|
||||
disPrice = double.parse(productModel.disPrice.toString()) <= 0 ? "0" : Constant.productCommissionPrice(vendorModel, productModel.disPrice.toString());
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SizedBox(
|
||||
height: 90,
|
||||
width: Responsive.width(100, context),
|
||||
child: NetworkImageWidget(imageUrl: productModel.photo.toString(), fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
productModel.name!.capitalizeString(),
|
||||
textAlign: TextAlign.start,
|
||||
maxLines: 1,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
disPrice == "" || disPrice == "0"
|
||||
? Text(Constant.amountShow(amount: price), style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: AppThemeData.primary300))
|
||||
: Row(
|
||||
children: [
|
||||
Text(
|
||||
Constant.amountShow(amount: price),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: Colors.grey, decoration: TextDecoration.lineThrough),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
Constant.amountShow(amount: disPrice),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.warning50 : AppThemeData.warning50, borderRadius: BorderRadius.circular(30)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.star, size: 18, color: AppThemeData.warning400),
|
||||
Text(
|
||||
"${Constant.calculateReview(reviewCount: productModel.reviewsCount.toString(), reviewSum: productModel.reviewsSum.toString())} (${productModel.reviewsSum})",
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: AppThemeData.warning400),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
148
lib/screen_ui/ecommarce/all_category_product_screen.dart
Normal file
148
lib/screen_ui/ecommarce/all_category_product_screen.dart
Normal file
@@ -0,0 +1,148 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/all_category_product_controller.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/product_model.dart';
|
||||
import 'package:customer/models/vendor_model.dart';
|
||||
import 'package:customer/screen_ui/multi_vendor_service/restaurant_details_screen/restaurant_details_screen.dart';
|
||||
import 'package:customer/service/fire_store_utils.dart';
|
||||
import 'package:customer/themes/app_them_data.dart';
|
||||
import 'package:customer/themes/responsive.dart';
|
||||
import 'package:customer/utils/network_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class AllCategoryProductScreen extends StatelessWidget {
|
||||
const AllCategoryProductScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: AllCategoryProductController(),
|
||||
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, vertical: 20),
|
||||
child: GridView.builder(
|
||||
shrinkWrap: true,
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 3.5 / 6, crossAxisSpacing: 10),
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: controller.productList.length,
|
||||
itemBuilder: (context, index) {
|
||||
ProductModel productModel = controller.productList[index];
|
||||
return FutureBuilder(
|
||||
future: FireStoreUtils.getVendorById(productModel.vendorID.toString()),
|
||||
builder: (context, vendorSnapshot) {
|
||||
if (!vendorSnapshot.hasData || vendorSnapshot.connectionState == ConnectionState.waiting) {
|
||||
return const SizedBox(); // Show placeholder or loader
|
||||
}
|
||||
VendorModel? vendorModel = vendorSnapshot.data;
|
||||
String price = "0.0";
|
||||
String disPrice = "0.0";
|
||||
List<String> selectedVariants = [];
|
||||
List<String> selectedIndexVariants = [];
|
||||
List<String> selectedIndexArray = [];
|
||||
if (productModel.itemAttribute != null) {
|
||||
if (productModel.itemAttribute!.attributes!.isNotEmpty) {
|
||||
for (var element in productModel.itemAttribute!.attributes!) {
|
||||
if (element.attributeOptions!.isNotEmpty) {
|
||||
selectedVariants.add(
|
||||
productModel.itemAttribute!.attributes![productModel.itemAttribute!.attributes!.indexOf(element)].attributeOptions![0].toString(),
|
||||
);
|
||||
selectedIndexVariants.add(
|
||||
'${productModel.itemAttribute!.attributes!.indexOf(element)} _${productModel.itemAttribute!.attributes![0].attributeOptions![0].toString()}',
|
||||
);
|
||||
selectedIndexArray.add('${productModel.itemAttribute!.attributes!.indexOf(element)}_0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (productModel.itemAttribute!.variants!.where((element) => element.variantSku == selectedVariants.join('-')).isNotEmpty) {
|
||||
price = Constant.productCommissionPrice(
|
||||
vendorModel!,
|
||||
productModel.itemAttribute!.variants!.where((element) => element.variantSku == selectedVariants.join('-')).first.variantPrice ?? '0',
|
||||
);
|
||||
disPrice = "0";
|
||||
}
|
||||
} else {
|
||||
price = Constant.productCommissionPrice(vendorModel!, productModel.price.toString());
|
||||
disPrice = double.parse(productModel.disPrice.toString()) <= 0 ? "0" : Constant.productCommissionPrice(vendorModel, productModel.disPrice.toString());
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
Get.to(const RestaurantDetailsScreen(), arguments: {"vendorModel": vendorModel});
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: SizedBox(
|
||||
height: 90,
|
||||
width: Responsive.width(100, context),
|
||||
child: NetworkImageWidget(imageUrl: productModel.photo.toString(), fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
productModel.name!.capitalizeString(),
|
||||
textAlign: TextAlign.start,
|
||||
maxLines: 1,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
disPrice == "" || disPrice == "0"
|
||||
? Text(Constant.amountShow(amount: price), style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: AppThemeData.primary300))
|
||||
: Row(
|
||||
children: [
|
||||
Text(
|
||||
Constant.amountShow(amount: price),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: Colors.grey, decoration: TextDecoration.lineThrough),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
Constant.amountShow(amount: disPrice),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.warning50 : AppThemeData.warning50, borderRadius: BorderRadius.circular(30)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.star, size: 18, color: AppThemeData.warning400),
|
||||
Text(
|
||||
"${Constant.calculateReview(reviewCount: productModel.reviewsCount.toString(), reviewSum: productModel.reviewsSum.toString())} (${productModel.reviewsSum})",
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: AppThemeData.warning400),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
84
lib/screen_ui/ecommarce/dash_board_e_commerce_screen.dart
Normal file
84
lib/screen_ui/ecommarce/dash_board_e_commerce_screen.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
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/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 DashBoardEcommerceScreen extends StatelessWidget {
|
||||
const DashBoardEcommerceScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: DashBoardEcommerceController(),
|
||||
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 DashBoardEcommerceController 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
1131
lib/screen_ui/ecommarce/home_e_commerce_screen.dart
Normal file
1131
lib/screen_ui/ecommarce/home_e_commerce_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
149
lib/screen_ui/location_enable_screens/address_list_screen.dart
Normal file
149
lib/screen_ui/location_enable_screens/address_list_screen.dart
Normal file
@@ -0,0 +1,149 @@
|
||||
import 'package:customer/constant/assets.dart';
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/address_list_controller.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/user_model.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/enter_manually_location.dart';
|
||||
import 'package:customer/themes/app_them_data.dart' show AppThemeData;
|
||||
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';
|
||||
|
||||
class AddressListScreen extends StatelessWidget {
|
||||
const AddressListScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: AddressListController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: InkWell(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: const Icon(Icons.arrow_back, size: 24, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("My Addresses".tr, style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
const SizedBox(height: 5),
|
||||
Text("Allows users to view, manage, add, or edit delivery addresses.".tr, style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.grey600)),
|
||||
const SizedBox(height: 24),
|
||||
Expanded(
|
||||
child:
|
||||
controller.shippingAddressList.isEmpty
|
||||
? Constant.showEmptyView(message: "Address not found".tr)
|
||||
: ListView.separated(
|
||||
itemCount: controller.shippingAddressList.length,
|
||||
itemBuilder: (context, index) {
|
||||
ShippingAddress address = controller.shippingAddressList[index];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Get.back(result: address);
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.greyDark100 : AppThemeData.grey100, borderRadius: BorderRadius.circular(8)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
child: Text(
|
||||
address.addressAs.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
address.isDefault == true
|
||||
? Container(
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.success100 : AppThemeData.success100, borderRadius: BorderRadius.circular(8)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
child: Text(
|
||||
"Default".tr,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
address.getFullAddress().toString(),
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
await controller.deleteAddress(index);
|
||||
},
|
||||
child: SvgPicture.asset("assets/icons/ic_delete_address.svg"),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.to(EnterManuallyLocationScreen(), arguments: {"address": address, "mode": "Edit"})!.then((value) {
|
||||
if (value == true) {
|
||||
controller.getUser();
|
||||
}
|
||||
});
|
||||
},
|
||||
child: SvgPicture.asset("assets/icons/ic_edit_address.svg"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return Padding(padding: const EdgeInsets.symmetric(vertical: 20), child: Divider(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, height: 1));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 30, left: 16, right: 16, top: 20),
|
||||
child: RoundedButtonFill(
|
||||
title: "Add New Address",
|
||||
onPress: () {
|
||||
Get.to(EnterManuallyLocationScreen())!.then((value) {
|
||||
if (value == true) {
|
||||
controller.getUser();
|
||||
}
|
||||
});
|
||||
},
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
icon: SvgPicture.asset(AppAssets.icPlus, width: 20, height: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.greyDark900),
|
||||
color: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
|
||||
textColor: isDark ? AppThemeData.grey50 : AppThemeData.grey50,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/models/user_model.dart';
|
||||
import 'package:customer/service/fire_store_utils.dart';
|
||||
import 'package:customer/themes/show_toast_dialog.dart';
|
||||
import 'package:customer/utils/utils.dart';
|
||||
import 'package:customer/widget/osm_map/map_picker_page.dart';
|
||||
import 'package:customer/widget/place_picker/location_picker_screen.dart';
|
||||
import 'package:customer/widget/place_picker/selected_location_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../controllers/enter_manually_location_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
|
||||
class EnterManuallyLocationScreen extends StatelessWidget {
|
||||
const EnterManuallyLocationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX<EnterManuallyLocationController>(
|
||||
init: EnterManuallyLocationController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: InkWell(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: const Icon(Icons.arrow_back, size: 24, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
controller.mode == "Edit" ? "Edit Address".tr : "Add a New Address".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text("Enter your location details so we can deliver your orders quickly and accurately.".tr, style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.grey600)),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text("Set as Default Address".tr, style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.grey600))),
|
||||
Transform.scale(
|
||||
scale: 0.7, // Decrease the size (try 0.5, 0.6, etc.)
|
||||
child: Switch(
|
||||
value: controller.isDefault.value,
|
||||
onChanged: (value) {
|
||||
controller.isDefault.value = value;
|
||||
},
|
||||
activeThumbColor: Colors.green,
|
||||
inactiveThumbColor: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Constant.checkPermission(
|
||||
context: context,
|
||||
onTap: () async {
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
controller.localityEditingController.value.text = address.toString();
|
||||
controller.location.value = UserLocation(latitude: lat, longitude: lng);
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
controller.localityEditingController.value.text = Utils.formatAddress(selectedLocation: selectedLocationModel);
|
||||
controller.location.value = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
child: TextFieldWidget(
|
||||
title: "Choose Location".tr,
|
||||
hintText: "Choose Location".tr,
|
||||
readOnly: true,
|
||||
enable: false,
|
||||
controller: null,
|
||||
suffix: GestureDetector(
|
||||
onTap: () {
|
||||
Constant.checkPermission(
|
||||
context: context,
|
||||
onTap: () async {
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
controller.localityEditingController.value.text = address.toString();
|
||||
controller.location.value = UserLocation(latitude: lat, longitude: lng);
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
controller.localityEditingController.value.text = Utils.formatAddress(selectedLocation: selectedLocationModel);
|
||||
controller.location.value = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
Get.back();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Padding(padding: const EdgeInsets.only(right: 10), child: Icon(Icons.gps_fixed, size: 24, color: AppThemeData.ecommerce300)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(title: "Flat/House/Floor/Building*".tr, hintText: "Enter address details".tr, controller: controller.houseBuildingTextEditingController.value),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(title: "Area/Sector/Locality*".tr, hintText: "Enter area/locality".tr, controller: controller.localityEditingController.value),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(title: "Nearby Landmark".tr, hintText: "Add a landmark".tr, controller: controller.landmarkEditingController.value),
|
||||
const SizedBox(height: 30),
|
||||
Container(height: 1, color: AppThemeData.grey200),
|
||||
const SizedBox(height: 25),
|
||||
Text("Save Address As".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey900)),
|
||||
const SizedBox(height: 10),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
children:
|
||||
controller.saveAsList
|
||||
.map(
|
||||
(item) => GestureDetector(
|
||||
onTap: () {
|
||||
controller.selectedSaveAs.value = item;
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: controller.selectedSaveAs.value == item ? AppThemeData.primary300 : AppThemeData.grey100,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
child: Text(
|
||||
controller.getLocalizedSaveAs(item),
|
||||
style: AppThemeData.mediumTextStyle(color: controller.selectedSaveAs.value == item ? AppThemeData.grey50 : AppThemeData.grey600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
RoundedButtonFill(
|
||||
title: "Save Address".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey50,
|
||||
onPress: () async {
|
||||
if (controller.location.value.latitude == null || controller.location.value.longitude == null) {
|
||||
ShowToastDialog.showToast("Please select Location".tr);
|
||||
} else if (controller.houseBuildingTextEditingController.value.text.isEmpty) {
|
||||
ShowToastDialog.showToast("Please Enter Flat / House / Floor / Building".tr);
|
||||
} else if (controller.localityEditingController.value.text.isEmpty) {
|
||||
ShowToastDialog.showToast("Please Enter Area / Sector / Locality".tr);
|
||||
} else {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
|
||||
//Common values
|
||||
controller.shippingModel.value.location = controller.location.value;
|
||||
controller.shippingModel.value.addressAs = controller.selectedSaveAs.value;
|
||||
controller.shippingModel.value.address = controller.houseBuildingTextEditingController.value.text;
|
||||
controller.shippingModel.value.locality = controller.localityEditingController.value.text;
|
||||
controller.shippingModel.value.landmark = controller.landmarkEditingController.value.text;
|
||||
|
||||
if (controller.mode.value == "Edit") {
|
||||
//Edit Mode
|
||||
controller.shippingAddressList.value =
|
||||
controller.shippingAddressList.map((address) {
|
||||
if (address.id == controller.shippingModel.value.id) {
|
||||
return controller.shippingModel.value; // replace existing one
|
||||
}
|
||||
return address;
|
||||
}).toList();
|
||||
Constant.selectedLocation = controller.shippingModel.value;
|
||||
} else {
|
||||
//Add Mode
|
||||
controller.shippingModel.value.id = Constant.getUuid();
|
||||
controller.shippingModel.value.isDefault = controller.shippingAddressList.isEmpty ? true : false;
|
||||
controller.shippingAddressList.add(controller.shippingModel.value);
|
||||
}
|
||||
|
||||
//Handle default address switch
|
||||
if (controller.isDefault.value) {
|
||||
controller.shippingAddressList.value =
|
||||
controller.shippingAddressList.map((address) {
|
||||
address.isDefault = address.id == controller.shippingModel.value.id ? true : false;
|
||||
return address;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
controller.userModel.value.shippingAddress = controller.shippingAddressList;
|
||||
await FireStoreUtils.updateUser(controller.userModel.value);
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.back(result: true);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/user_model.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/address_list_screen.dart';
|
||||
import 'package:customer/screen_ui/service_home_screen/service_list_screen.dart';
|
||||
import 'package:customer/themes/app_them_data.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import 'package:customer/themes/show_toast_dialog.dart';
|
||||
import 'package:customer/widget/osm_map/map_picker_page.dart';
|
||||
import 'package:customer/widget/place_picker/location_picker_screen.dart';
|
||||
import 'package:customer/widget/place_picker/selected_location_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/assets.dart';
|
||||
import '../../utils/utils.dart';
|
||||
|
||||
class LocationPermissionScreen extends StatelessWidget {
|
||||
const LocationPermissionScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
Image.asset(AppAssets.icLocation),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Text(
|
||||
"Enable Location for a Personalized Experience".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 24, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
child: Text(
|
||||
"Allow location access to discover beauty stores and services near you.".tr,
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
RoundedButtonFill(
|
||||
title: "Use current location".tr,
|
||||
onPress: () async {
|
||||
Constant.checkPermission(
|
||||
context: context,
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
ShippingAddress addressModel = ShippingAddress();
|
||||
try {
|
||||
await Geolocator.requestPermission();
|
||||
Position newLocalData = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
await placemarkFromCoordinates(newLocalData.latitude, newLocalData.longitude).then((valuePlaceMaker) {
|
||||
Placemark placeMark = valuePlaceMaker[0];
|
||||
addressModel.addressAs = "Home";
|
||||
addressModel.location = UserLocation(latitude: newLocalData.latitude, longitude: newLocalData.longitude);
|
||||
String currentLocation =
|
||||
"${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}";
|
||||
addressModel.locality = currentLocation;
|
||||
});
|
||||
|
||||
Constant.selectedLocation = addressModel;
|
||||
Constant.currentLocation = await Utils.getCurrentLocation();
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
Get.offAll(const ServiceListScreen());
|
||||
} catch (e) {
|
||||
await placemarkFromCoordinates(19.228825, 72.854118).then((valuePlaceMaker) {
|
||||
Placemark placeMark = valuePlaceMaker[0];
|
||||
addressModel.addressAs = "Home";
|
||||
addressModel.location = UserLocation(latitude: 19.228825, longitude: 72.854118);
|
||||
String currentLocation =
|
||||
"${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}";
|
||||
addressModel.locality = currentLocation;
|
||||
});
|
||||
|
||||
Constant.selectedLocation = addressModel;
|
||||
Constant.currentLocation = await Utils.getCurrentLocation();
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
Get.offAll(const ServiceListScreen());
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
color: AppThemeData.grey900,
|
||||
textColor: AppThemeData.grey50,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
RoundedButtonFill(
|
||||
title: "Set from map".tr,
|
||||
onPress: () async {
|
||||
Constant.checkPermission(
|
||||
context: context,
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
ShippingAddress addressModel = ShippingAddress();
|
||||
try {
|
||||
await Geolocator.requestPermission();
|
||||
await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
ShowToastDialog.closeLoader();
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
addressModel.addressAs = "Home";
|
||||
addressModel.locality = address.toString();
|
||||
addressModel.location = UserLocation(latitude: lat, longitude: lng);
|
||||
Constant.selectedLocation = addressModel;
|
||||
Get.offAll(const ServiceListScreen());
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
addressModel.addressAs = "Home";
|
||||
addressModel.locality = Utils.formatAddress(selectedLocation: selectedLocationModel);
|
||||
addressModel.location = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
Constant.selectedLocation = addressModel;
|
||||
|
||||
Get.offAll(const ServiceListScreen());
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
await placemarkFromCoordinates(19.228825, 72.854118).then((valuePlaceMaker) {
|
||||
Placemark placeMark = valuePlaceMaker[0];
|
||||
addressModel.addressAs = "Home";
|
||||
addressModel.location = UserLocation(latitude: 19.228825, longitude: 72.854118);
|
||||
String currentLocation =
|
||||
"${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}";
|
||||
addressModel.locality = currentLocation;
|
||||
});
|
||||
|
||||
Constant.selectedLocation = addressModel;
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
Get.offAll(const ServiceListScreen());
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
color: AppThemeData.grey50,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Constant.userModel == null
|
||||
? const SizedBox()
|
||||
: GestureDetector(
|
||||
onTap: () async {
|
||||
Get.to(AddressListScreen())!.then((value) {
|
||||
if (value != null) {
|
||||
ShippingAddress addressModel = value;
|
||||
Constant.selectedLocation = addressModel;
|
||||
Get.offAll(const ServiceListScreen());
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Text("Enter Manually location".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:customer/themes/app_them_data.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MaintenanceModeScreen extends StatelessWidget {
|
||||
const MaintenanceModeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppThemeData.grey100,
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(child: Image.asset('assets/images/maintenance.png', height: 200, width: 200)),
|
||||
const SizedBox(height: 20),
|
||||
Text("We'll be back soon!".tr, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: Text(
|
||||
"Sorry for the inconvenience but we're performing some maintenance at the moment. We'll be back online shortly!".tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1212
lib/screen_ui/multi_vendor_service/cart_screen/cart_screen.dart
Normal file
1212
lib/screen_ui/multi_vendor_service/cart_screen/cart_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 {},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:customer/models/conversation_model.dart';
|
||||
|
||||
class ChatVideoContainer {
|
||||
Url videoUrl;
|
||||
|
||||
String thumbnailUrl;
|
||||
|
||||
ChatVideoContainer({required this.videoUrl, required this.thumbnailUrl});
|
||||
}
|
||||
310
lib/screen_ui/multi_vendor_service/chat_screens/chat_screen.dart
Normal file
310
lib/screen_ui/multi_vendor_service/chat_screens/chat_screen.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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())),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
"You’re 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};
|
||||
}
|
||||
}
|
||||
@@ -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!! We’ll 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();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
}
|
||||
2042
lib/screen_ui/multi_vendor_service/home_screen/home_screen.dart
Normal file
2042
lib/screen_ui/multi_vendor_service/home_screen/home_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
1074
lib/screen_ui/multi_vendor_service/home_screen/home_screen_two.dart
Normal file
1074
lib/screen_ui/multi_vendor_service/home_screen/home_screen_two.dart
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
171
lib/screen_ui/multi_vendor_service/home_screen/story_view.dart
Normal file
171
lib/screen_ui/multi_vendor_service/home_screen/story_view.dart
Normal 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)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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(
|
||||
"You’re 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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 you’ll 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}",
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
@@ -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)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
"You’re 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 }
|
||||
104
lib/screen_ui/on_boarding_screen/on_boarding_screen.dart
Normal file
104
lib/screen_ui/on_boarding_screen/on_boarding_screen.dart
Normal file
@@ -0,0 +1,104 @@
|
||||
import 'package:customer/constant/assets.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/on_boarding_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
import '../../utils/preferences.dart';
|
||||
import '../auth_screens/login_screen.dart';
|
||||
|
||||
class OnboardingScreen extends StatelessWidget {
|
||||
const OnboardingScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetX<OnboardingController>(
|
||||
init: OnboardingController(),
|
||||
builder: (controller) {
|
||||
final pageCount = controller.onboardingList.length;
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.asset(AppAssets.onBoardingBG, fit: BoxFit.cover),
|
||||
SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
style: AppThemeData.regularTextStyle(fontSize: 14),
|
||||
children: [
|
||||
TextSpan(text: "${controller.currentPage.value + 1}", style: AppThemeData.regularTextStyle(color: AppThemeData.grey800)),
|
||||
TextSpan(text: "/$pageCount", style: AppThemeData.regularTextStyle(color: AppThemeData.grey400)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
controller: controller.pageController,
|
||||
onPageChanged: controller.onPageChanged,
|
||||
itemCount: pageCount,
|
||||
itemBuilder: (context, index) {
|
||||
final item = controller.onboardingList[index];
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(item.title ?? '', style: AppThemeData.boldTextStyle(color: AppThemeData.grey900), textAlign: TextAlign.center),
|
||||
const SizedBox(height: 5),
|
||||
Text(item.description ?? '', style: AppThemeData.boldTextStyle(color: AppThemeData.grey500, fontSize: 14), textAlign: TextAlign.center),
|
||||
const SizedBox(height: 40),
|
||||
NetworkImageWidget(imageUrl: item.image ?? '', width: double.infinity, height: 500),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
controller.currentPage.value == pageCount - 1
|
||||
? RoundedButtonFill(
|
||||
title: "Let’s Get Started".tr,
|
||||
onPress: () {
|
||||
_finish();
|
||||
},
|
||||
color: AppThemeData.grey900,
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Expanded(child: RoundedButtonFill(title: "Skip".tr, onPress: () => _finish(), color: AppThemeData.grey50, textColor: AppThemeData.grey900)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "Next".tr,
|
||||
onPress: () {
|
||||
controller.nextPage();
|
||||
},
|
||||
color: AppThemeData.grey900,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _finish() async {
|
||||
await Preferences.setBoolean(Preferences.isFinishOnBoardingKey, true);
|
||||
Get.offAll(() => const LoginScreen());
|
||||
}
|
||||
}
|
||||
312
lib/screen_ui/on_demand_service/favourite_ondemand_screen.dart
Normal file
312
lib/screen_ui/on_demand_service/favourite_ondemand_screen.dart
Normal file
@@ -0,0 +1,312 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/favourite_ondemmand_controller.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/category_model.dart';
|
||||
import 'package:customer/models/provider_serivce_model.dart';
|
||||
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
|
||||
import 'package:customer/screen_ui/on_demand_service/on_demand_details_screen.dart';
|
||||
import 'package:customer/service/fire_store_utils.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';
|
||||
|
||||
class FavouriteOndemandScreen extends StatelessWidget {
|
||||
const FavouriteOndemandScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: FavouriteOndemmandController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.onDemand300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
Text("Favourite Services".tr, style: TextStyle(fontFamily: AppThemeData.semiBold, color: isDark ? AppThemeData.grey900 : AppThemeData.grey900, fontSize: 20)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
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(
|
||||
"You’re 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: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child:
|
||||
controller.lstFav.isEmpty
|
||||
? Constant.showEmptyView(message: "Favourite Service not found.".tr)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
scrollDirection: Axis.vertical,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: controller.lstFav.length,
|
||||
itemBuilder: (context, index) {
|
||||
return FutureBuilder<List<ProviderServiceModel>>(
|
||||
future: FireStoreUtils.getCurrentProviderService(controller.lstFav[index]),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (!snapshot.hasData || snapshot.data == null || snapshot.data!.isEmpty) {
|
||||
return const SizedBox(); // or a placeholder widget
|
||||
}
|
||||
|
||||
final provider = snapshot.data!.first; // safer way than [0]
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => OnDemandDetailsScreen(), arguments: {'providerModel': provider});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.16,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.grey500 : Colors.grey.shade100, width: 1),
|
||||
color: isDark ? AppThemeData.grey900 : Colors.white,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), topLeft: Radius.circular(10)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: provider.photos.isNotEmpty ? provider.photos.first : Constant.placeHolderImage,
|
||||
height: MediaQuery.of(context).size.height * 0.16,
|
||||
width: 110,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget: (context, url, error) => Image.network(Constant.placeHolderImage, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
provider.title ?? "",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => GestureDetector(
|
||||
onTap: () => controller.toggleFavourite(provider),
|
||||
child: Icon(
|
||||
controller.lstFav.where((element) => element.service_id == provider.id).isNotEmpty ? Icons.favorite : Icons.favorite_border,
|
||||
size: 24,
|
||||
color:
|
||||
controller.lstFav.where((element) => element.service_id == provider.id).isNotEmpty
|
||||
? AppThemeData.primary300
|
||||
: (isDark ? Colors.white38 : Colors.black38),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
FutureBuilder<CategoryModel?>(
|
||||
future: controller.getCategory(provider.categoryId ?? ""),
|
||||
builder: (ctx, snap) {
|
||||
if (!snap.hasData) return const SizedBox();
|
||||
return Text(snap.data?.title ?? "", style: TextStyle(fontSize: 14, color: isDark ? Colors.white : Colors.black));
|
||||
},
|
||||
),
|
||||
_buildPrice(provider, isDark: isDark),
|
||||
_buildRating(provider),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
FutureBuilder<List<ProviderServiceModel>>(
|
||||
future: FireStoreUtils.getCurrentProviderService(controller.lstFav[index]),
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.data != null
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => OnDemandDetailsScreen(), arguments: {'providerModel': snapshot.data![0]});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.16,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.grey500 : Colors.grey.shade100, width: 1),
|
||||
color: isDark ? AppThemeData.grey900 : Colors.white,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), topLeft: Radius.circular(10)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: snapshot.data![0].photos.isNotEmpty ? snapshot.data![0].photos[0] : Constant.placeHolderImage,
|
||||
height: MediaQuery.of(context).size.height * 0.16,
|
||||
width: 110,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget: (context, url, error) => Image.network(Constant.placeHolderImage, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
snapshot.data![0].title ?? "",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => GestureDetector(
|
||||
onTap: () => controller.toggleFavourite(snapshot.data![0]),
|
||||
child: Icon(
|
||||
controller.lstFav.where((element) => element.service_id == snapshot.data![0].id).isNotEmpty
|
||||
? Icons.favorite
|
||||
: Icons.favorite_border,
|
||||
size: 24,
|
||||
color:
|
||||
controller.lstFav.where((element) => element.service_id == snapshot.data![0].id).isNotEmpty
|
||||
? AppThemeData.primary300
|
||||
: (isDark ? Colors.white38 : Colors.black38),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
FutureBuilder<CategoryModel?>(
|
||||
future: controller.getCategory(snapshot.data![0].categoryId ?? ""),
|
||||
builder: (ctx, snap) {
|
||||
if (!snap.hasData) return const SizedBox();
|
||||
return Text(snap.data?.title ?? "", style: TextStyle(fontSize: 14, color: isDark ? Colors.white : Colors.black));
|
||||
},
|
||||
),
|
||||
_buildPrice(snapshot.data![0], isDark: isDark),
|
||||
_buildRating(snapshot.data![0]),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPrice(ProviderServiceModel provider, {bool isDark = false}) {
|
||||
if (provider.disPrice == "" || provider.disPrice == "0") {
|
||||
return Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.price) : '${Constant.amountShow(amount: provider.price ?? "0")}/${'hr'.tr}',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: isDark ? Colors.white : AppThemeData.primary300),
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.disPrice ?? '0') : '${Constant.amountShow(amount: provider.disPrice)}/${'hr'.tr}',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: isDark ? Colors.white : AppThemeData.primary300),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.price) : '${Constant.amountShow(amount: provider.price ?? "0")}/${'hr'.tr}',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey, decoration: TextDecoration.lineThrough),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildRating(ProviderServiceModel provider) {
|
||||
double rating = 0;
|
||||
if (provider.reviewsCount != null && provider.reviewsCount != 0) {
|
||||
rating = (provider.reviewsSum ?? 0) / (provider.reviewsCount ?? 1);
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: AppThemeData.warning400, borderRadius: BorderRadius.circular(16)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.star, size: 16, color: Colors.white),
|
||||
const SizedBox(width: 3),
|
||||
Text(rating.toStringAsFixed(1), style: const TextStyle(letterSpacing: 0.5, fontSize: 12, color: Colors.white)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
203
lib/screen_ui/on_demand_service/my_booking_on_demand_screen.dart
Normal file
203
lib/screen_ui/on_demand_service/my_booking_on_demand_screen.dart
Normal file
@@ -0,0 +1,203 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/my_booking_on_demand_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/onprovider_order_model.dart';
|
||||
import '../../models/worker_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import 'on_demand_order_details_screen.dart';
|
||||
|
||||
class MyBookingOnDemandScreen extends StatelessWidget {
|
||||
const MyBookingOnDemandScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<MyBookingOnDemandController>(
|
||||
init: MyBookingOnDemandController(),
|
||||
builder: (controller) {
|
||||
return DefaultTabController(
|
||||
length: controller.tabTitles.length,
|
||||
initialIndex: controller.tabTitles.indexOf(controller.selectedTab.value),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
centerTitle: false,
|
||||
title: Padding(padding: const EdgeInsets.only(bottom: 10), child: Text("Booking History".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900))),
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size.fromHeight(48),
|
||||
child: TabBar(
|
||||
onTap: (index) {
|
||||
controller.selectTab(controller.tabTitles[index]);
|
||||
},
|
||||
indicatorColor: AppThemeData.grey900,
|
||||
labelColor: AppThemeData.grey900,
|
||||
unselectedLabelColor: AppThemeData.grey900,
|
||||
labelStyle: AppThemeData.boldTextStyle(fontSize: 16),
|
||||
unselectedLabelStyle: AppThemeData.mediumTextStyle(fontSize: 16),
|
||||
tabs: controller.tabTitles.map((title) => Tab(child: Center(child: Text(title)))).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: TabBarView(
|
||||
children:
|
||||
controller.tabTitles.map((title) {
|
||||
final orders = controller.getOrdersForTab(title);
|
||||
|
||||
if (orders.isEmpty) {
|
||||
return Center(child: Text("No ride found".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: orders.length,
|
||||
itemBuilder: (context, index) {
|
||||
OnProviderOrderModel onProviderOrder = orders[index];
|
||||
WorkerModel? worker = controller.getWorker(onProviderOrder.workerId);
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Get.to(() => OnDemandOrderDetailsScreen(), arguments: onProviderOrder);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.grey500 : Colors.grey.shade100, width: 1),
|
||||
color: isDark ? AppThemeData.grey500 : Colors.white,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: onProviderOrder.provider.photos.first,
|
||||
height: 80,
|
||||
width: 80,
|
||||
imageBuilder:
|
||||
(context, imageProvider) =>
|
||||
Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), image: DecorationImage(image: imageProvider, fit: BoxFit.cover))),
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget:
|
||||
(context, url, error) => ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.network(Constant.placeHolderImage, fit: BoxFit.cover, cacheHeight: 80, cacheWidth: 80),
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
decoration: BoxDecoration(color: AppThemeData.info50, border: Border.all(color: AppThemeData.info300), borderRadius: BorderRadius.circular(12)),
|
||||
child: Text(onProviderOrder.status, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.info500)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Text(
|
||||
onProviderOrder.provider.title.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
Padding(padding: const EdgeInsets.only(top: 6), child: buildPriceText(onProviderOrder)),
|
||||
const SizedBox(height: 6),
|
||||
if (onProviderOrder.status != Constant.orderCompleted &&
|
||||
onProviderOrder.status != Constant.orderCancelled &&
|
||||
onProviderOrder.otp != null &&
|
||||
onProviderOrder.otp!.isNotEmpty)
|
||||
Text(
|
||||
"${'OTP :'.tr} ${onProviderOrder.otp}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
/// Bottom Details (Date, Provider, Worker)
|
||||
buildBottomDetails(context, onProviderOrder, isDark, worker),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildPriceText(OnProviderOrderModel order) {
|
||||
final hasDiscount = order.provider.disPrice != "" && order.provider.disPrice != "0";
|
||||
final price = hasDiscount ? order.provider.disPrice.toString() : order.provider.price.toString();
|
||||
|
||||
return Text(
|
||||
order.provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: price) : "${Constant.amountShow(amount: price)}/${'hr'.tr}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 16, color: AppThemeData.primary300),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBottomDetails(BuildContext context, OnProviderOrderModel order, bool isDark, WorkerModel? worker) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.grey400 : AppThemeData.grey100, width: 1),
|
||||
color: isDark ? AppThemeData.greyDark100 : AppThemeData.grey100,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
detailRow("Date & Time", DateFormat('dd-MMM-yyyy hh:mm a').format(order.scheduleDateTime!.toDate()), isDark),
|
||||
const Divider(thickness: 1),
|
||||
detailRow("Provider", order.provider.authorName.toString(), isDark),
|
||||
|
||||
if (order.provider.priceUnit == "Hourly") ...[
|
||||
if (order.startTime != null) ...[const Divider(thickness: 1), detailRow("Start Time", DateFormat('dd-MMM-yyyy hh:mm a').format(order.startTime!.toDate()), isDark)],
|
||||
if (order.endTime != null) ...[const Divider(thickness: 1), detailRow("End Time", DateFormat('dd-MMM-yyyy hh:mm a').format(order.endTime!.toDate()), isDark)],
|
||||
],
|
||||
|
||||
if (worker != null) ...[const Divider(thickness: 1), detailRow("Worker", worker.fullName().toString(), isDark)],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget detailRow(String label, String value, bool isDark) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(label.tr, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(value.tr, style: AppThemeData.regularTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
557
lib/screen_ui/on_demand_service/on_demand_booking_screen.dart
Normal file
557
lib/screen_ui/on_demand_service/on_demand_booking_screen.dart
Normal file
@@ -0,0 +1,557 @@
|
||||
import 'package:bottom_picker/bottom_picker.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../controllers/on_demand_booking_controller.dart';
|
||||
import '../../models/tax_model.dart';
|
||||
import '../../models/user_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/show_toast_dialog.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import '../../widget/osm_map/map_picker_page.dart';
|
||||
import '../../widget/place_picker/location_picker_screen.dart';
|
||||
import '../../widget/place_picker/selected_location_model.dart';
|
||||
import '../location_enable_screens/address_list_screen.dart';
|
||||
|
||||
class OnDemandBookingScreen extends StatelessWidget {
|
||||
const OnDemandBookingScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: OnDemandBookingController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Book Service".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Services Section
|
||||
Text("Services".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey100),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(controller.provider.value?.title ?? '', style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
const SizedBox(height: 5),
|
||||
Text(controller.categoryTitle.value, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
if (controller.provider.value?.priceUnit == "Fixed") ...[
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(onTap: controller.decrementQuantity, child: Icon(Icons.remove_circle_outline, color: AppThemeData.primary300, size: 30)),
|
||||
const SizedBox(width: 10),
|
||||
Text('${controller.quantity.value}', style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
const SizedBox(width: 10),
|
||||
GestureDetector(onTap: controller.incrementQuantity, child: Icon(Icons.add_circle_outline, color: AppThemeData.primary300, size: 30)),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Container(
|
||||
width: 100,
|
||||
height: 100,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: Colors.grey.shade300,
|
||||
image: controller.provider.value!.photos.isNotEmpty ? DecorationImage(image: NetworkImage(controller.provider.value?.photos.first), fit: BoxFit.cover) : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey100),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Address".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
SizedBox(height: 5),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
if (Constant.userModel != null) {
|
||||
Get.to(AddressListScreen())!.then((value) {
|
||||
if (value != null) {
|
||||
ShippingAddress shippingAddress = value;
|
||||
if (Constant.checkZoneCheck(shippingAddress.location!.latitude ?? 0.0, shippingAddress.location!.longitude ?? 0.0)) {
|
||||
controller.selectedAddress.value = shippingAddress;
|
||||
controller.calculatePrice();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Service not available in this area".tr);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Constant.checkPermission(
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
|
||||
ShippingAddress shippingAddress = ShippingAddress();
|
||||
|
||||
try {
|
||||
await Geolocator.requestPermission();
|
||||
await Geolocator.getCurrentPosition();
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
shippingAddress.addressAs = "Home";
|
||||
shippingAddress.locality = address.toString();
|
||||
shippingAddress.location = UserLocation(latitude: lat, longitude: lng);
|
||||
|
||||
controller.selectedAddress.value = shippingAddress;
|
||||
Get.back();
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
shippingAddress.addressAs = "Home";
|
||||
shippingAddress.location = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
shippingAddress.locality = "Picked from Map";
|
||||
|
||||
controller.selectedAddress.value = shippingAddress;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
await placemarkFromCoordinates(19.228825, 72.854118).then((valuePlaceMaker) {
|
||||
Placemark placeMark = valuePlaceMaker[0];
|
||||
shippingAddress.location = UserLocation(latitude: 19.228825, longitude: 72.854118);
|
||||
String currentLocation =
|
||||
"${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}";
|
||||
shippingAddress.locality = currentLocation;
|
||||
});
|
||||
|
||||
controller.selectedAddress.value = shippingAddress;
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
},
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
controller.selectedAddress.value.getFullAddress(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
TextFieldWidget(title: "Description".tr, hintText: "Enter Description".tr, controller: controller.descriptionController.value, maxLine: 5),
|
||||
const SizedBox(height: 10),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
BottomPicker.dateTime(
|
||||
onSubmit: (date) {
|
||||
controller.setDateTime(date);
|
||||
},
|
||||
minDateTime: DateTime.now(),
|
||||
buttonAlignment: MainAxisAlignment.center,
|
||||
displaySubmitButton: true,
|
||||
buttonSingleColor: AppThemeData.primary300,
|
||||
buttonPadding: 10,
|
||||
buttonWidth: 70,
|
||||
pickerTitle: Text("", style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
backgroundColor: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
pickerTextStyle: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
closeIconColor: isDark ? Colors.white : Colors.black,
|
||||
).show(context);
|
||||
},
|
||||
child: TextFieldWidget(title: "Booking Date & Slot".tr, hintText: "Choose Date and Time".tr, controller: controller.dateTimeController.value, enable: false),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
controller.provider.value?.priceUnit == "Fixed"
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
controller.couponList.isNotEmpty
|
||||
? SizedBox(
|
||||
height: 85,
|
||||
child: ListView.builder(
|
||||
itemCount: controller.couponList.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
final coupon = controller.couponList[index];
|
||||
return GestureDetector(onTap: () => controller.applyCoupon(coupon), child: buildOfferItem(controller, index, isDark));
|
||||
},
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
buildPromoCode(controller, isDark),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Text("Price Detail".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
),
|
||||
priceTotalRow(controller, isDark),
|
||||
],
|
||||
)
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: RoundedButtonFill(title: "Confirm".tr, color: AppThemeData.primary300, textColor: AppThemeData.grey50, onPress: () => controller.confirmBooking(context)),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildOfferItem(OnDemandBookingController controller, int index, bool isDark) {
|
||||
return Obx(() {
|
||||
final coupon = controller.couponList[index];
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.fromLTRB(7, 10, 7, 10),
|
||||
height: 85,
|
||||
child: DottedBorder(
|
||||
options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(10), color: AppThemeData.primary300),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 5, 12, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Image(image: AssetImage('assets/images/offer_icon.png'), height: 25, width: 25),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 3),
|
||||
child: Text(
|
||||
coupon.discountType == "Fix Price" ? "${Constant.amountShow(amount: coupon.discount.toString())} ${'OFF'.tr}" : "${coupon.discount} ${'% Off'.tr}",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, letterSpacing: 0.7, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(coupon.code ?? '', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.normal, letterSpacing: 0.5, color: Colors.orange)),
|
||||
Container(margin: const EdgeInsets.only(left: 15, right: 15, top: 3), width: 1, color: AppThemeData.grey50),
|
||||
Text(
|
||||
"valid till ".tr + controller.getDate(coupon.expiresAt!.toDate().toString()),
|
||||
style: TextStyle(letterSpacing: 0.5, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildPromoCode(OnDemandBookingController controller, bool isDark) {
|
||||
return GestureDetector(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(top: 10, bottom: 13),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey100),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 15.0, horizontal: 15),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset("assets/images/reedem.png", height: 50, width: 50),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Promo Code".tr, style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900), overflow: TextOverflow.ellipsis),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
"Apply promo code".tr,
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
FloatingActionButton(
|
||||
onPressed: () {
|
||||
Get.bottomSheet(promoCodeSheet(controller, isDark), isScrollControlled: true, isDismissible: true, backgroundColor: Colors.transparent, enableDrag: true);
|
||||
},
|
||||
mini: true,
|
||||
backgroundColor: Colors.blueGrey.shade50,
|
||||
elevation: 0,
|
||||
child: const Icon(Icons.add, color: Colors.black54),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget promoCodeSheet(OnDemandBookingController controller, bool isDark) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(bottom: Get.height / 4.3, left: 25, right: 25),
|
||||
height: Get.height * 0.88,
|
||||
decoration: BoxDecoration(color: Colors.transparent, border: Border.all(style: BorderStyle.none)),
|
||||
child: Column(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey100, width: 0.3),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, // ✅ visible color
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50),
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(padding: const EdgeInsets.only(top: 30), child: const Image(image: AssetImage('assets/images/redeem_coupon.png'), width: 100)),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Text('Redeem Your Coupons'.tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16)),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 10, left: 22, right: 22),
|
||||
child: Text("Voucher or Coupon code".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 20),
|
||||
child: DottedBorder(
|
||||
options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(12), color: AppThemeData.primary300),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
alignment: Alignment.center,
|
||||
child: TextFormField(
|
||||
textAlign: TextAlign.center,
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
controller: controller.couponTextController.value,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
hintText: "Write Coupon Code".tr,
|
||||
hintStyle: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 30, bottom: 30, left: 15, right: 15),
|
||||
child: RoundedButtonFill(
|
||||
title: "REDEEM NOW".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey50,
|
||||
onPress: () {
|
||||
final inputCode = controller.couponTextController.value.text.trim().toLowerCase();
|
||||
|
||||
final matchingCoupon = controller.couponList.firstWhereOrNull((c) => c.code?.toLowerCase() == inputCode);
|
||||
|
||||
if (matchingCoupon != null) {
|
||||
controller.applyCoupon(matchingCoupon);
|
||||
Get.back();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Applied coupon not valid.".tr);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget priceTotalRow(OnDemandBookingController controller, bool isDark) {
|
||||
return Obx(() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark400 : AppThemeData.grey100),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 5),
|
||||
rowText("Price".tr, Constant.amountShow(amount: controller.price.value.toString()), isDark),
|
||||
controller.discountAmount.value != 0 ? const Divider() : const SizedBox(),
|
||||
controller.discountAmount.value != 0
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${"Discount".tr} ${controller.discountType.value == 'Percentage' || controller.discountType.value == 'Percent' ? "(${controller.discountLabel.value}%)" : "(${Constant.amountShow(amount: controller.discountLabel.value)})"}",
|
||||
style: TextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
Text(controller.offerCode.value, style: TextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text("(-${Constant.amountShow(amount: controller.discountAmount.value.toString())})", style: const TextStyle(color: Colors.red)),
|
||||
],
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
const Divider(),
|
||||
rowText("SubTotal".tr, Constant.amountShow(amount: controller.subTotal.value.toString()), isDark),
|
||||
const Divider(),
|
||||
ListView.builder(
|
||||
itemCount: Constant.taxList.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
TaxModel taxModel = Constant.taxList[index];
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
"${taxModel.title} (${taxModel.type == "fix" ? Constant.amountShow(amount: taxModel.tax) : "${taxModel.tax}%"})",
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
Constant.amountShow(amount: Constant.getTaxValue(amount: controller.subTotal.value.toString(), taxModel: taxModel).toString()),
|
||||
style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
rowText("Total Amount".tr, Constant.amountShow(amount: controller.totalAmount.value.toString()), isDark),
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget rowText(String title, String value, bool isDark) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title.tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(value.tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
101
lib/screen_ui/on_demand_service/on_demand_category_screen.dart
Normal file
101
lib/screen_ui/on_demand_service/on_demand_category_screen.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:customer/screen_ui/on_demand_service/view_category_service_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/on_demand_category_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/category_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
|
||||
class OnDemandCategoryScreen extends StatelessWidget {
|
||||
const OnDemandCategoryScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX(
|
||||
init: OnDemandCategoryController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Explore services".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
Text(
|
||||
"Explore services tailored for you—quick, easy, and personalized.".tr,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
controller.categories.isEmpty
|
||||
? Center(child: Text("No Categories".tr))
|
||||
: GridView.builder(
|
||||
padding: const EdgeInsets.all(5),
|
||||
itemCount: controller.categories.length,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
|
||||
itemBuilder: (context, index) {
|
||||
return categoriesCell(context, controller.categories[index], index, isDark);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget categoriesCell(BuildContext context, CategoryModel category, int index, bool isDark) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => ViewCategoryServiceListScreen(), arguments: {'categoryId': category.id, 'categoryTitle': category.title});
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
ClipRRect(borderRadius: BorderRadius.circular(12), child: CachedNetworkImage(imageUrl: category.image ?? "", height: 60, width: 60, fit: BoxFit.cover)),
|
||||
const SizedBox(height: 5),
|
||||
Text(category.title ?? "", style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900), textAlign: TextAlign.center),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/cab_dashboard_controller.dart';
|
||||
import 'package:customer/controllers/theme_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/on_demand_dashboard_controller.dart';
|
||||
|
||||
class OnDemandDashboardScreen extends StatelessWidget {
|
||||
const OnDemandDashboardScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: OnDemandDashboardController(),
|
||||
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(CabDashboardController());
|
||||
}
|
||||
controller.selectedIndex.value = index;
|
||||
},
|
||||
items:
|
||||
Constant.walletSetting == false
|
||||
? [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_cab.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_booking_cab.svg", label: 'My Bookings'.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_cab.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_booking_cab.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 3, assetIcon: "assets/icons/ic_wallet_cab.svg", label: 'Wallet'.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 OnDemandDashboardController 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
499
lib/screen_ui/on_demand_service/on_demand_details_screen.dart
Normal file
499
lib/screen_ui/on_demand_service/on_demand_details_screen.dart
Normal file
@@ -0,0 +1,499 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/screen_ui/on_demand_service/provider_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/provider_serivce_model.dart';
|
||||
import '../../controllers/on_demand_details_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../auth_screens/login_screen.dart';
|
||||
import 'on_demand_booking_screen.dart';
|
||||
|
||||
class OnDemandDetailsScreen extends StatelessWidget {
|
||||
const OnDemandDetailsScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX<OnDemandDetailsController>(
|
||||
init: OnDemandDetailsController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
body: buildSliverScrollView(context, controller, controller.provider, controller.userModel, isDark),
|
||||
bottomNavigationBar:
|
||||
controller.isOpen.value == false
|
||||
? SizedBox()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
RoundedButtonFill(
|
||||
title: "Book Now".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey50,
|
||||
onPress: () async {
|
||||
if (Constant.userModel == null) {
|
||||
Get.offAll(const LoginScreen());
|
||||
} else {
|
||||
print("providerModel ::::::::${controller.provider.title ?? 'No provider'}");
|
||||
print("categoryTitle ::::::: ${controller.categoryTitle.value}");
|
||||
Get.to(() => OnDemandBookingScreen(), arguments: {'providerModel': controller.provider, 'categoryTitle': controller.categoryTitle.value});
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
SingleChildScrollView buildSliverScrollView(BuildContext context, OnDemandDetailsController controller, ProviderServiceModel provider, user, isDark) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
final height = MediaQuery.of(context).size.height;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
CachedNetworkImage(
|
||||
imageUrl: provider.photos.isNotEmpty ? provider.photos.first : "",
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget: (context, url, error) => Image.network(Constant.placeHolderImage, fit: BoxFit.fitWidth),
|
||||
fit: BoxFit.fitWidth,
|
||||
width: width,
|
||||
height: height * 0.45,
|
||||
),
|
||||
Positioned(top: height * 0.05, left: width * 0.03, child: _circleButton(context, icon: Icons.arrow_back, onTap: () => Get.back())),
|
||||
Positioned(
|
||||
top: height * 0.05,
|
||||
right: width * 0.03,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(40), color: controller.isOpen.value ? Colors.green : Colors.red),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Text(controller.isOpen.value ? "Open".tr : "Close".tr, style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 14)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||
child: GetBuilder<OnDemandDetailsController>(
|
||||
builder: (controller) {
|
||||
final provider = controller.provider;
|
||||
final categoryTitle = controller.categoryTitle.value;
|
||||
final subCategoryTitle = controller.subCategoryTitle.value;
|
||||
// final tabString = controller.tabString.value;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
provider.title.toString(),
|
||||
style: TextStyle(fontSize: 20, fontFamily: AppThemeData.regular, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
provider.disPrice == "" || provider.disPrice == "0"
|
||||
? Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.price ?? '0') : '${Constant.amountShow(amount: provider.price ?? '0')}/${'hr'.tr}',
|
||||
style: TextStyle(fontSize: 18, fontFamily: AppThemeData.regular, fontWeight: FontWeight.bold, color: isDark ? Colors.white : AppThemeData.primary300),
|
||||
)
|
||||
: Row(
|
||||
children: [
|
||||
Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.disPrice ?? '0') : '${Constant.amountShow(amount: provider.disPrice ?? '0')}/${'hr'.tr}',
|
||||
style: TextStyle(fontSize: 18, fontFamily: AppThemeData.regular, fontWeight: FontWeight.bold, color: isDark ? Colors.white : AppThemeData.primary300),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.price ?? '0') : '${Constant.amountShow(amount: provider.price ?? '0')}/${'hr'.tr}',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: Colors.grey, decoration: TextDecoration.lineThrough),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: Text(categoryTitle, style: TextStyle(fontSize: 14, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400, color: isDark ? Colors.white : Colors.black)),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.star, size: 16, color: AppThemeData.warning400),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
provider.reviewsCount != 0 ? ((provider.reviewsSum ?? 0.0) / (provider.reviewsCount ?? 0.0)).toStringAsFixed(1) : '0',
|
||||
style: const TextStyle(letterSpacing: 0.5, fontSize: 16, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500, color: AppThemeData.warning400),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
"(${provider.reviewsCount} ${'Reviews'.tr})",
|
||||
style: TextStyle(letterSpacing: 0.5, fontSize: 16, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500, color: isDark ? Colors.white : Colors.black),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: Row(
|
||||
children: [
|
||||
subCategoryTitle.isNotEmpty
|
||||
? Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: AppThemeData.primary300.withOpacity(0.20)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: Text(subCategoryTitle, style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, fontFamily: AppThemeData.regular, color: AppThemeData.primary300)),
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.green.withOpacity(0.20)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
showModalBottomSheet(
|
||||
isScrollControlled: true,
|
||||
isDismissible: true,
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
enableDrag: true,
|
||||
builder: (context) => showTiming(context, controller, isDark),
|
||||
);
|
||||
},
|
||||
child: Text("View Timing".tr, style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.green, letterSpacing: 0.5)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(Icons.location_on_outlined, color: isDark ? Colors.white : Colors.black, size: 20),
|
||||
const SizedBox(width: 5),
|
||||
Expanded(
|
||||
child: Text(
|
||||
provider.address.toString(),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontFamily: AppThemeData.regular, fontWeight: FontWeight.w400, color: isDark ? Colors.white : Colors.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
_tabBar(controller),
|
||||
Obx(() {
|
||||
if (controller.tabString.value == "About") {
|
||||
return aboutTabViewWidget(controller, controller.provider, isDark);
|
||||
} else if (controller.tabString.value == "Gallery") {
|
||||
return galleryTabViewWidget(controller);
|
||||
} else {
|
||||
return reviewTabViewWidget(controller, isDark);
|
||||
}
|
||||
}),
|
||||
const SizedBox(height: 15),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _circleButton(BuildContext context, {required IconData icon, required VoidCallback onTap}) {
|
||||
return ClipOval(
|
||||
child: Container(color: Colors.black.withOpacity(0.7), child: InkWell(onTap: onTap, child: Padding(padding: const EdgeInsets.all(8.0), child: Icon(icon, size: 30, color: Colors.white)))),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _tabBar(OnDemandDetailsController controller) {
|
||||
return Obx(() => Row(children: [_tabItem("About", controller), _tabItem("Gallery", controller), _tabItem("Review", controller)]));
|
||||
}
|
||||
|
||||
Widget _tabItem(String title, OnDemandDetailsController controller) {
|
||||
return GestureDetector(
|
||||
onTap: () => controller.changeTab(title),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(right: 10),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
decoration: BoxDecoration(color: controller.tabString.value == title ? AppThemeData.primary300 : Colors.grey.shade200, borderRadius: BorderRadius.circular(10)),
|
||||
child: Text(title.tr, style: TextStyle(fontWeight: FontWeight.bold, color: controller.tabString.value == title ? Colors.white : Colors.black)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget aboutTabViewWidget(OnDemandDetailsController controller, ProviderServiceModel providerModel, bool isDark) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text((providerModel.description ?? '').tr, style: TextStyle(color: isDark ? Colors.white : Colors.black, fontSize: 14, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500)),
|
||||
const SizedBox(height: 10),
|
||||
Obx(() {
|
||||
final user = controller.userModel.value;
|
||||
if (user == null) return const SizedBox();
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Get.to(() => ProviderScreen(), arguments: {'providerId': user.id});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.grey500 : Colors.grey.shade100, width: 1),
|
||||
color: isDark ? AppThemeData.grey500 : Colors.white,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(radius: 30, backgroundImage: NetworkImage(user.profilePictureURL?.isNotEmpty == true ? user.profilePictureURL! : Constant.placeHolderImage)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(user.fullName(), style: TextStyle(color: isDark ? Colors.white : Colors.black, fontFamily: AppThemeData.regular, fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 5),
|
||||
Text(user.email ?? '', style: TextStyle(color: isDark ? Colors.white : Colors.black, fontFamily: AppThemeData.regular, fontSize: 14)),
|
||||
const SizedBox(height: 10),
|
||||
// Rating Box
|
||||
Container(
|
||||
decoration: BoxDecoration(color: AppThemeData.warning400, borderRadius: BorderRadius.circular(16)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.star, size: 16, color: Colors.white),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
double.parse(user.reviewsCount.toString()) != 0
|
||||
? (double.parse(user.reviewsSum.toString()) / double.parse(user.reviewsCount.toString())).toStringAsFixed(1)
|
||||
: '0',
|
||||
style: const TextStyle(letterSpacing: 0.5, fontSize: 12, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Icon(Icons.chevron_right),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget galleryTabViewWidget(OnDemandDetailsController controller) {
|
||||
final photos = controller.provider.photos;
|
||||
|
||||
if (photos.isEmpty) {
|
||||
return Center(child: Text("No Image Found".tr));
|
||||
}
|
||||
|
||||
return GridView.builder(
|
||||
itemCount: photos.length,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, mainAxisSpacing: 0, crossAxisSpacing: 8, mainAxisExtent: 180),
|
||||
itemBuilder: (context, index) {
|
||||
final imageUrl = photos[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: imageUrl,
|
||||
height: 60,
|
||||
width: 60,
|
||||
imageBuilder: (context, imageProvider) => Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), image: DecorationImage(image: imageProvider, fit: BoxFit.cover))),
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget: (context, url, error) => Image.network(Constant.placeHolderImage, fit: BoxFit.cover),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget reviewTabViewWidget(OnDemandDetailsController controller, bool isDark) {
|
||||
final reviews = controller.ratingService;
|
||||
|
||||
if (reviews.isEmpty) {
|
||||
return SizedBox(height: 200, child: Center(child: Text("No review Found".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900))));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: reviews.length,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (context, index) {
|
||||
final review = reviews[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: ShapeDecoration(
|
||||
color: isDark ? AppThemeData.grey700 : AppThemeData.grey50,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
shadows: const [BoxShadow(color: Color(0x0A000000), blurRadius: 32, offset: Offset(0, 0), spreadRadius: 0)],
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(review.uname ?? '', style: TextStyle(fontSize: 16, letterSpacing: 1, fontWeight: FontWeight.w600, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(
|
||||
review.createdAt != null ? DateFormat('dd MMM').format(review.createdAt!.toDate()) : '',
|
||||
style: TextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
RatingBar.builder(
|
||||
initialRating: double.tryParse(review.rating.toString()) ?? 0,
|
||||
direction: Axis.horizontal,
|
||||
itemSize: 20,
|
||||
ignoreGestures: true,
|
||||
itemPadding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
itemBuilder: (context, _) => Icon(Icons.star, color: AppThemeData.primary300),
|
||||
onRatingUpdate: (rate) {},
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 5),
|
||||
Text(review.comment ?? '', style: TextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget showTiming(BuildContext context, OnDemandDetailsController controller, bool isDark) {
|
||||
final provider = controller.provider;
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.grey300 : Colors.white, borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20))),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
||||
child: Text("Service Timing".tr, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, fontFamily: AppThemeData.regular, color: AppThemeData.primary300)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: _timeCard(context, "Start Time : ".tr, provider.startTime.toString(), isDark)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: _timeCard(context, "End Time : ".tr, provider.endTime.toString(), isDark)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 16),
|
||||
child: Text("Service Days".tr, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, fontFamily: AppThemeData.regular, color: AppThemeData.primary300)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Wrap(
|
||||
spacing: 6.0,
|
||||
runSpacing: 6.0,
|
||||
children:
|
||||
provider.days
|
||||
.map(
|
||||
(day) => Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6), side: BorderSide(color: isDark ? const Color(0XFF3c3a2e) : const Color(0XFFC3C5D1), width: 1)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 7, horizontal: 20),
|
||||
child: Text(day, style: TextStyle(color: isDark ? const Color(0XFFa5a292) : const Color(0XFF5A5D6D))),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _timeCard(BuildContext context, String title, String value, bool isDark) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6), side: BorderSide(color: isDark ? const Color(0XFF3c3a2e) : const Color(0XFFC3C5D1), width: 1)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 7, horizontal: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(title, style: TextStyle(color: isDark ? const Color(0XFFa5a292) : const Color(0XFF5A5D6D))),
|
||||
Text(value, style: TextStyle(color: isDark ? const Color(0XFFa5a292) : const Color(0XFF5A5D6D))),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
532
lib/screen_ui/on_demand_service/on_demand_home_screen.dart
Normal file
532
lib/screen_ui/on_demand_service/on_demand_home_screen.dart
Normal file
@@ -0,0 +1,532 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/theme_controller.dart';
|
||||
import 'package:customer/models/banner_model.dart';
|
||||
import 'package:customer/models/user_model.dart';
|
||||
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/address_list_screen.dart';
|
||||
import 'package:customer/screen_ui/location_enable_screens/location_permission_screen.dart';
|
||||
import 'package:customer/screen_ui/on_demand_service/view_all_popular_service_screen.dart';
|
||||
import 'package:customer/screen_ui/on_demand_service/view_category_service_screen.dart';
|
||||
import 'package:customer/themes/app_them_data.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import 'package:customer/themes/show_toast_dialog.dart';
|
||||
import 'package:customer/utils/network_image_widget.dart';
|
||||
import 'package:customer/widget/osm_map/map_picker_page.dart';
|
||||
import 'package:customer/widget/place_picker/location_picker_screen.dart';
|
||||
import 'package:customer/widget/place_picker/selected_location_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/on_demand_home_controller.dart';
|
||||
import '../../models/category_model.dart';
|
||||
import '../../models/provider_serivce_model.dart';
|
||||
import 'on_demand_category_screen.dart';
|
||||
import 'on_demand_details_screen.dart';
|
||||
|
||||
class OnDemandHomeScreen extends StatelessWidget {
|
||||
const OnDemandHomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<OnDemandHomeController>(
|
||||
init: OnDemandHomeController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: const BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: const Center(child: Padding(padding: EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Constant.userModel == null
|
||||
? InkWell(onTap: () => Get.offAll(const LoginScreen()), child: Text("Login".tr, style: AppThemeData.boldTextStyle(color: AppThemeData.grey900, fontSize: 12)))
|
||||
: Text(Constant.userModel!.fullName(), style: AppThemeData.boldTextStyle(color: AppThemeData.grey900, fontSize: 12)),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
if (Constant.userModel != null) {
|
||||
Get.to(AddressListScreen())!.then((value) {
|
||||
if (value != null) {
|
||||
ShippingAddress shippingAddress = value;
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
controller.getData();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Constant.checkPermission(
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
|
||||
// ✅ declare it once here!
|
||||
ShippingAddress shippingAddress = ShippingAddress();
|
||||
|
||||
try {
|
||||
await Geolocator.requestPermission();
|
||||
await Geolocator.getCurrentPosition();
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
shippingAddress.addressAs = "Home";
|
||||
shippingAddress.locality = address.toString();
|
||||
shippingAddress.location = UserLocation(latitude: lat, longitude: lng);
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
controller.getData();
|
||||
Get.back();
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
shippingAddress.addressAs = "Home";
|
||||
shippingAddress.location = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
shippingAddress.locality = "Picked from Map"; // You can reverse-geocode
|
||||
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
controller.getData();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
await placemarkFromCoordinates(19.228825, 72.854118).then((valuePlaceMaker) {
|
||||
Placemark placeMark = valuePlaceMaker[0];
|
||||
shippingAddress.location = UserLocation(latitude: 19.228825, longitude: 72.854118);
|
||||
String currentLocation =
|
||||
"${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}";
|
||||
shippingAddress.locality = currentLocation;
|
||||
});
|
||||
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
ShowToastDialog.closeLoader();
|
||||
controller.getData();
|
||||
}
|
||||
},
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text.rich(
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: Constant.selectedLocation.getFullAddress(),
|
||||
style: TextStyle(fontFamily: AppThemeData.medium, overflow: TextOverflow.ellipsis, color: AppThemeData.grey900, fontSize: 14),
|
||||
),
|
||||
WidgetSpan(child: SvgPicture.asset("assets/icons/ic_down.svg")),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Constant.isZoneAvailable == false || controller.providerList.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());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
BannerView(bannerList: controller.bannerTopHome),
|
||||
const SizedBox(height: 20),
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height * 0.12,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark600 : AppThemeData.greyDark600, width: 1),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
),
|
||||
child:
|
||||
controller.categories.isEmpty
|
||||
? Constant.showEmptyView(message: "No Categories".tr)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: controller.categories.length > 3 ? 3 : controller.categories.length,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
final category = controller.categories[index];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Get.to(() => ViewCategoryServiceListScreen(), arguments: {'categoryId': category.id, 'categoryTitle': category.title});
|
||||
},
|
||||
child: CategoryView(category: category, index: index, isDark: isDark),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
if (controller.categories.length > 3)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.to(() => const OnDemandCategoryScreen());
|
||||
},
|
||||
child: ClipOval(child: Container(width: 50, height: 50, color: AppThemeData.grey200, child: const Center(child: Icon(Icons.chevron_right)))),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: 70,
|
||||
child: Center(
|
||||
child: Text(
|
||||
"View All".tr,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 1,
|
||||
style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20, bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Most Popular services".tr,
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black, fontSize: 18, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.to(() => ViewAllPopularServiceScreen());
|
||||
},
|
||||
child: Text("View all".tr, style: TextStyle(color: AppThemeData.primary300, fontSize: 14, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w600)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
controller.providerList.isEmpty
|
||||
? Center(child: Text("No Services Found".tr))
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: controller.providerList.length >= 6 ? 6 : controller.providerList.length,
|
||||
itemBuilder: (_, index) {
|
||||
return ServiceView(provider: controller.providerList[index], controller: controller, isDark: isDark);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BannerView extends StatelessWidget {
|
||||
final List<BannerModel> bannerList;
|
||||
final RxInt currentPage = 0.obs;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
|
||||
BannerView({super.key, required this.bannerList});
|
||||
|
||||
void onScroll(BuildContext context) {
|
||||
if (scrollController.hasClients && bannerList.isNotEmpty) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final itemWidth = screenWidth * 0.8 + 10; // banner width + spacing
|
||||
final offset = scrollController.offset;
|
||||
final index = (offset / itemWidth).round();
|
||||
|
||||
if (index != currentPage.value && index < bannerList.length) {
|
||||
currentPage.value = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
scrollController.addListener(() {
|
||||
onScroll(context);
|
||||
});
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: ListView.separated(
|
||||
controller: scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: bannerList.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 15),
|
||||
itemBuilder: (context, index) {
|
||||
final banner = bannerList[index];
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: SizedBox(width: MediaQuery.of(context).size.width * 0.8, child: NetworkImageWidget(imageUrl: banner.photo ?? '', fit: BoxFit.cover)),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Obx(() {
|
||||
return Row(
|
||||
children: List.generate(bannerList.length, (index) {
|
||||
bool isSelected = currentPage.value == index;
|
||||
return Expanded(child: Container(height: 4, decoration: BoxDecoration(color: isSelected ? AppThemeData.grey300 : AppThemeData.grey100, borderRadius: BorderRadius.circular(5))));
|
||||
}),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryView extends StatelessWidget {
|
||||
final CategoryModel category;
|
||||
final int index;
|
||||
final bool isDark;
|
||||
|
||||
const CategoryView({super.key, required this.category, required this.index, required this.isDark});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 55,
|
||||
width: 55,
|
||||
decoration: BoxDecoration(color: Constant.colorList[index % Constant.colorList.length], borderRadius: BorderRadius.circular(50)),
|
||||
child: ClipOval(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(14.0),
|
||||
child: CachedNetworkImage(imageUrl: category.image.toString(), errorWidget: (_, __, ___) => Image.network(Constant.placeHolderImage, fit: BoxFit.cover)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
SizedBox(
|
||||
width: 70,
|
||||
child: Center(
|
||||
child: Text(category.title ?? "", textAlign: TextAlign.center, maxLines: 1, style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceView extends StatelessWidget {
|
||||
final ProviderServiceModel provider;
|
||||
final bool isDark;
|
||||
final OnDemandHomeController? controller;
|
||||
|
||||
const ServiceView({super.key, required this.provider, this.isDark = false, this.controller});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => OnDemandDetailsScreen(), arguments: {'providerModel': provider});
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: isDark ? AppThemeData.grey500 : Colors.grey.shade200),
|
||||
color: isDark ? AppThemeData.grey900 : Colors.white,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// --- Left Image ---
|
||||
ClipRRect(
|
||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: provider.photos.isNotEmpty ? provider.photos[0] : Constant.placeHolderImage,
|
||||
width: 110,
|
||||
height: MediaQuery.of(context).size.height * 0.16,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget: (context, url, error) => Image.network(Constant.placeHolderImage, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
|
||||
// --- Right Content ---
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Title + Favourite icon
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
provider.title ?? "",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: isDark ? Colors.white : Colors.black),
|
||||
),
|
||||
),
|
||||
if (controller != null)
|
||||
Obx(
|
||||
() => GestureDetector(
|
||||
onTap: () => controller!.toggleFavourite(provider),
|
||||
child: Icon(
|
||||
controller!.lstFav.where((element) => element.service_id == provider.id).isNotEmpty ? Icons.favorite : Icons.favorite_border,
|
||||
size: 24,
|
||||
color: controller!.lstFav.where((element) => element.service_id == provider.id).isNotEmpty ? AppThemeData.primary300 : (isDark ? Colors.white38 : Colors.black38),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 4),
|
||||
|
||||
// Category
|
||||
if (controller != null)
|
||||
FutureBuilder<CategoryModel?>(
|
||||
future: controller!.getCategory(provider.categoryId ?? ""),
|
||||
builder: (ctx, snap) {
|
||||
if (!snap.hasData) return const SizedBox.shrink();
|
||||
return Text(snap.data?.title ?? "", style: TextStyle(fontSize: 13, color: isDark ? Colors.white70 : Colors.black54));
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 4),
|
||||
|
||||
// Price
|
||||
_buildPrice(),
|
||||
|
||||
const SizedBox(height: 6),
|
||||
|
||||
// Rating
|
||||
_buildRating(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPrice() {
|
||||
if (provider.disPrice == "" || provider.disPrice == "0") {
|
||||
return Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.price) : '${Constant.amountShow(amount: provider.price ?? "0")}/${'hr'.tr}',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: isDark ? Colors.white : AppThemeData.primary300),
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.disPrice ?? '0') : '${Constant.amountShow(amount: provider.disPrice)}/${'hr'.tr}',
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: isDark ? Colors.white : AppThemeData.primary300),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Flexible(
|
||||
child: Text(
|
||||
provider.priceUnit == 'Fixed' ? Constant.amountShow(amount: provider.price) : '${Constant.amountShow(amount: provider.price ?? "0")}/hr',
|
||||
style: const TextStyle(fontSize: 12, color: Colors.grey, decoration: TextDecoration.lineThrough),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildRating() {
|
||||
double rating = 0;
|
||||
if (provider.reviewsCount != null && provider.reviewsCount != 0) {
|
||||
rating = (provider.reviewsSum ?? 0) / (provider.reviewsCount ?? 1);
|
||||
}
|
||||
return Container(
|
||||
decoration: BoxDecoration(color: AppThemeData.warning400, borderRadius: BorderRadius.circular(12)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [const Icon(Icons.star, size: 14, color: Colors.white), const SizedBox(width: 3), Text(rating.toStringAsFixed(1), style: const TextStyle(fontSize: 12, color: Colors.white))],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1043
lib/screen_ui/on_demand_service/on_demand_order_details_screen.dart
Normal file
1043
lib/screen_ui/on_demand_service/on_demand_order_details_screen.dart
Normal file
File diff suppressed because it is too large
Load Diff
263
lib/screen_ui/on_demand_service/on_demand_payment_screen.dart
Normal file
263
lib/screen_ui/on_demand_service/on_demand_payment_screen.dart
Normal file
@@ -0,0 +1,263 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/0n_demand_payment_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../payment/createRazorPayOrderModel.dart';
|
||||
import '../../payment/rozorpayConroller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/show_toast_dialog.dart';
|
||||
import '../multi_vendor_service/wallet_screen/wallet_screen.dart';
|
||||
|
||||
class OnDemandPaymentScreen extends StatelessWidget {
|
||||
const OnDemandPaymentScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<OnDemandPaymentController>(
|
||||
init: OnDemandPaymentController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Select Payment Method".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.greyDark200 : Colors.white),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Preferred Payment".tr, textAlign: TextAlign.start, style: AppThemeData.boldTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 10),
|
||||
if (controller.walletSettingModel.value.isEnabled == true || controller.cashOnDeliverySettingModel.value.isEnabled == true)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
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: AppThemeData.boldTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
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")),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
RoundedButtonFill(
|
||||
title: "Continue".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
onPress: () async {
|
||||
print("getTotalAmount :::::::: ${"${controller.totalAmount.value}"}");
|
||||
if (controller.isOrderPlaced.value == false) {
|
||||
controller.isOrderPlaced.value = true;
|
||||
if (controller.selectedPaymentMethod.value == PaymentGateway.stripe.name) {
|
||||
controller.stripeMakePayment(amount: "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.paypal.name) {
|
||||
controller.paypalPaymentSheet("${controller.totalAmount.value}", context);
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payStack.name) {
|
||||
controller.payStackPayment("${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.mercadoPago.name) {
|
||||
controller.mercadoPagoMakePayment(context: context, amount: "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.flutterWave.name) {
|
||||
controller.flutterWaveInitiatePayment(context: context, amount: "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payFast.name) {
|
||||
controller.payFastPayment(context: context, amount: "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.wallet.name) {
|
||||
double totalAmount = double.parse("${controller.totalAmount.value}");
|
||||
double walletAmount = double.tryParse(Constant.userModel?.walletAmount?.toString() ?? "0") ?? 0;
|
||||
|
||||
if (walletAmount == 0) {
|
||||
ShowToastDialog.showToast("Wallet balance is 0. Please recharge wallet.".tr);
|
||||
} else if (walletAmount < totalAmount) {
|
||||
ShowToastDialog.showToast("Insufficient wallet balance. Please add funds.".tr);
|
||||
} else {
|
||||
controller.placeOrder();
|
||||
}
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.cod.name) {
|
||||
controller.placeOrder();
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.wallet.name) {
|
||||
controller.placeOrder();
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.midTrans.name) {
|
||||
controller.midtransMakePayment(context: context, amount: "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.orangeMoney.name) {
|
||||
controller.orangeMakePayment(context: context, amount: "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.xendit.name) {
|
||||
controller.xenditPayment(context, "${controller.totalAmount.value}");
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.razorpay.name) {
|
||||
RazorPayController().createOrderRazorPay(amount: double.parse("${controller.totalAmount.value}"), 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.totalAmount.value}", orderId: result.id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
controller.isOrderPlaced.value = false;
|
||||
ShowToastDialog.showToast("Please select payment method".tr);
|
||||
}
|
||||
controller.isOrderPlaced.value = false;
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Obx cardDecoration(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: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
Text(
|
||||
Constant.amountShow(amount: Constant.userModel?.walletAmount == null ? '0.0' : Constant.userModel?.walletAmount.toString()),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Text(
|
||||
value.name.capitalizeString(),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
122
lib/screen_ui/on_demand_service/on_demand_review_screen.dart
Normal file
122
lib/screen_ui/on_demand_service/on_demand_review_screen.dart
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/on_demand_review_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
|
||||
class OnDemandReviewScreen extends StatelessWidget {
|
||||
const OnDemandReviewScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetBuilder<OnDemandReviewController>(
|
||||
init: OnDemandReviewController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(controller.ratingModel.value != null ? "Update Review".tr : "Add Review".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: Obx(
|
||||
() =>
|
||||
(controller.reviewFor.value == "Worker" && controller.workerModel.value == null) || (controller.reviewFor.value == "Provider" && controller.provider.value == null)
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 50.0),
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 42, bottom: 20),
|
||||
child: Card(
|
||||
elevation: 2,
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 65),
|
||||
child: Column(
|
||||
children: [
|
||||
Text('Rate for'.tr, style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(
|
||||
controller.reviewFor.value == "Provider" ? controller.order.value!.provider.authorName ?? "" : controller.workerModel.value!.fullName(),
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: RatingBar.builder(
|
||||
initialRating: controller.ratings.value,
|
||||
minRating: 1,
|
||||
direction: Axis.horizontal,
|
||||
allowHalfRating: true,
|
||||
itemCount: 5,
|
||||
itemPadding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
itemBuilder: (context, _) => const Icon(Icons.star, color: Colors.amber),
|
||||
unratedColor: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400,
|
||||
onRatingUpdate: (rating) {
|
||||
controller.ratings.value = rating;
|
||||
},
|
||||
),
|
||||
),
|
||||
Padding(padding: EdgeInsets.all(20.0), child: TextFieldWidget(hintText: "Type comment....".tr, controller: controller.comment, maxLine: 5)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: RoundedButtonFill(
|
||||
title: controller.ratingModel.value != null ? "Update Review".tr : "Add Review".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey50,
|
||||
onPress: controller.submitReview,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: NetworkImageWidget(
|
||||
imageUrl: controller.reviewFor.value == "Provider" ? controller.order.value?.provider.authorProfilePic ?? '' : controller.workerModel.value?.profilePictureURL ?? '',
|
||||
fit: BoxFit.cover,
|
||||
height: 100,
|
||||
width: 100,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
127
lib/screen_ui/on_demand_service/provider_inbox_screen.dart
Normal file
127
lib/screen_ui/on_demand_service/provider_inbox_screen.dart
Normal 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/screen_ui/multi_vendor_service/chat_screens/chat_screen.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';
|
||||
|
||||
class ProviderInboxScreen extends StatelessWidget {
|
||||
const ProviderInboxScreen({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("Provider 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_provider').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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
116
lib/screen_ui/on_demand_service/provider_screen.dart
Normal file
116
lib/screen_ui/on_demand_service/provider_screen.dart
Normal file
@@ -0,0 +1,116 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/screen_ui/on_demand_service/on_demand_home_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/provider_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/provider_serivce_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
|
||||
class ProviderScreen extends StatelessWidget {
|
||||
const ProviderScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<ProviderController>(
|
||||
init: ProviderController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(automaticallyImplyLeading: true),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Center(child: Constant.loader())
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 50),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Center(
|
||||
child:
|
||||
(controller.userModel.value?.profilePictureURL ?? "").isNotEmpty
|
||||
? CircleAvatar(backgroundImage: NetworkImage(controller.userModel.value?.profilePictureURL ?? ''), radius: 50.0)
|
||||
: CircleAvatar(backgroundImage: NetworkImage(Constant.placeHolderImage), radius: 50.0),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
controller.userModel.value?.fullName() ?? '',
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black, fontFamily: AppThemeData.regular, fontSize: 20, fontWeight: FontWeight.w900),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_mail.svg", color: isDark ? Colors.white : Colors.black),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
controller.userModel.value?.email ?? '',
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black, fontFamily: AppThemeData.regular, fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_mobile.svg", color: isDark ? Colors.white : Colors.black),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
controller.userModel.value?.phoneNumber ?? '',
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black, fontFamily: AppThemeData.regular, fontSize: 14, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: const BoxDecoration(color: AppThemeData.warning400, borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.star, size: 16, color: Colors.white),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
_getRating(controller),
|
||||
style: const TextStyle(letterSpacing: 0.5, fontSize: 12, fontFamily: AppThemeData.regular, fontWeight: FontWeight.w500, color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Divider(),
|
||||
const SizedBox(height: 10),
|
||||
controller.providerList.isEmpty
|
||||
? Center(child: Text("No Services Found".tr))
|
||||
: Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: controller.providerList.length,
|
||||
padding: EdgeInsets.zero,
|
||||
itemBuilder: (context, index) {
|
||||
ProviderServiceModel data = controller.providerList[index];
|
||||
return ServiceView(provider: data, isDark: isDark, controller: controller.onDemandHomeController.value);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
String _getRating(ProviderController controller) {
|
||||
final reviewsCount = double.tryParse(controller.userModel.value?.reviewsCount?.toString() ?? "0") ?? 0;
|
||||
final reviewsSum = double.tryParse(controller.userModel.value?.reviewsSum?.toString() ?? "0") ?? 0;
|
||||
|
||||
if (reviewsCount == 0) return "0";
|
||||
final avg = reviewsSum / reviewsCount;
|
||||
return avg.toStringAsFixed(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:customer/screen_ui/on_demand_service/on_demand_home_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../controllers/view_all_popular_service_controller.dart';
|
||||
import '../../models/provider_serivce_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
|
||||
class ViewAllPopularServiceScreen extends StatelessWidget {
|
||||
const ViewAllPopularServiceScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX<ViewAllPopularServiceController>(
|
||||
init: ViewAllPopularServiceController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("All Services".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||
child: Column(
|
||||
children: [
|
||||
TextFieldWidget(hintText: "Search Service".tr, controller: controller.searchTextFiledController.value, onchange: (value) => controller.getFilterData(value.toString())),
|
||||
const SizedBox(height: 15),
|
||||
controller.providerList.isEmpty
|
||||
? Expanded(child: Center(child: Constant.showEmptyView(message: "No service Found".tr)))
|
||||
: Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: controller.providerList.length,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
scrollDirection: Axis.vertical,
|
||||
itemBuilder: (context, index) {
|
||||
ProviderServiceModel data = controller.providerList[index];
|
||||
return ServiceView(provider: data, isDark: isDark, controller: controller.onDemandHomeController.value);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../controllers/view_category_service_controller.dart';
|
||||
import '../../models/provider_serivce_model.dart';
|
||||
import '../../screen_ui/on_demand_service/on_demand_home_screen.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
|
||||
class ViewCategoryServiceListScreen extends StatelessWidget {
|
||||
const ViewCategoryServiceListScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<ViewCategoryServiceController>(
|
||||
init: ViewCategoryServiceController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(controller.categoryTitle.value, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: controller.providerList.isEmpty
|
||||
? Constant.showEmptyView(message: "No Service Found".tr)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
child: ListView.builder(
|
||||
itemCount: controller.providerList.length,
|
||||
shrinkWrap: true,
|
||||
padding: EdgeInsets.zero,
|
||||
itemBuilder: (context, index) {
|
||||
ProviderServiceModel providerModel = controller.providerList[index];
|
||||
return ServiceView(isDark: isDark, provider: providerModel, controller: controller.onDemandHomeController.value);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
128
lib/screen_ui/on_demand_service/worker_inbox_screen.dart
Normal file
128
lib/screen_ui/on_demand_service/worker_inbox_screen.dart
Normal file
@@ -0,0 +1,128 @@
|
||||
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/screen_ui/multi_vendor_service/chat_screens/chat_screen.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';
|
||||
|
||||
class WorkerInboxScreen extends StatelessWidget {
|
||||
const WorkerInboxScreen({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("Worker 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();
|
||||
|
||||
print("customerId: ${inboxModel.customerId}, restaurantId: ${inboxModel.restaurantId}");
|
||||
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_worker').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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
476
lib/screen_ui/parcel_service/book_parcel_screen.dart
Normal file
476
lib/screen_ui/parcel_service/book_parcel_screen.dart
Normal file
@@ -0,0 +1,476 @@
|
||||
import 'dart:io';
|
||||
import 'package:country_code_picker/country_code_picker.dart';
|
||||
import 'package:customer/themes/show_toast_dialog.dart';
|
||||
import 'package:customer/utils/utils.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:dropdown_textfield/dropdown_textfield.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/book_parcel_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/user_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import '../../widget/osm_map/map_picker_page.dart';
|
||||
import '../../widget/place_picker/location_picker_screen.dart';
|
||||
import '../../widget/place_picker/selected_location_model.dart';
|
||||
|
||||
class BookParcelScreen extends StatelessWidget {
|
||||
const BookParcelScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: BookParcelController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Book Your Document Delivery".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
Text(
|
||||
"Schedule a secure and timely pickup & delivery".tr,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 12, color: AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
selectDeliveryTypeView(controller, isDark, context),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
buildUploadBoxView(isDark, controller),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
buildInfoSectionView(
|
||||
title: "Sender Information".tr,
|
||||
locationController: controller.senderLocationController.value,
|
||||
nameController: controller.senderNameController.value,
|
||||
mobileController: controller.senderMobileController.value,
|
||||
noteController: controller.senderNoteController.value,
|
||||
countryCodeController: controller.senderCountryCodeController.value,
|
||||
showWeight: true,
|
||||
isDark: isDark,
|
||||
context: context,
|
||||
controller: controller,
|
||||
onTap: () async {
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
|
||||
if (Constant.checkZoneCheck(firstPlace.coordinates.latitude, firstPlace.coordinates.longitude) == true) {
|
||||
final address = firstPlace.address;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
controller.senderLocationController.value.text = address; // ✅
|
||||
controller.senderLocation.value = UserLocation(latitude: lat, longitude: lng); // ✅ <-- Add this
|
||||
} else {
|
||||
ShowToastDialog.showToast("Service is unavailable at the selected address.".tr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
if (Constant.checkZoneCheck(selectedLocationModel.latLng!.latitude, selectedLocationModel.latLng!.longitude) == true) {
|
||||
controller.senderLocationController.value.text = Utils.formatAddress(selectedLocation: selectedLocationModel);
|
||||
controller.senderLocation.value = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
} else {
|
||||
ShowToastDialog.showToast("Service is unavailable at the selected address.".tr);
|
||||
}
|
||||
// ✅ <-- Add this
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
buildInfoSectionView(
|
||||
title: "Receiver Information".tr,
|
||||
locationController: controller.receiverLocationController.value,
|
||||
nameController: controller.receiverNameController.value,
|
||||
mobileController: controller.receiverMobileController.value,
|
||||
noteController: controller.receiverNoteController.value,
|
||||
countryCodeController: controller.receiverCountryCodeController.value,
|
||||
showWeight: false,
|
||||
isDark: isDark,
|
||||
context: context,
|
||||
controller: controller,
|
||||
onTap: () async {
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
|
||||
if (Constant.checkZoneCheck(firstPlace.coordinates.latitude, firstPlace.coordinates.longitude) == true) {
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
controller.receiverLocationController.value.text = address; // ✅
|
||||
controller.receiverLocation.value = UserLocation(latitude: lat, longitude: lng);
|
||||
} else {
|
||||
ShowToastDialog.showToast("Service is unavailable at the selected address.".tr);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
if (Constant.checkZoneCheck(selectedLocationModel.latLng!.latitude, selectedLocationModel.latLng!.longitude) == true) {
|
||||
controller.receiverLocationController.value.text = Utils.formatAddress(selectedLocation: selectedLocationModel);
|
||||
controller.receiverLocation.value = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude); // ✅ <-- Add this
|
||||
} else {
|
||||
ShowToastDialog.showToast("Service is unavailable at the selected address.".tr);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 15),
|
||||
|
||||
RoundedButtonFill(
|
||||
title: "Continue".tr,
|
||||
onPress: () {
|
||||
controller.bookNow();
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget selectDeliveryTypeView(BookParcelController controller, bool isDark, BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Select delivery type".tr, style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, fontSize: 13)),
|
||||
const SizedBox(height: 10),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
controller.selectedDeliveryType.value = 'now';
|
||||
controller.isScheduled.value = false;
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset("assets/images/image_parcel.png", height: 38, width: 38),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: Text("As soon as possible".tr, style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16))),
|
||||
Icon(
|
||||
controller.selectedDeliveryType.value == 'now' ? Icons.radio_button_checked : Icons.radio_button_off,
|
||||
color: controller.selectedDeliveryType.value == 'now' ? AppThemeData.primary300 : (isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
controller.selectedDeliveryType.value = 'later';
|
||||
controller.isScheduled.value = true;
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Image.asset("assets/images/image_parcel_scheduled.png", height: 38, width: 38),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: Text("Scheduled".tr, style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16))),
|
||||
Icon(
|
||||
controller.selectedDeliveryType.value == 'later' ? Icons.radio_button_checked : Icons.radio_button_off,
|
||||
color: controller.selectedDeliveryType.value == 'later' ? AppThemeData.primary300 : (isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (controller.selectedDeliveryType.value == 'later') ...[
|
||||
const SizedBox(height: 10),
|
||||
GestureDetector(
|
||||
onTap: () => controller.pickScheduledDate(context),
|
||||
child: TextFieldWidget(
|
||||
hintText: "When to pickup at this address".tr,
|
||||
controller: controller.scheduledDateController.value,
|
||||
enable: false,
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
suffix: const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.calendar_month_outlined)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
GestureDetector(
|
||||
onTap: () => controller.pickScheduledTime(context),
|
||||
child: TextFieldWidget(
|
||||
hintText: "When to pickup at this address".tr,
|
||||
controller: controller.scheduledTimeController.value,
|
||||
enable: false,
|
||||
// onchange: (v) => controller.pickScheduledTime(context),
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
suffix: const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.access_time)),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildUploadBoxView(bool isDark, BookParcelController controller) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Upload parcel image".tr, style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, fontSize: 13)),
|
||||
const SizedBox(height: 10),
|
||||
DottedBorder(
|
||||
options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(10), color: isDark ? AppThemeData.greyDark300 : AppThemeData.grey300),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_upload_parcel.svg", height: 40, width: 40),
|
||||
const SizedBox(height: 10),
|
||||
Text("Upload Parcel Image".tr, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
const SizedBox(height: 4),
|
||||
Text("Supported: .jpg, .jpeg, .png".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
Text("Max size 1MB".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
const SizedBox(height: 8),
|
||||
RoundedButtonFill(
|
||||
title: "Browse Image".tr,
|
||||
onPress: () {
|
||||
controller.onCameraClick(Get.context!);
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
width: 40,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (controller.images.isEmpty) const SizedBox(),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children:
|
||||
controller.images.map((image) {
|
||||
return Stack(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.only(top: 20, right: 20),
|
||||
child: ClipRRect(borderRadius: BorderRadius.circular(8), child: Image.file(File(image.path), width: 70, height: 70, fit: BoxFit.cover)),
|
||||
),
|
||||
Positioned.fill(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: Align(
|
||||
alignment: Alignment.topRight,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.cancel, color: AppThemeData.danger300, size: 20),
|
||||
onPressed: () {
|
||||
controller.images.remove(image);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildInfoSectionView({
|
||||
required String title,
|
||||
required TextEditingController locationController,
|
||||
required TextEditingController nameController,
|
||||
required TextEditingController mobileController,
|
||||
required TextEditingController noteController,
|
||||
required TextEditingController countryCodeController,
|
||||
bool showWeight = false,
|
||||
GestureTapCallback? onTap,
|
||||
required bool isDark,
|
||||
required BookParcelController controller,
|
||||
required BuildContext context,
|
||||
}) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, fontSize: 13)),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
GestureDetector(
|
||||
onTap: onTap,
|
||||
child: TextFieldWidget(
|
||||
hintText: "Your Location".tr,
|
||||
controller: locationController,
|
||||
|
||||
suffix: const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.location_on_outlined)),
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
enable: false,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
TextFieldWidget(
|
||||
hintText: "Name".tr,
|
||||
controller: nameController,
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
TextFieldWidget(
|
||||
hintText: "Enter Mobile number".tr,
|
||||
controller: mobileController,
|
||||
textInputType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]')), LengthLimitingTextInputFormatter(10)],
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
prefix: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CountryCodePicker(
|
||||
onChanged: (value) {
|
||||
countryCodeController.text = value.dialCode ?? Constant.defaultCountryCode;
|
||||
},
|
||||
initialSelection: countryCodeController.text.isNotEmpty ? countryCodeController.text : Constant.defaultCountryCode,
|
||||
showCountryOnly: false,
|
||||
showOnlyCountryWhenClosed: false,
|
||||
alignLeft: false,
|
||||
textStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : Colors.black),
|
||||
dialogTextStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
searchStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
dialogBackgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
// const Icon(Icons.keyboard_arrow_down_rounded, size: 24, color: AppThemeData.grey400),
|
||||
Container(height: 24, width: 1, color: AppThemeData.grey400),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
if (showWeight) ...[
|
||||
const SizedBox(height: 10),
|
||||
DropDownTextField(
|
||||
controller: controller.senderWeightController.value,
|
||||
clearOption: false,
|
||||
enableSearch: false,
|
||||
textFieldDecoration: InputDecoration(
|
||||
hintText: "Select parcel Weight".tr,
|
||||
hintStyle: AppThemeData.regularTextStyle(fontSize: 14, color: isDark ? AppThemeData.grey400 : AppThemeData.greyDark400),
|
||||
filled: true,
|
||||
fillColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppThemeData.grey200)),
|
||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppThemeData.grey200)),
|
||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppThemeData.grey200)),
|
||||
),
|
||||
dropDownList:
|
||||
controller.parcelWeight.map((e) {
|
||||
return DropDownValueModel(
|
||||
name: e.title ?? 'Normal'.tr,
|
||||
value: e.title ?? 'Normal'.tr, // safer to use title string
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (val) {
|
||||
if (val is DropDownValueModel) {
|
||||
controller.senderWeightController.value.setDropDown(val);
|
||||
|
||||
// Link it to the selectedWeight object
|
||||
controller.selectedWeight = controller.parcelWeight.firstWhereOrNull((e) => e.title == val.value);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
const SizedBox(height: 10),
|
||||
TextFieldWidget(
|
||||
hintText: "Notes (Optional)".tr,
|
||||
controller: noteController,
|
||||
backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface,
|
||||
borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
275
lib/screen_ui/parcel_service/home_parcel_screen.dart
Normal file
275
lib/screen_ui/parcel_service/home_parcel_screen.dart
Normal file
@@ -0,0 +1,275 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geocoding/geocoding.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/home_parcel_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/banner_model.dart';
|
||||
import '../../models/parcel_category.dart';
|
||||
import '../../models/user_model.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/show_toast_dialog.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
import '../../widget/osm_map/map_picker_page.dart';
|
||||
import '../../widget/place_picker/location_picker_screen.dart';
|
||||
import '../../widget/place_picker/selected_location_model.dart';
|
||||
import '../auth_screens/login_screen.dart';
|
||||
import '../location_enable_screens/address_list_screen.dart';
|
||||
import 'book_parcel_screen.dart';
|
||||
|
||||
class HomeParcelScreen extends StatelessWidget {
|
||||
const HomeParcelScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<HomeParcelController>(
|
||||
init: HomeParcelController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Constant.userModel == null
|
||||
? InkWell(
|
||||
onTap: () {
|
||||
Get.offAll(const LoginScreen());
|
||||
},
|
||||
child: Text("Login".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey900)),
|
||||
)
|
||||
: Text(Constant.userModel!.fullName(), style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey900)),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
if (Constant.userModel != null) {
|
||||
Get.to(AddressListScreen())!.then((value) {
|
||||
if (value != null) {
|
||||
ShippingAddress shippingAddress = value;
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Constant.checkPermission(
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
|
||||
ShippingAddress shippingAddress = ShippingAddress();
|
||||
|
||||
try {
|
||||
await Geolocator.requestPermission();
|
||||
await Geolocator.getCurrentPosition();
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
if (Constant.selectedMapType == 'osm') {
|
||||
final result = await Get.to(() => MapPickerPage());
|
||||
if (result != null) {
|
||||
final firstPlace = result;
|
||||
final lat = firstPlace.coordinates.latitude;
|
||||
final lng = firstPlace.coordinates.longitude;
|
||||
final address = firstPlace.address;
|
||||
|
||||
shippingAddress.addressAs = "Home";
|
||||
shippingAddress.locality = address.toString();
|
||||
shippingAddress.location = UserLocation(latitude: lat, longitude: lng);
|
||||
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
Get.back();
|
||||
}
|
||||
} else {
|
||||
Get.to(LocationPickerScreen())!.then((value) async {
|
||||
if (value != null) {
|
||||
SelectedLocationModel selectedLocationModel = value;
|
||||
|
||||
shippingAddress.addressAs = "Home";
|
||||
shippingAddress.location = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude);
|
||||
shippingAddress.locality = "Picked from Map";
|
||||
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
await placemarkFromCoordinates(19.228825, 72.854118).then((valuePlaceMaker) {
|
||||
Placemark placeMark = valuePlaceMaker[0];
|
||||
shippingAddress.location = UserLocation(latitude: 19.228825, longitude: 72.854118);
|
||||
String currentLocation =
|
||||
"${placeMark.name}, ${placeMark.subLocality}, ${placeMark.locality}, ${placeMark.administrativeArea}, ${placeMark.postalCode}, ${placeMark.country}";
|
||||
shippingAddress.locality = currentLocation;
|
||||
});
|
||||
|
||||
Constant.selectedLocation = shippingAddress;
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
},
|
||||
context: context,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
Constant.selectedLocation.getFullAddress(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Center(child: Constant.loader())
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
BannerView(bannerList: controller.bannerTopHome),
|
||||
const SizedBox(height: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("What are you sending?".tr, style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
const SizedBox(height: 12),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: ListView.builder(
|
||||
itemCount: controller.parcelCategory.length,
|
||||
shrinkWrap: true,
|
||||
physics: const ScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
itemBuilder: (context, index) {
|
||||
return buildItems(item: controller.parcelCategory[index], isDark: isDark);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildItems({required ParcelCategory item, required bool isDark}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (Constant.userModel == null) {
|
||||
Get.to(const LoginScreen());
|
||||
} else {
|
||||
Get.to(const BookParcelScreen(), arguments: {'parcelCategory': item});
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
NetworkImageWidget(imageUrl: item.image ?? '', height: 38, width: 38),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: Text(item.title ?? '', style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16))),
|
||||
Icon(Icons.arrow_forward_ios, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800, size: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BannerView extends StatelessWidget {
|
||||
final List<BannerModel> bannerList;
|
||||
final RxInt currentPage = 0.obs;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
|
||||
BannerView({super.key, required this.bannerList});
|
||||
|
||||
/// Computes the visible item index from scroll offset
|
||||
void onScroll(BuildContext context) {
|
||||
if (scrollController.hasClients && bannerList.isNotEmpty) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final itemWidth = screenWidth * 0.8 + 10; // banner width + spacing
|
||||
final offset = scrollController.offset;
|
||||
final index = (offset / itemWidth).round();
|
||||
|
||||
if (index != currentPage.value && index < bannerList.length) {
|
||||
currentPage.value = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
scrollController.addListener(() {
|
||||
onScroll(context);
|
||||
});
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 150,
|
||||
child: ListView.separated(
|
||||
controller: scrollController,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: bannerList.length,
|
||||
separatorBuilder: (context, index) => const SizedBox(width: 15),
|
||||
itemBuilder: (context, index) {
|
||||
final banner = bannerList[index];
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: SizedBox(width: MediaQuery.of(context).size.width * 0.8, child: NetworkImageWidget(imageUrl: banner.photo ?? '', fit: BoxFit.cover)),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Obx(() {
|
||||
return Row(
|
||||
children: List.generate(bannerList.length, (index) {
|
||||
bool isSelected = currentPage.value == index;
|
||||
return Expanded(child: Container(height: 4, decoration: BoxDecoration(color: isSelected ? AppThemeData.grey300 : AppThemeData.grey100, borderRadius: BorderRadius.circular(5))));
|
||||
}),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
209
lib/screen_ui/parcel_service/my_booking_screen.dart
Normal file
209
lib/screen_ui/parcel_service/my_booking_screen.dart
Normal file
@@ -0,0 +1,209 @@
|
||||
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
|
||||
import 'package:customer/screen_ui/parcel_service/parcel_order_details.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/parcel_my_booking_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
|
||||
class MyBookingScreen extends StatelessWidget {
|
||||
const MyBookingScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<ParcelMyBookingController>(
|
||||
init: ParcelMyBookingController(),
|
||||
builder: (controller) {
|
||||
return DefaultTabController(
|
||||
length: controller.tabTitles.length,
|
||||
initialIndex: controller.tabTitles.indexOf(controller.selectedTab.value),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(children: [const SizedBox(width: 10), Text("Parcel History".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900))]),
|
||||
),
|
||||
bottom: TabBar(
|
||||
// don't re-subscribe onTap — just update selectedTab (optional)
|
||||
onTap: (index) {
|
||||
controller.selectTab(controller.tabTitles[index]);
|
||||
},
|
||||
indicatorColor: AppThemeData.parcelService500,
|
||||
labelColor: AppThemeData.parcelService500,
|
||||
unselectedLabelColor: AppThemeData.parcelService500,
|
||||
labelStyle: AppThemeData.boldTextStyle(fontSize: 15),
|
||||
unselectedLabelStyle: AppThemeData.mediumTextStyle(fontSize: 15),
|
||||
tabs: controller.tabTitles.map((title) => Tab(child: Center(child: Text(title)))).toList(),
|
||||
),
|
||||
),
|
||||
|
||||
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: [
|
||||
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(
|
||||
"You’re 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());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: TabBarView(
|
||||
children:
|
||||
controller.tabTitles.map((title) {
|
||||
final orders = controller.getOrdersForTab(title);
|
||||
|
||||
if (orders.isEmpty) {
|
||||
return Center(child: Text("No orders found".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: orders.length,
|
||||
itemBuilder: (context, index) {
|
||||
final order = orders[index];
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Get.to(() => const ParcelOrderDetails(), arguments: order);
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
"${'Order Date:'.tr}${order.isSchedule == true ? controller.formatDate(order.createdAt!) : controller.formatDate(order.senderPickupDateTime!)}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.info400),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Image.asset("assets/images/image_parcel.png", height: 32, width: 32),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(size.width / 2, 0)
|
||||
..lineTo(size.width / 2, size.height),
|
||||
),
|
||||
child: const SizedBox(width: 20, height: 95),
|
||||
),
|
||||
Image.asset("assets/images/image_parcel.png", height: 32, width: 32),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_infoSection(
|
||||
"Pickup Address (Sender):".tr,
|
||||
order.sender?.name ?? '',
|
||||
order.sender?.address ?? '',
|
||||
order.sender?.phone ?? '',
|
||||
// order.senderPickupDateTime != null
|
||||
// ? "Pickup Time: ${controller.formatDate(order.senderPickupDateTime!)}"
|
||||
// : '',
|
||||
order.status,
|
||||
isDark,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_infoSection(
|
||||
"Delivery Address (Receiver):".tr,
|
||||
order.receiver?.name ?? '',
|
||||
order.receiver?.address ?? '',
|
||||
order.receiver?.phone ?? '',
|
||||
// order.receiverPickupDateTime != null
|
||||
// ? "Delivery Time: ${controller.formatDate(order.receiverPickupDateTime!)}"
|
||||
// : '',
|
||||
null,
|
||||
isDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _infoSection(String title, String name, String address, String phone, String? status, bool isDark) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(title, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900), maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
if (status != null) ...[
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
decoration: BoxDecoration(color: AppThemeData.info50, border: Border.all(color: AppThemeData.info300), borderRadius: BorderRadius.circular(12)),
|
||||
child: Text(status, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.info500)),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
Text(name, style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(address, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(phone, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
//Text(time, style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
65
lib/screen_ui/parcel_service/order_successfully_placed.dart
Normal file
65
lib/screen_ui/parcel_service/order_successfully_placed.dart
Normal file
@@ -0,0 +1,65 @@
|
||||
import 'package:customer/screen_ui/parcel_service/parcel_dashboard_screen.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:customer/themes/app_them_data.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import '../../controllers/parcel_dashboard_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
|
||||
class OrderSuccessfullyPlaced extends StatelessWidget {
|
||||
const OrderSuccessfullyPlaced({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final parcelOrder = Get.arguments['parcelOrder'];
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset("assets/images/parcel_order_successfully_placed.png"),
|
||||
const SizedBox(height: 20),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 25),
|
||||
child: Text(
|
||||
"Your Order Has Been Placed!".tr,
|
||||
style: AppThemeData.boldTextStyle(fontSize: 22, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
child: Text(
|
||||
"We’ve received your parcel booking and it’s now being processed. You can track its status in real time.".tr,
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
RoundedButtonFill(
|
||||
title: "Track Your Order".tr,
|
||||
onPress: () {
|
||||
print("Tracking Order: $parcelOrder");
|
||||
//Get.to(() => TrackOrderScreen(), arguments: {'order': parcelOrder});
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
ParcelDashboardController controller = Get.put(ParcelDashboardController());
|
||||
controller.selectedIndex.value = 1;
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
138
lib/screen_ui/parcel_service/parcel_coupon_screen.dart
Normal file
138
lib/screen_ui/parcel_service/parcel_coupon_screen.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/parcel_coupon_controller.dart';
|
||||
import 'package:customer/controllers/theme_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/widget/my_separator.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ParcelCouponScreen extends StatelessWidget {
|
||||
const ParcelCouponScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: ParcelCouponController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Coupon".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: controller.cabCouponList.isEmpty
|
||||
? Constant.showEmptyView(message: "Coupon not found".tr)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: controller.cabCouponList.length,
|
||||
itemBuilder: (context, index) {
|
||||
CouponModel couponModel = controller.cabCouponList[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: () {
|
||||
Get.back(result: couponModel);
|
||||
},
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
80
lib/screen_ui/parcel_service/parcel_dashboard_screen.dart
Normal file
80
lib/screen_ui/parcel_service/parcel_dashboard_screen.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/parcel_dashboard_controller.dart';
|
||||
import 'package:customer/controllers/theme_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';
|
||||
|
||||
class ParcelDashboardScreen extends StatelessWidget {
|
||||
const ParcelDashboardScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: ParcelDashboardController(),
|
||||
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.parcelServiceDark300 : AppThemeData.primary300,
|
||||
unselectedItemColor: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500,
|
||||
onTap: (int index) {
|
||||
if (index == 0) {
|
||||
Get.put(ParcelDashboardController());
|
||||
}
|
||||
controller.selectedIndex.value = index;
|
||||
},
|
||||
items:
|
||||
Constant.walletSetting == false
|
||||
? [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_parcel.svg", label: 'Home'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_mybooking_parcel.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_profile_parcel.svg", label: 'Profile'.tr, controller: controller),
|
||||
]
|
||||
: [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_parcel.svg", label: 'Home'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_mybooking_parcel.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_wallet_parcel.svg", label: 'Wallet'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 3, assetIcon: "assets/icons/ic_profile_parcel.svg", label: 'Profile'.tr, controller: controller),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
BottomNavigationBarItem navigationBarItem(isDark, {required int index, required String label, required String assetIcon, required ParcelDashboardController controller}) {
|
||||
return BottomNavigationBarItem(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: SvgPicture.asset(
|
||||
assetIcon,
|
||||
height: label == 'Wallet'.tr ? 18 : 22,
|
||||
width: label == 'Wallet'.tr ? 18 : 22,
|
||||
color:
|
||||
controller.selectedIndex.value == index
|
||||
? isDark
|
||||
? AppThemeData.parcelServiceDark300
|
||||
: AppThemeData.primary300
|
||||
: isDark
|
||||
? AppThemeData.grey300
|
||||
: AppThemeData.grey600,
|
||||
),
|
||||
),
|
||||
label: label,
|
||||
);
|
||||
}
|
||||
}
|
||||
591
lib/screen_ui/parcel_service/parcel_order_confirmation.dart
Normal file
591
lib/screen_ui/parcel_service/parcel_order_confirmation.dart
Normal file
@@ -0,0 +1,591 @@
|
||||
import 'package:customer/models/coupon_model.dart';
|
||||
import 'package:customer/screen_ui/parcel_service/parcel_coupon_screen.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/parcel_order_confirmation_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../payment/createRazorPayOrderModel.dart';
|
||||
import '../../payment/rozorpayConroller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/show_toast_dialog.dart';
|
||||
import '../multi_vendor_service/wallet_screen/wallet_screen.dart';
|
||||
|
||||
class ParcelOrderConfirmationScreen extends StatelessWidget {
|
||||
const ParcelOrderConfirmationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: ParcelOrderConfirmationController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Order Confirmation".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Pickup and Delivery Info
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Timeline with icons and line
|
||||
Column(
|
||||
children: [
|
||||
Image.asset("assets/images/image_parcel.png", height: 32, width: 32),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(size.width / 2, 0)
|
||||
..lineTo(size.width / 2, size.height),
|
||||
),
|
||||
child: const SizedBox(width: 20, height: 95),
|
||||
),
|
||||
Image.asset("assets/images/image_parcel.png", height: 32, width: 32),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// Address Details
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_infoSection(
|
||||
"Pickup Address (Sender):".tr,
|
||||
controller.parcelOrder.value.sender?.name ?? '',
|
||||
controller.parcelOrder.value.sender?.address ?? '',
|
||||
controller.parcelOrder.value.sender?.phone ?? '',
|
||||
// controller.parcelOrder.value.senderPickupDateTime != null
|
||||
// ? "Pickup Time: ${controller.formatDate(controller.parcelOrder.value.senderPickupDateTime!)}"
|
||||
// : '',
|
||||
isDark,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_infoSection(
|
||||
"Delivery Address (Receiver):".tr,
|
||||
controller.parcelOrder.value.receiver?.name ?? '',
|
||||
controller.parcelOrder.value.receiver?.address ?? '',
|
||||
controller.parcelOrder.value.receiver?.phone ?? '',
|
||||
// controller.parcelOrder.value.receiverPickupDateTime != null
|
||||
// ? "Delivery Time: ${controller.formatDate(controller.parcelOrder.value.receiverPickupDateTime!)}"
|
||||
// : '',
|
||||
isDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Distance, Weight, Rate
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_iconTile("${controller.parcelOrder.value.distance ?? '--'} ${'KM'.tr}", "Distance".tr, "assets/icons/ic_distance_parcel.svg", isDark),
|
||||
_iconTile(controller.parcelOrder.value.parcelWeight ?? '--', "Weight".tr, "assets/icons/ic_weight_parcel.svg", isDark),
|
||||
_iconTile(Constant.amountShow(amount: controller.parcelOrder.value.subTotal), "Rate".tr, "assets/icons/ic_rate_parcel.svg", isDark),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 10),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text("Coupons".tr, style: AppThemeData.boldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900))),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.to(ParcelCouponScreen())!.then((value) {
|
||||
if (value != null) {
|
||||
double couponAmount = Constant.calculateDiscount(amount: controller.subTotal.value.toString(), offerModel: value);
|
||||
if (couponAmount < controller.subTotal.value) {
|
||||
controller.selectedCouponModel.value = value;
|
||||
controller.calculatePrice();
|
||||
} else {
|
||||
ShowToastDialog.showToast("This offer not eligible for this booking".tr);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
"View All".tr,
|
||||
style: AppThemeData.boldTextStyle(decoration: TextDecoration.underline, fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
// Coupon input
|
||||
DottedBorder(
|
||||
options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(10), color: isDark ? AppThemeData.parcelServiceDark300 : AppThemeData.primary300),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: AppThemeData.parcelService50, borderRadius: BorderRadius.circular(10)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_coupon_parcel.svg", height: 28, width: 28),
|
||||
SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: controller.couponController.value,
|
||||
style: AppThemeData.semiBoldTextStyle(color: AppThemeData.parcelService500, fontSize: 16),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Write coupon code".tr,
|
||||
hintStyle: AppThemeData.mediumTextStyle(fontSize: 16, color: AppThemeData.parcelService500),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
RoundedButtonFill(
|
||||
title: "Redeem now".tr,
|
||||
onPress: () {
|
||||
if (controller.couponList.where((element) => element.code!.toLowerCase() == controller.couponController.value.text.toLowerCase()).isNotEmpty) {
|
||||
CouponModel couponModel = controller.couponList.firstWhere((p0) => p0.code!.toLowerCase() == controller.couponController.value.text.toLowerCase());
|
||||
if (couponModel.expiresAt!.toDate().isAfter(DateTime.now())) {
|
||||
double couponAmount = Constant.calculateDiscount(amount: controller.subTotal.value.toString(), offerModel: couponModel);
|
||||
if (couponAmount < controller.subTotal.value) {
|
||||
controller.selectedCouponModel.value = couponModel;
|
||||
controller.calculatePrice();
|
||||
controller.update();
|
||||
} else {
|
||||
ShowToastDialog.showToast("This offer not eligible for this booking".tr);
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("This coupon code has been expired".tr);
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("Invalid coupon code".tr);
|
||||
}
|
||||
},
|
||||
borderRadius: 10,
|
||||
height: 4,
|
||||
width: 28,
|
||||
fontSizes: 14,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Order Summary".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey500)),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Subtotal
|
||||
_summaryTile("Subtotal".tr, Constant.amountShow(amount: controller.subTotal.value.toString()), isDark, null),
|
||||
|
||||
// Discount
|
||||
_summaryTile("Discount".tr, "-${Constant.amountShow(amount: controller.discount.value.toString())}", isDark, AppThemeData.dangerDark300),
|
||||
|
||||
// Tax List
|
||||
...List.generate(Constant.taxList.length, (index) {
|
||||
final taxModel = Constant.taxList[index];
|
||||
final taxTitle = "${taxModel.title} ${taxModel.type == 'fix' ? '(${Constant.amountShow(amount: taxModel.tax)})' : '(${taxModel.tax}%)'}";
|
||||
final taxAmount = Constant.getTaxValue(amount: (controller.subTotal.value - controller.discount.value).toString(), taxModel: taxModel).toString();
|
||||
|
||||
return _summaryTile(taxTitle, Constant.amountShow(amount: taxAmount), isDark, null);
|
||||
}),
|
||||
|
||||
const Divider(),
|
||||
|
||||
// Total
|
||||
_summaryTile("Order Total".tr, Constant.amountShow(amount: controller.totalAmount.value.toString()), isDark, null),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Title
|
||||
Text("Payment by".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// Row with Sender and Receiver options
|
||||
Row(
|
||||
children: [
|
||||
// Sender
|
||||
GestureDetector(
|
||||
onTap: () => controller.paymentBy.value = "Sender",
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
controller.paymentBy.value == "Sender" ? Icons.radio_button_checked : Icons.radio_button_off,
|
||||
color: controller.paymentBy.value == "Sender" ? AppThemeData.primary300 : (isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text("Sender".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 60),
|
||||
|
||||
// Receiver
|
||||
GestureDetector(
|
||||
onTap: () => controller.paymentBy.value = "Receiver",
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
controller.paymentBy.value == "Receiver" ? Icons.radio_button_checked : Icons.radio_button_off,
|
||||
color: controller.paymentBy.value == "Receiver" ? AppThemeData.primary300 : (isDark ? AppThemeData.greyDark500 : AppThemeData.grey500),
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text("Receiver".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Continue button
|
||||
RoundedButtonFill(
|
||||
title: controller.paymentBy.value == "Sender" ? "Select Payment Method".tr : "Continue".tr,
|
||||
onPress: () async {
|
||||
if (controller.paymentBy.value == "Sender") {
|
||||
Get.bottomSheet(
|
||||
paymentBottomSheet(context, controller, isDark), // your widget
|
||||
isScrollControlled: true, // ✅ allows full drag scrolling
|
||||
backgroundColor: Colors.transparent, // so your rounded corners are visible
|
||||
);
|
||||
} else {
|
||||
controller.placeOrder();
|
||||
}
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _infoSection(String title, String name, String address, String phone, bool isDark) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(name, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(address, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(phone, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
// Text(time, style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _iconTile(String value, title, icon, bool isDark) {
|
||||
return Column(
|
||||
children: [
|
||||
// Icon(icon, color: AppThemeData.primary300),
|
||||
SvgPicture.asset(icon, height: 28, width: 28, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
const SizedBox(height: 6),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
const SizedBox(height: 6),
|
||||
Text(title, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _summaryTile(String title, String value, bool isDark, Color? colors) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: title == "Order Total" ? 18 : 16, color: colors ?? (isDark ? AppThemeData.greyDark900 : AppThemeData.grey900))),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget paymentBottomSheet(BuildContext context, ParcelOrderConfirmationController controller, bool isDark) {
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 0.70,
|
||||
minChildSize: 0.30,
|
||||
maxChildSize: 0.8,
|
||||
expand: false,
|
||||
builder: (context, scrollController) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.grey500 : Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(24))),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Select Payment Method".tr, style: AppThemeData.mediumTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
controller: scrollController,
|
||||
children: [
|
||||
Text("Preferred Payment".tr, textAlign: TextAlign.start, style: AppThemeData.boldTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 10),
|
||||
if (controller.walletSettingModel.value.isEnabled == true || controller.cashOnDeliverySettingModel.value.isEnabled == true)
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
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) const SizedBox(height: 10),
|
||||
Text("Other Payment Options".tr, textAlign: TextAlign.start, style: AppThemeData.boldTextStyle(fontSize: 15, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
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")),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
RoundedButtonFill(
|
||||
title: "Continue".tr,
|
||||
color: AppThemeData.taxiBooking300,
|
||||
textColor: AppThemeData.grey900,
|
||||
onPress: () async {
|
||||
if (controller.selectedPaymentMethod.value == PaymentGateway.stripe.name) {
|
||||
controller.stripeMakePayment(amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.paypal.name) {
|
||||
controller.paypalPaymentSheet(controller.totalAmount.value.toString(), context);
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payStack.name) {
|
||||
controller.payStackPayment(controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.mercadoPago.name) {
|
||||
controller.mercadoPagoMakePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.flutterWave.name) {
|
||||
controller.flutterWaveInitiatePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.payFast.name) {
|
||||
controller.payFastPayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.cod.name) {
|
||||
controller.placeOrder();
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.wallet.name) {
|
||||
double walletBalance = double.tryParse(controller.userModel.value.walletAmount.toString()) ?? 0.0;
|
||||
double amountToPay = double.tryParse(controller.totalAmount.value.toString()) ?? 0.0;
|
||||
if (walletBalance < amountToPay) {
|
||||
ShowToastDialog.showToast("Insufficient wallet balance".tr);
|
||||
return;
|
||||
}
|
||||
controller.placeOrder();
|
||||
}
|
||||
// else if (controller.selectedPaymentMethod.value == PaymentGateway.wallet.name) {
|
||||
// controller.placeOrder();
|
||||
// }
|
||||
else if (controller.selectedPaymentMethod.value == PaymentGateway.midTrans.name) {
|
||||
controller.midtransMakePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.orangeMoney.name) {
|
||||
controller.orangeMakePayment(context: context, amount: controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.xendit.name) {
|
||||
controller.xenditPayment(context, controller.totalAmount.value.toString());
|
||||
} else if (controller.selectedPaymentMethod.value == PaymentGateway.razorpay.name) {
|
||||
RazorPayController().createOrderRazorPay(amount: double.parse(controller.totalAmount.value.toString()), 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.totalAmount.value.toString(), orderId: result.id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.showToast("Please select payment method".tr);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Obx cardDecoration(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: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
Text(
|
||||
Constant.amountShow(amount: Constant.userModel?.walletAmount == null ? '0.0' : Constant.userModel?.walletAmount.toString()),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Text(
|
||||
value.name.capitalizeString(),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
Radio(
|
||||
value: value.name,
|
||||
groupValue: controller.selectedPaymentMethod.value,
|
||||
activeColor: isDark ? AppThemeData.primary300 : AppThemeData.primary300,
|
||||
onChanged: (value) {
|
||||
controller.selectedPaymentMethod.value = value.toString();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
539
lib/screen_ui/parcel_service/parcel_order_details.dart
Normal file
539
lib/screen_ui/parcel_service/parcel_order_details.dart
Normal file
@@ -0,0 +1,539 @@
|
||||
import 'package:customer/screen_ui/parcel_service/parcel_review_screen.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/parcel_order_details_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../models/user_model.dart';
|
||||
import '../../service/fire_store_utils.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_border.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/show_toast_dialog.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
import '../multi_vendor_service/chat_screens/chat_screen.dart';
|
||||
|
||||
class ParcelOrderDetails extends StatelessWidget {
|
||||
const ParcelOrderDetails({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: ParcelOrderDetailsController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
InkWell(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Order Details".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
Text(
|
||||
"Your parcel is on the way. Track it in real time below.".tr,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
"${'Order Id:'.tr} ${Constant.orderId(orderId: controller.parcelOrder.value.id.toString())}".tr,
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(fontFamily: AppThemeData.semiBold, fontSize: 18, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Timeline with icons and line
|
||||
Column(
|
||||
children: [
|
||||
Image.asset("assets/images/image_parcel.png", height: 32, width: 32),
|
||||
DottedBorder(
|
||||
options: CustomPathDottedBorderOptions(
|
||||
color: Colors.grey.shade400,
|
||||
strokeWidth: 2,
|
||||
dashPattern: [4, 4],
|
||||
customPath:
|
||||
(size) =>
|
||||
Path()
|
||||
..moveTo(size.width / 2, 0)
|
||||
..lineTo(size.width / 2, size.height),
|
||||
),
|
||||
child: const SizedBox(width: 20, height: 95),
|
||||
),
|
||||
Image.asset("assets/images/image_parcel.png", height: 32, width: 32),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// Address Details
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_infoSection(
|
||||
"Pickup Address (Sender):".tr,
|
||||
controller.parcelOrder.value.sender?.name ?? '',
|
||||
controller.parcelOrder.value.sender?.address ?? '',
|
||||
controller.parcelOrder.value.sender?.phone ?? '',
|
||||
// controller.parcelOrder.value.senderPickupDateTime != null
|
||||
// ? "Pickup Time: ${controller.formatDate(controller.parcelOrder.value.senderPickupDateTime!)}"
|
||||
// : '',
|
||||
isDark,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_infoSection(
|
||||
"Delivery Address (Receiver):".tr,
|
||||
controller.parcelOrder.value.receiver?.name ?? '',
|
||||
controller.parcelOrder.value.receiver?.address ?? '',
|
||||
controller.parcelOrder.value.receiver?.phone ?? '',
|
||||
// controller.parcelOrder.value.receiverPickupDateTime != null
|
||||
// ? "Delivery Time: ${controller.formatDate(controller.parcelOrder.value.receiverPickupDateTime!)}"
|
||||
// : '',
|
||||
isDark,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
|
||||
if (controller.parcelOrder.value.isSchedule == true)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
"${'Schedule Pickup time:'.tr} ${controller.formatDate(controller.parcelOrder.value.senderPickupDateTime!)}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.info400),
|
||||
),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Text(
|
||||
"${'Order Date:'.tr}${controller.parcelOrder.value.isSchedule == true ? controller.formatDate(controller.parcelOrder.value.createdAt!) : controller.formatDate(controller.parcelOrder.value.senderPickupDateTime!)}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: AppThemeData.info400),
|
||||
),
|
||||
),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Parcel Type:".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
controller.parcelOrder.value.parcelType ?? '',
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
if (controller.getSelectedCategory()?.image != null && controller.getSelectedCategory()!.image!.isNotEmpty)
|
||||
NetworkImageWidget(imageUrl: controller.getSelectedCategory()?.image ?? '', height: 20, width: 20),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
controller.parcelOrder.value.parcelImages!.isEmpty
|
||||
? SizedBox()
|
||||
: SizedBox(
|
||||
height: 120,
|
||||
child: ListView.builder(
|
||||
itemCount: controller.parcelOrder.value.parcelImages!.length,
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemBuilder: (context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: NetworkImageWidget(imageUrl: controller.parcelOrder.value.parcelImages![index], width: 100, fit: BoxFit.cover, borderRadius: 10),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Distance, Weight, Rate
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_iconTile("${controller.parcelOrder.value.distance ?? '--'} ${Constant.distanceType}", "Distance".tr, "assets/icons/ic_distance_parcel.svg", isDark),
|
||||
_iconTile(controller.parcelOrder.value.parcelWeight ?? '--', "Weight".tr, "assets/icons/ic_weight_parcel.svg", isDark),
|
||||
_iconTile(Constant.amountShow(amount: controller.parcelOrder.value.subTotal), "Rate".tr, "assets/icons/ic_rate_parcel.svg", isDark),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (controller.parcelOrder.value.driver != null)
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("About Driver".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 52,
|
||||
height: 52,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadiusGeometry.circular(10),
|
||||
child: NetworkImageWidget(imageUrl: controller.driverUser.value?.profilePictureURL ?? '', height: 70, width: 70, borderRadius: 35),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Text(
|
||||
controller.parcelOrder.value.driver?.fullName() ?? '',
|
||||
style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 18),
|
||||
),
|
||||
],
|
||||
),
|
||||
RoundedButtonBorder(
|
||||
title: controller.driverUser.value!.averageRating.toStringAsFixed(1),
|
||||
width: 20,
|
||||
height: 3.5,
|
||||
radius: 10,
|
||||
isRight: false,
|
||||
isCenter: true,
|
||||
textColor: AppThemeData.warning400,
|
||||
borderColor: AppThemeData.warning400,
|
||||
color: AppThemeData.warning50,
|
||||
icon: SvgPicture.asset("assets/icons/ic_start.svg"),
|
||||
onPress: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
Visibility(
|
||||
visible: controller.parcelOrder.value.status == Constant.orderCompleted ? true : false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: RoundedButtonFill(
|
||||
title: controller.ratingModel.value.id != null && controller.ratingModel.value.id!.isNotEmpty ? 'Update Review'.tr : 'Add Review'.tr,
|
||||
onPress: () async {
|
||||
final result = await Get.to(() => ParcelReviewScreen(), arguments: {'order': controller.parcelOrder.value});
|
||||
|
||||
// If review was submitted successfully
|
||||
if (result == true) {
|
||||
await controller.fetchDriverDetails();
|
||||
}
|
||||
},
|
||||
height: 5,
|
||||
borderRadius: 15,
|
||||
color: Colors.orange,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (controller.parcelOrder.value.status != Constant.orderCompleted)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Constant.makePhoneCall(controller.parcelOrder.value.driver!.phoneNumber.toString());
|
||||
},
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 42,
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
|
||||
borderRadius: BorderRadius.circular(120),
|
||||
),
|
||||
),
|
||||
child: Padding(padding: const EdgeInsets.all(8.0), child: SvgPicture.asset("assets/icons/ic_phone_call.svg")),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
ShowToastDialog.showLoader("Please wait...".tr);
|
||||
|
||||
UserModel? customer = await FireStoreUtils.getUserProfile(controller.parcelOrder.value.authorID ?? '');
|
||||
UserModel? driverUser = await FireStoreUtils.getUserProfile(controller.parcelOrder.value.driverId ?? '');
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
Get.to(
|
||||
const ChatScreen(),
|
||||
arguments: {
|
||||
"customerName": customer?.fullName(),
|
||||
"restaurantName": driverUser?.fullName(),
|
||||
"orderId": controller.parcelOrder.value.id,
|
||||
"restaurantId": driverUser?.id,
|
||||
"customerId": customer?.id,
|
||||
"customerProfileImage": customer?.profilePictureURL,
|
||||
"restaurantProfileImage": driverUser?.profilePictureURL,
|
||||
"token": driverUser?.fcmToken,
|
||||
"chatType": "Driver",
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 42,
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: isDark ? AppThemeData.grey700 : AppThemeData.grey200),
|
||||
borderRadius: BorderRadius.circular(120),
|
||||
),
|
||||
),
|
||||
child: Padding(padding: const EdgeInsets.all(8.0), child: SvgPicture.asset("assets/icons/ic_wechat.svg")),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Order Summary".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey500)),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Subtotal
|
||||
_summaryTile("Subtotal".tr, Constant.amountShow(amount: controller.subTotal.value.toString()), isDark),
|
||||
|
||||
// Discount
|
||||
_summaryTile("Discount".tr, Constant.amountShow(amount: controller.discount.value.toString()), isDark),
|
||||
|
||||
// Tax List
|
||||
...List.generate(controller.parcelOrder.value.taxSetting!.length, (index) {
|
||||
return _summaryTile(
|
||||
"${controller.parcelOrder.value.taxSetting![index].title} ${controller.parcelOrder.value.taxSetting![index].type == 'fix' ? '' : '(${controller.parcelOrder.value.taxSetting![index].tax}%)'}",
|
||||
Constant.amountShow(
|
||||
amount:
|
||||
Constant.getTaxValue(
|
||||
amount:
|
||||
((double.tryParse(controller.parcelOrder.value.subTotal.toString()) ?? 0.0) - (double.tryParse(controller.parcelOrder.value.discount.toString()) ?? 0.0))
|
||||
.toString(),
|
||||
taxModel: controller.parcelOrder.value.taxSetting![index],
|
||||
).toString(),
|
||||
),
|
||||
isDark,
|
||||
);
|
||||
}),
|
||||
|
||||
const Divider(),
|
||||
|
||||
// Total
|
||||
_summaryTile("Order Total".tr, Constant.amountShow(amount: controller.totalAmount.value.toString()), isDark),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar:
|
||||
controller.parcelOrder.value.status == Constant.orderPlaced
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: RoundedButtonFill(
|
||||
title: "Cancel Parcel".tr,
|
||||
onPress: () {
|
||||
controller.cancelParcelOrder();
|
||||
},
|
||||
height: 5,
|
||||
borderRadius: 15,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900,
|
||||
),
|
||||
)
|
||||
: SizedBox(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget statusBottomSheet(BuildContext context, ParcelOrderDetailsController controller, bool isDark) {
|
||||
return DraggableScrollableSheet(
|
||||
initialChildSize: 0.30,
|
||||
minChildSize: 0.20,
|
||||
maxChildSize: 0.6,
|
||||
expand: false,
|
||||
builder: (context, scrollController) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
|
||||
decoration: BoxDecoration(color: isDark ? AppThemeData.grey500 : Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(24))),
|
||||
child: SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Parcel Status Timeline".tr, style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 18)),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Dynamic List
|
||||
Obx(() {
|
||||
final history = controller.parcelOrder.value.statusHistory ?? [];
|
||||
|
||||
if (history.isEmpty) {
|
||||
return SizedBox(
|
||||
height: 80,
|
||||
child: Center(child: Text("No status updates yet".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900))),
|
||||
);
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
height: history.length * 70.0,
|
||||
child: ListView.builder(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: history.length,
|
||||
itemBuilder: (context, index) {
|
||||
final statusUpdate = history[index];
|
||||
final isCompleted = index < history.length;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Image.asset(isCompleted ? "assets/images/image_status_timeline.png" : "assets/images/image_timeline.png", height: 48, width: 48),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Text(
|
||||
statusUpdate.status ?? '',
|
||||
style: AppThemeData.semiBoldTextStyle(color: isCompleted ? (isDark ? AppThemeData.greyDark900 : AppThemeData.grey900) : AppThemeData.grey500, fontSize: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _infoSection(String title, String name, String address, String phone, bool isDark) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title, style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(name, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(address, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Text(phone, style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
// Text(time, style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _iconTile(String value, title, icon, bool isDark) {
|
||||
return Column(
|
||||
children: [
|
||||
// Icon(icon, color: AppThemeData.primary300),
|
||||
SvgPicture.asset(icon, height: 28, width: 28, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800),
|
||||
const SizedBox(height: 6),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
const SizedBox(height: 6),
|
||||
Text(title, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _summaryTile(String title, String value, bool isDark) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: title == "Order Total".tr ? 18 : 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
145
lib/screen_ui/parcel_service/parcel_review_screen.dart
Normal file
145
lib/screen_ui/parcel_service/parcel_review_screen.dart
Normal file
@@ -0,0 +1,145 @@
|
||||
import 'package:customer/controllers/parcel_review_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
import '../../themes/text_field_widget.dart';
|
||||
import '../../utils/network_image_widget.dart';
|
||||
|
||||
class ParcelReviewScreen extends StatelessWidget {
|
||||
const ParcelReviewScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<ParcelReviewController>(
|
||||
init: ParcelReviewController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
leading: GestureDetector(onTap: () => Get.back(), child: Icon(Icons.arrow_back_ios, color: isDark ? Colors.white : Colors.black)),
|
||||
title: Text(
|
||||
controller.ratingModel.value != null && controller.ratingModel.value!.id!.isNotEmpty ? "Update Review".tr : "Add Review".tr,
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black, fontSize: 16),
|
||||
),
|
||||
),
|
||||
body: Obx(
|
||||
() =>
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 50, bottom: 20),
|
||||
child: Card(
|
||||
elevation: 2,
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 65),
|
||||
child: Column(
|
||||
children: [
|
||||
// Driver Name
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
controller.order.value!.driver?.fullName() ?? "",
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black87, fontFamily: AppThemeData.medium, fontSize: 18),
|
||||
),
|
||||
),
|
||||
// Car info
|
||||
const Padding(padding: EdgeInsets.symmetric(vertical: 12), child: Divider(color: Colors.grey)),
|
||||
|
||||
// Title
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: Text('How is your trip?'.tr, style: TextStyle(fontSize: 18, color: isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, letterSpacing: 2)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
'Your feedback will help us improve \n driving experience better'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: isDark ? Colors.white : Colors.black.withOpacity(0.60), letterSpacing: 0.8),
|
||||
),
|
||||
),
|
||||
|
||||
// Rating
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Text('Rate for'.tr, style: TextStyle(fontSize: 16, color: isDark ? Colors.white : Colors.black.withOpacity(0.60), letterSpacing: 0.8)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
controller.order.value!.driver?.fullName() ?? "",
|
||||
style: TextStyle(fontSize: 18, color: isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, letterSpacing: 2),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: RatingBar.builder(
|
||||
initialRating: controller.ratings.value,
|
||||
minRating: 1,
|
||||
direction: Axis.horizontal,
|
||||
allowHalfRating: true,
|
||||
itemCount: 5,
|
||||
itemBuilder: (context, _) => const Icon(Icons.star, color: Colors.amber),
|
||||
unratedColor: isDark ? AppThemeData.greyDark400 : AppThemeData.grey400,
|
||||
onRatingUpdate: (rating) => controller.ratings.value = rating,
|
||||
),
|
||||
),
|
||||
|
||||
// Comment
|
||||
Padding(padding: const EdgeInsets.all(20.0), child: TextFieldWidget(hintText: "Type comment....".tr, controller: controller.comment.value, maxLine: 5)),
|
||||
|
||||
// Submit Button
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: RoundedButtonFill(
|
||||
title: controller.ratingModel.value != null ? "Update Review".tr : "Add Review".tr,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: isDark ? Colors.white : Colors.black,
|
||||
onPress: controller.submitReview,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
color: Colors.white,
|
||||
boxShadow: [BoxShadow(color: Colors.grey.withOpacity(0.15), blurRadius: 8, spreadRadius: 6)],
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(60),
|
||||
child: NetworkImageWidget(imageUrl: controller.order.value?.driver?.profilePictureURL ?? '', fit: BoxFit.cover, height: 110, width: 110),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
332
lib/screen_ui/rental_service/my_rental_booking_screen.dart
Normal file
332
lib/screen_ui/rental_service/my_rental_booking_screen.dart
Normal file
@@ -0,0 +1,332 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:customer/models/rental_order_model.dart';
|
||||
import 'package:customer/screen_ui/auth_screens/login_screen.dart';
|
||||
import 'package:customer/screen_ui/multi_vendor_service/wallet_screen/wallet_screen.dart';
|
||||
import 'package:customer/screen_ui/rental_service/rental_order_details_screen.dart';
|
||||
import 'package:customer/themes/round_button_fill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constant/constant.dart';
|
||||
import '../../controllers/my_rental_booking_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
|
||||
class MyRentalBookingScreen extends StatelessWidget {
|
||||
const MyRentalBookingScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
|
||||
return GetX<MyRentalBookingController>(
|
||||
init: MyRentalBookingController(),
|
||||
builder: (controller) {
|
||||
return DefaultTabController(
|
||||
length: controller.tabTitles.length,
|
||||
initialIndex: controller.tabTitles.indexOf(controller.selectedTab.value),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(children: [const SizedBox(width: 10), Text("Rental History".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900))]),
|
||||
),
|
||||
bottom: TabBar(
|
||||
onTap: (index) {
|
||||
controller.selectTab(controller.tabTitles[index]);
|
||||
},
|
||||
indicatorColor: AppThemeData.parcelService500,
|
||||
labelColor: AppThemeData.parcelService500,
|
||||
unselectedLabelColor: AppThemeData.parcelService500,
|
||||
labelStyle: AppThemeData.boldTextStyle(fontSize: 13),
|
||||
unselectedLabelStyle: AppThemeData.mediumTextStyle(fontSize: 13),
|
||||
tabs: controller.tabTitles.map((title) => Tab(child: Center(child: Text(title)))).toList(),
|
||||
),
|
||||
),
|
||||
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: [
|
||||
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(
|
||||
"You’re 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());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: TabBarView(
|
||||
children:
|
||||
controller.tabTitles.map((title) {
|
||||
List<RentalOrderModel> orders = controller.getOrdersForTab(title);
|
||||
|
||||
if (orders.isEmpty) {
|
||||
return Center(child: Text("No orders found".tr, style: AppThemeData.mediumTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)));
|
||||
}
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: orders.length,
|
||||
itemBuilder: (context, index) {
|
||||
RentalOrderModel order = orders[index]; //use this
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
Get.to(() => RentalOrderDetailsScreen(), arguments: order);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(padding: const EdgeInsets.only(top: 5), child: Image.asset("assets/icons/pickup.png", height: 18, width: 18)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
//prevents overflow
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
//text wraps if too long
|
||||
child: Text(
|
||||
order.sourceLocationName ?? "-",
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
overflow: TextOverflow.ellipsis, //safe cutoff
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
if (order.status != null) ...[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppThemeData.info50,
|
||||
border: Border.all(color: AppThemeData.info300),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(order.status ?? '', style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.info500)),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (order.bookingDateTime != null)
|
||||
Text(
|
||||
Constant.timestampToDateTime(order.bookingDateTime!),
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text("Vehicle Type :".tr, style: AppThemeData.boldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
ClipRRect(
|
||||
//borderRadius: BorderRadius.circular(10),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: order.rentalVehicleType!.rentalVehicleIcon.toString(),
|
||||
height: 60,
|
||||
width: 60,
|
||||
imageBuilder:
|
||||
(context, imageProvider) => Container(
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), image: DecorationImage(image: imageProvider, fit: BoxFit.cover)),
|
||||
),
|
||||
placeholder: (context, url) => Center(child: CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppThemeData.primary300))),
|
||||
errorWidget: (context, url, error) => Image.network(Constant.placeHolderImage, fit: BoxFit.cover),
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${order.rentalVehicleType!.name}",
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0),
|
||||
child: Text(
|
||||
"${order.rentalVehicleType!.shortDescription}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text("Package info :".tr, style: AppThemeData.boldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
order.rentalPackageModel!.name.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
order.rentalPackageModel!.description.toString(),
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
Constant.amountShow(amount: order.rentalPackageModel!.baseFare.toString()),
|
||||
style: AppThemeData.boldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (Constant.isEnableOTPTripStartForRental == true)
|
||||
Text("${'OTP :'.tr} ${order.otpCode}", style: AppThemeData.boldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
children: [
|
||||
order.status == Constant.orderInTransit && order.paymentStatus == false
|
||||
? Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "Pay Now",
|
||||
onPress: () {
|
||||
Get.to(() => RentalOrderDetailsScreen(), arguments: order);
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
)
|
||||
: SizedBox(),
|
||||
order.status == Constant.orderPlaced || order.status == Constant.driverAccepted
|
||||
? Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "Cancel Booking",
|
||||
onPress: () => controller.cancelRentalRequest(order, taxList: order.taxSetting),
|
||||
color: AppThemeData.danger300,
|
||||
textColor: AppThemeData.surface,
|
||||
),
|
||||
)
|
||||
: SizedBox(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Obx cardDecoration(MyRentalBookingController 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: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.grey50 : AppThemeData.grey900),
|
||||
),
|
||||
Text(
|
||||
Constant.amountShow(amount: Constant.userModel!.walletAmount == null ? '0.0' : Constant.userModel!.walletAmount.toString()),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Text(
|
||||
value.name.capitalizeString(),
|
||||
textAlign: TextAlign.start,
|
||||
style: AppThemeData.semiBoldTextStyle(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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
332
lib/screen_ui/rental_service/rental_conformation_screen.dart
Normal file
332
lib/screen_ui/rental_service/rental_conformation_screen.dart
Normal file
@@ -0,0 +1,332 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/models/coupon_model.dart';
|
||||
import 'package:customer/screen_ui/rental_service/rental_coupon_screen.dart';
|
||||
import 'package:customer/themes/show_toast_dialog.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/svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/rental_conformation_controller.dart';
|
||||
import '../../controllers/theme_controller.dart';
|
||||
import '../../themes/app_them_data.dart';
|
||||
import '../../themes/round_button_fill.dart';
|
||||
|
||||
class RentalConformationScreen extends StatelessWidget {
|
||||
const RentalConformationScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: RentalConformationController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.back();
|
||||
},
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Confirm Rent a Car".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Center(child: Constant.loader())
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset("assets/icons/pickup.png", height: 15, width: 15),
|
||||
SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${controller.rentalOrderModel.value.sourceLocationName}",
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
Text(
|
||||
Constant.timestampToDate(controller.rentalOrderModel.value.bookingDateTime!),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Your Preference".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
controller.rentalOrderModel.value.rentalPackageModel!.name.toString(),
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
controller.rentalOrderModel.value.rentalPackageModel!.description.toString(),
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
Constant.amountShow(amount: controller.rentalOrderModel.value.rentalPackageModel!.baseFare.toString()),
|
||||
style: AppThemeData.boldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Vehicle Type".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500)),
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadiusGeometry.circular(10),
|
||||
child: NetworkImageWidget(imageUrl: controller.rentalOrderModel.value.rentalVehicleType!.rentalVehicleIcon.toString(), height: 50, width: 50, borderRadius: 10),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${controller.rentalOrderModel.value.rentalVehicleType!.name}",
|
||||
style: AppThemeData.semiBoldTextStyle(fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900),
|
||||
),
|
||||
Text(
|
||||
"${controller.rentalOrderModel.value.rentalVehicleType!.shortDescription}",
|
||||
style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark600 : AppThemeData.grey600),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text("Coupons".tr, style: AppThemeData.boldTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900))),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.to(RentalCouponScreen())!.then((value) {
|
||||
if (value != null) {
|
||||
double couponAmount = Constant.calculateDiscount(amount: controller.subTotal.value.toString(), offerModel: value);
|
||||
if (couponAmount < controller.subTotal.value) {
|
||||
controller.selectedCouponModel.value = value;
|
||||
controller.calculateAmount();
|
||||
} else {
|
||||
ShowToastDialog.showToast("This offer not eligible for this booking".tr);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
"View All".tr,
|
||||
style: AppThemeData.boldTextStyle(decoration: TextDecoration.underline, fontSize: 14, color: isDark ? AppThemeData.primary300 : AppThemeData.primary300),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
|
||||
// Coupon input
|
||||
DottedBorder(
|
||||
options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(10), color: isDark ? AppThemeData.parcelServiceDark300 : AppThemeData.primary300),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: AppThemeData.parcelService50, borderRadius: BorderRadius.circular(10)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset("assets/icons/ic_coupon_parcel.svg", height: 28, width: 28),
|
||||
SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: controller.couponController.value,
|
||||
style: AppThemeData.semiBoldTextStyle(color: AppThemeData.grey900),
|
||||
decoration: InputDecoration(
|
||||
hintText: "Write coupon code".tr,
|
||||
hintStyle: AppThemeData.mediumTextStyle(fontSize: 16, color: AppThemeData.parcelService500),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
RoundedButtonFill(
|
||||
title: "Redeem now".tr,
|
||||
onPress: () {
|
||||
if (controller.couponList.where((element) => element.code!.toLowerCase() == controller.couponController.value.text.toLowerCase()).isNotEmpty) {
|
||||
CouponModel couponModel = controller.couponList.firstWhere((p0) => p0.code!.toLowerCase() == controller.couponController.value.text.toLowerCase());
|
||||
if (couponModel.expiresAt!.toDate().isAfter(DateTime.now())) {
|
||||
double couponAmount = Constant.calculateDiscount(amount: controller.subTotal.value.toString(), offerModel: couponModel);
|
||||
if (couponAmount < controller.subTotal.value) {
|
||||
controller.selectedCouponModel.value = couponModel;
|
||||
controller.calculateAmount();
|
||||
controller.update();
|
||||
} else {
|
||||
ShowToastDialog.showToast("This offer not eligible for this booking".tr);
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("This coupon code has been expired".tr);
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("Invalid coupon code".tr);
|
||||
}
|
||||
},
|
||||
borderRadius: 10,
|
||||
height: 4,
|
||||
width: 28,
|
||||
fontSizes: 14,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50,
|
||||
border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200),
|
||||
),
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Order Summary".tr, style: AppThemeData.boldTextStyle(fontSize: 14, color: AppThemeData.grey500)),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Subtotal
|
||||
_summaryTile("Subtotal".tr, Constant.amountShow(amount: controller.subTotal.value.toString()), isDark, null),
|
||||
|
||||
// Discount
|
||||
_summaryTile("Discount".tr, Constant.amountShow(amount: controller.discount.value.toString()), isDark, AppThemeData.dangerDark300),
|
||||
|
||||
// Tax List
|
||||
...List.generate(controller.rentalOrderModel.value.taxSetting!.length, (index) {
|
||||
final taxModel = controller.rentalOrderModel.value.taxSetting![index];
|
||||
final taxTitle = "${taxModel.title} ${taxModel.type == 'fix' ? '(${Constant.amountShow(amount: taxModel.tax)})' : '(${taxModel.tax}%)'}";
|
||||
|
||||
return _summaryTile(
|
||||
taxTitle,
|
||||
Constant.amountShow(
|
||||
amount:
|
||||
Constant.getTaxValue(
|
||||
amount: (controller.subTotal.value - controller.discount.value).toString(),
|
||||
taxModel: controller.rentalOrderModel.value.taxSetting![index],
|
||||
).toString(),
|
||||
),
|
||||
isDark,
|
||||
null,
|
||||
);
|
||||
}),
|
||||
|
||||
const Divider(),
|
||||
|
||||
// Total
|
||||
_summaryTile("Order Total".tr, Constant.amountShow(amount: controller.totalAmount.value.toString()), isDark, null),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
RoundedButtonFill(
|
||||
title: "Book now".tr,
|
||||
onPress: () {
|
||||
controller.placeOrder();
|
||||
},
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey900,
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _summaryTile(String title, String value, bool isDark, Color? colors) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(title, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)),
|
||||
Text(value, style: AppThemeData.semiBoldTextStyle(fontSize: title == "Order Total" ? 18 : 16, color: colors ?? (isDark ? AppThemeData.greyDark900 : AppThemeData.grey900))),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
138
lib/screen_ui/rental_service/rental_coupon_screen.dart
Normal file
138
lib/screen_ui/rental_service/rental_coupon_screen.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/rental_coupon_controller.dart';
|
||||
import 'package:customer/controllers/theme_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/widget/my_separator.dart';
|
||||
import 'package:dotted_border/dotted_border.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class RentalCouponScreen extends StatelessWidget {
|
||||
const RentalCouponScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: RentalCouponController(),
|
||||
builder: (controller) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: AppThemeData.primary300,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => Get.back(),
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: 42,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50),
|
||||
child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text("Coupon".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body:
|
||||
controller.isLoading.value
|
||||
? Constant.loader()
|
||||
: controller.cabCouponList.isEmpty
|
||||
? Constant.showEmptyView(message: "Coupon not found".tr)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: controller.cabCouponList.length,
|
||||
itemBuilder: (context, index) {
|
||||
CouponModel couponModel = controller.cabCouponList[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: () {
|
||||
Get.back(result: couponModel);
|
||||
},
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
81
lib/screen_ui/rental_service/rental_dashboard_screen.dart
Normal file
81
lib/screen_ui/rental_service/rental_dashboard_screen.dart
Normal file
@@ -0,0 +1,81 @@
|
||||
import 'package:customer/constant/constant.dart';
|
||||
import 'package:customer/controllers/cab_dashboard_controller.dart';
|
||||
import 'package:customer/controllers/theme_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/cab_rental_dashboard_controllers.dart';
|
||||
|
||||
class RentalDashboardScreen extends StatelessWidget {
|
||||
const RentalDashboardScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
return Obx(() {
|
||||
final isDark = themeController.isDark.value;
|
||||
return GetX(
|
||||
init: CabRentalDashboardControllers(),
|
||||
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(CabDashboardController());
|
||||
}
|
||||
controller.selectedIndex.value = index;
|
||||
},
|
||||
items:
|
||||
Constant.walletSetting == false
|
||||
? [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_cab.svg", label: 'Home'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_booking_cab.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_profile.svg", label: 'Profile'.tr, controller: controller),
|
||||
]
|
||||
: [
|
||||
navigationBarItem(isDark, index: 0, assetIcon: "assets/icons/ic_home_cab.svg", label: 'Home'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 1, assetIcon: "assets/icons/ic_booking_cab.svg", label: 'My Bookings'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 2, assetIcon: "assets/icons/ic_wallet_cab.svg", label: 'Wallet'.tr, controller: controller),
|
||||
navigationBarItem(isDark, index: 3, assetIcon: "assets/icons/ic_profile.svg", label: 'Profile'.tr, controller: controller),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
BottomNavigationBarItem navigationBarItem(isDark, {required int index, required String label, required String assetIcon, required CabRentalDashboardControllers 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user