Service Request Done

master_new_changes
Faiz Hashmi 1 year ago
parent e7fa2bfac8
commit c3696586f3

@ -5,7 +5,7 @@ import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/models/user_models/register_user.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/advertisement/ads_buyer_chats_view.dart';
import 'package:mc_common_app/views/advertisement/ads_detail_view.dart';
import 'package:mc_common_app/views/advertisement/ads_detail_view/ads_detail_view.dart';
import 'package:mc_common_app/views/advertisement/ads_filter_view.dart';
import 'package:mc_common_app/views/advertisement/create_ad_view.dart';
import 'package:mc_common_app/views/advertisement/select_ad_type_view.dart';
@ -14,6 +14,7 @@ import 'package:mc_common_app/views/payments/payment_methods_view.dart';
import 'package:mc_common_app/views/profile/profile_view.dart';
import 'package:mc_common_app/views/requests/create_request_page.dart';
import 'package:mc_common_app/views/requests/offer_list_page.dart';
import 'package:mc_common_app/views/requests/request_detail_page.dart';
import 'package:mc_common_app/views/setting_options/provider_license_page.dart';
import 'package:mc_common_app/views/setting_options/setting_option_help.dart';
import 'package:mc_common_app/views/setting_options/setting_options_faqs.dart';
@ -148,12 +149,17 @@ class AppRoutes {
AppRoutes.adsFilterView: (context) => const AdsFilterView(),
AppRoutes.selectAdTypeView: (context) => SelectAdTypeView(arguments: ModalRoute.of(context)!.settings.arguments as List<bool>),
AppRoutes.chatView: (context) => ChatView(chatViewArguments: ModalRoute.of(context)!.settings.arguments as ChatViewArguments),
AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
AppRoutes.adsBuyerChatsListView: (context) => AdsBuyerChatsView(buyersListViewArguments: ModalRoute.of(context)!.settings.arguments as List<BuyersChatForAdsModel>),
AppRoutes.createRequestPage: (context) => const CreateRequestPage(),
AppRoutes.settingOptionsFaqs: (context) => const SettingOptionsFAQs(),
AppRoutes.settingOptionsInviteFriends: (context) => const SettingOptionsInviteFriends(),
AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes),
//Requests
AppRoutes.requestsDetailPage: (context) => RequestDetailPage(requestDetailPageArguments: ModalRoute.of(context)!.settings.arguments as RequestDetailPageArguments),
AppRoutes.createRequestPage: (context) => const CreateRequestPage(),
AppRoutes.offersListPage: (context) => OfferListPage(offerListPageArguments: ModalRoute.of(context)!.settings.arguments as OfferListPageArguments),
};
}

