Compare commits

...

7 Commits

Author SHA1 Message Date
Mirza.Shafique@cloudsolutions.com.sa 5eb483c3fd Appointment View Model Merge Fix 2 years ago
Mirza.Shafique@cloudsolutions.com.sa a7ba63d281 Merge branch 'mirza_development'
# Conflicts:
#	lib/view_models/appointments_view_model.dart
2 years ago
Faiz Hashmi 01d9ad68a2 Chat Implementation 1.0 2 years ago
Faiz Hashmi a76b8d8244 Merge branch 'master' into faiz_development_common 2 years ago
Faiz Hashmi 837191a5e1 Chat Implementation 1.0 2 years ago
Mirza.Shafique@cloudsolutions.com.sa 04cfd408bd Merge branch 'mirza_development'
# Conflicts:
#	pubspec.lock
2 years ago
Mirza.Shafique@cloudsolutions.com.sa 9e3cc01302 Merge Conflict 2 years ago

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.9 KiB

@ -138,9 +138,13 @@ class ApiConsts {
static String createRequest = "${baseUrlServices}api/RequestManagement/Request_Create";
static String getRequest = "${baseUrlServices}api/RequestManagement/Request_Get";
static String getRequestOffers = "${baseUrlServices}api/RequestManagement/ReqOffer_Get";
static String updateRequestOffer = "${baseUrlServices}api/RequestManagement/RequestOffer_UpdateStatus";
static String requestOffersSpsGet = "${baseUrlServices}api/RequestManagement/Request_OfferSPs_Get";
//Chat
static String chatHubUrl = "$baseUrlServices/McHub";
static String messageIsReadUpdate = "${baseUrlServices}api/RequestManagement/ReqOfferChatIsRead_Update";
static String getChatMessages = "${baseUrlServices}api/RequestManagement/ReqOfferChat_Get";
static List<String> closingUrls = ["PayFortResponse"];
}
@ -257,6 +261,10 @@ class MyAssets {
static String tamaraArPng = "${assetPath}icons/payments/tamara_ar.png";
static String tamaraEngPng = "${assetPath}icons/payments/tamara_en.png";
static String visaPng = "${assetPath}icons/payments/visa.png";
static String whatsAppIcon = "${assetPath}icons/whatsapp_icon.svg";
}
RegExp numReg = RegExp(r".*[0-9].*");

@ -1,4 +1,5 @@
import 'package:mc_common_app/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
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';
@ -110,17 +111,35 @@ class AppRoutes {
}
class ChatViewArguments {
final RequestModel? requestModel;
final int? requestId;
final ChatTypeEnum chatTypeEnum;
final String senderId;
final String receiverId;
final int providerIndex;
final int requestIndex;
final RequestModel? requestModel;
ChatViewArguments({required this.chatTypeEnum, this.requestModel, required this.senderId, required this.receiverId});
ChatViewArguments({
required this.chatTypeEnum,
this.requestId,
required this.senderId,
required this.receiverId,
required this.providerIndex,
required this.requestIndex,
this.requestModel,
});
}
class OfferListPageArguments {
final List<OffersModel> offersList;
final RequestModel? requestModel;
final List<ServiceProvidersOffers> serviceProviderOffers;
final int? requestId;
OfferListPageArguments({required this.serviceProviderOffers, this.requestId});
}
class RequestDetailPageArguments {
final int requestIndex;
final RequestModel requestModel;
OfferListPageArguments({required this.offersList, this.requestModel});
RequestDetailPageArguments({required this.requestIndex, required this.requestModel});
}