@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
@ -32,7 +33,7 @@ extension EmailValidator on String {
fontStyle: isItalic ? FontStyle.italic : null,
height: height,
decoration: isUnderLine ? TextDecoration.underline : textDecoration ?? TextDecoration.none,
decorationColor: decorationColor ?? MyColors.provTextColor,
decorationColor: decorationColor ?? color ?? MyColors.darkTextColor,
fontSize: fontSize ?? 10,
fontFamily: isArabic ? MyFonts.gessTwoFont : MyFonts.poppinsFont,
fontWeight: isBold ? FontWeight.bold : fontWeight ?? MyFonts.SemiBold,
@ -378,8 +379,6 @@ extension RequestTypeStatusToInt on RequestsTypeEnum {
}
}
// 290223243016
extension AdPostStatusToInt on AdPostStatus {
int getIdFromAdPostStatusEnum() {
switch (this) {
@ -665,6 +664,23 @@ extension FormatMonthByNumber on String {
}
}
extension NameExtensions on String {
String getInitials() {
final name = trim();
String initials = "U";
if (name.isEmpty) {
return initials;
}
final list = name.split(' ');
if (list.length > 1) {
initials = list[0][0].toUpperCase() + list[1][0].toUpperCase();
}
return initials;
}
}
extension ChatMessageTypeEnumExt on int {
//FreeText = 1,
// Image = 2,

@ -71,9 +71,9 @@ class ReqOffer {
int? offerStatus;
String? offerStatusText;
String? comment;
String? serviceItem;
String? itemManufacturer;
String? manufacturedDate;
String? serviceItemName;
int? manufacturedById;
String? manufacturedOn;
double? price;
RequestsTypeEnum? requestsTypeEnum;
RequestOfferStatusEnum? requestOfferStatusEnum;
@ -85,9 +85,9 @@ class ReqOffer {
this.offerStatus,
this.offerStatusText,
this.comment,
this.serviceItem,
this.itemManufacturer,
this.manufacturedDate,
this.serviceItemName,
this.manufacturedById,
this.manufacturedOn,
this.price,
this.requestOfferStatusEnum,
this.requestsTypeEnum,
@ -100,9 +100,9 @@ class ReqOffer {
offerStatus = json['offerStatus'];
offerStatusText = json['offerStatusText'];
comment = json['comment'];
serviceItem = json['serviceItem'];
itemManufacturer = json['itemManufacturer'];
manufacturedDate = json['manufacturedDate'];
serviceItemName = json['serviceItem'];
manufacturedById = json['itemManufacturer'];
manufacturedOn = json['manufacturedDate'];
price = json['price'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
requestsTypeEnum = RequestsTypeEnum.serviceRequest; // TODO: THIS SHOULD COME FROM API

@ -53,6 +53,7 @@ class ChatRepoImp implements ChatRepo {
client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true),
logging: (level, message) {},
))
.withAutomaticReconnect()
.build();
return hub;
}

@ -232,7 +232,7 @@ class AdVM extends BaseVM {
return;
}
// this means if the filter is reserved ads
if (adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) {
if (adPostStatusEnum == AdPostStatus.reserved) {
myAdsFilteredList = myReservedAds;
for (var ad in myAdsFilteredList) {
ad.isReservedByMe = true;
@ -245,24 +245,6 @@ class AdVM extends BaseVM {
notifyListeners();
}
Future<void> getMyAds() async {
setState(ViewState.busy);
myAds = await adsRepo.getAllAds(isMyAds: true);
final myActiveAds = myAds.where((element) => element.adPostStatus == AdPostStatus.active).toList();
myActiveAdsForHome = myActiveAds.length >= 3 ? myActiveAds.take(3).toList() : myActiveAds;
await getMyReservedAds();
notifyListeners();
setState(ViewState.idle);
}
Future<List<AdDetailsModel>> getMyReservedAds() async {
setState(ViewState.busy);
myReservedAds = await adsRepo.getMyReservedAds();
isFetchingLists = false;
setState(ViewState.idle);
return myReservedAds;
}
Future<bool> createReserveAd({required int adId, required BuildContext context}) async {
Utils.showLoading(context);
GenericRespModel genericRespModel = await adsRepo.createReserveAd(adId: adId);
@ -284,6 +266,25 @@ class AdVM extends BaseVM {
setState(ViewState.idle);
}
Future<void> getMyAds() async {
setState(ViewState.busy);
myAds = await adsRepo.getAllAds(isMyAds: true);
myAdsFilteredList = myAds;
final myActiveAds = myAds.where((element) => element.adPostStatus == AdPostStatus.active).toList();
myActiveAdsForHome = myActiveAds.length >= 3 ? myActiveAds.take(3).toList() : myActiveAds;
await getMyReservedAds();
notifyListeners();
setState(ViewState.idle);
}
Future<List<AdDetailsModel>> getMyReservedAds() async {
setState(ViewState.busy);
myReservedAds = await adsRepo.getMyReservedAds();
isFetchingLists = false;
setState(ViewState.idle);
return myReservedAds;
}
Future<void> getVehicleTypes() async {
vehicleTypes = await commonRepo.getVehicleTypes();
notifyListeners();

@ -221,12 +221,16 @@ class ChatVM extends ChangeNotifier {
required String message,
required int requestId,
required String offerPrice,
required String serviceItemName,
required int manufacturedById,
required String manufacturedOn,
required BuildContext context,
}) async {
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
logger.i("Hub Not Connected!!");
await buildHubConnection(context);
}
if (hubConnection!.state == HubConnectionState.connected) {
final providerId = AppState().getUser.data!.userInfo!.providerId;
final obj = <String, dynamic>{
@ -238,6 +242,9 @@ class ChatVM extends ChangeNotifier {
"ReqOffer": <String, dynamic>{
"RequestID": requestId,
"Price": double.parse(offerPrice),
"ServiceItem": serviceItemName,
"OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database
// "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"ServiceProviderID": providerId,
"OfferStatus": RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
"Comment": message,
@ -314,7 +321,10 @@ class ChatVM extends ChangeNotifier {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
}
} else {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId));
int providerIndex = context
.read<RequestsVM>()
.myFilteredRequests
.indexWhere((element) => (element.customerUserID == receiverId && element.id == requestId));
log("providerIndex2:$providerIndex");
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
@ -412,6 +422,9 @@ class ChatVM extends ChangeNotifier {
required int serviceProviderID,
required int requestOfferID,
required String offerPrice,
required String serviceItemName,
required int manufacturedById,
required String manufacturedOn,
required RequestOfferStatusEnum requestOfferStatusEnum,
required BuildContext context,
}) async {
@ -431,6 +444,9 @@ class ChatVM extends ChangeNotifier {
"ID": requestOfferID,
"RequestID": requestId,
"Price": double.parse(offerPrice),
"ServiceItem": serviceItemName,
"OfferedItemCreatedBy": manufacturedById, // TODO: The ID should be used from the manufacturer database
// "OfferedItemCreatedOn": manufacturedOn, // TODO: This should be in String on Server, Right now it is in DateTime
"ServiceProviderID": serviceProviderID,
"OfferStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum(),
"Comment": comments,
@ -545,8 +561,7 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
if (AppState().currentAppType == AppType.customer) {
} else {}
if (AppState().currentAppType == AppType.customer) {} else {}
return true;
}
return false;

@ -449,6 +449,9 @@ class RequestsVM extends BaseVM {
required String message,
required int requestId,
required String offerPrice,
required String serviceItemName,
required int manufacturedById,
required String manufacturedOn,
required RequestModel requestModel,
required int requestIndex,
required bool isFromChatScreen,
@ -461,6 +464,9 @@ class RequestsVM extends BaseVM {
message: message,
requestId: requestId,
offerPrice: offerPrice,
manufacturedById: manufacturedById,
manufacturedOn: manufacturedOn,
serviceItemName: serviceItemName,
context: context,
);
@ -470,23 +476,27 @@ class RequestsVM extends BaseVM {
resetSendOfferBottomSheet();
Navigator.pop(context);
ChatMessageModel chatMessageModel = ChatMessageModel(
isMyMessage: true,
chatText: message,
messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(),
senderName: senderName,
senderUserID: senderId,
receiverUserID: receiverId,
chatMessageTypeEnum: ChatMessageTypeEnum.offer,
requestID: requestModel.id,
isMyMessage: true,
chatText: message,
messageType: ChatMessageTypeEnum.offer.getIdFromChatMessageTypeEnum(),
senderName: senderName,
senderUserID: senderId,
receiverUserID: receiverId,
chatMessageTypeEnum: ChatMessageTypeEnum.offer,
requestID: requestModel.id,
offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
reqOffer: ReqOffer(
offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
reqOffer: ReqOffer(
offerStatus: RequestOfferStatusEnum.offer.getIdFromRequestOfferStatusEnum(),
requestID: requestModel.id,
price: double.parse(offerPrice),
requestOfferStatusEnum: RequestOfferStatusEnum.offer,
comment: message,
offerStatusText: "",
));
requestID: requestModel.id,
price: double.parse(offerPrice),
manufacturedById: manufacturedById,
manufacturedOn: manufacturedOn,
serviceItemName: serviceItemName,
requestOfferStatusEnum: RequestOfferStatusEnum.offer,
comment: message,
offerStatusText: "",
),
);
context.read<ChatVM>().onNewMessageReceivedForRequestOffer(messages: [chatMessageModel], requestsVM: this, isMyOwnOffer: true);
if (!isFromChatScreen) {

@ -207,6 +207,12 @@ class ServiceVM extends BaseVM {
countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""),
(element.id ?? "").toString(),
);
if (branchData.branchImages != null && branchData.branchImages!.isNotEmpty) {
for (var element in branchData.branchImages!) {
ImageModel image = ImageModel(id: element.id, isFromNetwork: true, filePath: element.imageUrl);
pickedBranchImages.add(image);
}
}
}
}
citiesDropList.add(

@ -39,11 +39,12 @@ class AdsBuyerChatsView extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.asset(
MyAssets.bnCar,
Container(
width: 34,
height: 34,
fit: BoxFit.fill,
alignment: Alignment.center,
color: MyColors.darkTextColor,
child: ((chatForAdsModel.buyerName ?? "").getInitials()).toText(color: MyColors.white, fontSize: 15),
).toCircle(borderRadius: 100),
SizedBox(width: 2.w),
Expanded(

@ -0,0 +1,371 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart';
import 'package:mc_common_app/models/general_models/widgets_models.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/views/advertisement/ads_detail_view/components.dart';
import 'package:mc_common_app/views/advertisement/bottom_sheets/ads_damage_part_pictures_sheet.dart';
import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class AdsDetailView extends StatefulWidget {
final AdDetailsModel adDetails;
const AdsDetailView({Key? key, required this.adDetails}) : super(key: key);
@override
State<AdsDetailView> createState() => _AdsDetailViewState();
}
class _AdsDetailViewState extends State<AdsDetailView> {
late AdVM adVM;
late List<VehicleDamageCard> damagePartsPictures;
@override
void initState() {
adVM = context.read<AdVM>();
damagePartsPictures = [];
scheduleMicrotask(() {
onAdDetailsLoaded();
populateDamagePartPictures();
});
super.initState();
}
Future<void> onAdDetailsLoaded() async {
context.read<PaymentVM>().updateCurrentAdId(id: widget.adDetails.id!);
if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus == AdPostStatus.reserved)) {
await adVM.getAdBankingAccountInfo(adId: widget.adDetails.id!);
}
}
void populateDamagePartPictures() {
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) {
for (var element in widget.adDetails.vehicle!.damagereport!) {
int index = -1;
ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true);
VehicleDamageCard vehicleDamageCard = VehicleDamageCard(
partSelectedId: SelectionModel(
selectedId: element.vehicleDamagePartID!,
selectedOption: element.partName ?? "",
),
partImages: [imageModel],
);
damagePartsPictures.add(vehicleDamageCard);
}
}
}
void deleteAdBottomSheet(BuildContext context) {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.deleteAdConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.deleteAdConfirmationMessage.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () {
Navigator.pop(context);
adVM.deleteMyAd(context, adId: widget.adDetails.id!);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
Widget buildVehicleDetailsWidget() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ImagesCorouselWidget(imagesList: widget.adDetails.vehicle!.image!),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: "${widget.adDetails.vehicle!.vehicleTitle} | ${widget.adDetails.vehicle!.color!.label} | ${widget.adDetails.id}".toText(
fontSize: 18,
isBold: true,
maxLines: 2,
),
),
(widget.adDetails.vehicle!.cityName ?? "").toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.model.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.modelyear!.label}".toText(
fontSize: 14,
isBold: true,
),
],
),
"${widget.adDetails.vehicle!.countryID}".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.mileage.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.mileage!.mileageEnd}Km".toText(
fontSize: 14,
isBold: true,
),
],
),
widget.adDetails.createdOn != null ? DateTime.parse(widget.adDetails.createdOn!).getTimeAgo().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor) : const SizedBox(),
],
),
Row(
children: [
("${LocaleKeys.transmission.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.transmission!.label}".toText(
fontSize: 14,
isBold: true,
),
],
),
8.height,
("${LocaleKeys.description.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.vehicleDescription}".toText(
fontSize: 14,
isBold: true,
),
if (widget.adDetails.isMyAd ?? false) ...[
8.height,
("${LocaleKeys.demand.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, height: 1.2, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
if (widget.adDetails.adPostStatus == AdPostStatus.expired) ...[
8.height,
const Divider(thickness: 1, height: 1),
8.height,
LocaleKeys.adDurationExpired.tr().toText(
color: MyColors.redColor,
fontSize: 12,
isItalic: true,
),
],
]
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildBankDetailsIfAvailable() {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
if (adVM.adsBankDetailsModel != null) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.bankDetails.tr().toText(fontSize: 18, isBold: true),
// Row(
// children: [
// "Full Name: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
// "${widget.adDetails.vehicle!.vehicleDescription}".toText(
// fontSize: 14,
// isBold: true,
// ),
// ],
// ),
Row(
children: [
("${LocaleKeys.bankName.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
(adVM.adsBankDetailsModel!.bankName ?? "").toText(
fontSize: 14,
isBold: true,
),
],
),
Row(
children: [
("${LocaleKeys.iban.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
(adVM.adsBankDetailsModel!.iban ?? "").toText(
fontSize: 14,
isBold: true,
),
],
),
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
return const SizedBox.shrink();
});
}
Widget buildAdRejectionDetails() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.rejectionComments.tr().toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor),
Row(
children: [
LocaleKeys.editAd.tr().toText(fontSize: 10, isBold: true),
2.width,
const Icon(Icons.edit, size: 15),
],
).onPress(() async {
Utils.showLoading(context);
await adVM.getVehicleTypes();
await adVM.getVehicleBrandsByVehicleTypeId(vehicleIdForEditAd: widget.adDetails.vehicle!.vehicleType ?? -1);
Utils.hideLoading(context);
adVM.onEditUpdateAdPressed(context: context, previousDetails: widget.adDetails, isFromExtendAd: false);
}),
],
).paddingOnly(bottom: 5),
Row(
children: [
Flexible(
child: (widget.adDetails.comment ?? "").toText(
color: MyColors.redColor,
fontSize: 12,
isItalic: true,
),
)
],
),
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildDamagePartDetails() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
MyAssets.carHitIcon.buildSvg(color: MyColors.darkTextColor),
10.width,
LocaleKeys.damagePartPictures.tr().toText(fontSize: 16),
],
),
const Icon(Icons.arrow_forward_ios_rounded, size: 20)
],
).paddingOnly(top: 5, bottom: 5).onPress(() {
showMyBottomSheet(context, child: AdDamagePartPicturesSheet(adDamageReportList: widget.adDetails.vehicle!.damagereport ?? []));
}).toWhiteContainer(width: double.infinity, allPading: 12);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.vehicleDetails.tr(),
profileImageUrl: MyAssets.bnCar,
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [
((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active)
? IconButton(
icon: const Icon(Icons.delete_outline, color: MyColors.redColor),
onPressed: () {
return deleteAdBottomSheet(context);
},
)
: IconButton(icon: const Icon(Icons.chat_outlined, color: Colors.black), onPressed: () => adVM.onMessagesButtonPressed(context: context, adDetailsModel: widget.adDetails)))
.toContainer(
margin: const EdgeInsets.fromLTRB(0, 8, 21, 8),
paddingAll: 0,
padding: const EdgeInsets.only(right: 21),
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 42,
)
],
onTap: () {},
),
body: Container(
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: Column(
children: [
Expanded(
child: ListView(
shrinkWrap: true,
children: [
buildVehicleDetailsWidget(),
12.height,
buildBankDetailsIfAvailable(),
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[
buildDamagePartDetails(),
],
12.height,
if (widget.adDetails.adPostStatus == AdPostStatus.rejected) ...[
buildAdRejectionDetails(),
],
],
),
),
SizedBox(
child: Column(
children: [
if (!(widget.adDetails.isMyAd ?? false)) ...[
const Divider(thickness: 1, height: 1),
18.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
14.height,
],
widget.adDetails.isMyAd ?? false ? BuildAdDetailsActionButtonForMyAds(adDetailsModel: widget.adDetails) : BuildAdDetailsActionButtonForExploreAds(adDetailsModel: widget.adDetails),
],
),
)
],
),
),
);
}
}

@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
@ -15,15 +14,11 @@ import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/ad_view_model.dart';
import 'package:mc_common_app/view_models/payment_view_model.dart';
import 'package:mc_common_app/views/advertisement/bottom_sheets/ad_duration_selection_sheet.dart';
import 'package:mc_common_app/views/advertisement/bottom_sheets/ads_damage_part_pictures_sheet.dart';
import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart';
import 'package:mc_common_app/views/advertisement/components/picked_images_container_widget.dart';
import 'package:mc_common_app/views/appointments/widgets/custom_calender_widget.dart';
import 'package:mc_common_app/widgets/bottom_sheet.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/common_widgets/time_slots.dart';
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
@ -32,354 +27,6 @@ import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class AdsDetailView extends StatefulWidget {
final AdDetailsModel adDetails;
const AdsDetailView({Key? key, required this.adDetails}) : super(key: key);
@override
State<AdsDetailView> createState() => _AdsDetailViewState();
}
class _AdsDetailViewState extends State<AdsDetailView> {
late AdVM adVM;
late List<VehicleDamageCard> damagePartsPictures;
@override
void initState() {
adVM = context.read<AdVM>();
damagePartsPictures = [];
scheduleMicrotask(() {
onAdDetailsLoaded();
populateDamagePartPictures();
});
super.initState();
}
Future<void> onAdDetailsLoaded() async {
context.read<PaymentVM>().updateCurrentAdId(id: widget.adDetails.id!);
if ((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus == AdPostStatus.reserved)) {
await adVM.getAdBankingAccountInfo(adId: widget.adDetails.id!);
}
}
void populateDamagePartPictures() {
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) {
for (var element in widget.adDetails.vehicle!.damagereport!) {
int index = -1;
ImageModel imageModel = ImageModel(id: element.id!, filePath: element.imageUrl!, isFromNetwork: true);
VehicleDamageCard vehicleDamageCard = VehicleDamageCard(
partSelectedId: SelectionModel(
selectedId: element.vehicleDamagePartID!,
selectedOption: element.partName ?? "",
),
partImages: [imageModel],
);
damagePartsPictures.add(vehicleDamageCard);
}
}
}
void deleteAdBottomSheet(BuildContext context) {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.deleteAdConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.deleteAdConfirmationMessage.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () {
Navigator.pop(context);
adVM.deleteMyAd(context, adId: widget.adDetails.id!);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
Widget buildVehicleDetailsWidget() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ImagesCorouselWidget(imagesList: widget.adDetails.vehicle!.image!),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: "${widget.adDetails.vehicle!.vehicleTitle} | ${widget.adDetails.vehicle!.color!.label} | ${widget.adDetails.id}".toText(
fontSize: 18,
isBold: true,
maxLines: 2,
),
),
(widget.adDetails.vehicle!.cityName ?? "").toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.model.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.modelyear!.label}".toText(
fontSize: 14,
isBold: true,
),
],
),
"${widget.adDetails.vehicle!.countryID}".toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
("${LocaleKeys.mileage.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.mileage!.mileageEnd}Km".toText(
fontSize: 14,
isBold: true,
),
],
),
widget.adDetails.createdOn != null ? DateTime.parse(widget.adDetails.createdOn!).getTimeAgo().toText(fontSize: 10, isBold: true, color: MyColors.lightTextColor) : const SizedBox(),
],
),
Row(
children: [
("${LocaleKeys.transmission.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.transmission!.label}".toText(
fontSize: 14,
isBold: true,
),
],
),
8.height,
("${LocaleKeys.description.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
"${widget.adDetails.vehicle!.vehicleDescription}".toText(
fontSize: 14,
isBold: true,
),
if (widget.adDetails.isMyAd ?? false) ...[
8.height,
("${LocaleKeys.demand.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, height: 1.2, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
if (widget.adDetails.adPostStatus == AdPostStatus.expired) ...[
8.height,
const Divider(thickness: 1, height: 1),
8.height,
LocaleKeys.adDurationExpired.tr().toText(
color: MyColors.redColor,
fontSize: 12,
isItalic: true,
),
],
]
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildBankDetailsIfAvailable() {
return Consumer(builder: (BuildContext context, AdVM adVM, Widget? child) {
if (adVM.adsBankDetailsModel != null) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LocaleKeys.bankDetails.tr().toText(fontSize: 18, isBold: true),
// Row(
// children: [
// "Full Name: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
// "${widget.adDetails.vehicle!.vehicleDescription}".toText(
// fontSize: 14,
// isBold: true,
// ),
// ],
// ),
Row(
children: [
("${LocaleKeys.bankName.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
(adVM.adsBankDetailsModel!.bankName ?? "").toText(
fontSize: 14,
isBold: true,
),
],
),
Row(
children: [
("${LocaleKeys.iban.tr()}: ").toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor),
(adVM.adsBankDetailsModel!.iban ?? "").toText(
fontSize: 14,
isBold: true,
),
],
),
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
return const SizedBox.shrink();
});
}
Widget buildAdRejectionDetails() {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
LocaleKeys.rejectionComments.tr().toText(fontSize: 13, isBold: true, color: MyColors.lightTextColor),
Row(
children: [
LocaleKeys.editAd.tr().toText(fontSize: 10, isBold: true),
2.width,
const Icon(Icons.edit, size: 15),
],
).onPress(() async {
Utils.showLoading(context);
await adVM.getVehicleTypes();
await adVM.getVehicleBrandsByVehicleTypeId(vehicleIdForEditAd: widget.adDetails.vehicle!.vehicleType ?? -1);
Utils.hideLoading(context);
adVM.onEditUpdateAdPressed(context: context, previousDetails: widget.adDetails, isFromExtendAd: false);
}),
],
).paddingOnly(bottom: 5),
Row(
children: [
Flexible(
child: (widget.adDetails.comment ?? "").toText(
color: MyColors.redColor,
fontSize: 12,
isItalic: true,
),
)
],
),
],
).toWhiteContainer(width: double.infinity, allPading: 12);
}
Widget buildDamagePartDetails() {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
MyAssets.carHitIcon.buildSvg(color: MyColors.darkTextColor),
10.width,
LocaleKeys.damagePartPictures.tr().toText(fontSize: 16),
],
),
const Icon(Icons.arrow_forward_ios_rounded, size: 20)
],
).paddingOnly(top: 5, bottom: 5).onPress(() {
showMyBottomSheet(context, child: AdDamagePartPicturesSheet(adDamageReportList: widget.adDetails.vehicle!.damagereport ?? []));
}).toWhiteContainer(width: double.infinity, allPading: 12);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.ads.tr(),
profileImageUrl: MyAssets.bnCar,
isRemoveBackButton: false,
isDrawerEnabled: false,
actions: [
((widget.adDetails.isMyAd ?? false) && (widget.adDetails.adPostStatus != AdPostStatus.reserved) && (widget.adDetails.adPostStatus != AdPostStatus.active)
? IconButton(
icon: const Icon(Icons.delete_outline, color: MyColors.redColor),
onPressed: () {
return deleteAdBottomSheet(context);
},
)
: IconButton(icon: const Icon(Icons.chat_outlined, color: Colors.black), onPressed: () => adVM.onMessagesButtonPressed(context: context, adDetailsModel: widget.adDetails)))
.toContainer(
margin: const EdgeInsets.fromLTRB(0, 8, 21, 8),
paddingAll: 0,
padding: const EdgeInsets.only(right: 21),
borderRadius: 100,
borderColor: MyColors.lightGreyEFColor,
isEnabledBorder: true,
height: 40,
width: 42,
)
],
onTap: () {},
),
body: Container(
padding: const EdgeInsets.only(bottom: 10, left: 21, right: 21),
child: Column(
children: [
Expanded(
child: ListView(
shrinkWrap: true,
children: [
buildVehicleDetailsWidget(),
12.height,
buildBankDetailsIfAvailable(),
if (widget.adDetails.vehicle!.damagereport != null && widget.adDetails.vehicle!.damagereport!.isNotEmpty) ...[
buildDamagePartDetails(),
],
12.height,
if (widget.adDetails.adPostStatus == AdPostStatus.rejected) ...[
buildAdRejectionDetails(),
],
],
),
),
SizedBox(
child: Column(
children: [
if (!(widget.adDetails.isMyAd ?? false)) ...[
const Divider(thickness: 1, height: 1),
18.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
widget.adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true),
LocaleKeys.sar.tr().toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5),
],
),
14.height,
],
widget.adDetails.isMyAd ?? false ? BuildAdDetailsActionButtonForMyAds(adDetailsModel: widget.adDetails) : BuildAdDetailsActionButtonForExploreAds(adDetailsModel: widget.adDetails),
],
),
)
],
),
),
);
}
}
class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
final AdDetailsModel adDetailsModel;
@ -856,11 +503,9 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget {
children: [
Expanded(
child: ShowFillButton(
backgroundColor: MyColors.grey98Color.withOpacity(0.3),
txtColor: MyColors.lightTextColor,
isDisabled: true,
maxHeight: 55,
title: pendingText,
isBold: false,
onPressed: () {},
),
),

@ -28,7 +28,7 @@ class CommonAppointmentSliderWidget extends StatelessWidget {
return CarouselSlider.builder(
options: CarouselOptions(
height: appType == AppType.provider ? 121 : 140,
viewportFraction: 0.99999, // Todo: Dont do anything
viewportFraction: 1, // Todo: Dont do anything
enlargeCenterPage: false,
enableInfiniteScroll: false,
),
@ -57,7 +57,7 @@ class CommonAppointmentSliderWidget extends StatelessWidget {
if (model.myUpComingAppointments.isEmpty) {
return LocaleKeys.noUpcomingAppointments.tr().toText(fontSize: 16, color: MyColors.lightTextColor).paddingAll(21);
} else {
return getCarouselSliderWidget(AppState().currentAppType, model);
return getCarouselSliderWidget(AppState().currentAppType, model).toWhiteContainer(width: double.infinity, margin: EdgeInsets.symmetric(horizontal: 21));
}
} else {
if (model.myUpComingAppointments.isEmpty) {
@ -74,7 +74,7 @@ class CommonAppointmentSliderWidget extends StatelessWidget {
),
).padding(const EdgeInsets.symmetric(vertical: 10, horizontal: 21));
} else {
return getCarouselSliderWidget(AppState().currentAppType, model);
return getCarouselSliderWidget(AppState().currentAppType, model).toWhiteContainer(width: double.infinity, margin: EdgeInsets.symmetric(horizontal: 21));
}
}
}
@ -215,10 +215,8 @@ class BuildAppointmentContainerForCustomer extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
(AppState().currentAppType == AppType.provider ? appointmentListModel!.branchName ?? "" : appointmentListModel!.branchName ?? "")
.toText(color: MyColors.greyColor, fontSize: 12, letterSpacing: 0, height: 19 / 12),
(AppState().currentAppType == AppType.provider ? appointmentListModel!.customerName ?? "" : appointmentListModel!.branchName ?? "")
.toText(color: MyColors.black, fontSize: 16, letterSpacing: -0.64, height: 24 / 16),
(AppState().currentAppType == AppType.provider ? appointmentListModel!.branchName ?? "" : appointmentListModel!.branchName ?? "").toText(color: MyColors.greyColor, fontSize: 12, letterSpacing: 0, height: 19 / 12),
(AppState().currentAppType == AppType.provider ? appointmentListModel!.customerName ?? "" : appointmentListModel!.branchName ?? "").toText(color: MyColors.black, fontSize: 16, letterSpacing: -0.64, height: 24 / 16),
Row(
children: [
!isForProvider ? MyAssets.miniClock.buildSvg(height: 12) : SizedBox(),

@ -1,25 +1,17 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/views/chat/widgets/chat_message_widget.dart';
import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
@ -65,70 +57,6 @@ class _ChatViewState extends State<ChatView> {
super.dispose();
}
// Future buildSendOfferBottomSheet(BuildContext context) {
// RequestModel requestDetail = chatViewArgumentsForRequest!.requestModel!;
// return showModalBottomSheet(
// context: context,
// isScrollControlled: true,
// enableDrag: true,
// builder: (BuildContext context) {
// return Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
// return InfoBottomSheet(
// title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
// description: Padding(
// padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// 12.height,
// TxtField(
// value: requestsVM.offerPrice,
// errorValue: requestsVM.offerPriceError,
// keyboardType: TextInputType.number,
// hint: LocaleKeys.enterAmount.tr(),
// numbersOnly: true,
// onChanged: (v) => requestsVM.updateOfferPrice(v),
// ),
// 12.height,
// TxtField(
// maxLines: 5,
// value: requestsVM.offerDescription,
// errorValue: requestsVM.offerDescriptionError,
// keyboardType: TextInputType.text,
// hint: LocaleKeys.description.tr(),
// onChanged: (v) => requestsVM.updateOfferDescription(v),
// ),
// ],
// ),
// 25.height,
// ShowFillButton(
// title: LocaleKeys.submit.tr(),
// onPressed: () {
// requestsVM.onSendOfferPressed(
// context: context,
// receiverId: requestDetail.customerID,
// message: requestsVM.offerDescription,
// requestId: requestDetail.id,
// offerPrice: requestsVM.offerPrice,
// requestModel: requestDetail,
// requestIndex: chatViewArgumentsForRequest!.requestIndex,
// isFromChatScreen: true,
// );
// },
// maxWidth: double.infinity,
// ),
// 19.height,
// ],
// ),
// ));
// });
// },
// );
// }
Future<bool> onTextMessageSend() async {
bool status = false;
if (chatTypeEnum == ChatTypeEnum.requestOffer) {
@ -205,7 +133,7 @@ class _ChatViewState extends State<ChatView> {
size: 30,
).onPress(
() {
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex , requestModel: chatViewArgumentsForRequest!.requestModel!);
RequestDetailPageArguments requestDetailArguments = RequestDetailPageArguments(requestIndex: chatViewArgumentsForRequest!.requestIndex, requestModel: chatViewArgumentsForRequest!.requestModel!);
buildSendOfferBottomSheet(context, requestDetailArguments);
},
),
@ -247,336 +175,3 @@ class _ChatViewState extends State<ChatView> {
);
}
}
class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
}
class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Future buildRejectOfferBottomSheet({required ChatMessageModel chatMessageModel}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5),
itemBuilder: (BuildContext context, int index) {
OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
title: '${offerRequestCommentModel.title}',
onSelected: () {
chatVM.updateSelectionInOfferRejectModelList(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) ...[
// comparing if the "other" is selected
12.height,
TxtField(
maxLines: 5,
value: chatVM.rejectOfferDescription,
errorValue: chatVM.rejectOfferDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.description.tr(),
onChanged: (v) => chatVM.updateRejectOfferDescription(v),
),
],
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () async {
String comments = "";
if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) //Other
{
comments = chatVM.rejectOfferDescription;
} else {
comments = chatVM.selectedOfferRequestCommentModel.title ?? "";
}
if (!chatVM.isRejectOfferButtonValidated()) {
return;
}
Navigator.pop(context);
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.receiverUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
requestOfferStatusEnum: RequestOfferStatusEnum.rejected,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected;
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Rejected");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
void offerAcceptConfirmationBottomSheet({required ChatMessageModel chatMessageModel}) {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.acceptOfferConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.acceptOfferConfirmationMessage.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () async {
log("ServiceProviderID: ${chatMessageModel.toString()}");
Navigator.pop(context);
// int status = await context.read<ChatVM>().onActionOfferTapped(
// context: context,
// requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
// reqOfferId: chatMessageModel.reqOfferID ?? -1,
// comments: GlobalConsts.acceptingThisOffer,
// );
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.receiverUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: GlobalConsts.acceptingThisOffer,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Accepted");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return Column(
children: [
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${chatMessageModel.reqOffer!.price}".toText(fontSize: 19, isBold: true, color: AppState().currentAppType == AppType.provider ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
if (widget.chatMessageModel.isMyMessage == false) ...[
10.height,
Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.accept.tr(),
fontSize: 9,
borderColor: MyColors.greenColor,
isFilled: false,
onPressed: () {
offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel);
},
backgroundColor: MyColors.white,
txtColor: MyColors.greenColor,
),
),
20.width,
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.reject.tr(),
borderColor: MyColors.redColor,
isFilled: false,
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () async {
buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel);
},
),
)
],
),
],
],
);
case RequestOfferStatusEnum.negotiate:
return Column(
children: [
Center(
child: LocaleKeys.newOfferRequired.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.accepted:
return Column(
children: [
Center(
child: LocaleKeys.offerHasBeenAccepted.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.rejected:
return Column(
children: [
Center(
child: LocaleKeys.offerHasBeenRejected.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.cancel:
return Column(
children: [
Center(
child: LocaleKeys.offerHasBeenCancelled.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
}
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: Image.asset(
MyAssets.bnCar,
width: 34,
height: 34,
fit: BoxFit.fill,
).toCircle(borderRadius: 100),
),
10.width,
Expanded(
flex: 10,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true),
],
),
5.height,
Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Directionality(
textDirection: TextDirection.ltr,
child: (widget.chatMessageModel.chatText ?? "").toText(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
// isBold: true,
),
),
),
],
),
if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context),
],
],
).toContainer(
isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false),
backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white,
borderRadius: 0,
margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0),
),
],
),
)
],
),
);
}
}