@ -28,6 +28,7 @@ extension EmailValidator on String {
decoration: isUnderLine
? TextDecoration.underline
: textDecoration ?? TextDecoration.none,
fontSize: fontSize ?? 10,
fontWeight: isBold ? FontWeight.bold : fontWeight ?? FontWeight.w600,
color: color ?? MyColors.darkTextColor,

@ -1,103 +0,0 @@
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/enums.dart';
class ChatMessageModel {
String? senderUserID;
String? senderName;
int? messageType;
ChatMessageTypeEnum? messageTypeEnum;
String? message;
RequestOffer? requestOffer;
int? requestID;
int? requestOfferID;
bool? isMyMessage;
ChatMessageModel({
this.senderUserID,
this.senderName,
this.messageType,
this.message,
this.requestOffer,
this.requestID,
this.requestOfferID,
this.isMyMessage = true,
});
ChatMessageModel.fromJson(Map<String, dynamic> json) {
final myUserId = AppState().getUser.data!.userInfo!.userId.toString();
senderUserID = json['senderUserID'];
senderName = json['senderName'];
messageType = json['messageType'];
messageTypeEnum = (json['messageType'] as int).toChatMessageTypeEnum();
message = json['message'];
requestOffer = json['requestOffer'] != null ? RequestOffer.fromJson(json['requestOffer']) : null;
requestID = json['requestID'];
requestOfferID = json['requestOfferID'];
isMyMessage = (json['senderUserId']).toString() == myUserId;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['senderUserID'] = senderUserID;
data['senderName'] = senderName;
data['messageType'] = messageType;
data['message'] = message;
if (requestOffer != null) {
data['requestOffer'] = requestOffer!.toJson();
}
data['requestID'] = requestID;
data['requestOfferID'] = requestOfferID;
return data;
}
}
class RequestOffer {
int? id;
int? requestID;
int? serviceProviderID;
int? offerStatus;
RequestOfferStatusEnum? requestOfferStatusEnum;
String? comment;
double? price;
String? offeredItemCreatedBy;
String? offeredItemCreatedOn;
RequestOffer({
this.id,
this.requestID,
this.serviceProviderID,
this.offerStatus,
this.comment,
this.price,
this.offeredItemCreatedBy,
this.offeredItemCreatedOn,
this.requestOfferStatusEnum = RequestOfferStatusEnum.offer,
});
RequestOffer.fromJson(Map<String, dynamic> json) {
id = json['id'];
requestID = json['requestID'];
serviceProviderID = json['serviceProviderID'];
offerStatus = json['offerStatus'];
offerStatus = json['offerStatus'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
comment = json['comment'];
price = json['price'];
offeredItemCreatedBy = json['offeredItemCreatedBy'];
offeredItemCreatedOn = json['offeredItemCreatedOn'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['requestID'] = requestID;
data['serviceProviderID'] = serviceProviderID;
data['offerStatus'] = offerStatus;
data['comment'] = comment;
data['price'] = price;
data['offeredItemCreatedBy'] = offeredItemCreatedBy;
data['offeredItemCreatedOn'] = offeredItemCreatedOn;
return data;
}
}

@ -0,0 +1,105 @@
import 'dart:developer';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/utils/enums.dart';
class ChatMessageModel {
int? id;
String? senderUserID;
String? senderName;
int? messageType;
ChatMessageTypeEnum? chatMessageTypeEnum;
String? chatText;
int? requestID;
int? reqOfferID;
int? serviceProviderID;
int? offerStatus;
ReqOffer? reqOffer;
bool? isRead;
String? readOn;
bool? isMyMessage;
ChatMessageModel({
this.id,
this.senderUserID,
this.senderName,
this.messageType,
this.chatMessageTypeEnum,
this.chatText,
this.requestID,
this.reqOfferID,
this.serviceProviderID,
this.offerStatus,
this.reqOffer,
this.isRead,
this.readOn,
this.isMyMessage,
});
@override
String toString() {
return 'ChatMessageModel{id: $id, senderUserID: $senderUserID, senderName: $senderName, messageType: $messageType, chatMessageTypeEnum: $chatMessageTypeEnum, chatText: $chatText, requestID: $requestID, reqOfferID: $reqOfferID, serviceProviderID: $serviceProviderID, offerStatus: $offerStatus, reqOffer: $reqOffer, isRead: $isRead, readOn: $readOn, isMyMessage: $isMyMessage}';
}
ChatMessageModel.fromJson(Map<String, dynamic> json) {
final myUserId = AppState().getUser.data!.userInfo!.userId.toString().toUpperCase();
id = json['id'];
senderUserID = json['senderUserID'];
senderName = json['senderName'];
messageType = json['messageType'];
chatMessageTypeEnum = (json['messageType'] as int).toChatMessageTypeEnum();
chatText = json['chatText'];
requestID = json['requestID'];
reqOfferID = json['reqOfferID'];
serviceProviderID = json['serviceProviderID'];
offerStatus = json['offerStatus'];
reqOffer = json['reqOffer'] != null ? ReqOffer.fromJson(json['reqOffer']) : null;
isRead = json['isRead'];
readOn = json['readOn'];
isMyMessage = (json['senderUserID']).toString().toUpperCase()== myUserId;
}
}
class ReqOffer {
int? id;
int? requestID;
int? serviceProviderID;
int? offerStatus;
String? offerStatusText;
String? comment;
double? price;
RequestOfferStatusEnum? requestOfferStatusEnum;
ReqOffer({
this.id,
this.requestID,
this.serviceProviderID,
this.offerStatus,
this.offerStatusText,
this.comment,
this.price,
this.requestOfferStatusEnum,
});
ReqOffer.fromJson(Map<String, dynamic> json) {
log("the json: $json");
id = json['id'];
requestID = json['requestID'];
serviceProviderID = json['serviceProviderID'];
offerStatus = json['offerStatus'];
offerStatusText = json['offerStatusText'];
comment = json['comment'];
price = json['price'];
requestOfferStatusEnum = ((json['offerStatus']) as int).toRequestOfferStatusEnum();
}
}
class OfferRequestCommentModel {
int? index;
String? title;
bool? isSelected;
OfferRequestCommentModel({this.index, this.title, this.isSelected});
}

@ -0,0 +1,77 @@
import 'dart:developer';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
class ProviderOffersModel {
int? id;
int? customerID;
int? requestType;
int? requestStatus;
String? brand;
String? model;
int? year;
bool? isNew;
String? description;
double? price;
int? spOfferCount;
List<ServiceProvidersOffers>? serviceProviders;
ProviderOffersModel({
this.id,
this.customerID,
this.requestType,
this.requestStatus,
this.brand,
this.model,
this.year,
this.isNew,
this.description,
this.price,
this.spOfferCount,
this.serviceProviders,
});
ProviderOffersModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
customerID = json['customerID'];
requestType = json['requestType'];
requestStatus = json['requestStatus'];
brand = json['brand'];
model = json['model'];
year = json['year'];
isNew = json['isNew'];
description = json['description'];
price = json['price'];
spOfferCount = json['spOfferCount'];
if (json['serviceProviders'] != null) {
serviceProviders = <ServiceProvidersOffers>[];
json['serviceProviders'].forEach((v) {
serviceProviders!.add(ServiceProvidersOffers.fromJson(v));
});
}
}
}
class ServiceProvidersOffers {
String? providerUserId;
int? providerId;
String? name;
String? mobileNo;
String? email;
String? companyName;
int? offerCount;
List<ChatMessageModel>? chatMessages;
ServiceProvidersOffers({this.providerId, this.name, this.mobileNo, this.email, this.companyName, this.offerCount, this.chatMessages, this.providerUserId});
ServiceProvidersOffers.fromJson(Map<String, dynamic> json) {
providerId = json['providerID'];
providerUserId = json['providerUserId'] ?? "c680271c-b2a7-4ecf-7e95-08db545aec1b"; //TODO Remove this when parameter is added in backend
name = json['name'];
mobileNo = json['mobileNo'];
email = json['email'];
companyName = json['companyName'];
offerCount = json['offerCount'];
chatMessages = [];
}
}

@ -1,4 +1,5 @@
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/utils/enums.dart';
class RequestModel {
@ -15,6 +16,8 @@ class RequestModel {
int offerCount;
int id;
int customerId;
bool isChatted;
String customerUserID;
dynamic customer;
String brand;
String model;
@ -34,6 +37,7 @@ class RequestModel {
DateTime createdOn;
dynamic modifiedBy;
dynamic modifiedOn;
List<ChatMessageModel> chatMessages;
RequestModel({
required this.requestType,
@ -49,6 +53,8 @@ class RequestModel {
required this.offerCount,
required this.id,
required this.customerId,
required this.isChatted,
required this.customerUserID,
required this.customer,
required this.brand,
required this.model,
@ -68,6 +74,7 @@ class RequestModel {
required this.createdOn,
required this.modifiedBy,
required this.modifiedOn,
required this.chatMessages,
});
factory RequestModel.fromJson(Map<String, dynamic> json) {
@ -85,6 +92,8 @@ class RequestModel {
offerCount: json["offerCount"],
id: json["id"],
customerId: json["customerID"],
isChatted: json["isChatted"],
customerUserID: json["customerUserID"],
customer: json["customer"],
brand: json["brand"],
model: json["model"],
@ -104,40 +113,7 @@ class RequestModel {
createdOn: DateTime.parse(json["createdOn"]),
modifiedBy: json["modifiedBy"],
modifiedOn: json["modifiedOn"],
chatMessages: [],
);
}
Map<String, dynamic> toJson() => {
"requestType": requestType,
"requestTypeName": requestTypeName,
"requestStatusName": requestStatusName,
"requestStatus": requestStatus,
"cityName": cityName,
"vehicleTypeName": vehicleTypeName,
"countryName": countryName,
"customerName": customerName,
"serviceProviders": serviceProviders,
"offerCount": offerCount,
"id": id,
"customerID": customerId,
"customer": customer,
"brand": brand,
"model": model,
"year": year,
"isNew": isNew,
"description": description,
"requestImages": List<dynamic>.from(requestImages.map((x) => x)),
"cityID": cityId,
"city": city,
"price": price,
"paymentStatus": paymentStatus,
"vehicleTypeID": vehicleTypeId,
"countryID": countryId,
"requestProviderItem": List<dynamic>.from(requestProviderItem.map((x) => x)),
"isActive": isActive,
"createdBy": createdBy,
"createdOn": createdOn.toIso8601String(),
"modifiedBy": modifiedBy,
"modifiedOn": modifiedOn,
};
}

@ -1,14 +1,27 @@
import 'dart:developer';
import 'dart:io';
import 'package:http/io_client.dart';
import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependencies.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/general_models/generic_resp_model.dart';
import 'package:signalr_core/signalr_core.dart';
abstract class ChatRepo {
Future<HubConnection> getHubConnection();
Future<void> markMessageAsRead({required int messageId});
Future<List<ChatMessageModel>> getUsersChatMessages({required int providerId, required int customerId, required int requestOfferId, required int requestId, int pageIndex = 0, int pageSize = 0});
}
class ChatRepoImp implements ChatRepo {
ApiClient apiClient = injector.get<ApiClient>();
AppState appState = injector.get<AppState>();
@override
Future<HubConnection> getHubConnection() async {
final userId = AppState().getUser.data!.userInfo!.userId ?? "";
@ -23,10 +36,50 @@ class ChatRepoImp implements ChatRepo {
client: IOClient(HttpClient()
..badCertificateCallback = (x, y, z) => true),
logging: (level, message) {
print(message);
log(message);
},
))
.build();
return hub;
}
@override
Future<void> markMessageAsRead({required int messageId}) async {
var queryParameters = {
"id": messageId.toString(),
};
await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.messageIsReadUpdate,
queryParameters,
token: appState.getUser.data!.accessToken,
);
}
@override
Future<List<ChatMessageModel>> getUsersChatMessages(
{required int providerId, required int customerId, required int requestOfferId, required int requestId, int pageIndex = 0, int pageSize = 0}) async {
var queryParameters = {
"ReqOfferID": requestOfferId.toString(),
"RequestID": requestId.toString(),
"ProviderID": providerId.toString(),
"CustomerID": customerId.toString(),
"PageSize": pageSize.toString(),
"PageIndex": pageIndex.toString(),
};
GenericRespModel genericRespModel = await apiClient.getJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.getChatMessages,
queryParameters: queryParameters,
token: appState.getUser.data!.accessToken,
);
if (genericRespModel.messageStatus == 1 && genericRespModel.data != null) {
List<ChatMessageModel> chatMessages = List.generate(
genericRespModel.data.length,
(index) => ChatMessageModel.fromJson(genericRespModel.data[index]),
);
return chatMessages;
}
return [];
}
}

@ -2,16 +2,23 @@ import 'package:mc_common_app/api/api_client.dart';
import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/dependencies.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/models/requests_models/request_model.dart';
import 'package:mc_common_app/utils/enums.dart';
abstract class RequestRepo {
Future<GenericRespModel> createRequest(Map<String, dynamic> map);
Future<List<OffersModel>> getOffersByRequest({required int requestId, int serviceProviderId = 0});
Future<ProviderOffersModel> getOffersFromProvidersByRequest({required int requestId});
Future<List<RequestModel>> getRequests(Map<String, dynamic> postParams);
Future<GenericRespModel> updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId});
}
class RequestRepoImp implements RequestRepo {
@ -60,10 +67,44 @@ class RequestRepoImp implements RequestRepo {
);
List<OffersModel> offersList = List.generate(
genericRespModel.data.length,
(index) => OffersModel.fromJson(
genericRespModel.data[index],
),
(index) => OffersModel.fromJson(genericRespModel.data[index]),
);
return offersList;
}
@override
Future<ProviderOffersModel> getOffersFromProvidersByRequest({required int requestId}) async {
final customerId = appState.getUser.data!.userInfo!.customerId;
var queryParameters = {
"customerID": customerId.toString(),
"requestID": requestId.toString(),
};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.requestOffersSpsGet,
queryParameters,
token: appState.getUser.data!.accessToken,
);
ProviderOffersModel providerOffersModel = ProviderOffersModel();
if (genericRespModel.data != null && genericRespModel.data.length > 0) {
providerOffersModel = ProviderOffersModel.fromJson(genericRespModel.data.first);
}
return providerOffersModel;
}
@override
Future<GenericRespModel> updateOfferRequestStatus({required RequestOfferStatusEnum requestOfferStatusEnum, required int requestOfferId}) async {
var queryParameters = {
"requestOfferId": requestOfferId.toString(),
"offerStatus": requestOfferStatusEnum.getIdFromRequestOfferStatusEnum().toString(),
};
GenericRespModel genericRespModel = await apiClient.postJsonForObject(
(json) => GenericRespModel.fromJson(json),
ApiConsts.updateRequestOffer,
queryParameters,
token: appState.getUser.data!.accessToken,
);
return genericRespModel;
}
}