@ -0,0 +1,389 @@
import 'dart:developer';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/dialogs_and_bottomsheets.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/checkbox_with_title_desc.dart';
import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/txt_field.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart' as lcl;
class ChatMessageCustomWidget extends StatefulWidget {
final ChatMessageModel chatMessageModel;
const ChatMessageCustomWidget({super.key, required this.chatMessageModel});
@override
State<ChatMessageCustomWidget> createState() => _ChatMessageCustomWidgetState();
}
class _ChatMessageCustomWidgetState extends State<ChatMessageCustomWidget> {
Future buildRejectOfferBottomSheet({required ChatMessageModel chatMessageModel}) {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return InfoBottomSheet(
title: LocaleKeys.makeAnOffer.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
description: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
ListView.separated(
shrinkWrap: true,
itemCount: chatVM.offerRejectModelList.length,
separatorBuilder: (BuildContext context, int index) => const Divider(thickness: 0.5),
itemBuilder: (BuildContext context, int index) {
OfferRequestCommentModel offerRequestCommentModel = chatVM.offerRejectModelList[index];
return CircleCheckBoxWithTitle(
isChecked: offerRequestCommentModel.isSelected ?? false,
title: '${offerRequestCommentModel.title}',
onSelected: () {
chatVM.updateSelectionInOfferRejectModelList(index);
},
selectedColor: MyColors.darkPrimaryColor,
);
},
),
if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) ...[
// comparing if the "other" is selected
12.height,
TxtField(
maxLines: 5,
value: chatVM.rejectOfferDescription,
errorValue: chatVM.rejectOfferDescriptionError,
keyboardType: TextInputType.text,
hint: LocaleKeys.description.tr(),
onChanged: (v) => chatVM.updateRejectOfferDescription(v),
),
],
],
),
25.height,
ShowFillButton(
title: LocaleKeys.submit.tr(),
onPressed: () async {
String comments = "";
if (chatVM.selectedOfferRequestCommentModel.index == chatVM.offerRejectModelList.length - 1) //Other
{
comments = chatVM.rejectOfferDescription;
} else {
comments = chatVM.selectedOfferRequestCommentModel.title ?? "";
}
if (!chatVM.isRejectOfferButtonValidated()) {
return;
}
Navigator.pop(context);
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: comments,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
requestOfferStatusEnum: RequestOfferStatusEnum.rejected,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.rejected;
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Rejected");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
void offerAcceptConfirmationBottomSheet({required ChatMessageModel chatMessageModel}) {
return actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.acceptOfferConfirmation.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.acceptOfferConfirmationMessage.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () async {
Navigator.pop(context);
bool status = await context.read<ChatVM>().onSendMessageForActionOnRequestOffer(
receiverId: chatMessageModel.senderUserID ?? "",
chatMessageType: ChatMessageTypeEnum.offer,
comments: GlobalConsts.acceptingThisOffer,
requestId: chatMessageModel.reqOffer!.requestID ?? -1,
serviceProviderID: chatMessageModel.serviceProviderID ?? 0,
requestOfferID: chatMessageModel.reqOffer!.id ?? -1,
offerPrice: chatMessageModel.reqOffer!.price.toString(),
serviceItemName: chatMessageModel.reqOffer!.serviceItemName ?? "",
manufacturedOn: chatMessageModel.reqOffer!.manufacturedOn ?? "",
manufacturedById: chatMessageModel.reqOffer!.manufacturedById ?? 0,
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
context: context,
);
if (status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
setState(() {});
// Navigator.pop(context);
Utils.showToast("Offer Accepted");
// navigateReplaceWithName(context, AppRoutes.dashboard);
}
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return Column(
children: [
5.height,
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${(chatMessageModel.reqOffer!.price ?? 0.0).toInt()}".toText(fontSize: 19, isBold: true, color: AppState().currentAppType == AppType.provider ? MyColors.white : MyColors.darkTextColor),
5.width,
LocaleKeys.sar.tr().toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
if (widget.chatMessageModel.isMyMessage == false) ...[
10.height,
Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.accept.tr(),
fontSize: 9,
borderColor: MyColors.greenColor,
isFilled: false,
onPressed: () {
offerAcceptConfirmationBottomSheet(chatMessageModel: chatMessageModel);
},
backgroundColor: MyColors.white,
txtColor: MyColors.greenColor,
),
),
20.width,
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: LocaleKeys.reject.tr(),
borderColor: MyColors.redColor,
isFilled: false,
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () async {
buildRejectOfferBottomSheet(chatMessageModel: chatMessageModel);
},
),
)
],
),
],
],
);
case RequestOfferStatusEnum.negotiate:
return Column(
children: [
Center(
child: LocaleKeys.newOfferRequired.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.accepted:
return Column(
children: [
Center(
child: LocaleKeys.offerHasBeenAccepted.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.rejected:
return Column(
children: [
Center(
child: LocaleKeys.offerHasBeenRejected.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
case RequestOfferStatusEnum.cancel:
return Column(
children: [
Center(
child: LocaleKeys.offerHasBeenCancelled.tr().toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
),
).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)),
],
);
}
}
Widget getProfilePicture({String profileImageUrl = ""}) {
Widget widget = const SizedBox();
if (profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userLocalImage != null) {
widget = Image.file(
AppState().getUser.data!.userInfo!.userLocalImage!,
width: 34,
height: 34,
fit: BoxFit.fill,
);
} else if (profileImageUrl.isEmpty && AppState().getUser.data!.userInfo!.userImageUrl != null) {
widget = CachedNetworkImage(
imageUrl: AppState().getUser.data!.userInfo!.userImageUrl,
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
placeholder: (context, url) => const Center(child: CircularProgressIndicator()),
errorWidget: (context, url, error) => const Icon(Icons.supervised_user_circle_outlined),
fadeInCurve: Curves.easeIn,
width: 34,
height: 34,
fit: BoxFit.fill,
fadeInDuration: const Duration(milliseconds: 1000),
useOldImageOnUrlChange: false);
}
return widget.toCircle(borderRadius: 100);
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: (widget.chatMessageModel.isMyMessage ?? false)
? getProfilePicture()
: Container(
width: 34,
height: 34,
alignment: Alignment.center,
color: MyColors.darkTextColor,
child: ((widget.chatMessageModel.senderName ?? "").getInitials()).toText(color: MyColors.white, isBold: true, fontSize: 12),
).toCircle(borderRadius: 100),
),
10.width,
Expanded(
flex: 10,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true),
],
),
5.height,
Column(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Directionality(
textDirection: TextDirection.ltr,
child: (widget.chatMessageModel.chatText ?? "").toText(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
// isBold: true,
),
),
),
],
),
if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context),
],
],
).toContainer(
isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false),
backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white,
borderRadius: 0,
margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0),
),
],
),
)
],
),
);
}
}

@ -88,15 +88,18 @@ Future buildSendOfferBottomSheet(BuildContext context, RequestDetailPageArgument
maxHeight: 55,
onPressed: () {
requestsVM.onSendOfferPressed(
context: context,
receiverId: requestDetail.customerID,
message: requestsVM.offerDescription,
requestId: requestDetail.id,
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
requestIndex: requestDetailPageArguments.requestIndex,
isFromChatScreen: false,
);
context: context,
receiverId: requestDetail.customerID,
message: requestsVM.offerDescription,
requestId: requestDetail.id,
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
requestIndex: requestDetailPageArguments.requestIndex,
isFromChatScreen: false,
manufacturedById: 1,
// Todo: It should be the ID of the manufacturer
manufacturedOn: requestsVM.serviceItemCreatedOn,
serviceItemName: requestsVM.serviceItem);
},
maxWidth: double.infinity,
),

@ -0,0 +1,222 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:mc_common_app/views/advertisement/components/ads_images_corousel_widget.dart';
import 'package:mc_common_app/views/requests/request_bottomsheets.dart';
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class RequestDetailPage extends StatelessWidget {
final RequestDetailPageArguments requestDetailPageArguments;
const RequestDetailPage({Key? key, required this.requestDetailPageArguments}) : super(key: key);
Widget buildBottomButton({required RequestStatus requestStatus, required String statusText, required BuildContext context, required Function() onViewChatTapped}) {
switch (requestStatus) {
case RequestStatus.submitted:
case RequestStatus.inProgress:
case RequestStatus.paid:
case RequestStatus.shipping:
return ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.viewChat.tr(),
isBold: false,
onPressed: onViewChatTapped,
);
case RequestStatus.completed:
case RequestStatus.cancelled:
case RequestStatus.expired:
case RequestStatus.pending:
return buildDisabledButton(statusText);
}
}
Widget buildDisabledButton(String text) {
return ShowFillButton(
backgroundColor: MyColors.grey98Color.withOpacity(0.3),
txtColor: MyColors.lightTextColor,
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: text,
isBold: false,
onPressed: () {},
);
}
Future<void> onViewChatTapped(BuildContext context) async {
ChatViewArgumentsForRequest chatViewArgumentsForRequest = ChatViewArgumentsForRequest(
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: requestDetailPageArguments.requestModel.customerID,
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: requestDetailPageArguments.requestModel.id,
providerIndex: -1,
// This will be only sent in case of customer
requestModel: requestDetailPageArguments.requestModel,
requestIndex: requestDetailPageArguments.requestIndex, // This will be only sent in case of provider
);
ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer,
chatViewArgumentsForRequest: chatViewArgumentsForRequest,
);
final chatVM = context.read<ChatVM>();
await chatVM
.getUsersChatMessagesForProvider(
customerId: requestDetailPageArguments.requestModel.customerId,
context: context,
requestOfferId: 0,
requestId: requestDetailPageArguments.requestModel.id,
customerRequestIndex: requestDetailPageArguments.requestIndex,
)
.whenComplete(() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
title: LocaleKeys.requestDetail.tr(),
),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
ImagesCorouselWidget(imagesList: requestDetailPageArguments.requestModel.requestImages ?? []),
24.height,
buildRequestContainer(context),
],
).toWhiteContainer(
width: double.infinity,
allPading: 12,
margin: const EdgeInsets.only(top: 21, right: 21, left: 21),
),
if (!requestDetailPageArguments.requestModel.isChatted) ...[
ShowFillButton(
maxWidth: double.infinity,
margin: const EdgeInsets.all(15),
maxHeight: 55,
title: LocaleKeys.sendOffer.tr(),
isBold: false,
fontSize: 18,
onPressed: () => buildSendOfferBottomSheet(context, requestDetailPageArguments),
),
] else ...[
buildBottomButton(
requestStatus: requestDetailPageArguments.requestModel.requestStatus,
statusText: "Offer ${requestDetailPageArguments.requestModel.requestStatusName}",
context: context,
onViewChatTapped: () async => onViewChatTapped(context),
),
],
],
),
),
);
}
Widget buildRequestContainer(BuildContext context) {
final requestDetail = requestDetailPageArguments.requestModel;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
requestDetail.vehicleTypeName.toText(fontSize: 16, isBold: true),
showItem("Manufacturer:", requestDetail.brand),
showItem("Model:", "${requestDetail.year}"),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.start,
children: [
"${requestDetail.city ?? ""} ${requestDetail.countryName}".toText(
color: MyColors.lightTextColor,
),
if (requestDetail.createdOn != null) ...[
DateTime.parse(requestDetail.createdOn!).getTimeAgo().toText(color: MyColors.lightTextColor),
],
],
),
],
),
showItem("Customer Name: ", requestDetail.customerName),
showItem("Description: ", requestDetail.description),
showItem("Price Range:", ""),
Row(
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"${requestDetail.price.toInt()}".toText(fontSize: 25, isBold: true),
2.width,
LocaleKeys.sar.tr().toText(
color: MyColors.lightTextColor,
fontSize: 16,
height: 2.3,
),
],
),
Row(
children: [
Utils.statusContainerChip(
text: (requestDetail.requestStatusName),
chipColor: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
),
],
),
],
),
),
// const Icon(Icons.arrow_forward)
],
),
],
);
}
Widget showItem(String title, String value) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title.isNotEmpty)
title.toText(
color: MyColors.lightTextColor,
),
if (title.isNotEmpty) 2.width,
if (value.isNotEmpty) Expanded(child: value.toText(isBold: true)),
],
);
}
}