@ -250,14 +250,18 @@ class Utils {
}
}
static statusContainerChip({required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor}) {
static statusContainerChip(
{required String text, EdgeInsetsGeometry padding = const EdgeInsets.symmetric(vertical: 3, horizontal: 6), Color chipColor = MyColors.greenColor, Color textColor = MyColors.white}) {
return Container(
decoration: BoxDecoration(
color: chipColor,
borderRadius: const BorderRadius.all(Radius.circular(200)),
),
padding: padding,
child: text.toText(fontSize: 10, color: MyColors.white));
child: text.toText(
fontSize: 10,
color: textColor,
));
}
static InputDecoration txtField(String label) {

@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:mc_common_app/utils/enums.dart';
import '../utils/enums.dart';
class BaseVM extends ChangeNotifier {
ViewState _state = ViewState.idle;

@ -1,21 +1,30 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:developer';
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/extensions/string_extensions.dart';
import 'package:mc_common_app/main.dart';
import 'package:mc_common_app/models/chat_models/cat_message_model.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_model.dart';
import 'package:mc_common_app/repositories/chat_repo.dart';
import 'package:mc_common_app/repositories/request_repo.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:provider/provider.dart';
import 'package:signalr_core/signalr_core.dart';
class ChatVM extends ChangeNotifier {
final ChatRepo chatRepo;
final RequestRepo requestRepo;
ChatVM({required this.chatRepo});
ChatVM({required this.chatRepo, required this.requestRepo});
late HubConnection hubConnection;
List<ChatMessageModel> chatMessages = [];
HubConnection? hubConnection;
String chatMessageText = "";
@ -28,32 +37,107 @@ class ChatVM extends ChangeNotifier {
notifyListeners();
}
Future<void> onNewMessageReceived({required List<ChatMessageModel> messages}) async {
chatMessages.addAll(messages);
List<OfferRequestCommentModel> offerRejectModelList = [
OfferRequestCommentModel(
index: 0,
isSelected: false,
title: "I have changed my mind.",
),
OfferRequestCommentModel(
index: 1,
isSelected: false,
title: "Very High Price.",
),
OfferRequestCommentModel(
index: 2,
isSelected: false,
title: "Already Sold",
),
OfferRequestCommentModel(
index: 3,
isSelected: true,
title: "Other",
),
];
void updateSelectionInOfferRejectModelList(int index) {
for (var value in offerRejectModelList) {
value.isSelected = false;
}
offerRejectModelList[index].isSelected = true;
notifyListeners();
}
Future<void> buildHubConnection() async {
// if (hubConnection.state != HubConnectionState.Connected) {
try {
hubConnection = await chatRepo.getHubConnection();
await hubConnection.start();
hubConnection.on("ReceiveMessageRequestOffer", (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage);
String rejectOfferDescription = "";
String rejectOfferDescriptionError = "";
void updateRejectOfferDescription(String value) {
rejectOfferDescription = value;
if (value.isNotEmpty) {
rejectOfferDescriptionError = "";
}
}
//REJECTING OFFER
bool isRejectOfferButtonValidated() {
bool isValidated = true;
if (rejectOfferDescription.isEmpty) {
rejectOfferDescriptionError = GlobalConsts.descriptionError;
isValidated = false;
} else {
rejectOfferDescriptionError = "";
}
notifyListeners();
return isValidated;
}
Future<void> onNewMessageReceived({required List<ChatMessageModel> messages, required BuildContext context}) async {
if (AppState().currentAppType == AppType.customer) {
for (var msg in messages) {
log("printing senderUserID: ${msg.senderUserID}");
log("printing providerUserId: ${serviceProviderOffersList.first.providerUserId}");
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == msg.senderUserID);
if (providerIndex != -1) {
serviceProviderOffersList[providerIndex].chatMessages!.add(msg);
}
onNewMessageReceived(messages: chat);
logger.i("I received ping : ${arguments.toString()}");
});
} catch (e) {
logger.i("Error: ${e.toString()}");
}
} else {
for (var msg in messages) {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerUserID == msg.senderUserID);
log("here is it: ${msg.senderUserID}");
log("here is it: ${context.read<RequestsVM>().myFilteredRequests.first.customerUserID.toString()}");
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: msg, index: providerIndex);
}
}
}
notifyListeners();
// }
}
Future<void> buildHubConnection(BuildContext context) async {
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
try {
hubConnection = await chatRepo.getHubConnection();
await hubConnection!.start();
hubConnection!.on("ReceiveMessageRequestOffer", (List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) return;
List<ChatMessageModel> chat = [];
for (var message in arguments) {
final chatMessage = ChatMessageModel.fromJson(message as Map<String, dynamic>);
chat.add(chatMessage);
}
onNewMessageReceived(messages: chat, context: context);
logger.i("I received ping : ${arguments.toString()}");
});
} catch (e) {
logger.i("Error: ${e.toString()}");
}
notifyListeners();
}
}
Future<bool> onSendMessageForRequestOffer({
@ -62,22 +146,23 @@ class ChatVM extends ChangeNotifier {
required String message,
required int requestId,
required String offerPrice,
required BuildContext context,
}) async {
if (hubConnection.state != HubConnectionState.connected) {
await buildHubConnection();
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
await buildHubConnection(context);
}
if (hubConnection.state == HubConnectionState.connected) {
if (hubConnection!.state == HubConnectionState.connected) {
final providerId = AppState().getUser.data!.userInfo!.providerId;
hubConnection.invoke(
hubConnection!.invoke(
"SendMessageRequestOffer",
args: <Object>[
<String, dynamic>{
"ReceiverUserID": receiverId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"Message": message,
"ChatText": message,
"RequestID": requestId,
"RequestOfferID": 0,
"RequestOffer": <String, dynamic>{
"ReqOfferID": 0,
"ReqOffer": <String, dynamic>{
"RequestID": requestId,
"Price": double.parse(offerPrice),
"ServiceProviderID": providerId,
@ -101,23 +186,27 @@ class ChatVM extends ChangeNotifier {
required String message,
required int requestId,
required String offerPrice,
required BuildContext context,
}) async {
if (message.isEmpty) return false;
if (hubConnection.state != HubConnectionState.connected) {
await buildHubConnection();
if (hubConnection == null || hubConnection!.state != HubConnectionState.connected) {
await buildHubConnection(context);
}
if (hubConnection.state == HubConnectionState.connected) {
if (hubConnection!.state == HubConnectionState.connected) {
final userId = AppState().getUser.data!.userInfo!.userId.toString();
final name = AppState().getUser.data!.userInfo!.firstName.toString();
hubConnection.invoke(
hubConnection!.invoke(
"SendMessageRequestOffer",
args: <Object>[
<String, dynamic>{
"ReceiverUserID": receiverId,
"SenderUserID": userId,
"MessageType": chatMessageType.getIdFromChatMessageTypeEnum(),
"Message": message,
"ChatText": message,
"RequestID": requestId,
"ReqOfferID": 0,
}
],
).catchError((e) {
@ -127,15 +216,98 @@ class ChatVM extends ChangeNotifier {
});
ChatMessageModel chatMessageModel = ChatMessageModel(
messageType: chatMessageType.getIdFromChatMessageTypeEnum(),
message: message,
chatText: message,
isMyMessage: true,
requestID: requestId,
senderName: name,
senderUserID: userId,
);
onNewMessageReceived(messages: [chatMessageModel]);
if (AppState().currentAppType == AppType.customer) {
int providerIndex = serviceProviderOffersList.indexWhere((element) => element.providerUserId == receiverId);
if (providerIndex != -1) {
serviceProviderOffersList[providerIndex].chatMessages!.add(chatMessageModel);
}
} else {
int providerIndex = context.read<RequestsVM>().myFilteredRequests.indexWhere((element) => element.customerID == receiverId);
if (providerIndex != -1) {
context.read<RequestsVM>().addChatMessagesInRequestsModel(msg: chatMessageModel, index: providerIndex);
}
}
return true;
}
return false;
}
List<ServiceProvidersOffers> serviceProviderOffersList = [];
Future<void> getOffersFromProvidersByRequest({required int requestId, required BuildContext context}) async {
try {
Utils.showLoading(context);
ProviderOffersModel providerOffersModel = await requestRepo.getOffersFromProvidersByRequest(requestId: requestId);
Utils.hideLoading(context);
serviceProviderOffersList.clear();
serviceProviderOffersList = providerOffersModel.serviceProviders ?? [];
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
Future<void> getUsersChatMessagesForCustomer({
required BuildContext context,
required int providerId,
required int requestOfferId,
required int requestId,
required int providerOfferIndex,
}) async {
try {
int customerId = AppState().getUser.data!.userInfo!.customerId!;
Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
serviceProviderOffersList[providerOfferIndex].chatMessages = chatMessages;
Utils.hideLoading(context);
notifyListeners();
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
Future<void> getUsersChatMessagesForProvider({
required BuildContext context,
required int customerId,
required int requestOfferId,
required int requestId,
required int customerRequestIndex,
}) async {
try {
int providerId = AppState().getUser.data!.userInfo!.providerId!;
Utils.showLoading(context);
List<ChatMessageModel> chatMessages = await chatRepo.getUsersChatMessages(providerId: providerId, customerId: customerId, requestOfferId: requestOfferId, requestId: requestId);
context.read<RequestsVM>().overwriteChatMessagesInRequestsModel(messages: chatMessages, index: customerRequestIndex);
Utils.hideLoading(context);
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
}
}
Future<int> onActionOfferTapped({required BuildContext context, required RequestOfferStatusEnum requestOfferStatusEnum, required int reqOfferId}) async {
try {
Utils.showLoading(context);
GenericRespModel genericRespModel = await requestRepo.updateOfferRequestStatus(requestOfferStatusEnum: requestOfferStatusEnum, requestOfferId: reqOfferId);
Utils.hideLoading(context);
return genericRespModel.messageStatus == 1 ? reqOfferId : -1;
} catch (e) {
logger.i(e.toString());
Utils.showToast(e.toString());
Utils.hideLoading(context);
return -1;
}
return true;
}
}

@ -9,7 +9,7 @@ import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart';
import 'package:mc_common_app/models/chat_models/cat_message_model.dart';
import 'package:mc_common_app/models/chat_models/chat_message_model.dart';
import 'package:mc_common_app/models/general_models/enums_model.dart';
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
import 'package:mc_common_app/models/requests_models/offers_model.dart';
@ -87,6 +87,17 @@ class RequestsVM extends BaseVM {
notifyListeners();
}
addChatMessagesInRequestsModel({required ChatMessageModel msg, required int index}) {
myFilteredRequests[index].chatMessages.add(msg);
notifyListeners();
}
overwriteChatMessagesInRequestsModel({required List<ChatMessageModel> messages, required int index}) {
myFilteredRequests[index].chatMessages.clear();
myFilteredRequests[index].chatMessages = messages;
notifyListeners();
}
applyFilterOnRequestsVM({required RequestsTypeEnum requestsTypeEnum}) {
if (requestsTypeFilterOptions.isEmpty) return;
for (var value in requestsTypeFilterOptions) {
@ -230,7 +241,8 @@ class RequestsVM extends BaseVM {
}
//Request Management
String price = "", description = "";
String price = "",
description = "";
updatePrice(String v) {
price = v;
@ -243,7 +255,9 @@ class RequestsVM extends BaseVM {
Future<VehiclePostingImages> convertFileToRequestPostingImages({required File file}) async {
List<int> imageBytes = await file.readAsBytes();
String image = base64Encode(imageBytes);
String fileName = file.path.split('/').last;
String fileName = file.path
.split('/')
.last;
VehiclePostingImages vehiclePostingImages = VehiclePostingImages(
imageName: fileName,
imageStr: image,
@ -397,15 +411,20 @@ class RequestsVM extends BaseVM {
required int requestId,
required String offerPrice,
required RequestModel requestModel,
required int requestIndex,
required bool isFromChatScreen,
}) async {
if (isSendOfferValidated()) {
bool status = await context.read<ChatVM>().onSendMessageForRequestOffer(
receiverId: receiverId,
chatMessageType: ChatMessageTypeEnum.offer,
message: message,
requestId: requestId,
offerPrice: offerPrice,
);
final chatVM = context.read<ChatVM>();
bool status = await chatVM.onSendMessageForRequestOffer(
receiverId: receiverId,
chatMessageType: ChatMessageTypeEnum.offer,
message: message,
requestId: requestId,
offerPrice: offerPrice,
context: context,
);
if (status) {
final senderName = AppState().getUser.data!.userInfo!.firstName;
@ -414,19 +433,23 @@ class RequestsVM extends BaseVM {
Navigator.pop(context);
ChatMessageModel chatMessageModel = ChatMessageModel(
isMyMessage: true,
message: message,
chatText: message,
messageType: ChatMessageTypeEnum.freeText.getIdFromChatMessageTypeEnum(),
senderName: senderName,
senderUserID: senderId,
);
context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel]);
ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer,
requestModel: requestModel,
receiverId: requestModel.customerID,
senderId: senderId ?? "",
);
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
context.read<ChatVM>().onNewMessageReceived(messages: [chatMessageModel], context: context);
if (!isFromChatScreen) {
ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer,
requestId: requestModel.id,
receiverId: requestModel.customerID,
senderId: senderId ?? "",
requestIndex: requestIndex,
providerIndex: -1,
);
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
}
}
}
}

@ -434,20 +434,19 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
},
),
),
if (adDetailsModel.whatsAppNo != null) ...[
if (adDetailsModel.whatsAppNo != null && adDetailsModel.whatsAppNo!.isNotEmpty) ...[
8.width,
Container(
height: 55,
width: 55,
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
//TODO: It Will be replaced by a WhatsApp Icon
child: const Icon(Icons.message, color: MyColors.black),
height: 55,
width: 55,
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
child: MyAssets.whatsAppIcon.buildSvg()
).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}),
],
if (adDetailsModel.phoneNo != null) ...[
if (adDetailsModel.phoneNo != null && adDetailsModel.phoneNo!.isNotEmpty) ...[
8.width,
Container(
height: 55,
@ -490,7 +489,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget {
alignment: Alignment.center,
decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)),
//TODO: It Will be replaced by a WhatsApp Icon
child: const Icon(Icons.message, color: MyColors.black),
child: MyAssets.whatsAppIcon.buildSvg()
).onPress(() {
Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? "");
}),

@ -17,14 +17,9 @@ import 'package:provider/provider.dart';
class AppointmentDetailView extends StatelessWidget {
final AppointmentListModel appointmentListModel;
AppointmentDetailView({Key? key, required this.appointmentListModel})
: super(key: key);
const AppointmentDetailView({Key? key, required this.appointmentListModel}) : super(key: key);
Widget getBaseActionButtonWidget(
{required Color color,
required String text,
Color textColor = MyColors.white,
required Function() onPressed}) {
Widget getBaseActionButtonWidget({required Color color, required String text, Color textColor = MyColors.white, required Function() onPressed}) {
return Expanded(
child: ShowFillButton(
maxHeight: 55,
@ -37,26 +32,47 @@ class AppointmentDetailView extends StatelessWidget {
);
}
Widget buildBottomActionButton(
{required AppointmentStatusEnum appointmentStatusEnum,
required BuildContext context}) {
Widget getArrivedBottomActionButton({required BuildContext context, required AppointmentPaymentStatusEnum appointmentPaymentStatusEnum}) {
switch (appointmentPaymentStatusEnum) {
case AppointmentPaymentStatusEnum.defaultStatus:
case AppointmentPaymentStatusEnum.paid:
case AppointmentPaymentStatusEnum.payLater:
case AppointmentPaymentStatusEnum.payPartial:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "In Progress"),
],
),
);
case AppointmentPaymentStatusEnum.payNow:
return Expanded(
child: ShowFillButton(
maxHeight: 55,
title: "Pay Now",
onPressed: () {},
backgroundColor: MyColors.darkPrimaryColor,
txtColor: MyColors.white,
fontSize: 18,
),
);
}
}
Widget buildBottomActionButton({required AppointmentStatusEnum appointmentStatusEnum, required BuildContext context}) {
switch (appointmentStatusEnum) {
case AppointmentStatusEnum.booked:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.redColor,
onPressed: () => appointmentCancelConfirmationSheet(context),
text: "Cancel"),
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(
context: context,
appointmentId: appointmentListModel.id);
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: "Confirm"),
],
@ -67,36 +83,18 @@ class AppointmentDetailView extends StatelessWidget {
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.redColor,
onPressed: () => appointmentCancelConfirmationSheet(context),
text: "Cancel"),
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
],
),
);
case AppointmentStatusEnum.arrived:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: "In Progress"),
],
),
);
return getArrivedBottomActionButton(appointmentPaymentStatusEnum: appointmentListModel.appointmentPaymentStatusEnum ?? AppointmentPaymentStatusEnum.defaultStatus, context: context);
case AppointmentStatusEnum.cancelled:
return Align(
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.grey98Color.withOpacity(0.3),
textColor: MyColors.lightTextColor,
onPressed: () {},
text: "Cancelled"),
getBaseActionButtonWidget(color: MyColors.grey98Color.withOpacity(0.3), textColor: MyColors.lightTextColor, onPressed: () {}, text: "Cancelled"),
],
),
);
@ -107,17 +105,12 @@ class AppointmentDetailView extends StatelessWidget {
alignment: Alignment.bottomCenter,
child: Row(
children: [
getBaseActionButtonWidget(
color: MyColors.redColor,
onPressed: () => appointmentCancelConfirmationSheet(context),
text: "Cancel"),
getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"),
12.width,
getBaseActionButtonWidget(
color: MyColors.greenColor,
onPressed: () {
context.read<AppointmentsVM>().onConfirmAppointmentPressed(
context: context,
appointmentId: appointmentListModel.id);
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
text: "Confirm"),
],
@ -132,10 +125,8 @@ class AppointmentDetailView extends StatelessWidget {
final appointmentsVm = context.read<AppointmentsVM>();
return actionConfirmationBottomSheet(
context: context,
title: "Do you want to cancel this appointment?"
.toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle:
"Your appointment will be cancelled and you cannot undo this action.",
title: "Do you want to cancel this appointment?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: "Your appointment will be cancelled and you cannot undo this action.",
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
@ -143,8 +134,7 @@ class AppointmentDetailView extends StatelessWidget {
fontSize: 15,
onPressed: () {
Navigator.pop(context);
appointmentsVm.onCancelAppointmentPressed(
context: context, appointmentListModel: appointmentListModel);
appointmentsVm.onCancelAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
},
),
),
@ -182,8 +172,7 @@ class AppointmentDetailView extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
appointmentListModel.providerName!
.toText(fontSize: 16, isBold: true),
appointmentListModel.providerName!.toText(fontSize: 16, isBold: true),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
@ -193,23 +182,14 @@ class AppointmentDetailView extends StatelessWidget {
fit: BoxFit.fill,
),
5.width,
"${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}"
.toText(
fontSize: 12,
isBold: true,
color: MyColors.lightTextColor),
"${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}".toText(fontSize: 12, isBold: true, color: MyColors.lightTextColor),
],
),
13.height,
if (appointmentListModel.appointmentServicesList != null &&
appointmentListModel
.appointmentServicesList!.isNotEmpty) ...[
if (appointmentListModel.appointmentServicesList != null && appointmentListModel.appointmentServicesList!.isNotEmpty) ...[
Column(
children: List.generate(
appointmentListModel.appointmentServicesList!.length,
(index) {
ServiceModel service =
appointmentListModel.appointmentServicesList![index];
children: List.generate(appointmentListModel.appointmentServicesList!.length, (index) {
ServiceModel service = appointmentListModel.appointmentServicesList![index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -221,18 +201,14 @@ class AppointmentDetailView extends StatelessWidget {
// fit: BoxFit.fill,
// ),
// 10.width,
"${index + 1}. ${service.providerServiceDescription}"
.toText(fontSize: 14, isBold: true),
"${index + 1}. ${service.providerServiceDescription}".toText(fontSize: 14, isBold: true),
],
),
if (service.serviceItems != null &&
service.serviceItems!.isNotEmpty) ...[
if (service.serviceItems != null && service.serviceItems!.isNotEmpty) ...[
Column(
children: List.generate(
service.serviceItems!.length,
(index) =>
"${service.serviceItems![index].name}"
.toText(
(index) => "${service.serviceItems![index].name}".toText(
textAlign: TextAlign.start,
fontSize: 12,
color: MyColors.lightTextColor,
@ -244,22 +220,15 @@ class AppointmentDetailView extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
((service.currentTotalServicePrice).toString())
.toText(fontSize: 25, isBold: true),
((service.currentTotalServicePrice).toString()).toText(fontSize: 25, isBold: true),
2.width,
"SAR"
.toText(
color: MyColors.lightTextColor,
fontSize: 16,
isBold: true)
.paddingOnly(bottom: 5),
"SAR".toText(color: MyColors.lightTextColor, fontSize: 16, isBold: true).paddingOnly(bottom: 5),
Icon(
Icons.arrow_drop_down,
size: 30,
)
],
).onPress(() => appointmentsVM.priceBreakDownClicked(
context, service)),
).onPress(() => appointmentsVM.priceBreakDownClicked(context, service)),
],
);
}),
@ -271,25 +240,16 @@ class AppointmentDetailView extends StatelessWidget {
CardButtonWithIcon(
title: "Reschedule Appointment",
onCardTapped: () {
context
.read<AppointmentsVM>()
.onRescheduleAppointmentPressed(
context: context,
appointmentListModel: appointmentListModel);
context.read<AppointmentsVM>().onRescheduleAppointmentPressed(context: context, appointmentListModel: appointmentListModel);
},
icon: MyAssets.scheduleAppointmentIcon.buildSvg(),
),
if (appointmentListModel.appointmentStatusEnum ==
AppointmentStatusEnum.booked) ...[
if (appointmentListModel.appointmentStatusEnum == AppointmentStatusEnum.booked) ...[
10.width,
CardButtonWithIcon(
title: "Pay for Appointment",
onCardTapped: () {
context
.read<AppointmentsVM>()
.onConfirmAppointmentPressed(
context: context,
appointmentId: appointmentListModel.id);
context.read<AppointmentsVM>().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id);
},
icon: MyAssets.creditCardIcon.buildSvg(),
),
@ -299,10 +259,7 @@ class AppointmentDetailView extends StatelessWidget {
15.height,
],
).toWhiteContainer(width: double.infinity, allPading: 12),
buildBottomActionButton(
appointmentStatusEnum:
appointmentListModel.appointmentStatusEnum!,
context: context),
buildBottomActionButton(appointmentStatusEnum: appointmentListModel.appointmentStatusEnum!, context: context),
],
),
),

@ -1,14 +1,21 @@
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/models/chat_models/cat_message_model.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/enums.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/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';
@ -18,95 +25,226 @@ class ChatView extends StatelessWidget {
const ChatView({super.key, required this.chatViewArguments});
Future buildSendOfferBottomSheet(BuildContext context) {
RequestModel requestDetail = chatViewArguments.requestModel!;
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) {
return InfoBottomSheet(
title: "Make an offer".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: "Enter amount",
onChanged: (v) => requestsVM.updateOfferPrice(v),
),
12.height,
TxtField(
maxLines: 5,
value: requestsVM.offerDescription,
errorValue: requestsVM.offerDescriptionError,
keyboardType: TextInputType.text,
hint: "Description",
onChanged: (v) => requestsVM.updateOfferDescription(v),
),
],
),
25.height,
ShowFillButton(
title: "Submit",
onPressed: () {
requestsVM.onSendOfferPressed(
context: context,
receiverId: requestDetail.customerID,
message: requestsVM.offerDescription,
requestId: requestDetail.id,
offerPrice: requestsVM.offerPrice,
requestModel: requestDetail,
requestIndex: chatViewArguments.requestIndex,
isFromChatScreen: true,
);
},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const CustomAppBar(title: "Chat"),
body: Consumer<ChatVM>(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return Column(
children: [
Expanded(
child: ListView.separated(
itemCount: chatVM.chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatVM.chatMessages[index];
return ChatMessageCustomWidget(
isSent: chatMessageModel.isMyMessage ?? false,
profileUrl: MyAssets.bnCar,
messageText: "${chatMessageModel.message}",
messageTypeEnum: chatMessageModel.messageTypeEnum ?? ChatMessageTypeEnum.freeText,
requestOfferStatusEnum:
(chatMessageModel.requestOffer != null ? chatMessageModel.requestOffer!.requestOfferStatusEnum : RequestOfferStatusEnum.offer) ?? RequestOfferStatusEnum.offer,
senderName: "${chatMessageModel.senderName}",
);
}).horPaddingMain(),
),
10.width,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
flex: 7,
child: TxtField(
value: chatVM.chatMessageText,
hint: "Type your message here..",
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
body: Consumer2<ChatVM, RequestsVM>(builder: (BuildContext context, ChatVM chatVM, RequestsVM requestVM, Widget? child) {
final chatMessages = AppState().currentAppType == AppType.customer
? chatVM.serviceProviderOffersList[chatViewArguments.providerIndex].chatMessages
: requestVM.myFilteredRequests[chatViewArguments.requestIndex].chatMessages;
return chatMessages == null
? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor))
: Column(
children: [
Expanded(
child: ListView.separated(
itemCount: chatMessages.length,
separatorBuilder: (BuildContext context, int index) => 20.height,
itemBuilder: (BuildContext context, int index) {
ChatMessageModel chatMessageModel = chatMessages[index];
return ChatMessageCustomWidget(chatMessageModel: chatMessageModel);
},
).horPaddingMain(),
),
),
Expanded(
flex: 1,
child: const Icon(
Icons.send_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() async {
final status = await chatVM.onTextMessageSend(
receiverId: chatViewArguments.receiverId,
message: chatVM.chatMessageText,
requestId: chatViewArguments.chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArguments.requestModel!.id : 0,
offerPrice: "0.0",
chatMessageType: ChatMessageTypeEnum.freeText,
);
10.height,
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (AppState().currentAppType == AppType.provider) ...[
Expanded(
flex: 1,
child: const Icon(
Icons.local_offer_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() async {
buildSendOfferBottomSheet(context);
},
),
),
],
Expanded(
flex: 8,
child: TxtField(
value: chatVM.chatMessageText,
hint: "Type your message here..",
keyboardType: TextInputType.text,
isNeedBorder: false,
onChanged: (v) => chatVM.updateChatMessageText(v),
),
),
Expanded(
flex: 1,
child: const Icon(
Icons.send_rounded,
color: MyColors.darkPrimaryColor,
size: 30,
).onPress(
() async {
log("chatViewArguments.receiverId:${chatViewArguments.receiverId}");
final status = await chatVM.onTextMessageSend(
context: context,
receiverId: chatViewArguments.receiverId,
message: chatVM.chatMessageText,
requestId: chatViewArguments.chatTypeEnum == ChatTypeEnum.requestOffer ? chatViewArguments.requestId ?? 0 : 0,
offerPrice: "0.0",
chatMessageType: ChatMessageTypeEnum.freeText,
);
if (status) {
chatVM.clearChatMessageText();
}
},
),
)
],
).toContainer(isShadowEnabled: true),
],
);
if (status) {
chatVM.clearChatMessageText();
}
},
),
)
],
).toContainer(isShadowEnabled: true),
],
);
}),
// body:
);
}
}
class ChatMessageCustomWidget extends StatelessWidget {
final String profileUrl;
final String senderName;
final String messageText;
final ChatMessageTypeEnum messageTypeEnum;
final RequestOfferStatusEnum requestOfferStatusEnum;
final bool isSent;
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() {
return showModalBottomSheet(
context: context,
isScrollControlled: true,
enableDrag: true,
builder: (BuildContext context) {
return Consumer(builder: (BuildContext context, ChatVM chatVM, Widget? child) {
return InfoBottomSheet(
title: "Make an offer".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,
);
},
),
12.height,
TxtField(
maxLines: 5,
value: chatVM.rejectOfferDescription,
errorValue: chatVM.rejectOfferDescriptionError,
keyboardType: TextInputType.text,
hint: "Description",
onChanged: (v) => chatVM.updateRejectOfferDescription(v),
),
],
),
25.height,
ShowFillButton(
title: "Submit",
onPressed: () {},
maxWidth: double.infinity,
),
19.height,
],
),
));
});
},
);
}
const ChatMessageCustomWidget({
super.key,
required this.profileUrl,
required this.senderName,
required this.messageText,
required this.messageTypeEnum,
this.requestOfferStatusEnum = RequestOfferStatusEnum.offer,
required this.isSent,
});
Widget buildOfferDetailsInChatMessage({required ChatMessageModel chatMessageModel, required BuildContext context}) {
final requestOfferStatusEnum = chatMessageModel.reqOffer!.requestOfferStatusEnum ?? RequestOfferStatusEnum.offer;
Widget buildOfferDetailsInChatMessage({required RequestOfferStatusEnum requestOfferStatusEnum}) {
switch (requestOfferStatusEnum) {
case RequestOfferStatusEnum.offer:
return Column(
@ -115,48 +253,66 @@ class ChatMessageCustomWidget extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
"40000".toText(fontSize: 19, isBold: true),
2.width,
"${chatMessageModel.reqOffer!.price}".toText(fontSize: 19, isBold: true, color: MyColors.white),
5.width,
"SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true),
],
),
10.height,
Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: "Accept",
fontSize: 9,
borderColor: MyColors.greenColor,
isFilled: false,
onPressed: () {},
backgroundColor: MyColors.white,
txtColor: MyColors.greenColor,
),
),
20.width,
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: "Reject",
borderColor: MyColors.redColor,
isFilled: false,
onPressed: () {},
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
if (widget.chatMessageModel.isMyMessage == false) ...[
10.height,
Row(
children: [
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: "Accept",
fontSize: 9,
borderColor: MyColors.greenColor,
isFilled: false,
onPressed: () async {
int status = await context.read<ChatVM>().onActionOfferTapped(
context: context,
requestOfferStatusEnum: RequestOfferStatusEnum.accepted,
reqOfferId: chatMessageModel.reqOfferID ?? -1,
);
if (status != -1) {
log("accepted: $status");
if (chatMessageModel.reqOfferID == status) {
chatMessageModel.reqOffer!.requestOfferStatusEnum = RequestOfferStatusEnum.accepted;
setState(() {});
}
}
},
backgroundColor: MyColors.white,
txtColor: MyColors.greenColor,
),
),
)
],
),
20.width,
Expanded(
child: ShowFillButton(
maxHeight: 27,
title: "Reject",
borderColor: MyColors.redColor,
isFilled: false,
backgroundColor: MyColors.white,
txtColor: MyColors.redColor,
fontSize: 9,
onPressed: () async {
buildRejectOfferBottomSheet();
},
),
)
],
),
],
],
);
case RequestOfferStatusEnum.negotiate:
return Column(
children: [
Center(
child: "You asked for the new offer.".toText(
child: "New Offer Required.".toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
@ -168,7 +324,7 @@ class ChatMessageCustomWidget extends StatelessWidget {
return Column(
children: [
Center(
child: "You have accepted the offer.".toText(
child: "Offer has been accepted.".toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
@ -180,7 +336,7 @@ class ChatMessageCustomWidget extends StatelessWidget {
return Column(
children: [
Center(
child: "You have rejected the offer.".toText(
child: "Offer has been rejected.".toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
@ -192,7 +348,7 @@ class ChatMessageCustomWidget extends StatelessWidget {
return Column(
children: [
Center(
child: "You have cancelled the offer.".toText(
child: "Offer has been cancelled.".toText(
color: MyColors.adPendingStatusColor,
fontSize: 12,
isItalic: true,
@ -206,14 +362,15 @@ class ChatMessageCustomWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: isSent ? TextDirection.rtl : TextDirection.ltr,
textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
// textDirection: (widget.chatMessageModel.isMyMessage ?? false) ? TextDirection.rtl : TextDirection.ltr,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: Image.asset(
profileUrl,
MyAssets.bnCar,
width: 34,
height: 34,
fit: BoxFit.fill,
@ -228,7 +385,7 @@ class ChatMessageCustomWidget extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(isSent ? "You" : senderName).toText(fontSize: 16, isBold: true),
((widget.chatMessageModel.isMyMessage ?? false) ? "You" : widget.chatMessageModel.senderName ?? "").toText(fontSize: 16, isBold: true),
],
),
5.height,
@ -238,23 +395,27 @@ class ChatMessageCustomWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: messageText.toText(
color: isSent ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
// isBold: true,
child: Directionality(
textDirection: TextDirection.ltr,
child: (widget.chatMessageModel.chatText ?? "").toText(
color: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.white : MyColors.lightTextColor,
fontSize: 12,
// isBold: true,
),
),
),
],
),
if (messageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(requestOfferStatusEnum: requestOfferStatusEnum),
if (widget.chatMessageModel.chatMessageTypeEnum == ChatMessageTypeEnum.offer) ...[
buildOfferDetailsInChatMessage(chatMessageModel: widget.chatMessageModel, context: context),
],
],
).toContainer(
isShadowEnabled: !isSent,
backgroundColor: isSent ? MyColors.darkIconColor : MyColors.white,
isShadowEnabled: !(widget.chatMessageModel.isMyMessage ?? false),
backgroundColor: (widget.chatMessageModel.isMyMessage ?? false) ? MyColors.darkIconColor : MyColors.white,
borderRadius: 0,
margin: EdgeInsets.fromLTRB(isSent ? 25 : 0, 0, !isSent ? 25 : 0, 0),
margin: EdgeInsets.fromLTRB((widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0, !(widget.chatMessageModel.isMyMessage ?? false) ? 25 : 0, 0),
),
],
),

@ -5,12 +5,15 @@ 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/models/requests_models/offers_model.dart';
import 'package:mc_common_app/models/requests_models/provider_offers_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/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/view_models/chat_view_model.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';
class OfferListPage extends StatelessWidget {
final OfferListPageArguments offerListPageArguments;
@ -19,76 +22,86 @@ class OfferListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List<OffersModel> offersList = offerListPageArguments.offersList;
final List<ServiceProvidersOffers> serviceProviderOffers = offerListPageArguments.serviceProviderOffers;
return Scaffold(
appBar: const CustomAppBar(title: "Offers"),
body: offersList.isEmpty
body: serviceProviderOffers.isEmpty
? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor))
: ListView.separated(
itemCount: offersList.length,
itemCount: serviceProviderOffers.length,
padding: const EdgeInsets.all(16),
itemBuilder: (context, index) {
OffersModel offersModel = offersList[index];
return Stack(
alignment: Alignment.bottomRight,
ServiceProvidersOffers offersModel = serviceProviderOffers[index];
return Column(
children: [
Column(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
(offersModel.serviceProvider!.companyName ?? "").toText(fontSize: 16, isBold: true),
//Un read offers count
// Center(
// child: "2".toText(
// color: Colors.white,
// isBold: true,
// fontSize: 10,
// ),
// ).toContainer(
// backgroundColor: MyColors.redColor,
// borderRadius: 100,
// paddingAll: 1,
// width: 20,
// height: 20,
// ),
],
(offersModel.name ?? "").toText(fontSize: 16, isBold: true),
Center(
child: "${offersModel.offerCount}".toText(
color: Colors.white,
isBold: true,
fontSize: 10,
),
).toContainer(
backgroundColor: MyColors.redColor,
borderRadius: 100,
paddingAll: 1,
width: 20,
height: 20,
),
8.height,
],
),
8.height,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: "${offersModel.comment} and This is dummy text edition.".toText(
color: MyColors.lightTextColor,
fontSize: 12,
),
"${offersModel.companyName}".toText(
color: MyColors.lightTextColor,
fontSize: 12,
),
12.width,
"9 Hours Ago".toText(
" | 1 hour ago".toText(
color: MyColors.lightTextColor,
)
fontSize: 12,
),
],
),
const Icon(
Icons.arrow_forward,
color: MyColors.darkIconColor,
size: 18,
),
],
),
const Icon(
Icons.arrow_forward,
color: MyColors.darkIconColor,
size: 18,
),
],
).onPress(() {
log("here: ${offersModel.serviceProvider!.providerGUID}");
).onPress(() async {
ChatViewArguments chatViewArguments = ChatViewArguments(
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.serviceProvider!.providerGUID}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestModel: offerListPageArguments.requestModel);
navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments);
chatTypeEnum: ChatTypeEnum.requestOffer,
receiverId: "${offersModel.providerUserId}",
senderId: AppState().getUser.data!.userInfo!.userId.toString(),
requestId: offerListPageArguments.requestId,
providerIndex: index,
requestIndex: -1, // This will be only send in case of provider
);
final chatVM = context.read<ChatVM>();
await chatVM
.getUsersChatMessagesForCustomer(
context: context,
providerId: offersModel.providerId ?? 0,
requestOfferId: 0,
requestId: offerListPageArguments.requestId ?? 0,
providerOfferIndex: index,
)
.whenComplete(
() => navigateWithName(context, AppRoutes.chatView, arguments: chatViewArguments),
);
}).toContainer(isShadowEnabled: true);
},
separatorBuilder: (context, index) => 16.height,

@ -1,12 +1,13 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:developer';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/view_models/requests_view_model.dart';
import 'package:mc_common_app/view_models/chat_view_model.dart';
import 'package:flutter/material.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/models/requests_models/offers_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/navigator.dart';
@ -17,8 +18,9 @@ import 'package:provider/provider.dart';
class RequestItem extends StatelessWidget {
final RequestModel request;
final AppType appType;
final int requestIndex;
const RequestItem({super.key, required this.request, required this.appType});
const RequestItem({super.key, required this.request, required this.appType, required this.requestIndex});
@override
Widget build(BuildContext context) {
@ -80,19 +82,19 @@ class RequestItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
request.price.toString().toText(
fontSize: 20,
color: Colors.black,
isBold: true,
),
fontSize: 20,
color: Colors.black,
isBold: true,
),
2.width,
"SAR"
.toText(
color: MyColors.lightTextColor,
fontSize: 10,
)
color: MyColors.lightTextColor,
fontSize: 10,
)
.paddingOnly(
bottom: 3,
),
bottom: 3,
),
],
),
const Icon(
@ -105,10 +107,12 @@ class RequestItem extends StatelessWidget {
],
).toContainer(isShadowEnabled: true).onPress(() async {
if (appType == AppType.provider) {
navigateWithName(context, AppRoutes.requestsDetailPage, arguments: request);
RequestDetailPageArguments requestDetailPageArguments = RequestDetailPageArguments(requestIndex: requestIndex, requestModel: request);
navigateWithName(context, AppRoutes.requestsDetailPage, arguments: requestDetailPageArguments);
} else {
List<OffersModel> offers = await context.read<RequestsVM>().getOffersByRequest(requestId: request.id, context: context);
OfferListPageArguments offerListPageArguments = OfferListPageArguments(offersList: offers, requestModel: request);
ChatVM chatVM = context.read<ChatVM>();
await chatVM.getOffersFromProvidersByRequest(requestId: request.id, context: context);
OfferListPageArguments offerListPageArguments = OfferListPageArguments(serviceProviderOffers: chatVM.serviceProviderOffersList, requestId: request.id);
navigateWithName(context, AppRoutes.offersListPage, arguments: offerListPageArguments);
}
});

@ -34,26 +34,9 @@ class SettingOptionsInviteFriends extends StatelessWidget {
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.person, size: 20), titleText: "Account", onTap: () {}),
],
).toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, margin: const EdgeInsets.fromLTRB(24, 24, 24, 0), borderRadius: 0),
CustomSettingOptionsTile(leadingWidget: const Icon(Icons.translate, size: 20), titleText: "Language", isForLanguage: true, onTap: () {})
.toContainer(width: double.infinity, isShadowEnabled: true, paddingAll: 10, marginAll: 21, borderRadius: 0),
],
),
),
Row(
children: [
Expanded(
child: ShowFillButton(
borderColor: MyColors.redColor,
txtColor: MyColors.redColor,
isFilled: false,
fontSize: 16,
maxHeight: 55,
title: "Log Out",
onPressed: () {},
),
),
],
).paddingAll(21),
],
),
);