@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:flutter/material.dart';
@ -126,7 +127,7 @@ class RequestItem extends StatelessWidget {
children: [
title.toText(color: MyColors.lightTextColor),
3.width,
Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis )),
Flexible(child: value.toText(isBold: true, overflow: TextOverflow.ellipsis)),
],
);
}

@ -4,18 +4,19 @@ import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class ShowFillButton extends StatelessWidget {
String title;
Color? backgroundColor;
VoidCallback onPressed;
Color txtColor;
double elevation, radius, maxWidth, maxHeight, fontSize, horizontalPadding, horizontalMargin, verticalMargin;
bool isFlatButton, isBold;
EdgeInsets? margin;
bool isFilled;
Color borderColor;
Widget? iconWidget;
final String title;
final Color? backgroundColor;
final VoidCallback onPressed;
final Color txtColor;
final double elevation, radius, maxWidth, maxHeight, fontSize, horizontalPadding, horizontalMargin, verticalMargin;
final bool isFlatButton, isBold;
final EdgeInsets? margin;
final bool isFilled;
final bool isDisabled;
final Color borderColor;
final Widget? iconWidget;
ShowFillButton({
const ShowFillButton({
super.key,
required this.title,
required this.onPressed,
@ -23,6 +24,7 @@ class ShowFillButton extends StatelessWidget {
this.backgroundColor = MyColors.darkPrimaryColor,
this.elevation = 0,
this.isFilled = true,
this.isDisabled = false,
this.radius = 0,
this.maxWidth = 88,
this.maxHeight = 45,
@ -65,11 +67,13 @@ class ShowFillButton extends StatelessWidget {
Widget showButton() {
return Container(
// decoration: isFlatButton ? null : MyColors.gradientButton,
color: isFlatButton
? null
: isFilled
? backgroundColor
: null,
color: isDisabled
? MyColors.grey98Color.withOpacity(0.3)
: (isFlatButton
? null
: isFilled
? backgroundColor
: null),
margin: EdgeInsets.symmetric(horizontal: horizontalMargin, vertical: verticalMargin),
child: MaterialButton(
onPressed: onPressed,
@ -85,7 +89,7 @@ class ShowFillButton extends StatelessWidget {
title.toText(
fontSize: fontSize,
isBold: isBold,
color: txtColor,
color: isDisabled ? MyColors.lightTextColor : txtColor,
maxLines: 1,
),
],
@ -93,7 +97,7 @@ class ShowFillButton extends StatelessWidget {
: title.toText(
fontSize: fontSize,
isBold: isBold,
color: txtColor,
color: isDisabled ? MyColors.lightTextColor : txtColor,
maxLines: 1,
),
),

@ -30,8 +30,6 @@ class BranchDetailCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Provider Image Title" + title);
print("Provider Image URL" + providerImageUrl);
return Padding(
padding: EdgeInsets.zero,
// padding: const EdgeInsets.only(

@ -24,8 +24,7 @@ extension ExtendedText on Widget {
}
extension ContainerExt on Widget {
Widget toWhiteContainer(
{required double width, double? allPading, EdgeInsets? pading, EdgeInsets? margin, VoidCallback? onTap, Color? backgroundColor, bool isBorderRequired = false, double borderRadius = 0}) {
Widget toWhiteContainer({required double width, double? allPading, EdgeInsets? pading, EdgeInsets? margin, VoidCallback? onTap, Color? backgroundColor, bool isBorderRequired = false, double borderRadius = 0}) {
return InkWell(
onTap: onTap,
child: Container(
@ -84,12 +83,12 @@ extension ContainerExt on Widget {
boxShadow: !isShadowEnabled
? null
: [
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
BoxShadow(
color: const Color(0xff000000).withOpacity(.05),
blurRadius: 26,
offset: const Offset(0, -3),
),
],
),
padding: padding ?? EdgeInsets.all(paddingAll),
margin: margin ?? EdgeInsets.all(marginAll),
@ -234,8 +233,7 @@ extension WidgetExt on Widget {
);
}
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) =>
Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this);
Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this);
// Widget onTap(VoidCallback onTap, {double corners = 0}) {
// return Clickable.widget(child: this, corners: corners, onTap: onTap);
@ -365,10 +363,14 @@ extension BuildSVG on String? {
if (imageChunk == null) {
return child!;
}
return const Center(
child: CircularProgressIndicator(
strokeWidth: 0.5,
color: MyColors.darkPrimaryColor,
return Center(
child: SizedBox(
height: height,
width: width,
child: const CircularProgressIndicator(
strokeWidth: 0.6,
color: MyColors.darkPrimaryColor,
),
),
);
},
@ -382,12 +384,7 @@ extension BuildSVG on String? {
extension LocaleSetup on MultiProvider {
Widget setupLocale() {
return EasyLocalization(supportedLocales: MyLocales.supportedLocales,
fallbackLocale: MyLocales.fallBackLocale,
startLocale: MyLocales.startLocale,
assetLoader: MyLocales.assetLoader,
path: MyLocales.localesAssetPath,
child: this);
return EasyLocalization(supportedLocales: MyLocales.supportedLocales, fallbackLocale: MyLocales.fallBackLocale, startLocale: MyLocales.startLocale, assetLoader: MyLocales.assetLoader, path: MyLocales.localesAssetPath, child: this);
}
}

Loading…
Cancel
Save