@ -33,7 +33,7 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
ClassType type = ClassType.EMAIL;
//TODO: ONLY FOR DEVELOPMENT PURPOSE
String phoneNum = "966504278212", password = "Fa@123";
String phoneNum = "966504278213", password = "Fa@1234";
String email = "";
String countryCode = "";
Country? _country;
@ -44,8 +44,8 @@ class _LoginWithPasswordState extends State<LoginWithPassword> {
void initState() {
super.initState();
if (AppState().currentAppType == AppType.provider) {
phoneNum = "966580816976";
password = "123@Shf";
phoneNum = "966598646795";
password = "Zahoor@123";
}
scheduleMicrotask(() {
userVM = Provider.of(context, listen: false);

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
class CheckBoxWithTitleDescription extends StatelessWidget {
final bool isSelected;
@ -38,3 +40,49 @@ class CheckBoxWithTitleDescription extends StatelessWidget {
);
}
}
class CircleCheckBoxWithTitle extends StatelessWidget {
final bool isChecked;
final String title;
final Function() onSelected;
final Color selectedColor;
const CircleCheckBoxWithTitle({super.key, required this.title, required this.isChecked, required this.onSelected, required this.selectedColor});
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 20,
width: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isChecked ? selectedColor : Colors.transparent,
border: Border.all(
color: isChecked ? Colors.transparent : MyColors.darkTextColor,
),
),
child: const Center(child: Icon(Icons.check_rounded, size: 16, color: MyColors.white)),
),
5.width,
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
title.toText(fontSize: 14, isBold: false),
],
),
),
],
).onPress((){
onSelected();
}),
);
}
}

@ -93,10 +93,10 @@ packages:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
version: "1.17.1"
country_code_picker:
dependency: "direct main"
description:
@ -513,10 +513,10 @@ packages:
dependency: "direct main"
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6
url: "https://pub.dev"
source: hosted
version: "0.18.1"
version: "0.18.0"
js:
dependency: transitive
description:
@ -601,26 +601,26 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.15"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.2.0"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.9.1"
mime:
dependency: transitive
description:
@ -910,10 +910,10 @@ packages:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.9.1"
sqflite:
dependency: transitive
description:
@ -942,18 +942,18 @@ packages:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "1.11.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.1"
stream_transform:
dependency: transitive
description:
@ -998,10 +998,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.5.1"
tuple:
dependency: transitive
description:
@ -1106,14 +1106,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
url: "https://pub.dev"
source: hosted
version: "0.3.0"
web_socket_channel:
dependency: transitive
description:
@ -1147,5 +1139,5 @@ packages:
source: hosted
version: "6.3.0"
sdks:
dart: ">=3.2.0-194.0.dev <4.0.0"
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.0"

Loading…
Cancel
Save