Initial commit
This commit is contained in:
59
lib/controllers/bank_details_controller.dart
Normal file
59
lib/controllers/bank_details_controller.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class BankDetailsController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
Rx<TextEditingController> bankNameController = TextEditingController().obs;
|
||||
Rx<TextEditingController> branchNameController = TextEditingController().obs;
|
||||
Rx<TextEditingController> holderNameController = TextEditingController().obs;
|
||||
Rx<TextEditingController> accountNoController = TextEditingController().obs;
|
||||
Rx<TextEditingController> otherInfoController = TextEditingController().obs;
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getCurrentUser();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> saveBank() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
userModel.value.userBankDetails ??= UserBankDetails();
|
||||
userModel.value.userBankDetails!.accountNumber = accountNoController.value.text;
|
||||
userModel.value.userBankDetails!.bankName = bankNameController.value.text;
|
||||
userModel.value.userBankDetails!.branchName = branchNameController.value.text;
|
||||
userModel.value.userBankDetails!.holderName = holderNameController.value.text;
|
||||
userModel.value.userBankDetails!.otherDetails = otherInfoController.value.text;
|
||||
|
||||
await FireStoreUtils.updateUser(userModel.value).then(
|
||||
(value) {
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getCurrentUser() async {
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.userBankDetails != null) {
|
||||
bankNameController.value.text = userModel.value.userBankDetails!.bankName.toString();
|
||||
branchNameController.value.text = userModel.value.userBankDetails!.branchName.toString();
|
||||
holderNameController.value.text = userModel.value.userBankDetails!.holderName.toString();
|
||||
accountNoController.value.text = userModel.value.userBankDetails!.accountNumber.toString();
|
||||
otherInfoController.value.text = userModel.value.userBankDetails!.otherDetails.toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
119
lib/controllers/cab_dashboard_controller.dart
Normal file
119
lib/controllers/cab_dashboard_controller.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:location/location.dart';
|
||||
|
||||
import '../themes/theme_controller.dart';
|
||||
|
||||
class CabDashBoardController extends GetxController {
|
||||
RxInt drawerIndex = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
|
||||
getUser();
|
||||
getTheme();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
DateTime? currentBackPressTime;
|
||||
RxBool canPopNow = false.obs;
|
||||
|
||||
Future<void> getUser() async {
|
||||
await updateCurrentLocation();
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
Constant.userModel = UserModel.fromJson(event.data()!);
|
||||
if (userModel.value.sectionId != null && userModel.value.sectionId!.isNotEmpty) {
|
||||
await FireStoreUtils.getSectionBySectionId(userModel.value.sectionId!).then((sectionValue) {
|
||||
if (sectionValue != null) {
|
||||
Constant.sectionModel = sectionValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
RxString isDarkMode = "Light".obs;
|
||||
RxBool isDarkModeSwitch = false.obs;
|
||||
|
||||
void getTheme() {
|
||||
bool isDark = Preferences.getBoolean(Preferences.themKey);
|
||||
isDarkMode.value = isDark ? "Dark" : "Light";
|
||||
isDarkModeSwitch.value = isDark;
|
||||
}
|
||||
|
||||
void toggleDarkMode(bool value) {
|
||||
isDarkModeSwitch.value = value;
|
||||
isDarkMode.value = value ? "Dark" : "Light";
|
||||
Preferences.setBoolean(Preferences.themKey, value);
|
||||
// Update ThemeController for instant app theme change
|
||||
if (Get.isRegistered<ThemeController>()) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
themeController.isDark.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
Location location = Location();
|
||||
|
||||
Future<void> updateCurrentLocation() async {
|
||||
try {
|
||||
PermissionStatus permissionStatus = await location.hasPermission();
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location = UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
location.requestPermission().then((permissionStatus) {
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location =
|
||||
UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
892
lib/controllers/cab_home_controller.dart
Normal file
892
lib/controllers/cab_home_controller.dart
Normal file
@@ -0,0 +1,892 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/app/wallet_screen/payment_list_screen.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/cab_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/wallet_transaction_model.dart';
|
||||
import 'package:driver/services/audio_player_service.dart';
|
||||
import 'package:driver/themes/app_them_data.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart' as flutterMap;
|
||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:latlong2/latlong.dart' as location;
|
||||
|
||||
class CabHomeController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
flutterMap.MapController osmMapController = flutterMap.MapController();
|
||||
RxList<flutterMap.Marker> osmMarkers = <flutterMap.Marker>[].obs;
|
||||
|
||||
StreamSubscription<DocumentSnapshot<Map<String, dynamic>>>? _driverSub;
|
||||
StreamSubscription<DocumentSnapshot<Map<String, dynamic>>>? _orderDocSub;
|
||||
StreamSubscription<QuerySnapshot<Map<String, dynamic>>>? _orderQuerySub;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getData();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_driverSub?.cancel();
|
||||
_orderDocSub?.cancel();
|
||||
_orderQuerySub?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> getData() async {
|
||||
_subscribeDriver();
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Rx<CabOrderModel> currentOrder = CabOrderModel().obs;
|
||||
Rx<UserModel> driverModel = UserModel().obs;
|
||||
Rx<UserModel> ownerModel = UserModel().obs;
|
||||
|
||||
Future<void> acceptOrder() async {
|
||||
try {
|
||||
await AudioPlayerService.playSound(false);
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
|
||||
driverModel.value.inProgressOrderID ??= [];
|
||||
driverModel.value.inProgressOrderID!.add(currentOrder.value.id);
|
||||
driverModel.value.orderCabRequestData = null;
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
|
||||
currentOrder.value.status = Constant.driverAccepted;
|
||||
currentOrder.value.driverId = driverModel.value.id;
|
||||
currentOrder.value.driver = driverModel.value;
|
||||
await FireStoreUtils.setCabOrder(currentOrder.value);
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
await SendNotification.sendFcmMessage(Constant.driverAcceptedNotification, currentOrder.value.author?.fcmToken ?? "", {});
|
||||
} catch (e, s) {
|
||||
ShowToastDialog.closeLoader();
|
||||
debugPrint("Error in acceptOrder: $e");
|
||||
debugPrintStack(stackTrace: s);
|
||||
ShowToastDialog.showToast("Something went wrong. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> rejectOrder() async {
|
||||
try {
|
||||
await AudioPlayerService.playSound(false);
|
||||
|
||||
// 1️⃣ Immediately update local state (UI)
|
||||
currentOrder.value.status = Constant.driverRejected;
|
||||
|
||||
currentOrder.value.rejectedByDrivers ??= [];
|
||||
if (!currentOrder.value.rejectedByDrivers!.contains(driverModel.value.id)) {
|
||||
currentOrder.value.rejectedByDrivers!.add(driverModel.value.id);
|
||||
}
|
||||
|
||||
// Immediately update UI so bottom sheet hides right away
|
||||
currentOrder.refresh();
|
||||
|
||||
// 2️⃣ Update driver local state right away
|
||||
driverModel.value.orderCabRequestData = null;
|
||||
driverModel.value.inProgressOrderID = [];
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
|
||||
// 3️⃣ Close bottom sheet immediately (don’t wait for Firestore)
|
||||
if (Get.isBottomSheetOpen ?? false) {
|
||||
Get.back();
|
||||
} else if (Constant.singleOrderReceive == false) {
|
||||
Get.back();
|
||||
}
|
||||
|
||||
// 4️⃣ Clear map immediately
|
||||
await clearMap();
|
||||
|
||||
// 5️⃣ Update Firestore in background (no UI wait)
|
||||
unawaited(FireStoreUtils.setCabOrder(currentOrder.value));
|
||||
|
||||
// 6️⃣ Reset local current order after short delay
|
||||
Future.delayed(const Duration(milliseconds: 300), () {
|
||||
currentOrder.value = CabOrderModel();
|
||||
});
|
||||
} catch (e, s) {
|
||||
print("rejectOrder() error: $e\n$s");
|
||||
}
|
||||
}
|
||||
|
||||
bool get shouldShowOrderSheet {
|
||||
final status = currentOrder.value.status;
|
||||
return currentOrder.value.id != null &&
|
||||
![Constant.driverPending, Constant.driverRejected, Constant.orderCompleted, Constant.orderCancelled].contains(status);
|
||||
}
|
||||
|
||||
Future<void> clearMap() async {
|
||||
await AudioPlayerService.playSound(false);
|
||||
if (Constant.selectedMapType != 'osm') {
|
||||
markers.clear();
|
||||
polyLines.clear();
|
||||
} else {
|
||||
osmMarkers.clear();
|
||||
routePoints.clear();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> onRideStatus() async {
|
||||
await AudioPlayerService.playSound(false);
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
currentOrder.value.status = Constant.orderInTransit;
|
||||
await FireStoreUtils.setCabOrder(currentOrder.value);
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.back();
|
||||
}
|
||||
|
||||
Future<void> completeRide() async {
|
||||
try {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
await updateCabWalletAmount(currentOrder.value);
|
||||
|
||||
await FireStoreUtils.getFirestOrderOrNOtCabService(currentOrder.value).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateReferralAmountCabService(currentOrder.value);
|
||||
}
|
||||
});
|
||||
|
||||
currentOrder.value.status = Constant.orderCompleted;
|
||||
driverModel.value.inProgressOrderID = [];
|
||||
driverModel.value.orderCabRequestData = null;
|
||||
await FireStoreUtils.setCabOrder(currentOrder.value);
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
} catch (e) {
|
||||
ShowToastDialog.closeLoader();
|
||||
log("Error in completeRide(): $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateCabWalletAmount(CabOrderModel orderModel) async {
|
||||
try {
|
||||
double totalTax = 0.0;
|
||||
double discount = 0.0;
|
||||
double subTotal = 0.0;
|
||||
double adminComm = 0.0;
|
||||
double totalAmount = 0.0;
|
||||
|
||||
subTotal = double.tryParse(orderModel.subTotal ?? '0.0') ?? 0.0;
|
||||
discount = double.tryParse(orderModel.discount ?? '0.0') ?? 0.0;
|
||||
|
||||
if (orderModel.taxSetting != null) {
|
||||
for (var element in orderModel.taxSetting!) {
|
||||
totalTax += Constant.calculateTax(amount: subTotal.toString(), taxModel: element);
|
||||
}
|
||||
}
|
||||
|
||||
if ((orderModel.adminCommission ?? '').isNotEmpty) {
|
||||
adminComm = Constant.calculateAdminCommission(
|
||||
amount: (subTotal - discount).toString(),
|
||||
adminCommissionType: orderModel.adminCommissionType.toString(),
|
||||
adminCommission: orderModel.adminCommission ?? '0');
|
||||
}
|
||||
totalAmount = (subTotal + totalTax) - discount;
|
||||
|
||||
final ownerId = orderModel.driver?.ownerId;
|
||||
final userIdForWallet = (ownerId != null && ownerId.isNotEmpty) ? ownerId : FireStoreUtils.getCurrentUid();
|
||||
|
||||
if (orderModel.paymentMethod.toString() != PaymentGateway.cod.name) {
|
||||
WalletTransactionModel transactionModel = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: totalAmount,
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: orderModel.paymentMethod ?? '',
|
||||
transactionUser: "driver",
|
||||
userId: userIdForWallet,
|
||||
isTopup: true,
|
||||
orderId: orderModel.id,
|
||||
note: "Booking amount credited",
|
||||
paymentStatus: "success");
|
||||
|
||||
final setTx = await FireStoreUtils.setWalletTransaction(transactionModel);
|
||||
if (setTx == true) {
|
||||
await FireStoreUtils.updateUserWallet(amount: totalAmount.toString(), userId: userIdForWallet);
|
||||
}
|
||||
}
|
||||
|
||||
WalletTransactionModel adminTx = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: adminComm,
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: orderModel.paymentMethod ?? '',
|
||||
transactionUser: "driver",
|
||||
userId: userIdForWallet,
|
||||
isTopup: false,
|
||||
orderId: orderModel.id,
|
||||
note: "Admin commission deducted",
|
||||
paymentStatus: "success");
|
||||
|
||||
final setAdmin = await FireStoreUtils.setWalletTransaction(adminTx);
|
||||
if (setAdmin == true) {
|
||||
await FireStoreUtils.updateUserWallet(amount: "-${adminComm.toString()}", userId: userIdForWallet);
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error in updateCabWalletAmount(): $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getCurrentOrder() async {
|
||||
try {
|
||||
await _orderDocSub?.cancel();
|
||||
await _orderQuerySub?.cancel();
|
||||
|
||||
final inProgress = driverModel.value.inProgressOrderID;
|
||||
if (inProgress != null && inProgress.isNotEmpty) {
|
||||
final String id = inProgress.first.toString();
|
||||
_orderDocSub = FireStoreUtils.fireStore
|
||||
.collection(CollectionName.ridesBooking)
|
||||
.doc(id)
|
||||
.snapshots()
|
||||
.listen((docSnap) => _handleOrderDoc(docSnap, id));
|
||||
return;
|
||||
}
|
||||
|
||||
final pendingRequest = driverModel.value.orderCabRequestData;
|
||||
if (pendingRequest != null) {
|
||||
final id = pendingRequest.id?.toString();
|
||||
if (id != null && id.isNotEmpty) {
|
||||
_orderDocSub = FireStoreUtils.fireStore
|
||||
.collection(CollectionName.ridesBooking)
|
||||
.doc(id)
|
||||
.snapshots()
|
||||
.listen((docSnap) => _handleOrderDoc(docSnap, id));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
currentOrder.value = CabOrderModel();
|
||||
await clearMap();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
} catch (e) {
|
||||
log("getCurrentOrder() error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleOrderDoc(DocumentSnapshot<Map<String, dynamic>> docSnap, String id) async {
|
||||
try {
|
||||
if (docSnap.exists) {
|
||||
final data = docSnap.data();
|
||||
if (data != null) {
|
||||
currentOrder.value = CabOrderModel.fromJson(data);
|
||||
await changeData();
|
||||
if (currentOrder.value.status == Constant.orderCompleted) {
|
||||
driverModel.value.inProgressOrderID = [];
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
currentOrder.value = CabOrderModel();
|
||||
await clearMap();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
return;
|
||||
} else if (currentOrder.value.status == Constant.orderRejected || currentOrder.value.status == Constant.orderCancelled) {
|
||||
driverModel.value.inProgressOrderID = [];
|
||||
driverModel.value.orderCabRequestData = null;
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
currentOrder.value = CabOrderModel();
|
||||
await clearMap();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
_orderQuerySub = FireStoreUtils.fireStore
|
||||
.collection(CollectionName.ridesBooking)
|
||||
.where('id', isEqualTo: id)
|
||||
.limit(1)
|
||||
.snapshots()
|
||||
.listen((qSnap) => _handleOrderQuery(qSnap));
|
||||
} catch (e) {
|
||||
log("Error listening to order doc: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleOrderQuery(QuerySnapshot<Map<String, dynamic>> qSnap) async {
|
||||
try {
|
||||
if (qSnap.docs.isNotEmpty) {
|
||||
final doc = qSnap.docs.first;
|
||||
final data = doc.data();
|
||||
currentOrder.value = CabOrderModel.fromJson(data);
|
||||
await changeData();
|
||||
if (currentOrder.value.status == Constant.orderCompleted) {
|
||||
driverModel.value.inProgressOrderID = [];
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
currentOrder.value = CabOrderModel();
|
||||
await clearMap();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
return;
|
||||
}
|
||||
update();
|
||||
return;
|
||||
} else {
|
||||
currentOrder.value = CabOrderModel();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error parsing order from query fallback: $e");
|
||||
}
|
||||
}
|
||||
|
||||
RxBool isChange = false.obs;
|
||||
|
||||
Future<void> changeData() async {
|
||||
if (Constant.mapType == "inappmap") {
|
||||
if (Constant.selectedMapType == "osm") {
|
||||
await getOSMPolyline();
|
||||
} else {
|
||||
await getGooglePolyline();
|
||||
}
|
||||
}
|
||||
if (currentOrder.value.status == Constant.driverPending) {
|
||||
await AudioPlayerService.playSound(true);
|
||||
} else {
|
||||
await AudioPlayerService.playSound(false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _subscribeDriver() async {
|
||||
_driverSub = FireStoreUtils.fireStore
|
||||
.collection(CollectionName.users)
|
||||
.doc(FireStoreUtils.getCurrentUid())
|
||||
.snapshots()
|
||||
.listen((event) => _onDriverSnapshot(event));
|
||||
|
||||
if (Constant.userModel!.ownerId != null && Constant.userModel!.ownerId!.isNotEmpty) {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(Constant.userModel!.ownerId).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
ownerModel.value = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onDriverSnapshot(DocumentSnapshot<Map<String, dynamic>> event) async {
|
||||
try {
|
||||
if (event.exists && event.data() != null) {
|
||||
driverModel.value = UserModel.fromJson(event.data()!);
|
||||
_updateCurrentLocationMarkers();
|
||||
if (driverModel.value.id != null) {
|
||||
await getCurrentOrder();
|
||||
await changeData();
|
||||
if (driverModel.value.sectionId != null && driverModel.value.sectionId!.isNotEmpty) {
|
||||
await FireStoreUtils.getSectionBySectionId(driverModel.value.sectionId!).then((sectionValue) {
|
||||
if (sectionValue != null) {
|
||||
Constant.sectionModel = sectionValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
update();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log("getDriver() listener error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void _updateCurrentLocationMarkers() async {
|
||||
try {
|
||||
final loc = driverModel.value.location;
|
||||
final latLng = _safeLatLngFromLocation(loc);
|
||||
|
||||
// Update reactive current location
|
||||
current.value = location.LatLng(latLng.latitude, latLng.longitude);
|
||||
|
||||
// --- OSM Section ---
|
||||
try {
|
||||
setOsmMapMarker();
|
||||
|
||||
if (latLng.latitude != 0.0 || latLng.longitude != 0.0) {
|
||||
osmMapController.move(location.LatLng(latLng.latitude, latLng.longitude), 16);
|
||||
}
|
||||
} catch (e) {
|
||||
log("OSM map move ignored (controller not ready): $e");
|
||||
}
|
||||
|
||||
// --- GOOGLE MAP Section ---
|
||||
try {
|
||||
final driverIcon = await _bitmapDescriptorFromUrl(
|
||||
Constant.sectionModel?.markerIcon ?? '',
|
||||
width: 120,
|
||||
);
|
||||
|
||||
// Remove old driver marker
|
||||
markers.remove("Driver");
|
||||
|
||||
// Create new Google Marker
|
||||
markers["Driver"] = Marker(
|
||||
markerId: const MarkerId("Driver"),
|
||||
infoWindow: const InfoWindow(title: "Driver"),
|
||||
position: LatLng(current.value.latitude, current.value.longitude),
|
||||
icon: driverIcon,
|
||||
rotation: _safeRotation(),
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
flat: true,
|
||||
);
|
||||
|
||||
// Animate camera to current driver location
|
||||
if (mapController != null && !(current.value.latitude == 0.0 && current.value.longitude == 0.0)) {
|
||||
mapController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(
|
||||
CameraPosition(
|
||||
target: LatLng(current.value.latitude, current.value.longitude),
|
||||
zoom: 16,
|
||||
bearing: _safeRotation(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log("Google map update ignored (controller not ready): $e");
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
log('_updateCurrentLocationMarkers: lat=${latLng.latitude}, lng=${latLng.longitude}, '
|
||||
'osmMarkers=${osmMarkers.length}, googleMarkers=${markers.length}');
|
||||
} catch (e) {
|
||||
log("_updateCurrentLocationMarkers error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
GoogleMapController? mapController;
|
||||
|
||||
Rx<PolylinePoints> polylinePoints = PolylinePoints(apiKey: Constant.mapAPIKey).obs;
|
||||
RxMap<PolylineId, Polyline> polyLines = <PolylineId, Polyline>{}.obs;
|
||||
RxMap<String, Marker> markers = <String, Marker>{}.obs;
|
||||
|
||||
// BitmapDescriptor? departureIcon;
|
||||
// BitmapDescriptor? destinationIcon;
|
||||
// BitmapDescriptor? taxiIcon;
|
||||
|
||||
// Future<void> setIcons() async {
|
||||
// try {
|
||||
// if (Constant.selectedMapType == 'google') {
|
||||
// final Uint8List departure = await Constant().getBytesFromAsset('assets/images/location_black3x.png', 100);
|
||||
// final Uint8List destination = await Constant().getBytesFromAsset('assets/images/location_orange3x.png', 100);
|
||||
// final Uint8List driver = Constant.sectionModel!.markerIcon == null || Constant.sectionModel!.markerIcon!.isEmpty
|
||||
// ? await Constant().getBytesFromAsset('assets/images/ic_cab.png', 50)
|
||||
// : await Constant().getBytesFromUrl(Constant.sectionModel!.markerIcon.toString(), width: 120);
|
||||
//
|
||||
// departureIcon = BitmapDescriptor.fromBytes(departure);
|
||||
// destinationIcon = BitmapDescriptor.fromBytes(destination);
|
||||
// taxiIcon = BitmapDescriptor.fromBytes(driver);
|
||||
// }
|
||||
// } catch (e) {
|
||||
// log("setIcons error: $e");
|
||||
// }
|
||||
// }
|
||||
|
||||
LatLng _safeLatLngFromLocation(dynamic loc) {
|
||||
final lat = (loc?.latitude is num) ? loc.latitude.toDouble() : 0.0;
|
||||
final lng = (loc?.longitude is num) ? loc.longitude.toDouble() : 0.0;
|
||||
return LatLng(lat, lng);
|
||||
}
|
||||
|
||||
double _safeRotation() {
|
||||
return double.tryParse(driverModel.value.rotation.toString()) ?? 0.0;
|
||||
}
|
||||
|
||||
Future<void> getGooglePolyline() async {
|
||||
try {
|
||||
if (currentOrder.value.id == null) return;
|
||||
|
||||
final driverLatLng = _safeLatLngFromLocation(driverModel.value.location);
|
||||
List<LatLng> polylineCoordinates = [];
|
||||
|
||||
// Check order status
|
||||
if (currentOrder.value.status != Constant.driverPending) {
|
||||
// Case 1: Driver Accepted or Order Shipped → Driver → Pickup
|
||||
if (currentOrder.value.status == Constant.driverAccepted || currentOrder.value.status == Constant.orderShipped) {
|
||||
final sourceLatLng = _safeLatLngFromLocation(currentOrder.value.sourceLocation);
|
||||
|
||||
await _drawGoogleRoute(
|
||||
origin: driverLatLng,
|
||||
destination: sourceLatLng,
|
||||
addDriver: true,
|
||||
addSource: true,
|
||||
addDestination: false,
|
||||
);
|
||||
|
||||
animateToSource();
|
||||
}
|
||||
|
||||
// Case 2: Order In Transit → Driver → Destination
|
||||
else if (currentOrder.value.status == Constant.orderInTransit) {
|
||||
final destLatLng = _safeLatLngFromLocation(currentOrder.value.destinationLocation);
|
||||
|
||||
await _drawGoogleRoute(
|
||||
origin: driverLatLng,
|
||||
destination: destLatLng,
|
||||
addDriver: true,
|
||||
addSource: false,
|
||||
addDestination: true,
|
||||
);
|
||||
|
||||
animateToSource();
|
||||
}
|
||||
}
|
||||
|
||||
// Case 3: Before driver assigned → Source → Destination
|
||||
else {
|
||||
final sourceLatLng = _safeLatLngFromLocation(currentOrder.value.sourceLocation);
|
||||
final destLatLng = _safeLatLngFromLocation(currentOrder.value.destinationLocation);
|
||||
|
||||
await _drawGoogleRoute(
|
||||
origin: sourceLatLng,
|
||||
destination: destLatLng,
|
||||
addDriver: false,
|
||||
addSource: true,
|
||||
addDestination: true,
|
||||
);
|
||||
|
||||
animateToSource();
|
||||
}
|
||||
} catch (e, s) {
|
||||
log('getGooglePolyline() error: $e');
|
||||
debugPrintStack(stackTrace: s);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _drawGoogleRoute({
|
||||
required LatLng origin,
|
||||
required LatLng destination,
|
||||
bool addDriver = true,
|
||||
bool addSource = true,
|
||||
bool addDestination = true,
|
||||
}) async {
|
||||
try {
|
||||
if ((origin.latitude == 0.0 && origin.longitude == 0.0) || (destination.latitude == 0.0 && destination.longitude == 0.0)) return;
|
||||
|
||||
// Get route points from Google Directions API
|
||||
final result = await polylinePoints.value.getRouteBetweenCoordinates(
|
||||
request: PolylineRequest(
|
||||
origin: PointLatLng(origin.latitude, origin.longitude),
|
||||
destination: PointLatLng(destination.latitude, destination.longitude),
|
||||
mode: TravelMode.driving,
|
||||
),
|
||||
);
|
||||
|
||||
if (result.points.isEmpty) {
|
||||
log('Google route not found');
|
||||
return;
|
||||
}
|
||||
|
||||
final List<LatLng> polylineCoordinates = result.points.map((p) => LatLng(p.latitude, p.longitude)).toList();
|
||||
|
||||
// Draw polyline
|
||||
addPolyLine(polylineCoordinates);
|
||||
|
||||
// --- Update markers (same style as OSM) ---
|
||||
await _updateGoogleMarkers(
|
||||
driverLatLng: addDriver ? origin : null,
|
||||
sourceLatLng: addSource ? _safeLatLngFromLocation(currentOrder.value.sourceLocation) : null,
|
||||
destinationLatLng: addDestination ? _safeLatLngFromLocation(currentOrder.value.destinationLocation) : null,
|
||||
);
|
||||
} catch (e) {
|
||||
log('_drawGoogleRoute error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateGoogleMarkers({
|
||||
LatLng? driverLatLng,
|
||||
LatLng? sourceLatLng,
|
||||
LatLng? destinationLatLng,
|
||||
}) async {
|
||||
final Map<String, Marker> newMarkers = {};
|
||||
|
||||
// Driver Marker (Network Icon)
|
||||
if (driverLatLng != null) {
|
||||
final driverIcon = await _bitmapDescriptorFromUrl(
|
||||
Constant.sectionModel?.markerIcon ?? '',
|
||||
width: 120,
|
||||
);
|
||||
newMarkers['Driver'] = Marker(
|
||||
markerId: const MarkerId('Driver'),
|
||||
position: driverLatLng,
|
||||
rotation: _safeRotation(),
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
flat: true,
|
||||
icon: driverIcon,
|
||||
);
|
||||
}
|
||||
|
||||
// Source Marker
|
||||
if (sourceLatLng != null) {
|
||||
final srcIcon = await _bitmapDescriptorFromAsset(
|
||||
'assets/images/location_black3x.png',
|
||||
width: 100,
|
||||
);
|
||||
newMarkers['Source'] = Marker(
|
||||
markerId: const MarkerId('Source'),
|
||||
position: sourceLatLng,
|
||||
icon: srcIcon,
|
||||
);
|
||||
}
|
||||
|
||||
// Destination Marker
|
||||
if (destinationLatLng != null) {
|
||||
final dstIcon = await _bitmapDescriptorFromAsset(
|
||||
'assets/images/location_orange3x.png',
|
||||
width: 100,
|
||||
);
|
||||
newMarkers['Destination'] = Marker(
|
||||
markerId: const MarkerId('Destination'),
|
||||
position: destinationLatLng,
|
||||
icon: dstIcon,
|
||||
);
|
||||
}
|
||||
|
||||
// Apply all markers
|
||||
// ✅ Apply all markers to your RxMap<String, Marker>
|
||||
markers
|
||||
..clear()
|
||||
..addAll(newMarkers);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
Future<BitmapDescriptor> _bitmapDescriptorFromUrl(String url, {int width = 100}) async {
|
||||
try {
|
||||
final Uint8List bytes = await Constant().getBytesFromUrl(url, width: width);
|
||||
return BitmapDescriptor.fromBytes(bytes);
|
||||
} catch (e) {
|
||||
log('Error loading network icon: $e');
|
||||
return BitmapDescriptor.defaultMarker;
|
||||
}
|
||||
}
|
||||
|
||||
Future<BitmapDescriptor> _bitmapDescriptorFromAsset(String asset, {int width = 100}) async {
|
||||
try {
|
||||
final Uint8List bytes = await Constant().getBytesFromAsset(asset, width);
|
||||
return BitmapDescriptor.fromBytes(bytes);
|
||||
} catch (e) {
|
||||
log('Error loading asset icon: $e');
|
||||
return BitmapDescriptor.defaultMarker;
|
||||
}
|
||||
}
|
||||
|
||||
void addPolyLine(List<LatLng> polylineCoordinates) {
|
||||
if (polylineCoordinates.isEmpty) {
|
||||
// nothing to draw, but ensure markers updated
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
PolylineId id = const PolylineId("poly");
|
||||
Polyline polyline = Polyline(
|
||||
polylineId: id,
|
||||
color: AppThemeData.primary300,
|
||||
points: polylineCoordinates,
|
||||
width: 8,
|
||||
geodesic: true,
|
||||
);
|
||||
polyLines[id] = polyline;
|
||||
update();
|
||||
updateCameraLocation(polylineCoordinates.first);
|
||||
}
|
||||
|
||||
Future<void> updateCameraLocation([LatLng? source]) async {
|
||||
try {
|
||||
if (mapController == null || source == null) return;
|
||||
await mapController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(
|
||||
CameraPosition(
|
||||
target: source,
|
||||
zoom: currentOrder.value.id == null || currentOrder.value.status == Constant.driverPending ? 16 : 20,
|
||||
bearing: _safeRotation(),
|
||||
),
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
log("updateCameraLocation error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void animateToSource() {
|
||||
double lat = 0.0;
|
||||
double lng = 0.0;
|
||||
final loc = driverModel.value.location;
|
||||
if (loc != null) {
|
||||
// Use string parsing to avoid nullable-toDouble issues and handle numbers/strings.
|
||||
lat = double.tryParse('${loc.latitude}') ?? 0.0;
|
||||
lng = double.tryParse('${loc.longitude}') ?? 0.0;
|
||||
}
|
||||
_updateCurrentLocationMarkers();
|
||||
osmMapController.move(location.LatLng(lat, lng), 16);
|
||||
}
|
||||
|
||||
Rx<location.LatLng> source = location.LatLng(21.1702, 72.8311).obs; // Start (e.g., Surat)
|
||||
Rx<location.LatLng> current = location.LatLng(21.1800, 72.8400).obs; // Moving marker
|
||||
Rx<location.LatLng> destination = location.LatLng(21.2000, 72.8600).obs; // Destination
|
||||
|
||||
void setOsmMapMarker() {
|
||||
final List<flutterMap.Marker> mk = [];
|
||||
|
||||
// Add driver/current marker only when we have a valid location
|
||||
if (!(current.value.latitude == 0.0 && current.value.longitude == 0.0)) {
|
||||
mk.add(flutterMap.Marker(
|
||||
point: current.value,
|
||||
width: 45,
|
||||
height: 45,
|
||||
rotate: true,
|
||||
child: CachedNetworkImage(
|
||||
width: 50,
|
||||
height: 50,
|
||||
imageUrl: Constant.sectionModel!.markerIcon.toString(),
|
||||
placeholder: (context, url) => Constant.loader(),
|
||||
errorWidget: (context, url, error) => SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
// Add source marker if we have a valid source location (or an active order with non-zero coords)
|
||||
final hasSource = currentOrder.value.sourceLocation != null &&
|
||||
!(currentOrder.value.sourceLocation?.latitude == null || currentOrder.value.sourceLocation?.longitude == null) &&
|
||||
!(currentOrder.value.sourceLocation?.latitude == 0.0 && currentOrder.value.sourceLocation?.longitude == 0.0);
|
||||
if (hasSource) {
|
||||
source.value =
|
||||
location.LatLng(currentOrder.value.sourceLocation!.latitude ?? 0.0, currentOrder.value.sourceLocation!.longitude ?? 0.0);
|
||||
mk.add(flutterMap.Marker(
|
||||
point: source.value,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Image.asset('assets/images/location_black3x.png'),
|
||||
));
|
||||
}
|
||||
|
||||
// Add destination marker if valid
|
||||
final hasDest = currentOrder.value.destinationLocation != null &&
|
||||
!(currentOrder.value.destinationLocation?.latitude == null || currentOrder.value.destinationLocation?.longitude == null) &&
|
||||
!(currentOrder.value.destinationLocation?.latitude == 0.0 && currentOrder.value.destinationLocation?.longitude == 0.0);
|
||||
if (hasDest) {
|
||||
destination.value = location.LatLng(
|
||||
currentOrder.value.destinationLocation!.latitude ?? 0.0, currentOrder.value.destinationLocation!.longitude ?? 0.0);
|
||||
mk.add(flutterMap.Marker(
|
||||
point: destination.value,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Image.asset('assets/images/location_orange3x.png'),
|
||||
));
|
||||
}
|
||||
|
||||
osmMarkers.value = mk;
|
||||
}
|
||||
|
||||
Future<void> getOSMPolyline() async {
|
||||
try {
|
||||
if (currentOrder.value.id == null) return;
|
||||
|
||||
if (currentOrder.value.status != Constant.driverPending) {
|
||||
if (currentOrder.value.status == Constant.driverAccepted || currentOrder.value.status == Constant.orderShipped) {
|
||||
final lat = (driverModel.value.location?.latitude as num?)?.toDouble() ?? 0.0;
|
||||
final lng = (driverModel.value.location?.longitude as num?)?.toDouble() ?? 0.0;
|
||||
current.value = location.LatLng(lat, lng);
|
||||
source.value = location.LatLng(
|
||||
currentOrder.value.sourceLocation?.latitude ?? 0.0,
|
||||
currentOrder.value.sourceLocation?.longitude ?? 0.0,
|
||||
);
|
||||
animateToSource();
|
||||
await fetchRoute(current.value, source.value);
|
||||
setOsmMapMarker();
|
||||
} else if (currentOrder.value.status == Constant.orderInTransit) {
|
||||
final lat = (driverModel.value.location?.latitude as num?)?.toDouble() ?? 0.0;
|
||||
final lng = (driverModel.value.location?.longitude as num?)?.toDouble() ?? 0.0;
|
||||
current.value = location.LatLng(lat, lng);
|
||||
destination.value = location.LatLng(
|
||||
currentOrder.value.destinationLocation?.latitude ?? 0.0,
|
||||
currentOrder.value.destinationLocation?.longitude ?? 0.0,
|
||||
);
|
||||
await fetchRoute(current.value, destination.value);
|
||||
setOsmMapMarker();
|
||||
animateToSource();
|
||||
}
|
||||
} else {
|
||||
current.value =
|
||||
location.LatLng(currentOrder.value.sourceLocation?.latitude ?? 0.0, currentOrder.value.sourceLocation?.longitude ?? 0.0);
|
||||
destination.value = location.LatLng(
|
||||
currentOrder.value.destinationLocation?.latitude ?? 0.0, currentOrder.value.destinationLocation?.longitude ?? 0.0);
|
||||
await fetchRoute(current.value, destination.value);
|
||||
setOsmMapMarker();
|
||||
animateToSource();
|
||||
}
|
||||
} catch (e) {
|
||||
log('getOSMPolyline error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
RxList<location.LatLng> routePoints = <location.LatLng>[].obs;
|
||||
|
||||
Future<void> fetchRoute(location.LatLng source, location.LatLng destination) async {
|
||||
try {
|
||||
// ensure valid coords
|
||||
final bothZero = source.latitude == 0.0 && source.longitude == 0.0 && destination.latitude == 0.0 && destination.longitude == 0.0;
|
||||
if (bothZero) {
|
||||
routePoints.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
final url = Uri.parse(
|
||||
'https://router.project-osrm.org/route/v1/driving/${source.longitude},${source.latitude};${destination.longitude},${destination.latitude}?overview=full&geometries=geojson',
|
||||
);
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = json.decode(response.body);
|
||||
if (decoded != null &&
|
||||
decoded['routes'] != null &&
|
||||
decoded['routes'] is List &&
|
||||
(decoded['routes'] as List).isNotEmpty &&
|
||||
decoded['routes'][0]['geometry'] != null) {
|
||||
final geometry = decoded['routes'][0]['geometry']['coordinates'];
|
||||
routePoints.clear();
|
||||
for (var coord in geometry) {
|
||||
if (coord is List && coord.length >= 2) {
|
||||
final lon = coord[0];
|
||||
final lat = coord[1];
|
||||
if (lat is num && lon is num) {
|
||||
routePoints.add(location.LatLng(lat.toDouble(), lon.toDouble()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
routePoints.clear();
|
||||
} else {
|
||||
log("Failed to get route: ${response.statusCode} ${response.body}");
|
||||
routePoints.clear();
|
||||
}
|
||||
} catch (e) {
|
||||
log("fetchRoute error: $e");
|
||||
routePoints.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
142
lib/controllers/cab_order_details_controller.dart
Normal file
142
lib/controllers/cab_order_details_controller.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart' as gmap;
|
||||
import 'package:latlong2/latlong.dart' as osm;
|
||||
import '../constant/constant.dart';
|
||||
import '../models/cab_order_model.dart';
|
||||
import '../models/user_model.dart';
|
||||
import '../themes/app_them_data.dart';
|
||||
import '../utils/fire_store_utils.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
|
||||
class CabOrderDetailsController extends GetxController {
|
||||
Rx<CabOrderModel> cabOrder = CabOrderModel().obs;
|
||||
|
||||
RxBool isLoading = false.obs;
|
||||
|
||||
// Google Maps Data
|
||||
RxSet<gmap.Marker> googleMarkers = <gmap.Marker>{}.obs;
|
||||
RxSet<gmap.Polyline> googlePolylines = <gmap.Polyline>{}.obs;
|
||||
|
||||
// OSM Data
|
||||
RxList<osm.LatLng> osmPolyline = <osm.LatLng>[].obs;
|
||||
|
||||
final String googleApiKey = Constant.mapAPIKey;
|
||||
|
||||
final Rx<UserModel?> driverUser = Rx<UserModel?>(null);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
if (args != null) {
|
||||
cabOrder.value = args['cabOrderModel'] as CabOrderModel;
|
||||
calculateTotalAmount();
|
||||
_setMarkers();
|
||||
_getGoogleRoute();
|
||||
_getOsmRoute();
|
||||
}
|
||||
}
|
||||
|
||||
String formatDate(Timestamp timestamp) {
|
||||
final dateTime = timestamp.toDate();
|
||||
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
|
||||
}
|
||||
|
||||
RxDouble subTotal = 0.0.obs;
|
||||
RxDouble discount = 0.0.obs;
|
||||
RxDouble taxAmount = 0.0.obs;
|
||||
RxDouble totalAmount = 0.0.obs;
|
||||
RxDouble adminCommission = 0.0.obs;
|
||||
|
||||
Future<void> fetchDriverDetails() async {
|
||||
if (cabOrder.value.driverId != null) {
|
||||
await FireStoreUtils.getUserProfile(cabOrder.value.driverId ?? '').then((value) {
|
||||
if (value != null) {
|
||||
driverUser.value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void calculateTotalAmount() {
|
||||
taxAmount = 0.0.obs;
|
||||
discount = 0.0.obs;
|
||||
subTotal.value = double.parse(cabOrder.value.subTotal.toString());
|
||||
discount.value = double.parse(cabOrder.value.discount ?? '0.0');
|
||||
|
||||
for (var element in cabOrder.value.taxSetting!) {
|
||||
taxAmount.value = (taxAmount.value + Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element));
|
||||
}
|
||||
|
||||
if (cabOrder.value.adminCommission!.isNotEmpty) {
|
||||
adminCommission.value = Constant.calculateAdminCommission(
|
||||
amount: (subTotal.value - discount.value).toString(),
|
||||
adminCommissionType: cabOrder.value.adminCommissionType.toString(),
|
||||
adminCommission: cabOrder.value.adminCommission ?? '0');
|
||||
}
|
||||
|
||||
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
|
||||
update();
|
||||
}
|
||||
|
||||
void _setMarkers() {
|
||||
final sourceLat = cabOrder.value.sourceLocation!.latitude;
|
||||
final sourceLng = cabOrder.value.sourceLocation!.longitude;
|
||||
final destLat = cabOrder.value.destinationLocation!.latitude;
|
||||
final destLng = cabOrder.value.destinationLocation!.longitude;
|
||||
|
||||
googleMarkers.value = {
|
||||
gmap.Marker(
|
||||
markerId: const gmap.MarkerId('source'),
|
||||
position: gmap.LatLng(sourceLat!, sourceLng!),
|
||||
icon: gmap.BitmapDescriptor.defaultMarkerWithHue(gmap.BitmapDescriptor.hueGreen),
|
||||
),
|
||||
gmap.Marker(
|
||||
markerId: const gmap.MarkerId('destination'),
|
||||
position: gmap.LatLng(destLat!, destLng!),
|
||||
icon: gmap.BitmapDescriptor.defaultMarkerWithHue(gmap.BitmapDescriptor.hueRed),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
///Google Directions API
|
||||
Future<void> _getGoogleRoute() async {
|
||||
final src = cabOrder.value.sourceLocation;
|
||||
final dest = cabOrder.value.destinationLocation;
|
||||
|
||||
final url = "https://maps.googleapis.com/maps/api/directions/json?origin=${src!.latitude},${src.longitude}&destination=${dest!.latitude},${dest.longitude}&key=$googleApiKey";
|
||||
|
||||
final response = await http.get(Uri.parse(url));
|
||||
final data = jsonDecode(response.body);
|
||||
|
||||
if (data["routes"].isNotEmpty) {
|
||||
final points = data["routes"][0]["overview_polyline"]["points"];
|
||||
final polylinePoints = PolylinePoints.decodePolyline(points);
|
||||
|
||||
final polylineCoords = polylinePoints.map((p) => gmap.LatLng(p.latitude, p.longitude)).toList();
|
||||
|
||||
googlePolylines.value = {gmap.Polyline(polylineId: const gmap.PolylineId("google_route"), color: AppThemeData.onDemandDark100, width: 5, points: polylineCoords)};
|
||||
}
|
||||
}
|
||||
|
||||
/// OSM Route (OSRM API)
|
||||
Future<void> _getOsmRoute() async {
|
||||
final src = cabOrder.value.sourceLocation;
|
||||
final dest = cabOrder.value.destinationLocation;
|
||||
|
||||
final url = "http://router.project-osrm.org/route/v1/driving/${src!.longitude},${src.latitude};${dest!.longitude},${dest.latitude}?overview=full&geometries=geojson";
|
||||
|
||||
final response = await http.get(Uri.parse(url));
|
||||
final data = jsonDecode(response.body);
|
||||
|
||||
if (data["routes"].isNotEmpty) {
|
||||
final coords = data["routes"][0]["geometry"]["coordinates"] as List<dynamic>;
|
||||
|
||||
osmPolyline.value = coords.map((c) => osm.LatLng(c[1].toDouble(), c[0].toDouble())).toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
lib/controllers/cab_order_list_controller.dart
Normal file
60
lib/controllers/cab_order_list_controller.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../models/cab_order_model.dart';
|
||||
import '../utils/fire_store_utils.dart';
|
||||
|
||||
class CabOrderListController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxString selectedTab = "On Going".obs;
|
||||
RxList<CabOrderModel> cabOrder = <CabOrderModel>[].obs;
|
||||
|
||||
RxList<String> tabTitles = ["On Going", "Completed", "Cancelled"].obs;
|
||||
|
||||
RxString driverId = ''.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
driverId.value = args?['driverId'] ?? FireStoreUtils.getCurrentUid();
|
||||
fetchCabOrders();
|
||||
}
|
||||
|
||||
void selectTab(String tab) {
|
||||
selectedTab.value = tab;
|
||||
fetchCabOrders();
|
||||
}
|
||||
|
||||
void fetchCabOrders() {
|
||||
isLoading.value = true;
|
||||
|
||||
FireStoreUtils.getCabDriverOrders(driverId.value).listen((orders) {
|
||||
print("cabOrder length ::::::${cabOrder.length}");
|
||||
cabOrder.value = orders;
|
||||
isLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/// Return filtered list for a specific tab title
|
||||
List<CabOrderModel> getOrdersForTab(String tab) {
|
||||
switch (tab) {
|
||||
case "On Going":
|
||||
return cabOrder.where((order) => ["Order Placed", "Order Accepted", "Driver Accepted", "Driver Pending", "Order Shipped", "In Transit"].contains(order.status)).toList();
|
||||
|
||||
case "Completed":
|
||||
return cabOrder.where((order) => ["Order Completed"].contains(order.status)).toList();
|
||||
|
||||
case "Cancelled":
|
||||
return cabOrder.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
String formatDate(Timestamp timestamp) {
|
||||
final dateTime = timestamp.toDate();
|
||||
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
|
||||
}
|
||||
}
|
||||
44
lib/controllers/change_language_controller.dart
Normal file
44
lib/controllers/change_language_controller.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/language_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../constant/collection_name.dart';
|
||||
|
||||
class ChangeLanguageController extends GetxController {
|
||||
Rx<LanguageModel> selectedLanguage = LanguageModel().obs;
|
||||
RxList<LanguageModel> languageList = <LanguageModel>[].obs;
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getLanguage();
|
||||
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getLanguage() async {
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("languages").get().then((event) {
|
||||
if (event.exists) {
|
||||
List languageListTemp = event.data()!["list"];
|
||||
for (var element in languageListTemp) {
|
||||
LanguageModel languageModel = LanguageModel.fromJson(element);
|
||||
languageList.add(languageModel);
|
||||
}
|
||||
|
||||
if (Preferences.getString(Preferences.languageCodeKey).toString().isNotEmpty) {
|
||||
LanguageModel pref = Constant.getLanguage();
|
||||
for (var element in languageList) {
|
||||
if (element.slug == pref.slug) {
|
||||
selectedLanguage.value = element;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
110
lib/controllers/chat_controller.dart
Normal file
110
lib/controllers/chat_controller.dart
Normal file
@@ -0,0 +1,110 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/models/conversation_model.dart';
|
||||
import 'package:driver/models/inbox_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class ChatController extends GetxController {
|
||||
Rx<TextEditingController> messageController = TextEditingController().obs;
|
||||
|
||||
final ScrollController scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
if (scrollController.hasClients) {
|
||||
Timer(const Duration(milliseconds: 500), () => scrollController.jumpTo(scrollController.position.maxScrollExtent));
|
||||
}
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
RxString orderId = "".obs;
|
||||
RxString customerId = "".obs;
|
||||
RxString customerName = "".obs;
|
||||
RxString customerProfileImage = "".obs;
|
||||
RxString restaurantId = "".obs;
|
||||
RxString restaurantName = "".obs;
|
||||
RxString restaurantProfileImage = "".obs;
|
||||
RxString token = "".obs;
|
||||
RxString chatType = "".obs;
|
||||
|
||||
void getArgument() {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
orderId.value = argumentData['orderId'];
|
||||
customerId.value = argumentData['customerId'];
|
||||
customerName.value = argumentData['customerName'];
|
||||
customerProfileImage.value = argumentData['customerProfileImage'];
|
||||
restaurantId.value = argumentData['restaurantId'];
|
||||
restaurantName.value = argumentData['restaurantName'];
|
||||
restaurantProfileImage.value = argumentData['restaurantProfileImage'];
|
||||
token.value = argumentData['token'];
|
||||
chatType.value = argumentData['chatType'];
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> sendMessage(String message, Url? url, String videoThumbnail, String messageType) async {
|
||||
InboxModel inboxModel = InboxModel(
|
||||
lastSenderId: customerId.value,
|
||||
customerId: customerId.value,
|
||||
customerName: customerName.value,
|
||||
restaurantId: restaurantId.value,
|
||||
restaurantName: restaurantName.value,
|
||||
createdAt: Timestamp.now(),
|
||||
orderId: orderId.value,
|
||||
customerProfileImage: customerProfileImage.value,
|
||||
restaurantProfileImage: restaurantProfileImage.value,
|
||||
lastMessage: messageController.value.text,
|
||||
chatType: chatType.value);
|
||||
|
||||
await FireStoreUtils.addDriverInbox(inboxModel);
|
||||
|
||||
ConversationModel conversationModel = ConversationModel(
|
||||
id: const Uuid().v4(),
|
||||
message: message,
|
||||
senderId: restaurantId.value,
|
||||
receiverId: customerId.value,
|
||||
createdAt: Timestamp.now(),
|
||||
url: url,
|
||||
orderId: orderId.value,
|
||||
messageType: messageType,
|
||||
videoThumbnail: videoThumbnail);
|
||||
|
||||
if (url != null) {
|
||||
if (url.mime.contains('image')) {
|
||||
conversationModel.message = "sent a message".tr;
|
||||
} else if (url.mime.contains('video')) {
|
||||
conversationModel.message = "Sent a video".tr;
|
||||
} else if (url.mime.contains('audio')) {
|
||||
conversationModel.message = "Sent a audio".tr;
|
||||
}
|
||||
}
|
||||
await FireStoreUtils.addDriverChat(conversationModel);
|
||||
|
||||
print("Token Value ===> ${token.value}");
|
||||
await SendNotification.sendChatFcmMessage(restaurantName.value, conversationModel.message.toString(), token.value, {});
|
||||
}
|
||||
|
||||
final ImagePicker imagePicker = ImagePicker();
|
||||
|
||||
// Future pickFile({required ImageSource source}) async {
|
||||
// try {
|
||||
// XFile? image = await imagePicker.pickImage(source: source);
|
||||
// if (image == null) return;
|
||||
// Url url = await FireStoreUtils.uploadChatImageToFireStorage(File(image.path), Get.context!);
|
||||
// sendMessage('', url, '', 'image');
|
||||
// Get.back();
|
||||
// } on PlatformException catch (e) {
|
||||
// ShowToastDialog.showToast("${"failed_to_pick".tr} : \n $e");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
144
lib/controllers/dash_board_controller.dart
Normal file
144
lib/controllers/dash_board_controller.dart
Normal file
@@ -0,0 +1,144 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:location/location.dart';
|
||||
|
||||
import '../themes/theme_controller.dart';
|
||||
|
||||
class DashBoardController extends GetxController {
|
||||
RxInt drawerIndex = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
|
||||
getUser();
|
||||
updateDriverOrder();
|
||||
getTheme();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
DateTime? currentBackPressTime;
|
||||
RxBool canPopNow = false.obs;
|
||||
|
||||
Future<void> getUser() async {
|
||||
await updateCurrentLocation();
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
Constant.userModel = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
RxString isDarkMode = "Light".obs;
|
||||
RxBool isDarkModeSwitch = false.obs;
|
||||
|
||||
void getTheme() {
|
||||
bool isDark = Preferences.getBoolean(Preferences.themKey);
|
||||
isDarkMode.value = isDark ? "Dark" : "Light";
|
||||
isDarkModeSwitch.value = isDark;
|
||||
}
|
||||
|
||||
void toggleDarkMode(bool value) {
|
||||
isDarkModeSwitch.value = value;
|
||||
isDarkMode.value = value ? "Dark" : "Light";
|
||||
Preferences.setBoolean(Preferences.themKey, value);
|
||||
// Update ThemeController for instant app theme change
|
||||
if (Get.isRegistered<ThemeController>()) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
themeController.isDark.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateDriverOrder() async {
|
||||
Timestamp startTimestamp = Timestamp.now();
|
||||
DateTime currentDate = startTimestamp.toDate();
|
||||
currentDate = currentDate.subtract(const Duration(hours: 3));
|
||||
startTimestamp = Timestamp.fromDate(currentDate);
|
||||
|
||||
List<OrderModel> orders = [];
|
||||
|
||||
await FirebaseFirestore.instance
|
||||
.collection(CollectionName.vendorOrders)
|
||||
.where('status', whereIn: [Constant.orderAccepted, Constant.orderRejected])
|
||||
.where('createdAt', isGreaterThan: startTimestamp)
|
||||
.get()
|
||||
.then((value) async {
|
||||
await Future.forEach(value.docs, (QueryDocumentSnapshot<Map<String, dynamic>> element) {
|
||||
try {
|
||||
orders.add(OrderModel.fromJson(element.data()));
|
||||
} catch (e, s) {
|
||||
print('watchOrdersStatus parse error ${element.id}$e $s');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
orders.forEach((element) async {
|
||||
OrderModel orderModel = element;
|
||||
orderModel.triggerDelivery = Timestamp.now();
|
||||
await FireStoreUtils.setOrder(orderModel);
|
||||
});
|
||||
}
|
||||
|
||||
Location location = Location();
|
||||
|
||||
Future<void> updateCurrentLocation() async {
|
||||
try {
|
||||
PermissionStatus permissionStatus = await location.hasPermission();
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location = UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
location.requestPermission().then((permissionStatus) {
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location = UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
lib/controllers/deliver_order_controller.dart
Normal file
77
lib/controllers/deliver_order_controller.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:driver/models/wallet_transaction_model.dart';
|
||||
import 'package:driver/services/audio_player_service.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class DeliverOrderController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxBool conformPickup = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<OrderModel> orderModel = OrderModel().obs;
|
||||
|
||||
RxInt totalQuantity = 0.obs;
|
||||
|
||||
void getArgument() {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
orderModel.value = argumentData['orderModel'];
|
||||
for (var element in orderModel.value.products!) {
|
||||
totalQuantity.value += (element.quantity ?? 0);
|
||||
}
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> completedOrder() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
await AudioPlayerService.playSound(false);
|
||||
orderModel.value.status = Constant.orderCompleted;
|
||||
await FireStoreUtils.updateWallateAmount(orderModel.value);
|
||||
if (orderModel.value.cashback?.cashbackValue != null && orderModel.value.cashback?.id != null) {
|
||||
WalletTransactionModel transactionModel = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: double.parse("${orderModel.value.cashback?.cashbackValue ?? 0.0}"),
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: "Cashback Amount",
|
||||
transactionUser: "user",
|
||||
userId: orderModel.value.author?.id,
|
||||
isTopup: true,
|
||||
orderId: orderModel.value.id,
|
||||
note: "Cashback Amount",
|
||||
paymentStatus: "success");
|
||||
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateUserWallet(
|
||||
amount: double.parse("${orderModel.value.cashback?.cashbackValue ?? 0.0}").toString(), userId: orderModel.value.author!.id.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
await FireStoreUtils.setOrder(orderModel.value);
|
||||
if (Constant.userModel?.vendorID?.isNotEmpty == true) {
|
||||
Constant.userModel?.orderRequestData?.remove(orderModel.value.id);
|
||||
Constant.userModel?.inProgressOrderID?.remove(orderModel.value.id);
|
||||
await FireStoreUtils.updateUser(Constant.userModel!);
|
||||
}
|
||||
await FireStoreUtils.getFirestOrderOrNOt(orderModel.value).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateReferralAmount(orderModel.value);
|
||||
}
|
||||
});
|
||||
|
||||
await SendNotification.sendFcmMessage(Constant.driverCompleted, orderModel.value.author!.fcmToken.toString(), {});
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.back(result: true);
|
||||
}
|
||||
}
|
||||
340
lib/controllers/driver_create_controller.dart
Normal file
340
lib/controllers/driver_create_controller.dart
Normal file
@@ -0,0 +1,340 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/car_makes.dart';
|
||||
import 'package:driver/models/car_model.dart';
|
||||
import 'package:driver/models/section_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/vehicle_type.dart';
|
||||
import 'package:driver/models/zone_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/notification_service.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class DriverCreateController extends GetxController {
|
||||
// Add your methods and properties here
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
Rx<TextEditingController> firstNameEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> lastNameEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> emailEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> phoneNUmberEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> countryCodeEditingController = TextEditingController(text: Constant.defaultCountryCode).obs;
|
||||
Rx<TextEditingController> passwordEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> conformPasswordEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> carPlatNumberEditingController = TextEditingController().obs;
|
||||
|
||||
RxBool passwordVisible = true.obs;
|
||||
RxBool conformPasswordVisible = true.obs;
|
||||
|
||||
RxList<ZoneModel> zoneList = <ZoneModel>[].obs;
|
||||
Rx<ZoneModel> selectedZone = ZoneModel().obs;
|
||||
RxList<String> service = ['Cab Service', 'Parcel Service', 'Rental Service'].obs; // Option 2
|
||||
RxString selectedService = 'Cab Service'.obs;
|
||||
|
||||
RxString selectedValue = 'ride'.obs;
|
||||
|
||||
RxList<SectionModel> sectionList = <SectionModel>[].obs;
|
||||
Rx<SectionModel> selectedSection = SectionModel().obs;
|
||||
|
||||
RxList<VehicleType> cabVehicleType = <VehicleType>[].obs;
|
||||
Rx<VehicleType> selectedVehicleType = VehicleType().obs;
|
||||
|
||||
RxList<CarMakes> carMakesList = <CarMakes>[].obs;
|
||||
Rx<CarMakes> selectedCarMakes = CarMakes().obs;
|
||||
|
||||
RxList<CarModel> carModelList = <CarModel>[].obs;
|
||||
Rx<CarModel> selectedCarModel = CarModel().obs;
|
||||
|
||||
Rx<UserModel> driverModel = UserModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getArguments();
|
||||
super.onInit();
|
||||
ever(isLoading, (loading) async {
|
||||
if (loading == false && driverModel.value.id == null) {
|
||||
await getSection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> signUp() async {
|
||||
if (driverModel.value.id != null && driverModel.value.id!.isNotEmpty) {
|
||||
await updateDriver();
|
||||
} else {
|
||||
try {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
FirebaseApp secondaryApp = await Firebase.initializeApp(
|
||||
name: 'SecondaryApp',
|
||||
options: Firebase.app().options,
|
||||
);
|
||||
|
||||
FirebaseAuth secondaryAuth = FirebaseAuth.instanceFor(app: secondaryApp);
|
||||
|
||||
final credential = await secondaryAuth.createUserWithEmailAndPassword(
|
||||
email: emailEditingController.value.text.trim(),
|
||||
password: passwordEditingController.value.text.trim(),
|
||||
);
|
||||
if (credential.user != null) {
|
||||
driverModel.value.id = credential.user!.uid;
|
||||
driverModel.value.firstName = firstNameEditingController.value.text.toString();
|
||||
driverModel.value.lastName = lastNameEditingController.value.text.toString();
|
||||
driverModel.value.email = emailEditingController.value.text.toString().toLowerCase();
|
||||
driverModel.value.phoneNumber = phoneNUmberEditingController.value.text.toString();
|
||||
driverModel.value.role = Constant.userRoleDriver;
|
||||
driverModel.value.fcmToken = await NotificationService.getToken();
|
||||
driverModel.value.active = true;
|
||||
driverModel.value.isActive = false;
|
||||
driverModel.value.isDocumentVerify = true;
|
||||
driverModel.value.countryCode = countryCodeEditingController.value.text;
|
||||
driverModel.value.createdAt = Timestamp.now();
|
||||
driverModel.value.zoneId = selectedZone.value.id;
|
||||
driverModel.value.appIdentifier = Platform.isAndroid ? 'android' : 'ios';
|
||||
driverModel.value.provider = 'email';
|
||||
driverModel.value.carNumber = carPlatNumberEditingController.value.text.toString();
|
||||
driverModel.value.isOwner = false;
|
||||
driverModel.value.ownerId = FireStoreUtils.getCurrentUid();
|
||||
driverModel.value.serviceType = selectedService.value == "Cab Service"
|
||||
? "cab-service"
|
||||
: selectedService.value == "Parcel Service"
|
||||
? "parcel_delivery"
|
||||
: selectedService.value == "Rental Service"
|
||||
? "rental-service"
|
||||
: "delivery-service";
|
||||
|
||||
if (selectedService.value == "Cab Service") {
|
||||
driverModel.value.carMakes = selectedCarMakes.value.name;
|
||||
driverModel.value.carName = selectedCarModel.value.name;
|
||||
driverModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
driverModel.value.sectionId = selectedSection.value.id;
|
||||
driverModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
driverModel.value.rideType = selectedValue.value;
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
driverModel.value.carMakes = selectedCarMakes.value.name;
|
||||
driverModel.value.carName = selectedCarModel.value.name;
|
||||
driverModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
driverModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
driverModel.value.sectionId = selectedSection.value.id;
|
||||
} else if (selectedService.value == "Parcel Service") {
|
||||
driverModel.value.sectionId = selectedSection.value.id;
|
||||
}
|
||||
|
||||
await FireStoreUtils.updateUser(driverModel.value).then(
|
||||
(value) async {
|
||||
ShowToastDialog.showToast("Driver created successfully".tr);
|
||||
Get.back(result: true);
|
||||
},
|
||||
);
|
||||
}
|
||||
} on FirebaseAuthException catch (e) {
|
||||
if (e.code == 'weak-password') {
|
||||
ShowToastDialog.showToast("The password provided is too weak.".tr);
|
||||
} else if (e.code == 'email-already-in-use') {
|
||||
ShowToastDialog.showToast("The account already exists for that email.".tr);
|
||||
} else if (e.code == 'invalid-email') {
|
||||
ShowToastDialog.showToast("Enter email is Invalid".tr);
|
||||
}
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateDriver() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
driverModel.value.firstName = firstNameEditingController.value.text.toString();
|
||||
driverModel.value.lastName = lastNameEditingController.value.text.toString();
|
||||
driverModel.value.email = emailEditingController.value.text.toString().toLowerCase();
|
||||
driverModel.value.phoneNumber = phoneNUmberEditingController.value.text.toString();
|
||||
driverModel.value.role = Constant.userRoleDriver;
|
||||
driverModel.value.fcmToken = await NotificationService.getToken();
|
||||
driverModel.value.active = true;
|
||||
driverModel.value.isActive = false;
|
||||
driverModel.value.isDocumentVerify = true;
|
||||
driverModel.value.countryCode = countryCodeEditingController.value.text;
|
||||
driverModel.value.createdAt = Timestamp.now();
|
||||
driverModel.value.zoneId = selectedZone.value.id;
|
||||
driverModel.value.appIdentifier = Platform.isAndroid ? 'android' : 'ios';
|
||||
driverModel.value.provider = 'email';
|
||||
driverModel.value.carNumber = carPlatNumberEditingController.value.text.toString();
|
||||
driverModel.value.isOwner = false;
|
||||
driverModel.value.ownerId = FireStoreUtils.getCurrentUid();
|
||||
driverModel.value.serviceType = selectedService.value == "Cab Service"
|
||||
? "cab-service"
|
||||
: selectedService.value == "Parcel Service"
|
||||
? "parcel_delivery"
|
||||
: selectedService.value == "Rental Service"
|
||||
? "rental-service"
|
||||
: "delivery-service";
|
||||
|
||||
if (selectedService.value == "Cab Service") {
|
||||
driverModel.value.carMakes = selectedCarMakes.value.name;
|
||||
driverModel.value.carName = selectedCarModel.value.name;
|
||||
driverModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
driverModel.value.sectionId = selectedSection.value.id;
|
||||
driverModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
driverModel.value.rideType = selectedValue.value;
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
driverModel.value.carMakes = selectedCarMakes.value.name;
|
||||
driverModel.value.carName = selectedCarModel.value.name;
|
||||
driverModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
driverModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
driverModel.value.sectionId = selectedSection.value.id;
|
||||
} else if (selectedService.value == "Parcel Service") {
|
||||
driverModel.value.sectionId = selectedSection.value.id;
|
||||
}
|
||||
|
||||
await FireStoreUtils.updateUser(driverModel.value).then(
|
||||
(value) async {
|
||||
ShowToastDialog.showToast("Driver update successfully".tr);
|
||||
Get.back(result: true);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getArguments() async {
|
||||
await FireStoreUtils.getZone().then((value) {
|
||||
if (value != null) {
|
||||
zoneList.value = value;
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.getCarMakes().then((value) {
|
||||
carMakesList.value = value;
|
||||
});
|
||||
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
driverModel.value = argumentData['driverModel'] as UserModel;
|
||||
|
||||
firstNameEditingController.value.text = driverModel.value.firstName ?? "";
|
||||
lastNameEditingController.value.text = driverModel.value.lastName ?? "";
|
||||
emailEditingController.value.text = driverModel.value.email ?? "";
|
||||
phoneNUmberEditingController.value.text = driverModel.value.phoneNumber ?? "";
|
||||
countryCodeEditingController.value.text = driverModel.value.countryCode ?? "+91";
|
||||
carPlatNumberEditingController.value.text = driverModel.value.carNumber ?? "";
|
||||
|
||||
selectedValue.value = driverModel.value.rideType ?? '';
|
||||
selectedService.value = driverModel.value.serviceType == "cab-service"
|
||||
? "Cab Service"
|
||||
: driverModel.value.serviceType == "parcel_delivery"
|
||||
? "Parcel Service"
|
||||
: driverModel.value.serviceType == "rental-service"
|
||||
? "Rental Service"
|
||||
: "Parcel Service";
|
||||
|
||||
|
||||
await getSection();
|
||||
|
||||
selectedZone.value = ZoneModel();
|
||||
for (var element in zoneList) {
|
||||
if (element.id == driverModel.value.zoneId) {
|
||||
selectedZone.value = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
selectedCarMakes.value = CarMakes();
|
||||
for (var element in carMakesList) {
|
||||
if (element.name == driverModel.value.carMakes) {
|
||||
selectedCarMakes.value = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedCarMakes.value.id != null) {
|
||||
await getCarModel();
|
||||
|
||||
selectedCarModel.value = CarModel();
|
||||
for (var element in carModelList) {
|
||||
if (element.name == driverModel.value.carName) {
|
||||
selectedCarModel.value = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> getSection() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
await FireStoreUtils.getSections(selectedService.value == "Cab Service"
|
||||
? "cab-service"
|
||||
: selectedService.value == "Parcel Service"
|
||||
? "parcel_delivery"
|
||||
: selectedService.value == "Rental Service"
|
||||
? "rental-service"
|
||||
: "")
|
||||
.then((value) {
|
||||
sectionList.value = value;
|
||||
print("Section List Length: ${sectionList.length}");
|
||||
if (sectionList.isNotEmpty) {
|
||||
selectedSection.value = sectionList.first;
|
||||
}
|
||||
});
|
||||
await getVehicleType();
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> getVehicleType() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
cabVehicleType.clear();
|
||||
if (selectedService.value == "Cab Service") {
|
||||
await FireStoreUtils.getCabVehicleType(selectedSection.value.id.toString()).then((value) {
|
||||
cabVehicleType.value = value;
|
||||
|
||||
if (driverModel.value.vehicleId != null && driverModel.value.vehicleId!.isNotEmpty) {
|
||||
selectedVehicleType.value = VehicleType();
|
||||
for (var element in cabVehicleType) {
|
||||
if (element.id == driverModel.value.vehicleId) {
|
||||
selectedVehicleType.value = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cabVehicleType.isNotEmpty) {
|
||||
selectedVehicleType.value = cabVehicleType.first;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
await FireStoreUtils.getRentalVehicleType(selectedSection.value.id.toString()).then((value) {
|
||||
cabVehicleType.value = value;
|
||||
if (driverModel.value.vehicleId != null && driverModel.value.vehicleId!.isNotEmpty) {
|
||||
selectedVehicleType.value = VehicleType();
|
||||
for (var element in cabVehicleType) {
|
||||
if (element.id == driverModel.value.vehicleId) {
|
||||
selectedVehicleType.value = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cabVehicleType.isNotEmpty) {
|
||||
selectedVehicleType.value = cabVehicleType.first;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> getCarModel() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
carModelList.clear();
|
||||
selectedCarModel.value = CarModel();
|
||||
await FireStoreUtils.getCarModel(selectedCarMakes.value.name.toString()).then((value) {
|
||||
carModelList.value = value;
|
||||
});
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
}
|
||||
390
lib/controllers/driver_location_controller.dart
Normal file
390
lib/controllers/driver_location_controller.dart
Normal file
@@ -0,0 +1,390 @@
|
||||
import 'dart:async';
|
||||
import 'package:driver/app/owner_screen/driver_order_list.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/themes/app_them_data.dart';
|
||||
import 'package:driver/themes/round_button_fill.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/network_image_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:flutter_map/flutter_map.dart' as flutterMap;
|
||||
import 'package:latlong2/latlong.dart' as latLng2;
|
||||
|
||||
class DriverLocationController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxList<UserModel> driverList = <UserModel>[].obs;
|
||||
RxSet<Marker> markers = <Marker>{}.obs;
|
||||
|
||||
/// OSM map data
|
||||
final flutterMap.MapController osmMapController = flutterMap.MapController();
|
||||
RxList<flutterMap.Marker> osmMarkers = <flutterMap.Marker>[].obs;
|
||||
Rx<latLng2.LatLng> current = const latLng2.LatLng(12.9716, 77.5946).obs;
|
||||
|
||||
BitmapDescriptor? driverIcon;
|
||||
final Completer<GoogleMapController> mapController = Completer();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getDriverList();
|
||||
}
|
||||
|
||||
Future<void> getDriverList() async {
|
||||
isLoading.value = true;
|
||||
await _loadDriverIcon();
|
||||
|
||||
FireStoreUtils.fireStore
|
||||
.collection(CollectionName.users)
|
||||
.where("ownerId", isEqualTo: FireStoreUtils.getCurrentUid())
|
||||
.where("isOwner", isEqualTo: false)
|
||||
.snapshots()
|
||||
.listen((event) {
|
||||
driverList.value = event.docs.map((e) => UserModel.fromJson(e.data())).toList();
|
||||
updateMarkers();
|
||||
});
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> _loadDriverIcon() async {
|
||||
final Uint8List driverBytes = await Constant().getBytesFromAsset('assets/images/ic_cab.png', 70);
|
||||
driverIcon = BitmapDescriptor.fromBytes(driverBytes);
|
||||
}
|
||||
|
||||
/// Update both Google Map + OSM markers
|
||||
void updateMarkers() {
|
||||
final newMarkers = <Marker>{};
|
||||
final newOsmMarkers = <flutterMap.Marker>[];
|
||||
|
||||
for (var driver in driverList) {
|
||||
final lat = driver.location?.latitude;
|
||||
final lng = driver.location?.longitude;
|
||||
if (lat != null && lng != null) {
|
||||
// Google Map Marker
|
||||
newMarkers.add(
|
||||
Marker(
|
||||
markerId: MarkerId(driver.id ?? ''),
|
||||
position: LatLng(lat, lng),
|
||||
rotation: double.tryParse(driver.rotation.toString()) ?? 0,
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
flat: true,
|
||||
icon: driverIcon ?? BitmapDescriptor.defaultMarker,
|
||||
onTap: () => _showDriverBottomSheet(Get.context!, driver),
|
||||
),
|
||||
);
|
||||
|
||||
// OSM Marker
|
||||
newOsmMarkers.add(flutterMap.Marker(
|
||||
point: latLng2.LatLng(lat, lng),
|
||||
width: 60,
|
||||
height: 60,
|
||||
child: GestureDetector(
|
||||
onTap: () => _showDriverBottomSheet(Get.context!, driver),
|
||||
child: Image.asset('assets/images/ic_cab.png', width: 45),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
markers.value = newMarkers;
|
||||
osmMarkers.value = newOsmMarkers;
|
||||
|
||||
if (driverList.isNotEmpty) {
|
||||
final first = driverList.first;
|
||||
if (first.location != null) {
|
||||
current.value = latLng2.LatLng(first.location!.latitude!, first.location!.longitude!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Show driver bottom sheet
|
||||
void _showDriverBottomSheet(BuildContext context, UserModel driver) {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.white,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
height: 4,
|
||||
width: 40,
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ClipOval(
|
||||
child: NetworkImageWidget(
|
||||
imageUrl: driver.profilePictureURL ?? "",
|
||||
height: 55,
|
||||
width: 55,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(driver.fullName(), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
Text("Email: ${driver.email ?? '-'}"),
|
||||
Text("Phone: ${driver.countryCode ?? ''} ${driver.phoneNumber ?? ''}"),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "Call".tr,
|
||||
height: 5,
|
||||
width: 100,
|
||||
borderRadius: 10,
|
||||
color: AppThemeData.primary300,
|
||||
textColor: AppThemeData.grey50,
|
||||
onPress: () async {
|
||||
Constant.makePhoneCall(driver.phoneNumber.toString());
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: RoundedButtonFill(
|
||||
title: "View Booking".tr,
|
||||
height: 5,
|
||||
width: 100,
|
||||
borderRadius: 10,
|
||||
color: AppThemeData.driverApp300,
|
||||
textColor: AppThemeData.grey50,
|
||||
onPress: () async {
|
||||
Get.to(DriverOrderList(), arguments: {
|
||||
"driverId": driver.id,
|
||||
"serviceType": driver.serviceType,
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Move camera for Google Map
|
||||
Future<void> moveCameraToFirstDriver(GoogleMapController mapController) async {
|
||||
if (driverList.isNotEmpty) {
|
||||
final firstDriver = driverList.first;
|
||||
if (firstDriver.location?.latitude != null && firstDriver.location?.longitude != null) {
|
||||
final position = CameraPosition(
|
||||
target: LatLng(firstDriver.location!.latitude!, firstDriver.location!.longitude!),
|
||||
zoom: 15,
|
||||
);
|
||||
await mapController.animateCamera(CameraUpdate.newCameraPosition(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Animate OSM map
|
||||
void animateToSource() {
|
||||
if (driverList.isNotEmpty && driverList.first.location != null) {
|
||||
osmMapController.move(
|
||||
latLng2.LatLng(driverList.first.location!.latitude!, driverList.first.location!.longitude!),
|
||||
14.5,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// import 'dart:async';
|
||||
// import 'package:driver/app/owner_screen/driver_order_list.dart';
|
||||
// import 'package:driver/constant/collection_name.dart';
|
||||
// import 'package:driver/constant/constant.dart';
|
||||
// import 'package:driver/models/user_model.dart';
|
||||
// import 'package:driver/themes/app_them_data.dart';
|
||||
// import 'package:driver/themes/round_button_fill.dart';
|
||||
// import 'package:driver/utils/fire_store_utils.dart';
|
||||
// import 'package:driver/utils/network_image_widget.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/services.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
//
|
||||
// class DriverLocationController extends GetxController {
|
||||
// RxBool isLoading = true.obs;
|
||||
// RxList<UserModel> driverList = <UserModel>[].obs;
|
||||
// RxSet<Marker> markers = <Marker>{}.obs;
|
||||
// BitmapDescriptor? driverIcon;
|
||||
// final Completer<GoogleMapController> mapController = Completer();
|
||||
//
|
||||
// @override
|
||||
// void onInit() {
|
||||
// super.onInit();
|
||||
// getDriverList();
|
||||
// }
|
||||
//
|
||||
// Future<void> getDriverList() async {
|
||||
// isLoading.value = true;
|
||||
// await _loadDriverIcon();
|
||||
// FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.users)
|
||||
// .where("ownerId", isEqualTo: FireStoreUtils.getCurrentUid())
|
||||
// .where("isOwner", isEqualTo: false)
|
||||
// .snapshots()
|
||||
// .listen((event) {
|
||||
// driverList.value = event.docs.map((e) => UserModel.fromJson(e.data())).toList();
|
||||
// updateMarkers();
|
||||
// });
|
||||
//
|
||||
// isLoading.value = false;
|
||||
// }
|
||||
//
|
||||
// Future<void> _loadDriverIcon() async {
|
||||
// final Uint8List driverBytes = await Constant().getBytesFromAsset('assets/images/ic_cab.png', 70);
|
||||
// driverIcon = BitmapDescriptor.fromBytes(driverBytes);
|
||||
// }
|
||||
//
|
||||
// /// Call this whenever driverList changes (e.g. from Firestore stream)
|
||||
// void updateMarkers() {
|
||||
// final newMarkers = <Marker>{};
|
||||
// for (var driver in driverList) {
|
||||
// if (driver.location!.latitude != null && driver.location!.longitude != null) {
|
||||
// newMarkers.add(
|
||||
// Marker(
|
||||
// markerId: MarkerId(driver.id ?? ''),
|
||||
// position: LatLng(driver.location!.latitude!, driver.location!.longitude!),
|
||||
// rotation: double.parse(driver.rotation.toString()),
|
||||
// anchor: const Offset(0.5, 0.5),
|
||||
// flat: true,
|
||||
// zIndex: 2,
|
||||
// icon: driverIcon ?? BitmapDescriptor.defaultMarker,
|
||||
// onTap: () {
|
||||
// _showDriverBottomSheet(Get.context!, driver);
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// markers.value = newMarkers;
|
||||
// }
|
||||
//
|
||||
// void _showDriverBottomSheet(BuildContext context, UserModel driver) {
|
||||
// showModalBottomSheet(
|
||||
// context: context,
|
||||
// backgroundColor: Colors.white,
|
||||
// shape: const RoundedRectangleBorder(
|
||||
// borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
// ),
|
||||
// builder: (context) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
// child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// Container(
|
||||
// height: 4,
|
||||
// width: 40,
|
||||
// margin: const EdgeInsets.only(bottom: 12),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Colors.grey[300],
|
||||
// borderRadius: BorderRadius.circular(2),
|
||||
// ),
|
||||
// ),
|
||||
// Row(
|
||||
// children: [
|
||||
// ClipOval(
|
||||
// child: NetworkImageWidget(
|
||||
// imageUrl: driver.profilePictureURL.toString(),
|
||||
// height: 55,
|
||||
// width: 55,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 16),
|
||||
// Expanded(
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(driver.fullName(), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
// Text("email: ${driver.email}"),
|
||||
// Text("Phone Number: ${"${driver.countryCode} ${driver.phoneNumber}"}"),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
// Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: RoundedButtonFill(
|
||||
// title: "Call".tr,
|
||||
// height: 5,
|
||||
// width: 100,
|
||||
// borderRadius: 10,
|
||||
// color: AppThemeData.primary300,
|
||||
// textColor: AppThemeData.grey50,
|
||||
// onPress: () async {
|
||||
// Constant.makePhoneCall(driver.phoneNumber.toString());
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 20),
|
||||
// Expanded(
|
||||
// child: RoundedButtonFill(
|
||||
// title: "View Booking".tr,
|
||||
// height: 5,
|
||||
// width: 100,
|
||||
// borderRadius: 10,
|
||||
// color: AppThemeData.driverApp300,
|
||||
// textColor: AppThemeData.grey50,
|
||||
// onPress: () async {
|
||||
// Get.to(() => const DriverOrderList(), arguments: {
|
||||
// "driverId": driver.id,
|
||||
// "serviceType": driver.serviceType,
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 12),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// Future<void> moveCameraToFirstDriver(GoogleMapController mapController) async {
|
||||
// if (driverList.isNotEmpty) {
|
||||
// final firstDriver = driverList.first;
|
||||
// if (firstDriver.location!.latitude != null && firstDriver.location!.longitude != null) {
|
||||
// final position = CameraPosition(
|
||||
// target: LatLng(firstDriver.location!.latitude!, firstDriver.location!.longitude!),
|
||||
// zoom: 15, // adjust zoom level as needed
|
||||
// );
|
||||
// await mapController.animateCamera(
|
||||
// CameraUpdate.newCameraPosition(position),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
21
lib/controllers/driver_order_controller.dart
Normal file
21
lib/controllers/driver_order_controller.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class DriverOrderListController extends GetxController {
|
||||
RxString driverId = "".obs;
|
||||
RxString serviceType = "".obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
|
||||
if (args != null) {
|
||||
if (args['driverId'] != null) {
|
||||
driverId.value = args['driverId'];
|
||||
}
|
||||
if (args['serviceType'] != null) {
|
||||
serviceType.value = args['serviceType'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
95
lib/controllers/edit_profile_controller.dart
Normal file
95
lib/controllers/edit_profile_controller.dart
Normal file
@@ -0,0 +1,95 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/zone_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
class EditProfileController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
Rx<TextEditingController> firstNameController = TextEditingController().obs;
|
||||
Rx<TextEditingController> lastNameController = TextEditingController().obs;
|
||||
Rx<TextEditingController> emailController = TextEditingController().obs;
|
||||
Rx<TextEditingController> phoneNumberController = TextEditingController().obs;
|
||||
Rx<TextEditingController> countryCodeController = TextEditingController(text: "+91").obs;
|
||||
|
||||
Rx<ZoneModel> selectedZone = ZoneModel().obs;
|
||||
RxList<ZoneModel> zoneList = <ZoneModel>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getData();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getData() async {
|
||||
await FireStoreUtils.getZone().then((value) {
|
||||
if (value != null) {
|
||||
zoneList.value = value;
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
firstNameController.value.text = userModel.value.firstName.toString();
|
||||
lastNameController.value.text = userModel.value.lastName.toString();
|
||||
emailController.value.text = userModel.value.email.toString();
|
||||
phoneNumberController.value.text = userModel.value.phoneNumber.toString();
|
||||
countryCodeController.value.text = userModel.value.countryCode.toString();
|
||||
profileImage.value = userModel.value.profilePictureURL ?? '';
|
||||
|
||||
for (var element in zoneList) {
|
||||
if (element.id == userModel.value.zoneId) {
|
||||
selectedZone.value = element;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> saveData() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
if (Constant().hasValidUrl(profileImage.value) == false && profileImage.value.isNotEmpty) {
|
||||
profileImage.value = await Constant.uploadUserImageToFireStorage(
|
||||
File(profileImage.value),
|
||||
"profileImage/${FireStoreUtils.getCurrentUid()}",
|
||||
File(profileImage.value).path.split('/').last,
|
||||
);
|
||||
}
|
||||
|
||||
userModel.value.firstName = firstNameController.value.text;
|
||||
userModel.value.lastName = lastNameController.value.text;
|
||||
userModel.value.profilePictureURL = profileImage.value;
|
||||
userModel.value.zoneId = selectedZone.value.id;
|
||||
|
||||
await FireStoreUtils.updateUser(userModel.value).then((value) {
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.back(result: true);
|
||||
});
|
||||
}
|
||||
|
||||
final ImagePicker _imagePicker = ImagePicker();
|
||||
RxString profileImage = "".obs;
|
||||
|
||||
Future pickFile({required ImageSource source}) async {
|
||||
try {
|
||||
XFile? image = await _imagePicker.pickImage(source: source);
|
||||
if (image == null) return;
|
||||
Get.back();
|
||||
profileImage.value = image.path;
|
||||
} on PlatformException catch (e) {
|
||||
ShowToastDialog.showToast("${"failed_to_pick".tr} : \n $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
24
lib/controllers/forgot_password_controller.dart
Normal file
24
lib/controllers/forgot_password_controller.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ForgotPasswordController extends GetxController {
|
||||
Rx<TextEditingController> emailEditingController = TextEditingController().obs;
|
||||
|
||||
Future<void> forgotPassword() async {
|
||||
try {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
await FirebaseAuth.instance.sendPasswordResetEmail(
|
||||
email: emailEditingController.value.text,
|
||||
);
|
||||
ShowToastDialog.closeLoader();
|
||||
ShowToastDialog.showToast('${'Reset Password link sent your'.tr} ${emailEditingController.value.text} ${'email'.tr}');
|
||||
Get.back();
|
||||
} on FirebaseAuthException catch (e) {
|
||||
if (e.code == 'user-not-found') {
|
||||
ShowToastDialog.showToast('No user found for that email.'.tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
lib/controllers/global_setting_controller.dart
Normal file
50
lib/controllers/global_setting_controller.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/currency_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/notification_service.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../constant/collection_name.dart';
|
||||
|
||||
class GlobalSettingController extends GetxController {
|
||||
@override
|
||||
void onInit() {
|
||||
notificationInit();
|
||||
getCurrentCurrency();
|
||||
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getCurrentCurrency() async {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.currencies).where("isActive", isEqualTo: true).snapshots().listen((event) {
|
||||
if (event.docs.isNotEmpty) {
|
||||
Constant.currencyModel = CurrencyModel.fromJson(event.docs.first.data());
|
||||
} else {
|
||||
Constant.currencyModel = CurrencyModel(id: "", code: "USD", decimalDigits: 2, enable: true, name: "US Dollar", symbol: "\$", symbolAtRight: false);
|
||||
}
|
||||
});
|
||||
await FireStoreUtils().getSettings();
|
||||
}
|
||||
|
||||
NotificationService notificationService = NotificationService();
|
||||
|
||||
void notificationInit() {
|
||||
notificationService.initInfo().then((value) async {
|
||||
String token = await NotificationService.getToken();
|
||||
log(":::::::TOKEN:::::: $token");
|
||||
if (FirebaseAuth.instance.currentUser != null) {
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) {
|
||||
if (value != null) {
|
||||
UserModel driverUserModel = value;
|
||||
driverUserModel.fcmToken = token;
|
||||
FireStoreUtils.updateUser(driverUserModel);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
610
lib/controllers/home_controller.dart
Normal file
610
lib/controllers/home_controller.dart
Normal file
@@ -0,0 +1,610 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/services/audio_player_service.dart';
|
||||
import 'package:driver/themes/app_them_data.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_map/flutter_map.dart' as flutterMap;
|
||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:latlong2/latlong.dart' as location;
|
||||
|
||||
import '../models/order_model.dart';
|
||||
|
||||
class HomeController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
flutterMap.MapController osmMapController = flutterMap.MapController();
|
||||
RxList<flutterMap.Marker> osmMarkers = <flutterMap.Marker>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getArgument();
|
||||
setIcons();
|
||||
getDriver();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<OrderModel> orderModel = OrderModel().obs;
|
||||
Rx<OrderModel> currentOrder = OrderModel().obs;
|
||||
Rx<UserModel> driverModel = UserModel().obs;
|
||||
|
||||
void getArgument() {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
orderModel.value = argumentData['orderModel'];
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> acceptOrder() async {
|
||||
await AudioPlayerService.playSound(false);
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
driverModel.value.inProgressOrderID ?? [];
|
||||
driverModel.value.orderRequestData!.remove(currentOrder.value.id);
|
||||
driverModel.value.inProgressOrderID!.add(currentOrder.value.id);
|
||||
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
|
||||
currentOrder.value.status = Constant.driverAccepted;
|
||||
currentOrder.value.driverID = driverModel.value.id;
|
||||
currentOrder.value.driver = driverModel.value;
|
||||
|
||||
await FireStoreUtils.setOrder(currentOrder.value);
|
||||
print("SendNotification ===========>");
|
||||
SendNotification.sendFcmMessage(Constant.driverAcceptedNotification, currentOrder.value.author?.fcmToken ?? '', {});
|
||||
SendNotification.sendFcmMessage(Constant.driverAcceptedNotification, currentOrder.value.vendor?.fcmToken ?? '', {});
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> rejectOrder() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
// 🔊 Stop any ongoing alert sound (if playing)
|
||||
await AudioPlayerService.playSound(false);
|
||||
|
||||
final driver = driverModel.value;
|
||||
final order = currentOrder.value;
|
||||
|
||||
// 1️⃣ Validate order and driver
|
||||
if (order.id == null || driver.id == null) {
|
||||
debugPrint("⚠️ No valid order or driver found for rejection.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2️⃣ Add driver to rejected list safely
|
||||
order.rejectedByDrivers ??= [];
|
||||
if (!order.rejectedByDrivers!.contains(driver.id)) {
|
||||
order.rejectedByDrivers!.add(driver.id);
|
||||
}
|
||||
|
||||
// 3️⃣ Update order status
|
||||
order.status = Constant.driverRejected;
|
||||
|
||||
// 4️⃣ Push order update to Firestore
|
||||
await FireStoreUtils.setOrder(order);
|
||||
|
||||
// 5️⃣ Clean up driver's order tracking data safely
|
||||
driver.orderRequestData?.remove(order.id);
|
||||
driver.inProgressOrderID?.remove(order.id);
|
||||
|
||||
// 6️⃣ Update driver info in Firestore
|
||||
await FireStoreUtils.updateUser(driver);
|
||||
|
||||
// 7️⃣ Reset order states
|
||||
currentOrder.value = OrderModel();
|
||||
orderModel.value = OrderModel();
|
||||
|
||||
// 8️⃣ Clear map visuals and UI
|
||||
await clearMap();
|
||||
update();
|
||||
|
||||
// 9️⃣ If multiple orders allowed, close dialog/screen
|
||||
if (Constant.singleOrderReceive == false && Get.isOverlaysOpen) {
|
||||
Get.back();
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
debugPrint("✅ Order ${order.id} rejected by driver ${driver.id}");
|
||||
}
|
||||
|
||||
Future<void> clearMap() async {
|
||||
await AudioPlayerService.playSound(false);
|
||||
if (Constant.selectedMapType != 'osm') {
|
||||
markers.clear();
|
||||
polyLines.clear();
|
||||
} else {
|
||||
osmMarkers.clear();
|
||||
routePoints.clear();
|
||||
// osmMapController = flutterMap.MapController();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> getCurrentOrder() async {
|
||||
final driver = driverModel.value;
|
||||
final currentId = currentOrder.value.id;
|
||||
|
||||
// 1️⃣ Reset if current order is invalid
|
||||
if (currentId != null &&
|
||||
!(driver.orderRequestData?.contains(currentId) ?? false) &&
|
||||
!(driver.inProgressOrderID?.contains(currentId) ?? false)) {
|
||||
await _resetCurrentOrder();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2️⃣ Handle single-order mode
|
||||
if (Constant.singleOrderReceive == true) {
|
||||
final inProgress = driver.inProgressOrderID;
|
||||
final requests = driver.orderRequestData;
|
||||
|
||||
if (inProgress != null && inProgress.isNotEmpty) {
|
||||
_listenToOrder(inProgress.first);
|
||||
return;
|
||||
}
|
||||
|
||||
if (requests != null && requests.isNotEmpty) {
|
||||
_listenToOrder(requests.first, checkInRequestData: true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3️⃣ Handle fallback (when orderModel has ID)
|
||||
final fallbackId = orderModel.value.id;
|
||||
if (fallbackId != null) {
|
||||
_listenToOrder(fallbackId);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _resetCurrentOrder() async {
|
||||
currentOrder.value = OrderModel();
|
||||
await clearMap();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
}
|
||||
|
||||
/// 🔹 Listen to Firestore order updates for a specific orderId
|
||||
void _listenToOrder(String orderId, {bool checkInRequestData = false}) {
|
||||
FireStoreUtils.fireStore
|
||||
.collection(CollectionName.vendorOrders)
|
||||
.where('status', whereNotIn: [
|
||||
Constant.orderCancelled,
|
||||
Constant.driverRejected,
|
||||
])
|
||||
.where('id', isEqualTo: orderId)
|
||||
.snapshots()
|
||||
.listen((event) async {
|
||||
if (event.docs.isEmpty) {
|
||||
await _handleOrderNotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
final data = event.docs.first.data();
|
||||
final newOrder = OrderModel.fromJson(data);
|
||||
|
||||
if (checkInRequestData && !(driverModel.value.orderRequestData?.contains(newOrder.id) ?? false)) {
|
||||
await _handleOrderNotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newOrder.rejectedByDrivers!.contains(driverModel.value.id)) {
|
||||
await _handleOrderNotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
currentOrder.value = newOrder;
|
||||
changeData();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _handleOrderNotFound() async {
|
||||
currentOrder.value = OrderModel();
|
||||
await AudioPlayerService.playSound(false);
|
||||
update();
|
||||
}
|
||||
|
||||
RxBool isChange = false.obs;
|
||||
|
||||
Future<void> changeData() async {
|
||||
print(
|
||||
"currentOrder.value.status :: ${currentOrder.value.id} :: ${currentOrder.value.status} :: ( ${orderModel.value.driver?.vendorID != null} :: ${orderModel.value.status})");
|
||||
|
||||
if (Constant.mapType == "inappmap") {
|
||||
if (Constant.selectedMapType == "osm") {
|
||||
getOSMPolyline();
|
||||
} else {
|
||||
getDirections();
|
||||
}
|
||||
}
|
||||
if (currentOrder.value.status == Constant.driverPending) {
|
||||
await AudioPlayerService.playSound(true);
|
||||
} else {
|
||||
await AudioPlayerService.playSound(false);
|
||||
}
|
||||
}
|
||||
|
||||
void getDriver() {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
driverModel.value = UserModel.fromJson(event.data()!);
|
||||
_updateCurrentLocationMarkers();
|
||||
if (driverModel.value.id != null) {
|
||||
isLoading.value = false;
|
||||
update();
|
||||
changeData();
|
||||
getCurrentOrder();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
GoogleMapController? mapController;
|
||||
|
||||
Rx<PolylinePoints> polylinePoints = PolylinePoints(apiKey: Constant.mapAPIKey).obs;
|
||||
RxMap<PolylineId, Polyline> polyLines = <PolylineId, Polyline>{}.obs;
|
||||
RxMap<String, Marker> markers = <String, Marker>{}.obs;
|
||||
|
||||
BitmapDescriptor? departureIcon;
|
||||
BitmapDescriptor? destinationIcon;
|
||||
BitmapDescriptor? taxiIcon;
|
||||
|
||||
Future<void> setIcons() async {
|
||||
if (Constant.selectedMapType == 'google') {
|
||||
final Uint8List departure = await Constant().getBytesFromAsset('assets/images/location_black3x.png', 100);
|
||||
final Uint8List destination = await Constant().getBytesFromAsset('assets/images/location_orange3x.png', 100);
|
||||
final Uint8List driver = await Constant().getBytesFromAsset('assets/images/food_delivery.png', 120);
|
||||
|
||||
departureIcon = BitmapDescriptor.fromBytes(departure);
|
||||
destinationIcon = BitmapDescriptor.fromBytes(destination);
|
||||
taxiIcon = BitmapDescriptor.fromBytes(driver);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getDirections() async {
|
||||
final order = currentOrder.value;
|
||||
final driver = driverModel.value;
|
||||
|
||||
// 1️⃣ Safety checks
|
||||
if (order.id == null) {
|
||||
debugPrint("⚠️ getDirections: Order ID is null");
|
||||
return;
|
||||
}
|
||||
|
||||
final driverLoc = driver.location;
|
||||
if (driverLoc == null) {
|
||||
debugPrint("⚠️ getDirections: Driver location is null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Icons must be loaded before proceeding
|
||||
if (taxiIcon == null || destinationIcon == null || departureIcon == null) {
|
||||
debugPrint("⚠️ getDirections: One or more map icons are null");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2️⃣ Get start and end coordinates based on order status
|
||||
LatLng? origin;
|
||||
LatLng? destination;
|
||||
|
||||
switch (order.status) {
|
||||
case Constant.orderShipped:
|
||||
origin = LatLng(driverLoc.latitude ?? 0.0, driverLoc.longitude ?? 0.0);
|
||||
destination = _toLatLng(order.vendor?.latitude, order.vendor?.longitude);
|
||||
break;
|
||||
|
||||
case Constant.orderInTransit:
|
||||
origin = LatLng(driverLoc.latitude ?? 0.0, driverLoc.longitude ?? 0.0);
|
||||
destination = _toLatLng(
|
||||
order.address?.location?.latitude,
|
||||
order.address?.location?.longitude,
|
||||
);
|
||||
break;
|
||||
|
||||
case Constant.driverPending:
|
||||
origin = _toLatLng(
|
||||
order.author?.location?.latitude,
|
||||
order.author?.location?.longitude,
|
||||
);
|
||||
destination = _toLatLng(order.vendor?.latitude, order.vendor?.longitude);
|
||||
break;
|
||||
|
||||
default:
|
||||
debugPrint("⚠️ getDirections: Unknown order status ${order.status}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (origin == null || destination == null) {
|
||||
debugPrint("⚠️ getDirections: Missing origin or destination");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3️⃣ Fetch polyline route
|
||||
final polylineCoordinates = await _fetchPolyline(origin, destination);
|
||||
if (polylineCoordinates.isEmpty) {
|
||||
debugPrint("⚠️ getDirections: No route found between origin and destination");
|
||||
}
|
||||
|
||||
// 4️⃣ Update markers safely
|
||||
markers.remove("Departure");
|
||||
markers.remove("Destination");
|
||||
markers.remove("Driver");
|
||||
|
||||
if (order.status == Constant.orderShipped || order.status == Constant.driverPending) {
|
||||
markers['Departure'] = Marker(
|
||||
markerId: const MarkerId('Departure'),
|
||||
infoWindow: const InfoWindow(title: "Departure"),
|
||||
position: _toLatLng(order.vendor?.latitude, order.vendor?.longitude) ?? const LatLng(0, 0),
|
||||
icon: departureIcon!,
|
||||
);
|
||||
}
|
||||
|
||||
if (order.status == Constant.orderInTransit || order.status == Constant.driverPending) {
|
||||
markers['Destination'] = Marker(
|
||||
markerId: const MarkerId('Destination'),
|
||||
infoWindow: const InfoWindow(title: "Destination"),
|
||||
position: _toLatLng(
|
||||
order.address?.location?.latitude,
|
||||
order.address?.location?.longitude,
|
||||
) ??
|
||||
const LatLng(0, 0),
|
||||
icon: destinationIcon!,
|
||||
);
|
||||
}
|
||||
|
||||
markers['Driver'] = Marker(
|
||||
markerId: const MarkerId('Driver'),
|
||||
infoWindow: const InfoWindow(title: "Driver"),
|
||||
position: LatLng(
|
||||
driverLoc.latitude ?? 0.0, // ✅ safe fallback
|
||||
driverLoc.longitude ?? 0.0, // ✅ safe fallback
|
||||
),
|
||||
icon: taxiIcon!,
|
||||
rotation: double.tryParse(driver.rotation.toString()) ?? 0,
|
||||
);
|
||||
|
||||
// 5️⃣ Draw polyline
|
||||
addPolyLine(polylineCoordinates);
|
||||
}
|
||||
|
||||
/// Helper: safely convert to LatLng if valid
|
||||
LatLng? _toLatLng(double? lat, double? lng) {
|
||||
if (lat == null || lng == null) return null;
|
||||
return LatLng(lat, lng);
|
||||
}
|
||||
|
||||
/// Helper: fetch polyline safely
|
||||
Future<List<LatLng>> _fetchPolyline(LatLng origin, LatLng destination) async {
|
||||
try {
|
||||
final result = await polylinePoints.value.getRouteBetweenCoordinates(
|
||||
request: PolylineRequest(
|
||||
origin: PointLatLng(origin.latitude, origin.longitude),
|
||||
destination: PointLatLng(destination.latitude, destination.longitude),
|
||||
mode: TravelMode.driving,
|
||||
),
|
||||
);
|
||||
|
||||
if (result.points.isEmpty) return [];
|
||||
|
||||
return result.points.map((p) => LatLng(p.latitude, p.longitude)).toList();
|
||||
} catch (e, st) {
|
||||
debugPrint("❌ getDirections _fetchPolyline error: $e\n$st");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
void addPolyLine(List<LatLng> polylineCoordinates) {
|
||||
// mapOsmController.clearAllRoads();
|
||||
PolylineId id = const PolylineId("poly");
|
||||
Polyline polyline = Polyline(
|
||||
polylineId: id,
|
||||
color: AppThemeData.primary300,
|
||||
points: polylineCoordinates,
|
||||
width: 8,
|
||||
geodesic: true,
|
||||
);
|
||||
polyLines[id] = polyline;
|
||||
update();
|
||||
updateCameraLocation(polylineCoordinates.first, mapController);
|
||||
}
|
||||
|
||||
Future<void> updateCameraLocation(
|
||||
LatLng source,
|
||||
GoogleMapController? mapController,
|
||||
) async {
|
||||
mapController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(
|
||||
CameraPosition(
|
||||
target: source,
|
||||
zoom: currentOrder.value.id == null || currentOrder.value.status == Constant.driverPending ? 16 : 20,
|
||||
bearing: double.parse(driverModel.value.rotation.toString()),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void animateToSource() {
|
||||
double lat = 0.0;
|
||||
double lng = 0.0;
|
||||
final loc = driverModel.value.location;
|
||||
if (loc != null) {
|
||||
// Use string parsing to avoid nullable-toDouble issues and handle numbers/strings.
|
||||
lat = double.tryParse('${loc.latitude}') ?? 0.0;
|
||||
lng = double.tryParse('${loc.longitude}') ?? 0.0;
|
||||
}
|
||||
_updateCurrentLocationMarkers();
|
||||
osmMapController.move(location.LatLng(lat, lng), 16);
|
||||
}
|
||||
|
||||
void _updateCurrentLocationMarkers() async {
|
||||
try {
|
||||
final loc = driverModel.value.location;
|
||||
final latLng = _safeLatLngFromLocation(loc);
|
||||
|
||||
// Update reactive current location
|
||||
current.value = location.LatLng(latLng.latitude, latLng.longitude);
|
||||
|
||||
// --- OSM Section ---
|
||||
try {
|
||||
setOsmMapMarker();
|
||||
|
||||
if (latLng.latitude != 0.0 || latLng.longitude != 0.0) {
|
||||
osmMapController.move(location.LatLng(latLng.latitude, latLng.longitude), 16);
|
||||
}
|
||||
} catch (e) {
|
||||
print("OSM map move ignored (controller not ready): $e");
|
||||
}
|
||||
|
||||
// --- GOOGLE MAP Section ---
|
||||
try {
|
||||
// Remove old driver marker
|
||||
markers.remove("Driver");
|
||||
|
||||
// Create new Google Marker
|
||||
markers["Driver"] = Marker(
|
||||
markerId: const MarkerId("Driver"),
|
||||
infoWindow: const InfoWindow(title: "Driver"),
|
||||
position: LatLng(current.value.latitude, current.value.longitude),
|
||||
icon: taxiIcon!,
|
||||
rotation: _safeRotation(),
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
flat: true,
|
||||
);
|
||||
|
||||
// Animate camera to current driver location
|
||||
if (mapController != null && !(current.value.latitude == 0.0 && current.value.longitude == 0.0)) {
|
||||
mapController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(
|
||||
CameraPosition(
|
||||
target: LatLng(current.value.latitude, current.value.longitude),
|
||||
zoom: 16,
|
||||
bearing: _safeRotation(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Google map update ignored (controller not ready): $e");
|
||||
}
|
||||
|
||||
update();
|
||||
} catch (e) {
|
||||
print("_updateCurrentLocationMarkers error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
double _safeRotation() {
|
||||
return double.tryParse(driverModel.value.rotation.toString()) ?? 0.0;
|
||||
}
|
||||
|
||||
LatLng _safeLatLngFromLocation(dynamic loc) {
|
||||
final lat = (loc?.latitude is num) ? loc.latitude.toDouble() : 0.0;
|
||||
final lng = (loc?.longitude is num) ? loc.longitude.toDouble() : 0.0;
|
||||
return LatLng(lat, lng);
|
||||
}
|
||||
|
||||
Rx<location.LatLng> source = location.LatLng(21.1702, 72.8311).obs; // Start (e.g., Surat)
|
||||
Rx<location.LatLng> current = location.LatLng(21.1800, 72.8400).obs; // Moving marker
|
||||
Rx<location.LatLng> destination = location.LatLng(21.2000, 72.8600).obs; // Destination
|
||||
|
||||
void setOsmMapMarker() {
|
||||
osmMarkers.value = [
|
||||
flutterMap.Marker(
|
||||
point: current.value,
|
||||
width: 45,
|
||||
height: 45,
|
||||
rotate: true,
|
||||
child: Image.asset('assets/images/food_delivery.png'),
|
||||
),
|
||||
flutterMap.Marker(
|
||||
point: source.value,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Image.asset('assets/images/location_black3x.png'),
|
||||
),
|
||||
flutterMap.Marker(
|
||||
point: destination.value,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Image.asset('assets/images/location_orange3x.png'),
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
void getOSMPolyline() async {
|
||||
try {
|
||||
if (currentOrder.value.id != null) {
|
||||
if (currentOrder.value.status != Constant.driverPending) {
|
||||
print("Order Status :: ${currentOrder.value.status} :: OrderId :: ${currentOrder.value.id}} ::");
|
||||
if (currentOrder.value.status == Constant.orderShipped) {
|
||||
current.value = location.LatLng(driverModel.value.location!.latitude ?? 0.0, driverModel.value.location!.longitude ?? 0.0);
|
||||
destination.value = location.LatLng(
|
||||
currentOrder.value.vendor!.latitude ?? 0.0,
|
||||
currentOrder.value.vendor!.longitude ?? 0.0,
|
||||
);
|
||||
animateToSource();
|
||||
fetchRoute(current.value, destination.value).then((value) {
|
||||
setOsmMapMarker();
|
||||
});
|
||||
} else if (currentOrder.value.status == Constant.orderInTransit) {
|
||||
print(":::::::::::::${currentOrder.value.status}::::::::::::::::::44");
|
||||
current.value = location.LatLng(driverModel.value.location!.latitude ?? 0.0, driverModel.value.location!.longitude ?? 0.0);
|
||||
destination.value = location.LatLng(
|
||||
currentOrder.value.address!.location!.latitude ?? 0.0,
|
||||
currentOrder.value.address!.location!.longitude ?? 0.0,
|
||||
);
|
||||
setOsmMapMarker();
|
||||
fetchRoute(current.value, destination.value).then((value) {
|
||||
setOsmMapMarker();
|
||||
});
|
||||
animateToSource();
|
||||
}
|
||||
} else {
|
||||
print("====>5");
|
||||
current.value =
|
||||
location.LatLng(currentOrder.value.author!.location!.latitude ?? 0.0, currentOrder.value.author!.location!.longitude ?? 0.0);
|
||||
|
||||
destination.value = location.LatLng(currentOrder.value.vendor!.latitude ?? 0.0, currentOrder.value.vendor!.longitude ?? 0.0);
|
||||
animateToSource();
|
||||
fetchRoute(current.value, destination.value).then((value) {
|
||||
setOsmMapMarker();
|
||||
});
|
||||
animateToSource();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
RxList<location.LatLng> routePoints = <location.LatLng>[].obs;
|
||||
|
||||
Future<void> fetchRoute(location.LatLng source, location.LatLng destination) async {
|
||||
final url = Uri.parse(
|
||||
'https://router.project-osrm.org/route/v1/driving/${source.longitude},${source.latitude};${destination.longitude},${destination.latitude}?overview=full&geometries=geojson',
|
||||
);
|
||||
|
||||
final response = await http.get(url);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = json.decode(response.body);
|
||||
final geometry = decoded['routes'][0]['geometry']['coordinates'];
|
||||
|
||||
routePoints.clear();
|
||||
for (var coord in geometry) {
|
||||
final lon = coord[0];
|
||||
final lat = coord[1];
|
||||
routePoints.add(location.LatLng(lat, lon));
|
||||
}
|
||||
} else {
|
||||
print("Failed to get route: ${response.body}");
|
||||
}
|
||||
}
|
||||
}
|
||||
92
lib/controllers/home_screen_multiple_order_controller.dart
Normal file
92
lib/controllers/home_screen_multiple_order_controller.dart
Normal file
@@ -0,0 +1,92 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/services/audio_player_service.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class HomeScreenMultipleOrderController extends GetxController {
|
||||
Rx<UserModel> driverModel = Constant.userModel!.obs;
|
||||
RxBool isLoading = true.obs;
|
||||
RxInt selectedTabIndex = 0.obs;
|
||||
|
||||
RxList<dynamic> newOrder = [].obs;
|
||||
RxList<dynamic> activeOrder = [].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInt
|
||||
getDriver();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getDriver() async {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
driverModel.value = UserModel.fromJson(event.data()!);
|
||||
Constant.userModel = driverModel.value;
|
||||
newOrder.clear();
|
||||
activeOrder.clear();
|
||||
if (driverModel.value.orderRequestData != null) {
|
||||
for (var element in driverModel.value.orderRequestData!) {
|
||||
newOrder.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
if (driverModel.value.inProgressOrderID != null) {
|
||||
for (var element in driverModel.value.inProgressOrderID!) {
|
||||
activeOrder.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
if (newOrder.isEmpty == true) {
|
||||
await AudioPlayerService.playSound(false);
|
||||
}
|
||||
|
||||
if (newOrder.isNotEmpty) {
|
||||
if (driverModel.value.vendorID?.isEmpty == true) {
|
||||
await AudioPlayerService.playSound(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> acceptOrder(OrderModel currentOrder) async {
|
||||
await AudioPlayerService.playSound(false);
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
driverModel.value.inProgressOrderID ?? [];
|
||||
driverModel.value.orderRequestData!.remove(currentOrder.id);
|
||||
driverModel.value.inProgressOrderID!.add(currentOrder.id);
|
||||
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
|
||||
currentOrder.status = Constant.driverAccepted;
|
||||
currentOrder.driverID = driverModel.value.id;
|
||||
currentOrder.driver = driverModel.value;
|
||||
|
||||
await FireStoreUtils.setOrder(currentOrder);
|
||||
ShowToastDialog.closeLoader();
|
||||
await SendNotification.sendFcmMessage(Constant.driverAcceptedNotification, currentOrder.author!.fcmToken.toString(), {});
|
||||
await SendNotification.sendFcmMessage(Constant.driverAcceptedNotification, currentOrder.vendor!.fcmToken.toString(), {});
|
||||
}
|
||||
|
||||
Future<void> rejectOrder(OrderModel currentOrder) async {
|
||||
await AudioPlayerService.playSound(false);
|
||||
currentOrder.rejectedByDrivers ??= [];
|
||||
|
||||
currentOrder.rejectedByDrivers!.add(driverModel.value.id);
|
||||
currentOrder.status = Constant.driverRejected;
|
||||
await FireStoreUtils.setOrder(currentOrder);
|
||||
|
||||
driverModel.value.orderRequestData!.remove(currentOrder.id);
|
||||
await FireStoreUtils.updateUser(driverModel.value);
|
||||
}
|
||||
}
|
||||
278
lib/controllers/login_controller.dart
Normal file
278
lib/controllers/login_controller.dart
Normal file
@@ -0,0 +1,278 @@
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:driver/app/auth_screen/signup_screen.dart';
|
||||
import 'package:driver/app/cab_screen/cab_dashboard_screen.dart';
|
||||
import 'package:driver/app/dash_board_screen/dash_board_screen.dart';
|
||||
import 'package:driver/app/owner_screen/owner_dashboard_screen.dart';
|
||||
import 'package:driver/app/parcel_screen/parcel_dashboard_screen.dart';
|
||||
import 'package:driver/app/rental_service/rental_dashboard_screen.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/notification_service.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||
|
||||
class LoginController extends GetxController {
|
||||
Rx<TextEditingController> emailEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> passwordEditingController = TextEditingController().obs;
|
||||
|
||||
RxBool passwordVisible = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> loginWithEmailAndPassword() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
try {
|
||||
final credential = await FirebaseAuth.instance.signInWithEmailAndPassword(
|
||||
email: emailEditingController.value.text.toLowerCase().trim(),
|
||||
password: passwordEditingController.value.text.trim(),
|
||||
);
|
||||
UserModel? userModel = await FireStoreUtils.getUserProfile(credential.user!.uid);
|
||||
if (userModel?.role == Constant.userRoleDriver) {
|
||||
if (userModel?.active == true) {
|
||||
userModel?.fcmToken = await NotificationService.getToken();
|
||||
await FireStoreUtils.updateUser(userModel!);
|
||||
if (Constant.autoApproveDriver == true) {
|
||||
if (userModel.isOwner == true) {
|
||||
Get.offAll(OwnerDashboardScreen());
|
||||
} else {
|
||||
print(userModel.serviceType);
|
||||
if (userModel.serviceType == "delivery-service") {
|
||||
Get.offAll(const DashBoardScreen());
|
||||
} else if (userModel.serviceType == "cab-service") {
|
||||
Get.offAll(const CabDashboardScreen());
|
||||
} else if (userModel.serviceType == "parcel_delivery") {
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
} else if (userModel.serviceType == "rental-service") {
|
||||
Get.offAll(const RentalDashboardScreen());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
ShowToastDialog.showToast("This user is disable please contact to administrator".tr);
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
ShowToastDialog.showToast("This user is not created in driver application.".tr);
|
||||
}
|
||||
} on FirebaseAuthException catch (e) {
|
||||
print(e.code);
|
||||
if (e.code == 'user-not-found') {
|
||||
ShowToastDialog.showToast("No user found for that email.".tr);
|
||||
} else if (e.code == 'wrong-password') {
|
||||
ShowToastDialog.showToast("Wrong password provided for that user.".tr);
|
||||
} else if (e.code == 'invalid-email') {
|
||||
ShowToastDialog.showToast("Invalid Email.".tr);
|
||||
}
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> loginWithGoogle() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
await signInWithGoogle().then((value) async {
|
||||
ShowToastDialog.closeLoader();
|
||||
if (value != null) {
|
||||
if (value.additionalUserInfo!.isNewUser) {
|
||||
UserModel userModel = UserModel();
|
||||
userModel.id = value.user!.uid;
|
||||
userModel.email = value.user!.email;
|
||||
userModel.firstName = value.user!.displayName?.split(' ').first;
|
||||
userModel.lastName = value.user!.displayName?.split(' ').last;
|
||||
userModel.provider = 'google';
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.to(const SignupScreen(), arguments: {
|
||||
"userModel": userModel,
|
||||
"type": "google",
|
||||
});
|
||||
} else {
|
||||
await FireStoreUtils.userExistOrNot(value.user!.uid).then((userExit) async {
|
||||
ShowToastDialog.closeLoader();
|
||||
if (userExit == true) {
|
||||
UserModel? userModel = await FireStoreUtils.getUserProfile(value.user!.uid);
|
||||
if (userModel != null && userModel.role == Constant.userRoleDriver) {
|
||||
if (userModel.active == true) {
|
||||
userModel.fcmToken = await NotificationService.getToken();
|
||||
await FireStoreUtils.updateUser(userModel);
|
||||
if (userModel.isOwner == true) {
|
||||
Get.offAll(OwnerDashboardScreen());
|
||||
} else {
|
||||
if (userModel.serviceType == "delivery-service") {
|
||||
Get.offAll(const DashBoardScreen());
|
||||
} else if (userModel.serviceType == "cab-service") {
|
||||
Get.offAll(const CabDashboardScreen());
|
||||
} else if (userModel.serviceType == "parcel_delivery") {
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
} else if (userModel.serviceType == "rental-service") {
|
||||
Get.offAll(const RentalDashboardScreen());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
ShowToastDialog.showToast("This user is disable please contact to administrator".tr);
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
// ShowToastDialog.showToast("This user is disable please contact to administrator".tr);
|
||||
}
|
||||
} else {
|
||||
UserModel userModel = UserModel();
|
||||
userModel.id = value.user!.uid;
|
||||
userModel.email = value.user!.email;
|
||||
userModel.firstName = value.user!.displayName?.split(' ').first;
|
||||
userModel.lastName = value.user!.displayName?.split(' ').last;
|
||||
userModel.provider = 'google';
|
||||
|
||||
Get.to(const SignupScreen(), arguments: {
|
||||
"userModel": userModel,
|
||||
"type": "google",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> loginWithApple() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
await signInWithApple().then((value) async {
|
||||
ShowToastDialog.closeLoader();
|
||||
if (value != null) {
|
||||
Map<String, dynamic> map = value;
|
||||
AuthorizationCredentialAppleID appleCredential = map['appleCredential'];
|
||||
UserCredential userCredential = map['userCredential'];
|
||||
if (userCredential.additionalUserInfo!.isNewUser) {
|
||||
UserModel userModel = UserModel();
|
||||
userModel.id = userCredential.user!.uid;
|
||||
userModel.email = appleCredential.email;
|
||||
userModel.firstName = appleCredential.givenName;
|
||||
userModel.lastName = appleCredential.familyName;
|
||||
userModel.provider = 'apple';
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.off(const SignupScreen(), arguments: {
|
||||
"userModel": userModel,
|
||||
"type": "apple",
|
||||
});
|
||||
} else {
|
||||
await FireStoreUtils.userExistOrNot(userCredential.user!.uid).then((userExit) async {
|
||||
ShowToastDialog.closeLoader();
|
||||
if (userExit == true) {
|
||||
UserModel? userModel = await FireStoreUtils.getUserProfile(userCredential.user!.uid);
|
||||
if (userModel != null && userModel.role == Constant.userRoleDriver) {
|
||||
if (userModel.active == true) {
|
||||
userModel.fcmToken = await NotificationService.getToken();
|
||||
await FireStoreUtils.updateUser(userModel);
|
||||
if (userModel.isOwner == true) {
|
||||
Get.offAll(OwnerDashboardScreen());
|
||||
} else {
|
||||
if (userModel.serviceType == "delivery-service") {
|
||||
Get.offAll(const DashBoardScreen());
|
||||
} else if (userModel.serviceType == "cab-service") {
|
||||
Get.offAll(const CabDashboardScreen());
|
||||
} else if (userModel.serviceType == "parcel_delivery") {
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
} else if (userModel.serviceType == "rental-service") {
|
||||
Get.offAll(const RentalDashboardScreen());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
ShowToastDialog.showToast("This user is disable please contact to administrator".tr);
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
// ShowToastDialog.showToast("This user is disable please contact to administrator".tr);
|
||||
}
|
||||
} else {
|
||||
UserModel userModel = UserModel();
|
||||
userModel.id = userCredential.user!.uid;
|
||||
userModel.email = appleCredential.email;
|
||||
userModel.firstName = appleCredential.givenName;
|
||||
userModel.lastName = appleCredential.familyName;
|
||||
userModel.provider = 'apple';
|
||||
|
||||
Get.off(const SignupScreen(), arguments: {
|
||||
"userModel": userModel,
|
||||
"type": "apple",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<UserCredential?> signInWithGoogle() async {
|
||||
try {
|
||||
final GoogleSignIn googleSignIn = GoogleSignIn.instance;
|
||||
|
||||
await googleSignIn.initialize();
|
||||
|
||||
final GoogleSignInAccount googleUser = await googleSignIn.authenticate();
|
||||
if (googleUser.id.isEmpty) return null;
|
||||
|
||||
final GoogleSignInAuthentication googleAuth = googleUser.authentication;
|
||||
|
||||
final credential = GoogleAuthProvider.credential(
|
||||
idToken: googleAuth.idToken,
|
||||
);
|
||||
final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
|
||||
|
||||
return userCredential;
|
||||
} catch (e) {
|
||||
print("Google Sign-In Error: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String sha256ofString(String input) {
|
||||
final bytes = utf8.encode(input);
|
||||
final digest = sha256.convert(bytes);
|
||||
return digest.toString();
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> signInWithApple() async {
|
||||
try {
|
||||
final rawNonce = generateNonce();
|
||||
final nonce = sha256ofString(rawNonce);
|
||||
|
||||
// Request credential for the currently signed in Apple account.
|
||||
AuthorizationCredentialAppleID appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
nonce: nonce,
|
||||
// webAuthenticationOptions: WebAuthenticationOptions(clientId: clientID, redirectUri: Uri.parse(redirectURL)),
|
||||
);
|
||||
|
||||
// Create an `OAuthCredential` from the credential returned by Apple.
|
||||
final oauthCredential = OAuthProvider("apple.com").credential(
|
||||
idToken: appleCredential.identityToken,
|
||||
rawNonce: rawNonce,
|
||||
accessToken: appleCredential.authorizationCode,
|
||||
);
|
||||
|
||||
// Sign in the user with Firebase. If the nonce we generated earlier does
|
||||
// not match the nonce in `appleCredential.identityToken`, sign in will fail.
|
||||
UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(oauthCredential);
|
||||
return {"appleCredential": appleCredential, "userCredential": userCredential};
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
45
lib/controllers/on_boarding_controller.dart
Normal file
45
lib/controllers/on_boarding_controller.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../models/on_boarding_model.dart';
|
||||
import '../utils/fire_store_utils.dart';
|
||||
|
||||
class OnboardingController extends GetxController {
|
||||
RxInt currentPage = 0.obs;
|
||||
late PageController pageController;
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
RxList<OnBoardingModel> onboardingList = <OnBoardingModel>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
pageController = PageController();
|
||||
getOnBoardingData();
|
||||
}
|
||||
|
||||
void nextPage() {
|
||||
if (currentPage.value < onboardingList.length - 1) {
|
||||
pageController.nextPage(duration: 300.milliseconds, curve: Curves.ease);
|
||||
}
|
||||
}
|
||||
|
||||
void onPageChanged(int index) {
|
||||
currentPage.value = index;
|
||||
}
|
||||
|
||||
Future<void> getOnBoardingData() async {
|
||||
isLoading.value = true;
|
||||
await FireStoreUtils.getOnBoardingList().then((value) {
|
||||
onboardingList.value = value;
|
||||
});
|
||||
|
||||
print(onboardingList.length);
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
pageController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
67
lib/controllers/order_details_controller.dart
Normal file
67
lib/controllers/order_details_controller.dart
Normal file
@@ -0,0 +1,67 @@
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class OrderDetailsController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<OrderModel> orderModel = OrderModel().obs;
|
||||
|
||||
Future<void> getArgument() async {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
orderModel.value = argumentData['orderModel'];
|
||||
}
|
||||
calculatePrice();
|
||||
update();
|
||||
}
|
||||
|
||||
RxDouble subTotal = 0.0.obs;
|
||||
RxDouble specialDiscountAmount = 0.0.obs;
|
||||
RxDouble taxAmount = 0.0.obs;
|
||||
RxDouble totalAmount = 0.0.obs;
|
||||
|
||||
Future<void> calculatePrice() async {
|
||||
subTotal.value = 0.0;
|
||||
specialDiscountAmount.value = 0.0;
|
||||
taxAmount.value = 0.0;
|
||||
totalAmount.value = 0.0;
|
||||
|
||||
for (var element in orderModel.value.products!) {
|
||||
if (double.parse(element.discountPrice.toString()) <= 0) {
|
||||
subTotal.value = subTotal.value +
|
||||
double.parse(element.price.toString()) * double.parse(element.quantity.toString()) +
|
||||
(double.parse(element.extrasPrice.toString()) * double.parse(element.quantity.toString()));
|
||||
} else {
|
||||
subTotal.value = subTotal.value +
|
||||
double.parse(element.discountPrice.toString()) * double.parse(element.quantity.toString()) +
|
||||
(double.parse(element.extrasPrice.toString()) * double.parse(element.quantity.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (orderModel.value.specialDiscount != null && orderModel.value.specialDiscount!['special_discount'] != null) {
|
||||
specialDiscountAmount.value = double.parse(orderModel.value.specialDiscount!['special_discount'].toString());
|
||||
}
|
||||
|
||||
if (orderModel.value.taxSetting != null) {
|
||||
for (var element in orderModel.value.taxSetting!) {
|
||||
taxAmount.value = taxAmount.value +
|
||||
Constant.calculateTax(amount: (subTotal.value - double.parse(orderModel.value.discount.toString()) - specialDiscountAmount.value).toString(), taxModel: element);
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount.value = (subTotal.value - double.parse(orderModel.value.discount.toString()) - specialDiscountAmount.value) +
|
||||
taxAmount.value +
|
||||
double.parse(orderModel.value.deliveryCharge.toString()) +
|
||||
double.parse(orderModel.value.tipAmount.toString());
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
38
lib/controllers/order_list_controller.dart
Normal file
38
lib/controllers/order_list_controller.dart
Normal file
@@ -0,0 +1,38 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class OrderListController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getOrder();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
RxList<OrderModel> orderList = <OrderModel>[].obs;
|
||||
|
||||
Future<void> getOrder() async {
|
||||
await FireStoreUtils.fireStore
|
||||
.collection(CollectionName.vendorOrders)
|
||||
.where('driverID', isEqualTo: Constant.userModel!.id.toString())
|
||||
.orderBy('createdAt', descending: true)
|
||||
.get()
|
||||
.then((value) {
|
||||
for (var element in value.docs) {
|
||||
OrderModel dailyEarningModel = OrderModel.fromJson(element.data());
|
||||
orderList.add(dailyEarningModel);
|
||||
}
|
||||
}).catchError((error) {
|
||||
log(error.toString());
|
||||
});
|
||||
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
50
lib/controllers/otp_controller.dart
Normal file
50
lib/controllers/otp_controller.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class OtpController extends GetxController {
|
||||
Rx<TextEditingController> otpController = TextEditingController().obs;
|
||||
|
||||
RxString countryCode = "".obs;
|
||||
RxString phoneNumber = "".obs;
|
||||
RxString verificationId = "".obs;
|
||||
RxInt resendToken = 0.obs;
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getArgument() async {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
countryCode.value = argumentData['countryCode'];
|
||||
phoneNumber.value = argumentData['phoneNumber'];
|
||||
verificationId.value = argumentData['verificationId'];
|
||||
}
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<bool> sendOTP() async {
|
||||
await FirebaseAuth.instance.verifyPhoneNumber(
|
||||
phoneNumber: countryCode.value + phoneNumber.value,
|
||||
verificationCompleted: (PhoneAuthCredential credential) {},
|
||||
verificationFailed: (FirebaseAuthException e) {},
|
||||
codeSent: (String verificationId0, int? resendToken0) async {
|
||||
verificationId.value = verificationId0;
|
||||
resendToken.value = resendToken0!;
|
||||
ShowToastDialog.showToast("OTP sent".tr);
|
||||
},
|
||||
timeout: const Duration(seconds: 25),
|
||||
forceResendingToken: resendToken.value,
|
||||
codeAutoRetrievalTimeout: (String verificationId0) {
|
||||
verificationId0 = verificationId.value;
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
58
lib/controllers/owner_dashboard_controller.dart
Normal file
58
lib/controllers/owner_dashboard_controller.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../themes/theme_controller.dart';
|
||||
|
||||
class OwnerDashboardController extends GetxController{
|
||||
RxInt drawerIndex = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
|
||||
getUser();
|
||||
getTheme();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
DateTime? currentBackPressTime;
|
||||
RxBool canPopNow = false.obs;
|
||||
|
||||
Future<void> getUser() async {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
Constant.userModel = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
RxString isDarkMode = "Light".obs;
|
||||
RxBool isDarkModeSwitch = false.obs;
|
||||
|
||||
void getTheme() {
|
||||
bool isDark = Preferences.getBoolean(Preferences.themKey);
|
||||
isDarkMode.value = isDark ? "Dark" : "Light";
|
||||
isDarkModeSwitch.value = isDark;
|
||||
}
|
||||
|
||||
void toggleDarkMode(bool value) {
|
||||
isDarkModeSwitch.value = value;
|
||||
isDarkMode.value = value ? "Dark" : "Light";
|
||||
Preferences.setBoolean(Preferences.themKey, value);
|
||||
// Update ThemeController for instant app theme change
|
||||
if (Get.isRegistered<ThemeController>()) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
themeController.isDark.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
188
lib/controllers/owner_home_controller.dart
Normal file
188
lib/controllers/owner_home_controller.dart
Normal file
@@ -0,0 +1,188 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/models/cab_order_model.dart';
|
||||
import 'package:driver/models/parcel_order_model.dart';
|
||||
import 'package:driver/models/rental_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../constant/constant.dart';
|
||||
import '../constant/show_toast_dialog.dart';
|
||||
import '../models/tax_model.dart';
|
||||
|
||||
class OwnerHomeController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxList<UserModel> driverList = <UserModel>[].obs;
|
||||
|
||||
RxMap<String, int> driverRideCounts = <String, int>{}.obs;
|
||||
|
||||
RxMap<String, double> driverEarnings = <String, double>{}.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getDriverList();
|
||||
}
|
||||
|
||||
Future<void> getDriverList() async {
|
||||
isLoading.value = true;
|
||||
driverList.value = await FireStoreUtils.getOwnerDriver();
|
||||
isLoading.value = false;
|
||||
|
||||
for (var driver in driverList) {
|
||||
if (driver.id != null && driver.id!.isNotEmpty) {
|
||||
_listenDriverOrders(driver.id!);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _listenDriverOrders(String driverId) {
|
||||
FireStoreUtils.getCabDriverOrders(driverId).listen((orders) {
|
||||
_processCabOrders(driverId, orders);
|
||||
});
|
||||
|
||||
FireStoreUtils.listenParcelOrders(driverId).listen((orders) {
|
||||
_processParcelOrders(driverId, orders);
|
||||
});
|
||||
|
||||
FireStoreUtils.getRentalOrders(driverId).listen((orders) {
|
||||
_processRentalOrders(driverId, orders);
|
||||
});
|
||||
}
|
||||
|
||||
void _processCabOrders(String driverId, List<CabOrderModel> orders) {
|
||||
final completedOrders = orders.where((o) => o.status == Constant.orderCompleted).toList();
|
||||
|
||||
driverRideCounts["cab_$driverId"] = completedOrders.length;
|
||||
_updateDriverTotalRides(driverId);
|
||||
|
||||
double earnings = 0;
|
||||
for (var order in completedOrders) {
|
||||
double subTotal = double.tryParse(order.subTotal ?? "0") ?? 0;
|
||||
double discount = double.tryParse(order.discount ?? "0") ?? 0;
|
||||
double tip = double.tryParse(order.tipAmount ?? "0") ?? 0;
|
||||
|
||||
double taxTotal = _calculateTax(order.taxSetting, subTotal, discount);
|
||||
double driverEarned = (subTotal - discount + tip + taxTotal).clamp(0, double.infinity);
|
||||
|
||||
earnings += driverEarned;
|
||||
}
|
||||
|
||||
driverEarnings["cab_$driverId"] = earnings;
|
||||
_updateDriverTotalEarnings(driverId);
|
||||
}
|
||||
|
||||
void _processParcelOrders(String driverId, List<ParcelOrderModel> orders) {
|
||||
final completedOrders = orders.where((o) => o.status == Constant.orderCompleted).toList();
|
||||
|
||||
driverRideCounts["parcel_$driverId"] = completedOrders.length;
|
||||
_updateDriverTotalRides(driverId);
|
||||
|
||||
double earnings = 0;
|
||||
for (var order in completedOrders) {
|
||||
double subTotal = double.tryParse(order.subTotal?.toString() ?? "0") ?? 0;
|
||||
double discount = double.tryParse(order.discount?.toString() ?? "0") ?? 0;
|
||||
|
||||
double taxTotal = _calculateTax(order.taxSetting, subTotal, discount);
|
||||
double driverEarned = (subTotal - discount + taxTotal).clamp(0, double.infinity);
|
||||
|
||||
earnings += driverEarned;
|
||||
}
|
||||
|
||||
driverEarnings["parcel_$driverId"] = earnings;
|
||||
_updateDriverTotalEarnings(driverId);
|
||||
}
|
||||
|
||||
void _processRentalOrders(String driverId, List<RentalOrderModel> orders) {
|
||||
final completedOrders = orders.where((o) => o.status == Constant.orderCompleted).toList();
|
||||
|
||||
driverRideCounts["rental_$driverId"] = completedOrders.length;
|
||||
_updateDriverTotalRides(driverId);
|
||||
|
||||
double earnings = 0;
|
||||
for (var order in completedOrders) {
|
||||
double subTotal = double.tryParse(order.subTotal ?? "0") ?? 0;
|
||||
double discount = double.tryParse(order.discount ?? "0") ?? 0;
|
||||
double tip = double.tryParse(order.tipAmount ?? "0") ?? 0;
|
||||
|
||||
double taxTotal = _calculateTax(order.taxSetting, subTotal, discount);
|
||||
double driverEarned = (subTotal - discount + tip + taxTotal).clamp(0, double.infinity);
|
||||
|
||||
earnings += driverEarned;
|
||||
}
|
||||
|
||||
driverEarnings["rental_$driverId"] = earnings;
|
||||
_updateDriverTotalEarnings(driverId);
|
||||
}
|
||||
|
||||
double _calculateTax(List<TaxModel>? taxList, double subTotal, double discount) {
|
||||
if (taxList == null) return 0.0;
|
||||
double totalTax = 0.0;
|
||||
|
||||
for (var tax in taxList) {
|
||||
if (tax.enable != true) continue;
|
||||
|
||||
double taxVal = double.tryParse(tax.tax ?? '0') ?? 0.0;
|
||||
String type = (tax.type ?? 'fix').toLowerCase();
|
||||
|
||||
if (type == "percentage") {
|
||||
totalTax += (subTotal - discount) * taxVal / 100;
|
||||
} else {
|
||||
totalTax += taxVal;
|
||||
}
|
||||
}
|
||||
|
||||
return totalTax;
|
||||
}
|
||||
|
||||
void _updateDriverTotalRides(String driverId) {
|
||||
final cab = driverRideCounts["cab_$driverId"] ?? 0;
|
||||
final parcel = driverRideCounts["parcel_$driverId"] ?? 0;
|
||||
final rental = driverRideCounts["rental_$driverId"] ?? 0;
|
||||
driverRideCounts[driverId] = cab + parcel + rental;
|
||||
}
|
||||
|
||||
void _updateDriverTotalEarnings(String driverId) {
|
||||
final cab = driverEarnings["cab_$driverId"] ?? 0;
|
||||
final parcel = driverEarnings["parcel_$driverId"] ?? 0;
|
||||
final rental = driverEarnings["rental_$driverId"] ?? 0;
|
||||
driverEarnings[driverId] = cab + parcel + rental;
|
||||
}
|
||||
|
||||
/// Totals by type across all drivers
|
||||
int get totalCabRides => driverRideCounts.entries.where((e) => e.key.startsWith("cab_")).fold(0, (sum, e) => sum + e.value);
|
||||
|
||||
int get totalParcelRides => driverRideCounts.entries.where((e) => e.key.startsWith("parcel_")).fold(0, (sum, e) => sum + e.value);
|
||||
|
||||
int get totalRentalRides => driverRideCounts.entries.where((e) => e.key.startsWith("rental_")).fold(0, (sum, e) => sum + e.value);
|
||||
|
||||
double get totalCabEarnings => driverEarnings.entries.where((e) => e.key.startsWith("cab_")).fold(0.0, (sum, e) => sum + e.value);
|
||||
|
||||
double get totalParcelEarnings => driverEarnings.entries.where((e) => e.key.startsWith("parcel_")).fold(0.0, (sum, e) => sum + e.value);
|
||||
|
||||
double get totalRentalEarnings => driverEarnings.entries.where((e) => e.key.startsWith("rental_")).fold(0.0, (sum, e) => sum + e.value);
|
||||
|
||||
/// Grand totals across all drivers
|
||||
int get totalRidesAllDrivers => driverList.fold(0, (total, driver) => total + (driverRideCounts[driver.id] ?? 0));
|
||||
|
||||
double get totalEarningsAllDrivers => driverList.fold(0.0, (total, driver) => total + (driverEarnings[driver.id] ?? 0));
|
||||
|
||||
Future<void> deleteDriver(String driverId) async {
|
||||
ShowToastDialog.showLoader("Deleting driver...".tr);
|
||||
|
||||
await FireStoreUtils.deleteDriverId(driverId).then((isDeleted) async {
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
if (isDeleted) {
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.users).doc(driverId).delete();
|
||||
ShowToastDialog.showToast("Driver account deleted successfully".tr);
|
||||
getDriverList();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Failed to delete driver. Please contact administrator.".tr);
|
||||
}
|
||||
}).catchError((error) {
|
||||
ShowToastDialog.closeLoader();
|
||||
ShowToastDialog.showToast("An error occurred while deleting driver: $error".tr);
|
||||
});
|
||||
}
|
||||
}
|
||||
263
lib/controllers/owner_order_list_controller.dart
Normal file
263
lib/controllers/owner_order_list_controller.dart
Normal file
@@ -0,0 +1,263 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import '../models/cab_order_model.dart';
|
||||
import '../models/parcel_order_model.dart';
|
||||
import '../models/rental_order_model.dart';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class OwnerOrderListController extends GetxController {
|
||||
// Loading flags
|
||||
RxBool isLoadingCab = false.obs;
|
||||
RxBool isLoadingParcel = false.obs;
|
||||
RxBool isLoadingRental = false.obs;
|
||||
|
||||
// Service list
|
||||
RxList<String> serviceList = ['Cab Service', 'Parcel Service', 'Rental Service'].obs;
|
||||
RxString selectedService = 'Cab Service'.obs;
|
||||
RxString serviceKey = 'cab-service'.obs;
|
||||
|
||||
// Driver list
|
||||
RxList<UserModel> driverList = <UserModel>[].obs;
|
||||
Rx<UserModel?> selectedDriver = Rx<UserModel?>(null);
|
||||
RxBool isDriverSelected = false.obs;
|
||||
RxString driverId = ''.obs;
|
||||
|
||||
// Cab Orders
|
||||
RxList<CabOrderModel> cabOrders = <CabOrderModel>[].obs;
|
||||
RxString cabSelectedTab = 'On Going'.obs;
|
||||
final List<String> cabTabTitles = ["On Going", "Completed", "Cancelled"];
|
||||
|
||||
// Parcel Orders
|
||||
RxList<ParcelOrderModel> parcelOrders = <ParcelOrderModel>[].obs;
|
||||
RxString parcelSelectedTab = 'In Transit'.obs;
|
||||
final List<String> parcelTabTitles = ["In Transit", "Delivered", "Cancelled"];
|
||||
|
||||
// Rental Orders
|
||||
RxList<RentalOrderModel> rentalOrders = <RentalOrderModel>[].obs;
|
||||
RxString rentalSelectedTab = 'On Going'.obs;
|
||||
final List<String> rentalTabTitles = ["On Going", "Completed", "Cancelled"];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getDriverList();
|
||||
}
|
||||
|
||||
// Fetch all drivers
|
||||
Future<void> getDriverList() async {
|
||||
try {
|
||||
driverList.value = await FireStoreUtils.getOwnerDriver();
|
||||
|
||||
print("Drivers fetched: ${driverList.length}");
|
||||
for (var d in driverList) {
|
||||
print("${d.firstName} ${d.lastName} - ${d.serviceType}");
|
||||
}
|
||||
|
||||
// Default: Cab Service + All drivers
|
||||
selectedService.value = 'Cab Service';
|
||||
serviceKey.value = 'cab-service';
|
||||
selectedDriver.value = null;
|
||||
isDriverSelected.value = false;
|
||||
|
||||
fetchOrdersByService();
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Error fetching drivers: $e");
|
||||
}
|
||||
}
|
||||
|
||||
// Filter drivers by selected service
|
||||
List<UserModel> get filteredDrivers {
|
||||
final serviceKeyMap = {
|
||||
'cab service': 'cab-service',
|
||||
'parcel service': 'parcel_delivery',
|
||||
'rental service': 'rental-service',
|
||||
};
|
||||
final key = serviceKeyMap[selectedService.value.toLowerCase()];
|
||||
return driverList.where((driver) => driver.serviceType == key).toList();
|
||||
}
|
||||
|
||||
// Called on Search button
|
||||
void searchOrders() {
|
||||
if (selectedDriver.value != null) {
|
||||
driverId.value = selectedDriver.value!.id!;
|
||||
isDriverSelected.value = true;
|
||||
} else {
|
||||
driverId.value = '';
|
||||
isDriverSelected.value = false;
|
||||
}
|
||||
|
||||
final serviceMap = {
|
||||
'Cab Service': 'cab-service',
|
||||
'Parcel Service': 'parcel_delivery',
|
||||
'Rental Service': 'rental-service',
|
||||
};
|
||||
serviceKey.value = serviceMap[selectedService.value] ?? 'cab-service';
|
||||
|
||||
fetchOrdersByService();
|
||||
}
|
||||
|
||||
Future<void> fetchOrdersByService() async {
|
||||
if (serviceKey.value == 'cab-service') {
|
||||
await fetchCabOrders();
|
||||
} else if (serviceKey.value == 'parcel_delivery') {
|
||||
await fetchParcelOrders();
|
||||
} else if (serviceKey.value == 'rental-service') {
|
||||
await fetchRentalOrders();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- CAB ORDERS --------------------
|
||||
Future<void> fetchCabOrders() async {
|
||||
try {
|
||||
isLoadingCab.value = true;
|
||||
|
||||
print("Fetching Cab Orders for Driver ID: ${driverId.value}, Is Driver Selected: ${isDriverSelected.value}");
|
||||
if (isDriverSelected.value) {
|
||||
cabOrders.value = await FireStoreUtils.getCabDriverOrdersOnce(driverId.value);
|
||||
} else {
|
||||
List<UserModel> drivers = filteredDrivers;
|
||||
List<CabOrderModel> allOrders = [];
|
||||
for (var driver in drivers) {
|
||||
List<CabOrderModel> driverOrders =
|
||||
await FireStoreUtils.getCabDriverOrdersOnce(driver.id ?? '');
|
||||
allOrders.addAll(driverOrders);
|
||||
}
|
||||
cabOrders.value = allOrders;
|
||||
}
|
||||
print("Total Cab Orders: ${cabOrders.length}");
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Error fetching cab orders: $e");
|
||||
} finally {
|
||||
isLoadingCab.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<CabOrderModel> getCabOrdersForTab(String tab) {
|
||||
switch (tab) {
|
||||
case "On Going":
|
||||
return cabOrders
|
||||
.where((order) => [
|
||||
"Order Placed",
|
||||
"Order Accepted",
|
||||
"Driver Accepted",
|
||||
"Driver Pending",
|
||||
"Order Shipped",
|
||||
"In Transit"
|
||||
].contains(order.status))
|
||||
.toList();
|
||||
case "Completed":
|
||||
return cabOrders.where((order) => ["Order Completed"].contains(order.status)).toList();
|
||||
case "Cancelled":
|
||||
return cabOrders
|
||||
.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status))
|
||||
.toList();
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- PARCEL ORDERS --------------------
|
||||
Future<void> fetchParcelOrders() async {
|
||||
try {
|
||||
isLoadingParcel.value = true;
|
||||
|
||||
if (isDriverSelected.value) {
|
||||
parcelOrders.value = await FireStoreUtils.getParcelDriverOrdersOnce(driverId.value);
|
||||
} else {
|
||||
List<UserModel> drivers = filteredDrivers;
|
||||
List<ParcelOrderModel> allOrders = [];
|
||||
for (var driver in drivers) {
|
||||
List<ParcelOrderModel> driverOrders =
|
||||
await FireStoreUtils.getParcelDriverOrdersOnce(driver.id ?? '');
|
||||
allOrders.addAll(driverOrders);
|
||||
}
|
||||
parcelOrders.value = allOrders;
|
||||
}
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Error fetching parcel orders: $e");
|
||||
} finally {
|
||||
isLoadingParcel.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<ParcelOrderModel> getParcelOrdersForTab(String tab) {
|
||||
switch (tab) {
|
||||
case "In Transit":
|
||||
return parcelOrders
|
||||
.where((order) => [
|
||||
"Order Placed",
|
||||
"Order Accepted",
|
||||
"Driver Accepted",
|
||||
"Driver Pending",
|
||||
"Order Shipped",
|
||||
"In Transit"
|
||||
].contains(order.status))
|
||||
.toList();
|
||||
case "Delivered":
|
||||
return parcelOrders.where((order) => ["Order Completed"].contains(order.status)).toList();
|
||||
case "Cancelled":
|
||||
return parcelOrders
|
||||
.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status))
|
||||
.toList();
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- RENTAL ORDERS --------------------
|
||||
Future<void> fetchRentalOrders() async {
|
||||
try {
|
||||
isLoadingRental.value = true;
|
||||
|
||||
if (isDriverSelected.value) {
|
||||
rentalOrders.value = await FireStoreUtils.getRentalDriverOrdersOnce(driverId.value);
|
||||
} else {
|
||||
List<UserModel> drivers = filteredDrivers;
|
||||
List<RentalOrderModel> allOrders = [];
|
||||
for (var driver in drivers) {
|
||||
List<RentalOrderModel> driverOrders =
|
||||
await FireStoreUtils.getRentalDriverOrdersOnce(driver.id ?? '');
|
||||
allOrders.addAll(driverOrders);
|
||||
}
|
||||
rentalOrders.value = allOrders;
|
||||
}
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Error fetching rental orders: $e");
|
||||
} finally {
|
||||
isLoadingRental.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
List<RentalOrderModel> getRentalOrdersForTab(String tab) {
|
||||
switch (tab) {
|
||||
case "On Going":
|
||||
return rentalOrders
|
||||
.where((order) => [
|
||||
"Order Placed",
|
||||
"Order Accepted",
|
||||
"Driver Accepted",
|
||||
"Driver Pending",
|
||||
"Order Shipped",
|
||||
"In Transit"
|
||||
].contains(order.status))
|
||||
.toList();
|
||||
case "Completed":
|
||||
return rentalOrders.where((order) => ["Order Completed"].contains(order.status)).toList();
|
||||
case "Cancelled":
|
||||
return rentalOrders
|
||||
.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status))
|
||||
.toList();
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- DATE FORMAT --------------------
|
||||
String formatDate(Timestamp timestamp) {
|
||||
final dateTime = timestamp.toDate();
|
||||
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
|
||||
}
|
||||
}
|
||||
118
lib/controllers/parcel_dashboard_controller.dart
Normal file
118
lib/controllers/parcel_dashboard_controller.dart
Normal file
@@ -0,0 +1,118 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:location/location.dart';
|
||||
|
||||
import '../constant/constant.dart' show Constant;
|
||||
import '../themes/theme_controller.dart';
|
||||
|
||||
class ParcelDashboardController extends GetxController {
|
||||
RxInt drawerIndex = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
|
||||
getUser();
|
||||
getTheme();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
DateTime? currentBackPressTime;
|
||||
RxBool canPopNow = false.obs;
|
||||
|
||||
Future<void> getUser() async {
|
||||
await updateCurrentLocation();
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
Constant.userModel = UserModel.fromJson(event.data()!);
|
||||
if (userModel.value.sectionId != null && userModel.value.sectionId!.isNotEmpty) {
|
||||
await FireStoreUtils.getSectionBySectionId(userModel.value.sectionId!).then((sectionValue) {
|
||||
if (sectionValue != null) {
|
||||
Constant.sectionModel = sectionValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
RxString isDarkMode = "Light".obs;
|
||||
RxBool isDarkModeSwitch = false.obs;
|
||||
|
||||
void getTheme() {
|
||||
bool isDark = Preferences.getBoolean(Preferences.themKey);
|
||||
isDarkMode.value = isDark ? "Dark" : "Light";
|
||||
isDarkModeSwitch.value = isDark;
|
||||
}
|
||||
|
||||
void toggleDarkMode(bool value) {
|
||||
isDarkModeSwitch.value = value;
|
||||
isDarkMode.value = value ? "Dark" : "Light";
|
||||
Preferences.setBoolean(Preferences.themKey, value);
|
||||
// Update ThemeController for instant app theme change
|
||||
if (Get.isRegistered<ThemeController>()) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
themeController.isDark.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
Location location = Location();
|
||||
|
||||
Future<void> updateCurrentLocation() async {
|
||||
try {
|
||||
PermissionStatus permissionStatus = await location.hasPermission();
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location = UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
location.requestPermission().then((permissionStatus) {
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location = UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
167
lib/controllers/parcel_home_controller.dart
Normal file
167
lib/controllers/parcel_home_controller.dart
Normal file
@@ -0,0 +1,167 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/app/wallet_screen/payment_list_screen.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/parcel_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/wallet_transaction_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ParcelHomeController extends GetxController {
|
||||
RxList<ParcelOrderModel> parcelOrdersList = <ParcelOrderModel>[].obs;
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getParcelList();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
Rx<UserModel> ownerModel = UserModel().obs;
|
||||
|
||||
Future<void> getParcelList() async {
|
||||
await FireStoreUtils.getOnGoingParcelList().then(
|
||||
(value) {
|
||||
parcelOrdersList.value = value;
|
||||
update();
|
||||
},
|
||||
);
|
||||
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (Constant.userModel!.ownerId != null && Constant.userModel!.ownerId!.isNotEmpty) {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(Constant.userModel!.ownerId).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
ownerModel.value = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> pickupParcel(ParcelOrderModel parcelBookingData) async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
parcelBookingData.status = Constant.orderInTransit;
|
||||
await FireStoreUtils.setParcelOrder(parcelBookingData);
|
||||
await getParcelList();
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> completeParcel(ParcelOrderModel parcelBookingData) async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
parcelBookingData.status = Constant.orderCompleted;
|
||||
|
||||
await updateCabWalletAmount(parcelBookingData);
|
||||
await FireStoreUtils.setParcelOrder(parcelBookingData);
|
||||
Map<String, dynamic> payLoad = <String, dynamic>{"type": "parcel_order", "orderId": parcelBookingData.id};
|
||||
await SendNotification.sendFcmMessage(Constant.parcelCompleted, parcelBookingData.author!.fcmToken.toString(), payLoad);
|
||||
await getParcelList();
|
||||
await FireStoreUtils.getParcelFirstOrderOrNOt(parcelBookingData).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateParcelReferralAmount(parcelBookingData);
|
||||
}
|
||||
});
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> updateCabWalletAmount(ParcelOrderModel orderModel) async {
|
||||
double totalTax = 0.0;
|
||||
double adminComm = 0.0;
|
||||
double discount = 0.0;
|
||||
double subTotal = 0.0;
|
||||
double totalAmount = 0.0;
|
||||
|
||||
subTotal = double.parse(orderModel.subTotal ?? '0.0');
|
||||
discount = double.parse(orderModel.discount ?? '0.0');
|
||||
|
||||
for (var element in orderModel.taxSetting!) {
|
||||
totalTax = totalTax + Constant.calculateTax(amount: (subTotal - discount).toString(), taxModel: element);
|
||||
}
|
||||
|
||||
if (orderModel.adminCommission!.isNotEmpty) {
|
||||
adminComm = Constant.calculateAdminCommission(
|
||||
amount: (subTotal - discount).toString(),
|
||||
adminCommissionType: orderModel.adminCommissionType.toString(),
|
||||
adminCommission: orderModel.adminCommission ?? '0');
|
||||
}
|
||||
|
||||
totalAmount = ((subTotal - discount) + totalTax);
|
||||
if (orderModel.paymentMethod.toString() != PaymentGateway.cod.name) {
|
||||
WalletTransactionModel transactionModel = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: totalAmount,
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: orderModel.paymentMethod!,
|
||||
transactionUser: "driver",
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty
|
||||
? orderModel.driver!.ownerId.toString()
|
||||
: FireStoreUtils.getCurrentUid(),
|
||||
isTopup: true,
|
||||
orderId: orderModel.id,
|
||||
note: "Booking amount credited",
|
||||
paymentStatus: "success");
|
||||
|
||||
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateUserWallet(
|
||||
amount: totalAmount.toString(),
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty
|
||||
? orderModel.driver!.ownerId.toString()
|
||||
: FireStoreUtils.getCurrentUid());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WalletTransactionModel transactionModel = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: adminComm,
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: orderModel.paymentMethod!,
|
||||
transactionUser: "driver",
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty
|
||||
? orderModel.driver!.ownerId.toString()
|
||||
: FireStoreUtils.getCurrentUid(),
|
||||
isTopup: false,
|
||||
orderId: orderModel.id,
|
||||
note: "Admin commission deducted",
|
||||
paymentStatus: "success");
|
||||
|
||||
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateUserWallet(
|
||||
amount: "-${adminComm.toString()}",
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty
|
||||
? orderModel.driver!.ownerId.toString()
|
||||
: FireStoreUtils.getCurrentUid());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String calculateParcelTotalAmountBooking(ParcelOrderModel parcelBookingData) {
|
||||
String subTotal = parcelBookingData.subTotal.toString();
|
||||
String discount = parcelBookingData.discount ?? "0.0";
|
||||
String taxAmount = "0.0";
|
||||
for (var element in parcelBookingData.taxSetting!) {
|
||||
taxAmount = (double.parse(taxAmount) +
|
||||
Constant.calculateTax(amount: (double.parse(subTotal) - double.parse(discount)).toString(), taxModel: element))
|
||||
.toStringAsFixed(int.tryParse(Constant.currencyModel!.decimalDigits.toString()) ?? 2);
|
||||
}
|
||||
|
||||
return ((double.parse(subTotal) - (double.parse(discount))) + double.parse(taxAmount))
|
||||
.toStringAsFixed(int.tryParse(Constant.currencyModel!.decimalDigits.toString()) ?? 2);
|
||||
}
|
||||
}
|
||||
75
lib/controllers/parcel_order_details_controller.dart
Normal file
75
lib/controllers/parcel_order_details_controller.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../constant/constant.dart';
|
||||
import '../models/parcel_category.dart';
|
||||
import '../models/parcel_order_model.dart';
|
||||
import '../utils/fire_store_utils.dart';
|
||||
|
||||
class ParcelOrderDetailsController extends GetxController {
|
||||
Rx<ParcelOrderModel> parcelOrder = ParcelOrderModel().obs;
|
||||
RxList<ParcelCategory> parcelCategory = <ParcelCategory>[].obs;
|
||||
RxBool isLoading = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
if (args != null && args is ParcelOrderModel) {
|
||||
parcelOrder.value = args;
|
||||
}
|
||||
loadParcelCategories();
|
||||
calculateTotalAmount();
|
||||
}
|
||||
|
||||
RxDouble subTotal = 0.0.obs;
|
||||
RxDouble discount = 0.0.obs;
|
||||
RxDouble taxAmount = 0.0.obs;
|
||||
RxDouble totalAmount = 0.0.obs;
|
||||
RxDouble adminCommission = 0.0.obs;
|
||||
|
||||
void calculateTotalAmount() {
|
||||
taxAmount = 0.0.obs;
|
||||
discount = 0.0.obs;
|
||||
subTotal.value = double.parse(parcelOrder.value.subTotal.toString());
|
||||
discount.value = double.parse(parcelOrder.value.discount ?? '0.0');
|
||||
|
||||
for (var element in parcelOrder.value.taxSetting!) {
|
||||
taxAmount.value = (taxAmount.value + Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element));
|
||||
}
|
||||
|
||||
if (parcelOrder.value.adminCommission!.isNotEmpty) {
|
||||
adminCommission.value = Constant.calculateAdminCommission(
|
||||
amount: (subTotal.value - discount.value).toString(),
|
||||
adminCommissionType: parcelOrder.value.adminCommissionType.toString(),
|
||||
adminCommission: parcelOrder.value.adminCommission ?? '0');
|
||||
}
|
||||
|
||||
|
||||
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
|
||||
update();
|
||||
}
|
||||
|
||||
void loadParcelCategories() async {
|
||||
isLoading.value = true;
|
||||
final categories = await FireStoreUtils.getParcelServiceCategory();
|
||||
parcelCategory.value = categories;
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
String formatDate(Timestamp timestamp) {
|
||||
final dateTime = timestamp.toDate();
|
||||
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
|
||||
}
|
||||
|
||||
ParcelCategory? getSelectedCategory() {
|
||||
try {
|
||||
return parcelCategory.firstWhere(
|
||||
(cat) => cat.title?.toLowerCase().trim() == parcelOrder.value.parcelType?.toLowerCase().trim(),
|
||||
orElse: () => ParcelCategory(),
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
lib/controllers/parcel_order_list_controller.dart
Normal file
75
lib/controllers/parcel_order_list_controller.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'dart:async';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../models/parcel_order_model.dart';
|
||||
import '../utils/fire_store_utils.dart'; // adjust path if needed
|
||||
|
||||
class ParcelOrderListController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxList<ParcelOrderModel> parcelOrder = <ParcelOrderModel>[].obs;
|
||||
|
||||
RxString selectedTab = "In Transit".obs;
|
||||
RxList<String> tabTitles = ["In Transit", "Delivered", "Cancelled"].obs;
|
||||
|
||||
StreamSubscription<List<ParcelOrderModel>>? _parcelSubscription;
|
||||
|
||||
RxString driverId = ''.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
driverId.value = args?['driverId'] ?? FireStoreUtils.getCurrentUid();
|
||||
listenParcelOrders();
|
||||
}
|
||||
|
||||
/// only update the selectedTab (do NOT re-subscribe)
|
||||
void selectTab(String tab) {
|
||||
selectedTab.value = tab;
|
||||
}
|
||||
|
||||
/// Start listening to orders live. Cancel previous subscription to avoid leaks.
|
||||
void listenParcelOrders() {
|
||||
isLoading.value = true;
|
||||
_parcelSubscription?.cancel();
|
||||
_parcelSubscription = FireStoreUtils.listenParcelOrders(driverId.value).listen(
|
||||
(orders) {
|
||||
parcelOrder.assignAll(orders);
|
||||
isLoading.value = false;
|
||||
},
|
||||
onError: (err) {
|
||||
isLoading.value = false;
|
||||
// optional: handle the error or show toast/log
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Return filtered list for a specific tab title
|
||||
List<ParcelOrderModel> getOrdersForTab(String tab) {
|
||||
switch (tab) {
|
||||
case "In Transit":
|
||||
return parcelOrder.where((order) => ["Order Placed", "Order Accepted", "Driver Accepted", "Driver Pending", "Order Shipped", "In Transit"].contains(order.status)).toList();
|
||||
|
||||
case "Delivered":
|
||||
return parcelOrder.where((order) => ["Order Completed"].contains(order.status)).toList();
|
||||
|
||||
case "Cancelled":
|
||||
return parcelOrder.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
String formatDate(Timestamp timestamp) {
|
||||
final dateTime = timestamp.toDate();
|
||||
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_parcelSubscription?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
193
lib/controllers/parcel_search_controller.dart
Normal file
193
lib/controllers/parcel_search_controller.dart
Normal file
@@ -0,0 +1,193 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/models/parcel_category.dart';
|
||||
import 'package:driver/models/parcel_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/widget/geoflutterfire/src/geoflutterfire.dart';
|
||||
import 'package:driver/widget/geoflutterfire/src/models/point.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart' as latlong;
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class ParcelSearchController extends GetxController {
|
||||
// Implement parcel search logic here
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
final Rx<TextEditingController> sourceTextEditController = TextEditingController().obs;
|
||||
final Rx<TextEditingController> destinationTextEditController = TextEditingController().obs;
|
||||
final Rx<TextEditingController> dateTimeTextEditController = TextEditingController().obs;
|
||||
|
||||
// Journey
|
||||
final Rx<LatLng?> departureLatLong = Rx<LatLng?>(null);
|
||||
final Rx<LatLng?> destinationLatLong = Rx<LatLng?>(null);
|
||||
final Rx<latlong.LatLng?> departureLatLongOsm = Rx<latlong.LatLng?>(null);
|
||||
final Rx<latlong.LatLng?> destinationLatLongOsm = Rx<latlong.LatLng?>(null);
|
||||
|
||||
Rx<DateTime> pickUpDateTime = DateTime.now().obs;
|
||||
RxList<ParcelOrderModel> parcelList = <ParcelOrderModel>[].obs;
|
||||
|
||||
Rx<UserModel> driverModel = UserModel().obs;
|
||||
Rx<UserModel> ownerModel = UserModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
isLoading.value = false;
|
||||
driverModel.value = Constant.userModel!;
|
||||
// TODO: implement onInit
|
||||
if (driverModel.value.ownerId != null && driverModel.value.ownerId!.isNotEmpty) {
|
||||
getOwnerDetails(driverModel.value.ownerId!);
|
||||
}
|
||||
loadParcelCategories();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
String formatDate(Timestamp timestamp) {
|
||||
final dateTime = timestamp.toDate();
|
||||
return DateFormat("dd MMM yyyy, hh:mm a").format(dateTime);
|
||||
}
|
||||
|
||||
Future<void> getOwnerDetails(String ownerId) async {
|
||||
ownerModel.value = await FireStoreUtils.getUserProfile(ownerId) ?? UserModel();
|
||||
update();
|
||||
}
|
||||
|
||||
void searchParcel() {
|
||||
searchParcelsOnce(
|
||||
srcLat: Constant.selectedMapType == 'osm' ? departureLatLongOsm.value!.latitude : departureLatLong.value!.latitude,
|
||||
srcLng: Constant.selectedMapType == 'osm' ? departureLatLongOsm.value!.longitude : departureLatLong.value!.longitude,
|
||||
destLat: Constant.selectedMapType == 'osm' ? destinationLatLongOsm.value?.latitude : destinationLatLong.value?.latitude,
|
||||
destLng: Constant.selectedMapType == 'osm' ? destinationLatLongOsm.value?.longitude : destinationLatLong.value?.longitude,
|
||||
date: pickUpDateTime.value, // required
|
||||
).then(
|
||||
(event) {
|
||||
parcelList.value = event;
|
||||
update();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> acceptParcelBooking(ParcelOrderModel parcelBookingData) async {
|
||||
parcelBookingData.status = Constant.driverAccepted;
|
||||
parcelBookingData.driver = Constant.userModel;
|
||||
parcelBookingData.driverId = Constant.userModel!.id;
|
||||
parcelBookingData.receiverPickupDateTime = Timestamp.fromDate(DateTime.now());
|
||||
|
||||
await FireStoreUtils.setParcelOrder(parcelBookingData);
|
||||
Map<String, dynamic> payLoad = <String, dynamic>{"type": "parcel_order", "orderId": parcelBookingData.id};
|
||||
await SendNotification.sendFcmMessage(Constant.parcelAccepted, parcelBookingData.author!.fcmToken.toString(), payLoad);
|
||||
Get.back(result: true);
|
||||
}
|
||||
|
||||
String calculateParcelTotalAmountBooking(ParcelOrderModel parcelBookingData) {
|
||||
String subTotal = parcelBookingData.subTotal.toString();
|
||||
String discount = parcelBookingData.discount ?? "0.0";
|
||||
String taxAmount = "0.0";
|
||||
for (var element in parcelBookingData.taxSetting!) {
|
||||
taxAmount = (double.parse(taxAmount) + Constant.calculateTax(amount: (double.parse(subTotal) - double.parse(discount)).toString(), taxModel: element))
|
||||
.toStringAsFixed(int.tryParse(Constant.currencyModel!.decimalDigits.toString()) ?? 2);
|
||||
}
|
||||
|
||||
return ((double.parse(subTotal) - (double.parse(discount))) + double.parse(taxAmount)).toStringAsFixed(int.tryParse(Constant.currencyModel!.decimalDigits.toString()) ?? 2);
|
||||
}
|
||||
|
||||
Future<List<ParcelOrderModel>> searchParcelsOnce({
|
||||
required double srcLat,
|
||||
required double srcLng,
|
||||
double? destLat,
|
||||
double? destLng,
|
||||
required DateTime date,
|
||||
}) async {
|
||||
final ref = FireStoreUtils.fireStore.collection("parcel_orders").where("sectionId",isEqualTo: driverModel.value.sectionId).where('status', isEqualTo: "Order Placed");
|
||||
|
||||
GeoFirePoint center = Geoflutterfire().point(latitude: srcLat, longitude: srcLng);
|
||||
|
||||
// Take first snapshot from the stream
|
||||
final docs = await Geoflutterfire()
|
||||
.collection(collectionRef: ref)
|
||||
.within(
|
||||
center: center,
|
||||
radius: double.parse(Constant.parcelRadius),
|
||||
field: "sourcePoint",
|
||||
strictMode: true,
|
||||
)
|
||||
.first;
|
||||
|
||||
final filtered = docs.where((doc) {
|
||||
final data = doc.data() as Map<String, dynamic>;
|
||||
if (data['senderPickupDateTime'] == null) return false;
|
||||
|
||||
final driverZoneId = driverModel.value.zoneId;
|
||||
|
||||
// ✅ Check both sender and receiver zone
|
||||
final senderZoneId = data['senderZoneId'];
|
||||
final receiverZoneId = data['receiverZoneId'];
|
||||
|
||||
if (senderZoneId == null && receiverZoneId == null) return false;
|
||||
|
||||
// Match if driver zone equals either sender or receiver zone
|
||||
final zoneMatch = (senderZoneId == driverZoneId) || (receiverZoneId == driverZoneId);
|
||||
if (!zoneMatch) return false;
|
||||
|
||||
// ✅ Date check
|
||||
final Timestamp ts = data['senderPickupDateTime'];
|
||||
final orderDate = ts.toDate().toLocal();
|
||||
final inputDate = date.toLocal();
|
||||
|
||||
bool sameDay = orderDate.year == inputDate.year && orderDate.month == inputDate.month && orderDate.day == inputDate.day;
|
||||
|
||||
if (!sameDay) return false;
|
||||
|
||||
// ✅ Destination check
|
||||
if (destLat != null && destLng != null && data['receiverLatLong'] != null) {
|
||||
final rec = data['receiverLatLong'];
|
||||
double recLat = rec['latitude'];
|
||||
double recLng = rec['longitude'];
|
||||
|
||||
double distance = Geoflutterfire().point(latitude: destLat, longitude: destLng).kmDistance(lat: recLat, lng: recLng);
|
||||
|
||||
if (distance > double.parse(Constant.parcelRadius)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}).toList();
|
||||
|
||||
return filtered.map((e) => ParcelOrderModel.fromJson(e.data()!)).toList();
|
||||
}
|
||||
|
||||
Future<void> pickDateTime() async {
|
||||
DateTime? date = await showDatePicker(
|
||||
context: Get.context!,
|
||||
initialDate: pickUpDateTime.value,
|
||||
firstDate: DateTime.now(),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
if (date == null) return;
|
||||
pickUpDateTime.value = date;
|
||||
dateTimeTextEditController.value.text = DateFormat('dd-MMM-yyyy').format(date);
|
||||
update();
|
||||
}
|
||||
|
||||
RxList<ParcelCategory> parcelCategory = <ParcelCategory>[].obs;
|
||||
|
||||
|
||||
void loadParcelCategories() async {
|
||||
final categories = await FireStoreUtils.getParcelServiceCategory();
|
||||
parcelCategory.value = categories;
|
||||
}
|
||||
|
||||
ParcelCategory? getSelectedCategory(ParcelOrderModel parcelOrder) {
|
||||
try {
|
||||
return parcelCategory.firstWhere(
|
||||
(cat) => cat.title?.toLowerCase().trim() == parcelOrder.parcelType?.toLowerCase().trim(),
|
||||
orElse: () => ParcelCategory(),
|
||||
);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
864
lib/controllers/parcel_tracking_controller.dart
Normal file
864
lib/controllers/parcel_tracking_controller.dart
Normal file
@@ -0,0 +1,864 @@
|
||||
// import 'dart:async';
|
||||
// import 'dart:convert';
|
||||
// import 'dart:developer';
|
||||
// import 'package:driver/constant/collection_name.dart';
|
||||
// import 'package:driver/constant/constant.dart';
|
||||
// import 'package:driver/constant/show_toast_dialog.dart';
|
||||
// import 'package:driver/themes/app_them_data.dart';
|
||||
// import 'package:driver/utils/fire_store_utils.dart';
|
||||
// import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
// import 'package:latlong2/latlong.dart' as location;
|
||||
// import 'package:flutter_map/flutter_map.dart' as flutterMap;
|
||||
// import 'package:http/http.dart' as http;
|
||||
// import '../models/parcel_order_model.dart';
|
||||
// import '../models/user_model.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
//
|
||||
// class ParcelTrackingController extends GetxController {
|
||||
// GoogleMapController? mapController;
|
||||
// final flutterMap.MapController osmMapController = flutterMap.MapController();
|
||||
//
|
||||
// Rx<UserModel> driverUserModel = UserModel().obs;
|
||||
// Rx<ParcelOrderModel> orderModel = ParcelOrderModel().obs;
|
||||
// RxBool isLoading = true.obs;
|
||||
// RxString type = "parcelOrder".obs;
|
||||
//
|
||||
// StreamSubscription? orderSubscription;
|
||||
// StreamSubscription? driverSubscription;
|
||||
//
|
||||
// // Google Map markers & polylines
|
||||
// RxMap<MarkerId, Marker> markers = <MarkerId, Marker>{}.obs;
|
||||
// RxMap<PolylineId, Polyline> polyLines = <PolylineId, Polyline>{}.obs;
|
||||
//
|
||||
// // OSM map markers & route
|
||||
// RxList<flutterMap.Marker> osmMarkers = <flutterMap.Marker>[].obs;
|
||||
// RxList<location.LatLng> routePoints = <location.LatLng>[].obs;
|
||||
//
|
||||
// BitmapDescriptor? pickupIcon;
|
||||
// BitmapDescriptor? dropoffIcon;
|
||||
// BitmapDescriptor? driverIcon;
|
||||
//
|
||||
// PolylinePoints polylinePoints = PolylinePoints(apiKey: Constant.mapAPIKey);
|
||||
//
|
||||
// Rx<location.LatLng> source = location.LatLng(0.0, 0.0).obs;
|
||||
// Rx<location.LatLng> destination = location.LatLng(0.0, 0.0).obs;
|
||||
//
|
||||
// @override
|
||||
// void onInit() {
|
||||
// super.onInit();
|
||||
// _loadIcons();
|
||||
// _initArguments();
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// void onClose() {
|
||||
// orderSubscription?.cancel();
|
||||
// driverSubscription?.cancel();
|
||||
// ShowToastDialog.closeLoader();
|
||||
// super.onClose();
|
||||
// }
|
||||
//
|
||||
// Future<void> _initArguments() async {
|
||||
// final args = Get.arguments;
|
||||
// if (args != null && args['parcelOrder'] != null) {
|
||||
// orderModel.value = args['parcelOrder'];
|
||||
// type.value = args['type'] ?? "parcelOrder";
|
||||
// _listenToOrder();
|
||||
// } else {
|
||||
// ShowToastDialog.showToast("Order data not found");
|
||||
// Get.back();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Listen to order and driver updates
|
||||
// void _listenToOrder() {
|
||||
// orderSubscription = FireStoreUtils.fireStore.collection(CollectionName.parcelOrders).doc(orderModel.value.id).snapshots().listen((event) {
|
||||
// if (event.data() != null) {
|
||||
// orderModel.value = ParcelOrderModel.fromJson(event.data()!);
|
||||
//
|
||||
// // listen driver
|
||||
// if (orderModel.value.driverId != null) {
|
||||
// driverSubscription = FireStoreUtils.fireStore.collection(CollectionName.users).doc(orderModel.value.driverId).snapshots().listen((driverSnap) {
|
||||
// if (driverSnap.data() != null) {
|
||||
// driverUserModel.value = UserModel.fromJson(driverSnap.data()!);
|
||||
// _updateTracking();
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// if (orderModel.value.status == Constant.orderCompleted) {
|
||||
// Get.back();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// isLoading.value = false;
|
||||
// }
|
||||
//
|
||||
// void _updateTracking() {
|
||||
// if (Constant.selectedMapType == 'google') {
|
||||
// _updateGoogleMap();
|
||||
// } else {
|
||||
// _updateOsmMap();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void _updateGoogleMap() async {
|
||||
// final driverLat = driverUserModel.value.location?.latitude?.toDouble();
|
||||
// final driverLng = driverUserModel.value.location?.longitude?.toDouble();
|
||||
// if (driverLat == null || driverLng == null) return;
|
||||
//
|
||||
// double? dstLat;
|
||||
// double? dstLng;
|
||||
//
|
||||
// if (orderModel.value.status == Constant.driverAccepted) {
|
||||
// dstLat = orderModel.value.senderLatLong?.latitude?.toDouble();
|
||||
// dstLng = orderModel.value.senderLatLong?.longitude?.toDouble();
|
||||
// } else {
|
||||
// dstLat = orderModel.value.receiverLatLong?.latitude?.toDouble();
|
||||
// dstLng = orderModel.value.receiverLatLong?.longitude?.toDouble();
|
||||
// }
|
||||
//
|
||||
// if (dstLat == null || dstLng == null) return;
|
||||
//
|
||||
// final result = await polylinePoints.getRouteBetweenCoordinates(
|
||||
// request: PolylineRequest(
|
||||
// origin: PointLatLng(driverLat, driverLng),
|
||||
// destination: PointLatLng(dstLat, dstLng),
|
||||
// mode: TravelMode.driving,
|
||||
// ),
|
||||
// );
|
||||
//
|
||||
// final List<LatLng> routeCoords = [];
|
||||
// for (var p in result.points) {
|
||||
// routeCoords.add(LatLng(p.latitude, p.longitude));
|
||||
// }
|
||||
//
|
||||
// markers.clear();
|
||||
// polyLines.clear();
|
||||
//
|
||||
// _addGoogleMarker(driverLat, driverLng, "Driver", driverIcon!);
|
||||
// _addGoogleMarker(orderModel.value.senderLatLong?.latitude?.toDouble(), orderModel.value.senderLatLong?.longitude?.toDouble(), "Pickup", pickupIcon!);
|
||||
// _addGoogleMarker(orderModel.value.receiverLatLong?.latitude?.toDouble(), orderModel.value.receiverLatLong?.longitude?.toDouble(), "Dropoff", dropoffIcon!);
|
||||
//
|
||||
// if (routeCoords.isNotEmpty) {
|
||||
// PolylineId id = const PolylineId("route");
|
||||
// Polyline polyline = Polyline(
|
||||
// polylineId: id,
|
||||
// color: AppThemeData.primary300,
|
||||
// width: 6,
|
||||
// points: routeCoords,
|
||||
// );
|
||||
// polyLines[id] = polyline;
|
||||
// }
|
||||
//
|
||||
// if (routeCoords.length >= 2) {
|
||||
// await _animateCameraBounds(routeCoords.first, routeCoords.last);
|
||||
// }
|
||||
//
|
||||
// update();
|
||||
// }
|
||||
//
|
||||
// void _addGoogleMarker(double? lat, double? lng, String id, BitmapDescriptor icon) {
|
||||
// if (lat == null || lng == null) return;
|
||||
// markers[MarkerId(id)] = Marker(
|
||||
// markerId: MarkerId(id),
|
||||
// position: LatLng(lat, lng),
|
||||
// icon: icon,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// Future<void> _animateCameraBounds(LatLng src, LatLng dest) async {
|
||||
// if (mapController == null) return;
|
||||
//
|
||||
// LatLngBounds bounds;
|
||||
// if (src.latitude > dest.latitude && src.longitude > dest.longitude) {
|
||||
// bounds = LatLngBounds(southwest: dest, northeast: src);
|
||||
// } else if (src.longitude > dest.longitude) {
|
||||
// bounds = LatLngBounds(southwest: LatLng(src.latitude, dest.longitude), northeast: LatLng(dest.latitude, src.longitude));
|
||||
// } else if (src.latitude > dest.latitude) {
|
||||
// bounds = LatLngBounds(southwest: LatLng(dest.latitude, src.longitude), northeast: LatLng(src.latitude, dest.longitude));
|
||||
// } else {
|
||||
// bounds = LatLngBounds(southwest: src, northeast: dest);
|
||||
// }
|
||||
//
|
||||
// CameraUpdate cameraUpdate = CameraUpdate.newLatLngBounds(bounds, 60);
|
||||
// await mapController?.animateCamera(cameraUpdate);
|
||||
// }
|
||||
//
|
||||
// void _updateOsmMap() async {
|
||||
// final driverLat = driverUserModel.value.location?.latitude?.toDouble() ?? 0.0;
|
||||
// final driverLng = driverUserModel.value.location?.longitude?.toDouble() ?? 0.0;
|
||||
//
|
||||
// double? dstLat;
|
||||
// double? dstLng;
|
||||
//
|
||||
// if (orderModel.value.status == Constant.driverAccepted) {
|
||||
// dstLat = orderModel.value.senderLatLong?.latitude?.toDouble();
|
||||
// dstLng = orderModel.value.senderLatLong?.longitude?.toDouble();
|
||||
// } else {
|
||||
// dstLat = orderModel.value.receiverLatLong?.latitude?.toDouble();
|
||||
// dstLng = orderModel.value.receiverLatLong?.longitude?.toDouble();
|
||||
// }
|
||||
//
|
||||
// if (dstLat == null || dstLng == null) return;
|
||||
//
|
||||
// source.value = location.LatLng(driverLat, driverLng);
|
||||
// destination.value = location.LatLng(dstLat, dstLng);
|
||||
//
|
||||
// await _fetchOsmRoute(source.value, destination.value);
|
||||
//
|
||||
// // Update OSM markers
|
||||
// osmMarkers.clear();
|
||||
// osmMarkers.addAll([
|
||||
// flutterMap.Marker(
|
||||
// point: source.value,
|
||||
// width: 40,
|
||||
// height: 40,
|
||||
// child: Image.asset('assets/images/food_delivery.png', width: 40),
|
||||
// ),
|
||||
// flutterMap.Marker(
|
||||
// point: location.LatLng(
|
||||
// orderModel.value.senderLatLong?.latitude?.toDouble() ?? 0.0,
|
||||
// orderModel.value.senderLatLong?.longitude?.toDouble() ?? 0.0,
|
||||
// ),
|
||||
// width: 40,
|
||||
// height: 40,
|
||||
// child: Image.asset('assets/images/pickup.png', width: 40),
|
||||
// ),
|
||||
// flutterMap.Marker(
|
||||
// point: location.LatLng(
|
||||
// orderModel.value.receiverLatLong?.latitude?.toDouble() ?? 0.0,
|
||||
// orderModel.value.receiverLatLong?.longitude?.toDouble() ?? 0.0,
|
||||
// ),
|
||||
// width: 40,
|
||||
// height: 40,
|
||||
// child: Image.asset('assets/images/dropoff.png', width: 40),
|
||||
// ),
|
||||
// ]);
|
||||
//
|
||||
// osmMapController.move(source.value, 14);
|
||||
// update();
|
||||
// }
|
||||
//
|
||||
// Future<void> _fetchOsmRoute(location.LatLng src, location.LatLng dest) async {
|
||||
// try {
|
||||
// final url =
|
||||
// Uri.parse('https://router.project-osrm.org/route/v1/driving/${src.longitude},${src.latitude};${dest.longitude},${dest.latitude}?overview=full&geometries=geojson');
|
||||
// final response = await http.get(url);
|
||||
// if (response.statusCode == 200) {
|
||||
// final decoded = json.decode(response.body);
|
||||
// final geometry = decoded['routes'][0]['geometry']['coordinates'];
|
||||
// routePoints.clear();
|
||||
// for (var coord in geometry) {
|
||||
// final lon = coord[0];
|
||||
// final lat = coord[1];
|
||||
// routePoints.add(location.LatLng(lat, lon));
|
||||
// }
|
||||
// }
|
||||
// } catch (e) {
|
||||
// log("Error fetching OSM route: $e");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> _loadIcons() async {
|
||||
// if (Constant.selectedMapType == 'google') {
|
||||
// pickupIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/pickup.png', 100));
|
||||
// dropoffIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/dropoff.png', 100));
|
||||
// driverIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/food_delivery.png', 80));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/themes/app_them_data.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:latlong2/latlong.dart' as location;
|
||||
import 'package:flutter_map/flutter_map.dart' as flutterMap;
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
import '../models/parcel_order_model.dart';
|
||||
import '../models/user_model.dart';
|
||||
|
||||
class ParcelTrackingController extends GetxController {
|
||||
GoogleMapController? mapController;
|
||||
final flutterMap.MapController osmMapController = flutterMap.MapController();
|
||||
|
||||
Rx<UserModel> driverUserModel = UserModel().obs;
|
||||
Rx<ParcelOrderModel> orderModel = ParcelOrderModel().obs;
|
||||
RxBool isLoading = true.obs;
|
||||
RxString type = "parcelOrder".obs;
|
||||
|
||||
StreamSubscription? orderSubscription;
|
||||
StreamSubscription? driverSubscription;
|
||||
|
||||
// Google Maps
|
||||
RxMap<MarkerId, Marker> markers = <MarkerId, Marker>{}.obs;
|
||||
RxMap<PolylineId, Polyline> polyLines = <PolylineId, Polyline>{}.obs;
|
||||
|
||||
// OSM Map
|
||||
RxList<flutterMap.Marker> osmMarkers = <flutterMap.Marker>[].obs;
|
||||
RxList<location.LatLng> routePoints = <location.LatLng>[].obs;
|
||||
|
||||
BitmapDescriptor? pickupIcon;
|
||||
BitmapDescriptor? dropoffIcon;
|
||||
BitmapDescriptor? driverIcon;
|
||||
|
||||
PolylinePoints polylinePoints = PolylinePoints(apiKey: Constant.mapAPIKey);
|
||||
|
||||
Rx<location.LatLng> source = location.LatLng(0.0, 0.0).obs;
|
||||
Rx<location.LatLng> destination = location.LatLng(0.0, 0.0).obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
_initArguments();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
orderSubscription?.cancel();
|
||||
driverSubscription?.cancel();
|
||||
ShowToastDialog.closeLoader();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> _initArguments() async {
|
||||
await _loadIcons();
|
||||
|
||||
final args = Get.arguments;
|
||||
if (args != null && args['parcelOrder'] != null) {
|
||||
orderModel.value = args['parcelOrder'];
|
||||
type.value = args['type'] ?? "parcelOrder";
|
||||
_listenToOrder();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Order data not found");
|
||||
Get.back();
|
||||
}
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
void _listenToOrder() {
|
||||
orderSubscription =
|
||||
FireStoreUtils.fireStore.collection(CollectionName.parcelOrders).doc(orderModel.value.id).snapshots().listen((event) {
|
||||
if (event.data() != null) {
|
||||
orderModel.value = ParcelOrderModel.fromJson(event.data()!);
|
||||
|
||||
// Listen to driver updates
|
||||
if (orderModel.value.driverId != null) {
|
||||
driverSubscription =
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(orderModel.value.driverId).snapshots().listen((driverSnap) {
|
||||
if (driverSnap.data() != null) {
|
||||
driverUserModel.value = UserModel.fromJson(driverSnap.data()!);
|
||||
_updateTracking();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (orderModel.value.status == Constant.orderCompleted) {
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
void _updateTracking() {
|
||||
if (Constant.selectedMapType == 'google') {
|
||||
_updateGoogleMap();
|
||||
} else {
|
||||
_updateOsmMap();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateGoogleMap() async {
|
||||
final driverLat = driverUserModel.value.location?.latitude?.toDouble();
|
||||
final driverLng = driverUserModel.value.location?.longitude?.toDouble();
|
||||
if (driverLat == null || driverLng == null) return;
|
||||
|
||||
double? dstLat;
|
||||
double? dstLng;
|
||||
|
||||
// Decide destination based on order status
|
||||
if (orderModel.value.status == Constant.driverAccepted) {
|
||||
dstLat = orderModel.value.senderLatLong?.latitude?.toDouble();
|
||||
dstLng = orderModel.value.senderLatLong?.longitude?.toDouble();
|
||||
} else if ([Constant.orderInTransit].contains(orderModel.value.status)) {
|
||||
dstLat = orderModel.value.receiverLatLong?.latitude?.toDouble();
|
||||
dstLng = orderModel.value.receiverLatLong?.longitude?.toDouble();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dstLat == null || dstLng == null) return;
|
||||
|
||||
final result = await polylinePoints.getRouteBetweenCoordinates(
|
||||
request: PolylineRequest(
|
||||
origin: PointLatLng(driverLat, driverLng),
|
||||
destination: PointLatLng(dstLat, dstLng),
|
||||
mode: TravelMode.driving,
|
||||
),
|
||||
);
|
||||
|
||||
final List<LatLng> routeCoords = [];
|
||||
for (var p in result.points) {
|
||||
routeCoords.add(LatLng(p.latitude, p.longitude));
|
||||
}
|
||||
|
||||
markers.clear();
|
||||
polyLines.clear();
|
||||
|
||||
// Add markers
|
||||
_addGoogleMarker(driverLat, driverLng, "Driver", driverIcon!);
|
||||
if (orderModel.value.status == Constant.driverAccepted) {
|
||||
_addGoogleMarker(orderModel.value.senderLatLong?.latitude?.toDouble(), orderModel.value.senderLatLong?.longitude?.toDouble(),
|
||||
"Pickup", pickupIcon!);
|
||||
} else {
|
||||
_addGoogleMarker(orderModel.value.receiverLatLong?.latitude?.toDouble(), orderModel.value.receiverLatLong?.longitude?.toDouble(),
|
||||
"Dropoff", dropoffIcon!);
|
||||
}
|
||||
|
||||
// Add polyline
|
||||
if (routeCoords.isNotEmpty) {
|
||||
PolylineId id = const PolylineId("route");
|
||||
Polyline polyline = Polyline(
|
||||
polylineId: id,
|
||||
color: AppThemeData.primary300,
|
||||
width: 6,
|
||||
points: routeCoords,
|
||||
);
|
||||
polyLines[id] = polyline;
|
||||
}
|
||||
|
||||
if (routeCoords.length >= 2) {
|
||||
await _animateCameraBounds(routeCoords.first, routeCoords.last);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void _addGoogleMarker(double? lat, double? lng, String id, BitmapDescriptor icon) {
|
||||
if (lat == null || lng == null) return;
|
||||
markers[MarkerId(id)] = Marker(
|
||||
markerId: MarkerId(id),
|
||||
position: LatLng(lat, lng),
|
||||
icon: icon,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _animateCameraBounds(LatLng src, LatLng dest) async {
|
||||
if (mapController == null) return;
|
||||
|
||||
LatLngBounds bounds;
|
||||
if (src.latitude > dest.latitude && src.longitude > dest.longitude) {
|
||||
bounds = LatLngBounds(southwest: dest, northeast: src);
|
||||
} else if (src.longitude > dest.longitude) {
|
||||
bounds = LatLngBounds(southwest: LatLng(src.latitude, dest.longitude), northeast: LatLng(dest.latitude, src.longitude));
|
||||
} else if (src.latitude > dest.latitude) {
|
||||
bounds = LatLngBounds(southwest: LatLng(dest.latitude, src.longitude), northeast: LatLng(src.latitude, dest.longitude));
|
||||
} else {
|
||||
bounds = LatLngBounds(southwest: src, northeast: dest);
|
||||
}
|
||||
|
||||
CameraUpdate cameraUpdate = CameraUpdate.newLatLngBounds(bounds, 60);
|
||||
await mapController?.animateCamera(cameraUpdate);
|
||||
}
|
||||
|
||||
void _updateOsmMap() async {
|
||||
final driverLat = driverUserModel.value.location?.latitude?.toDouble() ?? 0.0;
|
||||
final driverLng = driverUserModel.value.location?.longitude?.toDouble() ?? 0.0;
|
||||
|
||||
double? dstLat;
|
||||
double? dstLng;
|
||||
|
||||
if (orderModel.value.status == Constant.driverAccepted) {
|
||||
dstLat = orderModel.value.senderLatLong?.latitude?.toDouble();
|
||||
dstLng = orderModel.value.senderLatLong?.longitude?.toDouble();
|
||||
} else if ([Constant.orderInTransit].contains(orderModel.value.status)) {
|
||||
dstLat = orderModel.value.receiverLatLong?.latitude?.toDouble();
|
||||
dstLng = orderModel.value.receiverLatLong?.longitude?.toDouble();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dstLat == null || dstLng == null) return;
|
||||
|
||||
source.value = location.LatLng(driverLat, driverLng);
|
||||
destination.value = location.LatLng(dstLat, dstLng);
|
||||
|
||||
await _fetchOsmRoute(source.value, destination.value);
|
||||
|
||||
// Update OSM markers
|
||||
osmMarkers.clear();
|
||||
osmMarkers.add(
|
||||
flutterMap.Marker(
|
||||
point: source.value,
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: CachedNetworkImage(
|
||||
width: 50,
|
||||
height: 50,
|
||||
imageUrl: Constant.sectionModel!.markerIcon.toString(),
|
||||
placeholder: (context, url) => Constant.loader(),
|
||||
errorWidget: (context, url, error) => SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (orderModel.value.status == Constant.driverAccepted) {
|
||||
osmMarkers.add(
|
||||
flutterMap.Marker(
|
||||
point: location.LatLng(
|
||||
orderModel.value.senderLatLong?.latitude?.toDouble() ?? 0.0,
|
||||
orderModel.value.senderLatLong?.longitude?.toDouble() ?? 0.0,
|
||||
),
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Image.asset('assets/images/pickup.png', width: 40),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
osmMarkers.add(
|
||||
flutterMap.Marker(
|
||||
point: location.LatLng(
|
||||
orderModel.value.receiverLatLong?.latitude?.toDouble() ?? 0.0,
|
||||
orderModel.value.receiverLatLong?.longitude?.toDouble() ?? 0.0,
|
||||
),
|
||||
width: 40,
|
||||
height: 40,
|
||||
child: Image.asset('assets/images/dropoff.png', width: 40),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
osmMapController.move(source.value, 14);
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> _fetchOsmRoute(location.LatLng src, location.LatLng dest) async {
|
||||
try {
|
||||
final url = Uri.parse(
|
||||
'https://router.project-osrm.org/route/v1/driving/${src.longitude},${src.latitude};${dest.longitude},${dest.latitude}?overview=full&geometries=geojson');
|
||||
final response = await http.get(url);
|
||||
if (response.statusCode == 200) {
|
||||
final decoded = json.decode(response.body);
|
||||
final geometry = decoded['routes'][0]['geometry']['coordinates'];
|
||||
routePoints.clear();
|
||||
for (var coord in geometry) {
|
||||
final lon = coord[0];
|
||||
final lat = coord[1];
|
||||
routePoints.add(location.LatLng(lat, lon));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error fetching OSM route: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadIcons() async {
|
||||
if (Constant.selectedMapType == 'google') {
|
||||
pickupIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/pickup.png', 100));
|
||||
dropoffIcon = BitmapDescriptor.fromBytes(await Constant().getBytesFromAsset('assets/images/dropoff.png', 100));
|
||||
driverIcon = BitmapDescriptor.fromBytes(Constant.sectionModel!.markerIcon == null || Constant.sectionModel!.markerIcon!.isEmpty
|
||||
? await Constant().getBytesFromAsset('assets/images/ic_cab.png', 50)
|
||||
: await Constant().getBytesFromUrl(Constant.sectionModel!.markerIcon.toString(), width: 120));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// import 'dart:async';
|
||||
// import 'dart:convert';
|
||||
// import 'dart:typed_data';
|
||||
// import 'package:driver/constant/collection_name.dart';
|
||||
// import 'package:driver/constant/constant.dart';
|
||||
// import 'package:driver/constant/show_toast_dialog.dart';
|
||||
// import 'package:driver/themes/app_them_data.dart';
|
||||
// import 'package:driver/utils/fire_store_utils.dart';
|
||||
// import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
// import 'package:get/get.dart';
|
||||
// import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
// import 'package:latlong2/latlong.dart' as location;
|
||||
// import 'package:flutter_map/flutter_map.dart' as flutterMap;
|
||||
// import 'package:http/http.dart' as http;
|
||||
// import '../models/parcel_order_model.dart';
|
||||
// import '../models/user_model.dart';
|
||||
//
|
||||
// class ParcelTrackingController extends GetxController {
|
||||
// GoogleMapController? mapController;
|
||||
//
|
||||
// Rx<UserModel> driverUserModel = UserModel().obs;
|
||||
// Rx<ParcelOrderModel> orderModel = ParcelOrderModel().obs;
|
||||
//
|
||||
// RxBool isLoading = true.obs;
|
||||
// RxString type = "".obs;
|
||||
//
|
||||
// StreamSubscription? orderSubscription;
|
||||
// StreamSubscription? driverSubscription;
|
||||
//
|
||||
// RxMap<MarkerId, Marker> markers = <MarkerId, Marker>{}.obs;
|
||||
// RxMap<PolylineId, Polyline> polyLines = <PolylineId, Polyline>{}.obs;
|
||||
//
|
||||
// BitmapDescriptor? departureIcon;
|
||||
// BitmapDescriptor? destinationIcon;
|
||||
// BitmapDescriptor? driverIcon;
|
||||
//
|
||||
// PolylinePoints polylinePoints = PolylinePoints(apiKey: Constant.mapAPIKey);
|
||||
//
|
||||
// // OSM
|
||||
// final flutterMap.MapController osmMapController = flutterMap.MapController();
|
||||
// Rx<location.LatLng> source = location.LatLng(0.0, 0.0).obs;
|
||||
// Rx<location.LatLng> destination = location.LatLng(0.0, 0.0).obs;
|
||||
// RxList<location.LatLng> routePoints = <location.LatLng>[].obs;
|
||||
//
|
||||
// @override
|
||||
// void onInit() {
|
||||
// addMarkerSetup();
|
||||
// getArgument();
|
||||
// super.onInit();
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// void onClose() {
|
||||
// orderSubscription?.cancel();
|
||||
// driverSubscription?.cancel();
|
||||
// ShowToastDialog.closeLoader();
|
||||
// super.onClose();
|
||||
// }
|
||||
//
|
||||
// Future<void> getArgument() async {
|
||||
// dynamic argumentData = Get.arguments;
|
||||
// if (argumentData != null) {
|
||||
// type.value = argumentData['type'] ?? "parcelOrder";
|
||||
// if (type.value == "parcelOrder") {
|
||||
// ParcelOrderModel argumentOrderModel = argumentData['parcelOrder'];
|
||||
//
|
||||
// /// Listen to Order
|
||||
// orderSubscription = FireStoreUtils.fireStore.collection(CollectionName.parcelOrders).doc(argumentOrderModel.id).snapshots().listen((event) {
|
||||
// if (event.data() != null) {
|
||||
// orderModel.value = ParcelOrderModel.fromJson(event.data()!);
|
||||
//
|
||||
// /// Listen to Driver Live Location
|
||||
// driverSubscription = FireStoreUtils.fireStore.collection(CollectionName.users).doc(orderModel.value.driverId).snapshots().listen((event) {
|
||||
// if (event.data() != null) {
|
||||
// driverUserModel.value = UserModel.fromJson(event.data()!);
|
||||
//
|
||||
// /// Update Map (Google or OSM)
|
||||
// _updateTracking();
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// if (orderModel.value.status == Constant.orderCompleted) {
|
||||
// Get.back();
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// isLoading.value = false;
|
||||
// update();
|
||||
// }
|
||||
//
|
||||
// /// Update Tracking depending on order status
|
||||
// void _updateTracking() {
|
||||
// if (Constant.selectedMapType == 'google') {
|
||||
// _updateGoogleMap();
|
||||
// } else {
|
||||
// _updateOsmMap();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ///Google Map Route
|
||||
// void _updateGoogleMap() async {
|
||||
// double? srcLat = driverUserModel.value.location?.latitude?.toDouble();
|
||||
// double? srcLng = driverUserModel.value.location?.longitude?.toDouble();
|
||||
//
|
||||
// double? dstLat;
|
||||
// double? dstLng;
|
||||
//
|
||||
// if (orderModel.value.status == Constant.driverAccepted) {
|
||||
// // Driver → Pickup
|
||||
// dstLat = orderModel.value.senderLatLong?.latitude?.toDouble();
|
||||
// dstLng = orderModel.value.senderLatLong?.longitude?.toDouble();
|
||||
// } else {
|
||||
// // Pickup → Destination
|
||||
// dstLat = orderModel.value.receiverLatLong?.latitude?.toDouble();
|
||||
// dstLng = orderModel.value.receiverLatLong?.longitude?.toDouble();
|
||||
// }
|
||||
//
|
||||
// if (srcLat == null || srcLng == null || dstLat == null || dstLng == null) return;
|
||||
//
|
||||
// // Get Polyline
|
||||
// List<LatLng> polylineCoordinates = [];
|
||||
// PolylineRequest polylineRequest = PolylineRequest(
|
||||
// origin: PointLatLng(srcLat, srcLng),
|
||||
// destination: PointLatLng(dstLat, dstLng),
|
||||
// mode: TravelMode.driving,
|
||||
// );
|
||||
//
|
||||
// PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
|
||||
// request: polylineRequest,
|
||||
// );
|
||||
//
|
||||
// polylineCoordinates.clear();
|
||||
// if (result.points.isNotEmpty) {
|
||||
// for (var point in result.points) {
|
||||
// polylineCoordinates.add(LatLng(point.latitude, point.longitude));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Add Markers
|
||||
// markers.clear();
|
||||
// addMarker(
|
||||
// latitude: orderModel.value.senderLatLong?.latitude?.toDouble(),
|
||||
// longitude: orderModel.value.senderLatLong?.longitude?.toDouble(),
|
||||
// id: "Pickup",
|
||||
// descriptor: departureIcon!,
|
||||
// rotation: 0.0,
|
||||
// );
|
||||
// addMarker(
|
||||
// latitude: orderModel.value.receiverLatLong?.latitude?.toDouble(),
|
||||
// longitude: orderModel.value.receiverLatLong?.longitude?.toDouble(),
|
||||
// id: "Dropoff",
|
||||
// descriptor: destinationIcon!,
|
||||
// rotation: 0.0,
|
||||
// );
|
||||
// addMarker(
|
||||
// latitude: srcLat,
|
||||
// longitude: srcLng,
|
||||
// id: "Driver",
|
||||
// descriptor: driverIcon!,
|
||||
// rotation: driverUserModel.value.rotation?.toDouble() ?? 0.0,
|
||||
// );
|
||||
//
|
||||
// // Add Polyline
|
||||
// polyLines.clear();
|
||||
// _addPolyLine(polylineCoordinates);
|
||||
// }
|
||||
//
|
||||
// ///OSM Route
|
||||
// void _updateOsmMap() async {
|
||||
// source.value = location.LatLng(
|
||||
// driverUserModel.value.location?.latitude?.toDouble() ?? 0.0,
|
||||
// driverUserModel.value.location?.longitude?.toDouble() ?? 0.0,
|
||||
// );
|
||||
//
|
||||
// if (orderModel.value.status == Constant.driverAccepted) {
|
||||
// destination.value = location.LatLng(
|
||||
// orderModel.value.senderLatLong?.latitude?.toDouble() ?? 0.0,
|
||||
// orderModel.value.senderLatLong?.longitude?.toDouble() ?? 0.0,
|
||||
// );
|
||||
// } else {
|
||||
// destination.value = location.LatLng(
|
||||
// orderModel.value.receiverLatLong?.latitude?.toDouble() ?? 0.0,
|
||||
// orderModel.value.receiverLatLong?.longitude?.toDouble() ?? 0.0,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// await fetchRoute(source.value, destination.value);
|
||||
// animateToSource();
|
||||
// }
|
||||
//
|
||||
// /// Google Add Marker
|
||||
// void addMarker({
|
||||
// required double? latitude,
|
||||
// required double? longitude,
|
||||
// required String id,
|
||||
// required BitmapDescriptor descriptor,
|
||||
// required double? rotation,
|
||||
// }) {
|
||||
// MarkerId markerId = MarkerId(id);
|
||||
// Marker marker = Marker(
|
||||
// markerId: markerId,
|
||||
// icon: descriptor,
|
||||
// position: LatLng(latitude ?? 0.0, longitude ?? 0.0),
|
||||
// rotation: rotation ?? 0.0,
|
||||
// );
|
||||
// markers[markerId] = marker;
|
||||
// }
|
||||
//
|
||||
// /// Setup Marker Icons
|
||||
// Future<void> addMarkerSetup() async {
|
||||
// if (Constant.selectedMapType == 'google') {
|
||||
// final Uint8List pickup = await Constant().getBytesFromAsset('assets/images/pickup.png', 100);
|
||||
// final Uint8List dropoff = await Constant().getBytesFromAsset('assets/images/dropoff.png', 100);
|
||||
// final Uint8List driver = await Constant().getBytesFromAsset('assets/images/food_delivery.png', 60);
|
||||
// departureIcon = BitmapDescriptor.fromBytes(pickup);
|
||||
// destinationIcon = BitmapDescriptor.fromBytes(dropoff);
|
||||
// driverIcon = BitmapDescriptor.fromBytes(driver);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Google Add Polyline
|
||||
// void _addPolyLine(List<LatLng> polylineCoordinates) {
|
||||
// if (polylineCoordinates.isEmpty) return;
|
||||
// PolylineId id = const PolylineId("poly");
|
||||
// Polyline polyline = Polyline(
|
||||
// polylineId: id,
|
||||
// points: polylineCoordinates,
|
||||
// width: 6,
|
||||
// color: AppThemeData.primary300,
|
||||
// );
|
||||
// polyLines[id] = polyline;
|
||||
// updateCameraLocation(polylineCoordinates.first, polylineCoordinates.last, mapController);
|
||||
// }
|
||||
//
|
||||
// Future<void> updateCameraLocation(
|
||||
// LatLng source,
|
||||
// LatLng destination,
|
||||
// GoogleMapController? mapController,
|
||||
// ) async {
|
||||
// if (mapController == null) return;
|
||||
//
|
||||
// LatLngBounds bounds;
|
||||
// if (source.latitude > destination.latitude && source.longitude > destination.longitude) {
|
||||
// bounds = LatLngBounds(southwest: destination, northeast: source);
|
||||
// } else if (source.longitude > destination.longitude) {
|
||||
// bounds = LatLngBounds(southwest: LatLng(source.latitude, destination.longitude), northeast: LatLng(destination.latitude, source.longitude));
|
||||
// } else if (source.latitude > destination.latitude) {
|
||||
// bounds = LatLngBounds(southwest: LatLng(destination.latitude, source.longitude), northeast: LatLng(source.latitude, destination.longitude));
|
||||
// } else {
|
||||
// bounds = LatLngBounds(southwest: source, northeast: destination);
|
||||
// }
|
||||
//
|
||||
// CameraUpdate cameraUpdate = CameraUpdate.newLatLngBounds(bounds, 50);
|
||||
// await mapController.animateCamera(cameraUpdate);
|
||||
// }
|
||||
//
|
||||
// /// OSM Fetch Route
|
||||
// Future<void> fetchRoute(location.LatLng source, location.LatLng destination) async {
|
||||
// final url = Uri.parse(
|
||||
// 'https://router.project-osrm.org/route/v1/driving/${source.longitude},${source.latitude};${destination.longitude},${destination.latitude}?overview=full&geometries=geojson',
|
||||
// );
|
||||
// final response = await http.get(url);
|
||||
// if (response.statusCode == 200) {
|
||||
// final decoded = json.decode(response.body);
|
||||
// final geometry = decoded['routes'][0]['geometry']['coordinates'];
|
||||
// routePoints.clear();
|
||||
// for (var coord in geometry) {
|
||||
// final lon = coord[0];
|
||||
// final lat = coord[1];
|
||||
// routePoints.add(location.LatLng(lat, lon));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void animateToSource() {
|
||||
// osmMapController.move(source.value, 16);
|
||||
// }
|
||||
// }
|
||||
43
lib/controllers/phone_number_controller.dart
Normal file
43
lib/controllers/phone_number_controller.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:driver/app/auth_screen/otp_screen.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../constant/constant.dart';
|
||||
|
||||
class PhoneNumberController extends GetxController {
|
||||
Rx<TextEditingController> phoneNUmberEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> countryCodeEditingController = TextEditingController(text:Constant.defaultCountryCode).obs;
|
||||
|
||||
Future<void> sendCode() async {
|
||||
ShowToastDialog.showLoader("Please wait".tr);
|
||||
await FirebaseAuth.instance
|
||||
.verifyPhoneNumber(
|
||||
phoneNumber: countryCodeEditingController.value.text + phoneNUmberEditingController.value.text,
|
||||
verificationCompleted: (PhoneAuthCredential credential) {},
|
||||
verificationFailed: (FirebaseAuthException e) {
|
||||
debugPrint("FirebaseAuthException--->${e.message}");
|
||||
ShowToastDialog.closeLoader();
|
||||
if (e.code == 'invalid-phone-number') {
|
||||
ShowToastDialog.showToast("invalid_phone_number".tr);
|
||||
} else {
|
||||
ShowToastDialog.showToast(e.message);
|
||||
}
|
||||
},
|
||||
codeSent: (String verificationId, int? resendToken) {
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.to(const OtpScreen(), arguments: {
|
||||
"countryCode": countryCodeEditingController.value.text,
|
||||
"phoneNumber": phoneNUmberEditingController.value.text,
|
||||
"verificationId": verificationId,
|
||||
});
|
||||
},
|
||||
codeAutoRetrievalTimeout: (String verificationId) {})
|
||||
.catchError((error) {
|
||||
debugPrint("catchError--->$error");
|
||||
ShowToastDialog.closeLoader();
|
||||
ShowToastDialog.showToast("multiple_time_request".tr);
|
||||
});
|
||||
}
|
||||
}
|
||||
25
lib/controllers/pickup_order_controller.dart
Normal file
25
lib/controllers/pickup_order_controller.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class PickupOrderController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxBool conformPickup = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<OrderModel> orderModel = OrderModel().obs;
|
||||
|
||||
void getArgument() {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
orderModel.value = argumentData['orderModel'];
|
||||
}
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
105
lib/controllers/rental_booking_search_controller.dart
Normal file
105
lib/controllers/rental_booking_search_controller.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/rental_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/widget/geoflutterfire/src/geoflutterfire.dart';
|
||||
import 'package:driver/widget/geoflutterfire/src/models/point.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class RentalBookingSearchController extends GetxController {
|
||||
// Implementation of the controller
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
Rx<UserModel> driverModel = UserModel().obs;
|
||||
Rx<UserModel> ownerModel = UserModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
driverModel.value = Constant.userModel!;
|
||||
if (driverModel.value.ownerId != null && driverModel.value.ownerId!.isNotEmpty) {
|
||||
getOwnerDetails(driverModel.value.ownerId!);
|
||||
}
|
||||
getData();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getData() async {
|
||||
await getRentalSearchBooking();
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> getOwnerDetails(String ownerId) async {
|
||||
ownerModel.value = await FireStoreUtils.getUserProfile(ownerId) ?? UserModel();
|
||||
update();
|
||||
}
|
||||
|
||||
RxList<RentalOrderModel> rentalBookingData = <RentalOrderModel>[].obs;
|
||||
|
||||
Future<void> getRentalSearchBooking() async {
|
||||
await searchParcelsOnce(srcLat: Constant.locationDataFinal!.latitude ?? 0.0, srcLng: Constant.locationDataFinal!.longitude ?? 0.0).then(
|
||||
(event) {
|
||||
rentalBookingData.value = event;
|
||||
update();
|
||||
},
|
||||
);
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<List<RentalOrderModel>> searchParcelsOnce({
|
||||
required double srcLat,
|
||||
required double srcLng,
|
||||
}) async {
|
||||
final ref = FireStoreUtils.fireStore
|
||||
.collection(CollectionName.rentalOrders)
|
||||
.where("vehicleId", isEqualTo: driverModel.value.vehicleId)
|
||||
.where("sectionId", isEqualTo: driverModel.value.sectionId)
|
||||
.where('status', isEqualTo: "Order Placed");
|
||||
|
||||
GeoFirePoint center = Geoflutterfire().point(latitude: srcLat, longitude: srcLng);
|
||||
|
||||
// Fetch documents once
|
||||
final docs = await Geoflutterfire()
|
||||
.collection(collectionRef: ref)
|
||||
.within(
|
||||
center: center,
|
||||
radius: double.parse(Constant.rentalRadius),
|
||||
field: "sourcePoint",
|
||||
strictMode: true,
|
||||
)
|
||||
.first;
|
||||
|
||||
final now = DateTime.now();
|
||||
|
||||
final filtered = docs.where((doc) {
|
||||
final data = doc.data() as Map<String, dynamic>;
|
||||
if (data['bookingDateTime'] == null) return false;
|
||||
|
||||
// ✅ Check zone match
|
||||
if (data['zoneId'] == null || data['zoneId'] != driverModel.value.zoneId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data['rejectedByDrivers'] != null) {
|
||||
List<dynamic> rejectedByDrivers = data['rejectedByDrivers'];
|
||||
if (rejectedByDrivers.contains(FireStoreUtils.getCurrentUid())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
final Timestamp ts = data['bookingDateTime'];
|
||||
final orderDate = ts.toDate().toLocal();
|
||||
|
||||
// ✅ Allow only today's or future bookings
|
||||
bool isToday = orderDate.year == now.year && orderDate.month == now.month && orderDate.day == now.day;
|
||||
|
||||
return orderDate.isAfter(now) || isToday;
|
||||
}).toList();
|
||||
|
||||
return filtered.map((e) => RentalOrderModel.fromJson(e.data()!)).toList();
|
||||
}
|
||||
}
|
||||
119
lib/controllers/rental_dashboard_controller.dart
Normal file
119
lib/controllers/rental_dashboard_controller.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:location/location.dart';
|
||||
|
||||
import '../themes/theme_controller.dart';
|
||||
|
||||
class RentalDashboardController extends GetxController {
|
||||
RxInt drawerIndex = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
|
||||
getUser();
|
||||
getTheme();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
DateTime? currentBackPressTime;
|
||||
RxBool canPopNow = false.obs;
|
||||
|
||||
Future<void> getUser() async {
|
||||
await updateCurrentLocation();
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
Constant.userModel = UserModel.fromJson(event.data()!);
|
||||
if (userModel.value.sectionId != null && userModel.value.sectionId!.isNotEmpty) {
|
||||
await FireStoreUtils.getSectionBySectionId(userModel.value.sectionId!).then((sectionValue) {
|
||||
if (sectionValue != null) {
|
||||
Constant.sectionModel = sectionValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
RxString isDarkMode = "Light".obs;
|
||||
RxBool isDarkModeSwitch = false.obs;
|
||||
|
||||
void getTheme() {
|
||||
bool isDark = Preferences.getBoolean(Preferences.themKey);
|
||||
isDarkMode.value = isDark ? "Dark" : "Light";
|
||||
isDarkModeSwitch.value = isDark;
|
||||
}
|
||||
|
||||
void toggleDarkMode(bool value) {
|
||||
isDarkModeSwitch.value = value;
|
||||
isDarkMode.value = value ? "Dark" : "Light";
|
||||
Preferences.setBoolean(Preferences.themKey, value);
|
||||
// Update ThemeController for instant app theme change
|
||||
if (Get.isRegistered<ThemeController>()) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
themeController.isDark.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
Location location = Location();
|
||||
|
||||
Future<void> updateCurrentLocation() async {
|
||||
try {
|
||||
PermissionStatus permissionStatus = await location.hasPermission();
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location = UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
location.requestPermission().then((permissionStatus) {
|
||||
if (permissionStatus == PermissionStatus.granted) {
|
||||
location.enableBackgroundMode(enable: true);
|
||||
location.changeSettings(accuracy: LocationAccuracy.high, distanceFilter: double.parse(Constant.driverLocationUpdate));
|
||||
location.onLocationChanged.listen((locationData) async {
|
||||
Constant.locationDataFinal = locationData;
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
if (userModel.value.isActive == true) {
|
||||
userModel.value.location =
|
||||
UserLocation(latitude: locationData.latitude ?? 0.0, longitude: locationData.longitude ?? 0.0);
|
||||
userModel.value.rotation = locationData.heading;
|
||||
await FireStoreUtils.updateUser(userModel.value);
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
199
lib/controllers/rental_home_controller.dart
Normal file
199
lib/controllers/rental_home_controller.dart
Normal file
@@ -0,0 +1,199 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/app/wallet_screen/payment_list_screen.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/send_notification.dart';
|
||||
import 'package:driver/models/rental_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/wallet_transaction_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../constant/show_toast_dialog.dart' show ShowToastDialog;
|
||||
|
||||
class RentalHomeController extends GetxController {
|
||||
RxList<RentalOrderModel> rentalBookingData = <RentalOrderModel>[].obs;
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
Rx<TextEditingController> currentKilometerController = TextEditingController().obs;
|
||||
Rx<TextEditingController> completeKilometerController = TextEditingController().obs;
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
Rx<UserModel> ownerModel = UserModel().obs;
|
||||
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getBookingData();
|
||||
// TODO: implement onInit
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
|
||||
Future<void> getBookingData() async {
|
||||
isLoading.value = true;
|
||||
rentalBookingData.clear();
|
||||
|
||||
// Driver’s active rental bookings
|
||||
FireStoreUtils.fireStore
|
||||
.collection(CollectionName.rentalOrders)
|
||||
.where("driverId", isEqualTo: FireStoreUtils.getCurrentUid())
|
||||
.where("status", whereIn: [
|
||||
Constant.driverAccepted,
|
||||
Constant.orderInTransit,
|
||||
Constant.orderShipped,
|
||||
])
|
||||
.snapshots()
|
||||
.listen((event) {
|
||||
rentalBookingData.clear();
|
||||
|
||||
for (var element in event.docs) {
|
||||
rentalBookingData.add(RentalOrderModel.fromJson(element.data()));
|
||||
}
|
||||
|
||||
// ✅ Turn off loader *after first snapshot*
|
||||
isLoading.value = false;
|
||||
update();
|
||||
});
|
||||
|
||||
// Driver user data listener
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(FireStoreUtils.getCurrentUid()).snapshots().listen((event) {
|
||||
if (event.exists) {
|
||||
userModel.value = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
});
|
||||
|
||||
if (Constant.userModel!.ownerId != null && Constant.userModel!.ownerId!.isNotEmpty) {
|
||||
FireStoreUtils.fireStore.collection(CollectionName.users).doc(Constant.userModel!.ownerId).snapshots().listen(
|
||||
(event) async {
|
||||
if (event.exists) {
|
||||
ownerModel.value = UserModel.fromJson(event.data()!);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> completeParcel(RentalOrderModel parcelBookingData) async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
parcelBookingData.status = Constant.orderCompleted;
|
||||
|
||||
await updateCabWalletAmount(parcelBookingData);
|
||||
await FireStoreUtils.rentalOrderPlace(parcelBookingData);
|
||||
Map<String, dynamic> payLoad = <String, dynamic>{"type": "rental_order", "orderId": parcelBookingData.id};
|
||||
SendNotification.sendFcmMessage(Constant.rentalCompleted, parcelBookingData.author!.fcmToken.toString(), payLoad);
|
||||
FireStoreUtils.getRentalFirstOrderOrNOt(parcelBookingData).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateRentalReferralAmount(parcelBookingData);
|
||||
}
|
||||
});
|
||||
ShowToastDialog.showToast("Ride completed successfully".tr);
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
RxDouble subTotal = 0.0.obs;
|
||||
RxDouble discount = 0.0.obs;
|
||||
RxDouble taxAmount = 0.0.obs;
|
||||
RxDouble totalAmount = 0.0.obs;
|
||||
RxDouble extraKilometerCharge = 0.0.obs;
|
||||
RxDouble extraMinutesCharge = 0.0.obs;
|
||||
RxDouble adminComm = 0.0.obs;
|
||||
|
||||
Future<void> updateCabWalletAmount(RentalOrderModel orderModel) async {
|
||||
subTotal.value = 0.0;
|
||||
discount.value = 0.0;
|
||||
taxAmount.value = 0.0;
|
||||
totalAmount.value = 0.0;
|
||||
extraKilometerCharge.value = 0.0;
|
||||
extraMinutesCharge.value = 0.0;
|
||||
adminComm.value = 0.0;
|
||||
subTotal.value = double.tryParse(orderModel.subTotal?.toString() ?? "0") ?? 0.0;
|
||||
discount.value = double.tryParse(orderModel.discount?.toString() ?? "0") ?? 0.0;
|
||||
|
||||
if (orderModel.endTime != null) {
|
||||
DateTime start = orderModel.startTime!.toDate();
|
||||
DateTime end = orderModel.endTime!.toDate();
|
||||
int hours = end.difference(start).inHours;
|
||||
if (hours >= int.parse(orderModel.rentalPackageModel!.includedHours.toString())) {
|
||||
hours = hours - int.parse(orderModel.rentalPackageModel!.includedHours.toString());
|
||||
double hourlyRate = double.tryParse(orderModel.rentalPackageModel?.extraMinuteFare?.toString() ?? "0") ?? 0.0;
|
||||
extraMinutesCharge.value = (hours * 60) * hourlyRate;
|
||||
}
|
||||
}
|
||||
|
||||
if (orderModel.startKitoMetersReading != null && orderModel.endKitoMetersReading != null) {
|
||||
double startKm = double.tryParse(orderModel.startKitoMetersReading?.toString() ?? "0") ?? 0.0;
|
||||
double endKm = double.tryParse(orderModel.endKitoMetersReading?.toString() ?? "0") ?? 0.0;
|
||||
if (endKm > startKm) {
|
||||
double totalKm = endKm - startKm;
|
||||
if (totalKm > double.parse(orderModel.rentalPackageModel!.includedDistance!)) {
|
||||
totalKm = totalKm - double.parse(orderModel.rentalPackageModel!.includedDistance!);
|
||||
double extraKmRate = double.tryParse(orderModel.rentalPackageModel?.extraKmFare?.toString() ?? "0") ?? 0.0;
|
||||
extraKilometerCharge.value = totalKm * extraKmRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
subTotal.value = subTotal.value + extraKilometerCharge.value + extraMinutesCharge.value;
|
||||
|
||||
if (orderModel.taxSetting != null) {
|
||||
for (var element in orderModel.taxSetting!) {
|
||||
taxAmount.value += Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element);
|
||||
}
|
||||
}
|
||||
|
||||
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
|
||||
|
||||
if (orderModel.adminCommission!.isNotEmpty) {
|
||||
adminComm.value = Constant.calculateAdminCommission(
|
||||
amount: (subTotal.value - discount.value).toString(), adminCommissionType: orderModel.adminCommissionType.toString(), adminCommission: orderModel.adminCommission ?? '0');
|
||||
}
|
||||
|
||||
if (orderModel.paymentMethod.toString() != PaymentGateway.cod.name) {
|
||||
WalletTransactionModel transactionModel = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: totalAmount.value,
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: orderModel.paymentMethod!,
|
||||
transactionUser: "driver",
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty ? orderModel.driver!.ownerId.toString() : FireStoreUtils.getCurrentUid(),
|
||||
isTopup: true,
|
||||
orderId: orderModel.id,
|
||||
note: "Booking amount credited",
|
||||
paymentStatus: "success");
|
||||
|
||||
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateUserWallet(
|
||||
amount: totalAmount.value.toString(),
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty ? orderModel.driver!.ownerId.toString() : FireStoreUtils.getCurrentUid());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WalletTransactionModel adminCommissionTrancation = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: adminComm.value,
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: orderModel.paymentMethod!,
|
||||
transactionUser: "driver",
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty ? orderModel.driver!.ownerId.toString() : FireStoreUtils.getCurrentUid(),
|
||||
isTopup: false,
|
||||
orderId: orderModel.id,
|
||||
note: "Admin commission deducted",
|
||||
paymentStatus: "success");
|
||||
|
||||
print("=================== Admin Commission: ${adminComm.value} ==================");
|
||||
log("=========${adminCommissionTrancation.toJson().toString()}=========}");
|
||||
await FireStoreUtils.setWalletTransaction(adminCommissionTrancation).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateUserWallet(
|
||||
amount: "-${adminComm.value.toString()}",
|
||||
userId: orderModel.driver!.ownerId != null && orderModel.driver!.ownerId!.isNotEmpty ? orderModel.driver!.ownerId.toString() : FireStoreUtils.getCurrentUid());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
131
lib/controllers/rental_order_details_controller.dart
Normal file
131
lib/controllers/rental_order_details_controller.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/rental_order_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../constant/constant.dart';
|
||||
import '../models/user_model.dart';
|
||||
|
||||
class RentalOrderDetailsController extends GetxController {
|
||||
Rx<RentalOrderModel> order = RentalOrderModel().obs;
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
RxDouble subTotal = 0.0.obs;
|
||||
RxDouble discount = 0.0.obs;
|
||||
RxDouble taxAmount = 0.0.obs;
|
||||
RxDouble totalAmount = 0.0.obs;
|
||||
RxDouble adminCommission = 0.0.obs;
|
||||
RxDouble extraKilometerCharge = 0.0.obs;
|
||||
RxDouble extraMinutesCharge = 0.0.obs;
|
||||
Rx<UserModel?> userData = Rx<UserModel?>(null);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
if (args != null && args["rentalOrder"] != null) {
|
||||
String orderId = args["rentalOrder"];
|
||||
fetchOrder(orderId);
|
||||
} else {
|
||||
isLoading.value = false;
|
||||
ShowToastDialog.showToast("Invalid order details");
|
||||
}
|
||||
}
|
||||
|
||||
String getExtraKm() {
|
||||
try {
|
||||
final double start = double.tryParse(order.value.startKitoMetersReading ?? '0') ?? 0.0;
|
||||
final double end = double.tryParse(order.value.endKitoMetersReading ?? '0') ?? 0.0;
|
||||
final double included = double.tryParse(order.value.rentalPackageModel?.includedDistance?.toString() ?? '0') ?? 0.0;
|
||||
|
||||
// Calculate extra km safely
|
||||
final double extra = (end - start - included);
|
||||
final double validExtra = extra > 0 ? extra : 0;
|
||||
|
||||
return "${validExtra.toStringAsFixed(2)} ${Constant.distanceType}";
|
||||
} catch (e) {
|
||||
return "0 ${Constant.distanceType}";
|
||||
}
|
||||
}
|
||||
|
||||
///Safe calculation after order is loaded
|
||||
void calculateTotalAmount() {
|
||||
try {
|
||||
subTotal.value = double.tryParse(order.value.subTotal?.toString() ?? "0") ?? 0.0;
|
||||
discount.value = double.tryParse(order.value.discount?.toString() ?? "0") ?? 0.0;
|
||||
taxAmount.value = 0.0;
|
||||
|
||||
if (order.value.endTime != null) {
|
||||
DateTime start = order.value.startTime!.toDate();
|
||||
DateTime end = order.value.endTime!.toDate();
|
||||
|
||||
// Total rented minutes
|
||||
int totalMinutes = end.difference(start).inMinutes;
|
||||
|
||||
int includedMinutes = (int.tryParse(order.value.rentalPackageModel?.includedHours.toString() ?? "0") ?? 0) * 60;
|
||||
|
||||
if (totalMinutes > includedMinutes) {
|
||||
int extraMinutes = totalMinutes - includedMinutes;
|
||||
|
||||
double minuteFare = double.tryParse(order.value.rentalPackageModel?.extraMinuteFare?.toString() ?? "0") ?? 0.0;
|
||||
|
||||
extraMinutesCharge.value = extraMinutes * minuteFare;
|
||||
} else {
|
||||
extraMinutesCharge.value = 0;
|
||||
}
|
||||
}
|
||||
if (order.value.startKitoMetersReading != null && order.value.endKitoMetersReading != null) {
|
||||
double startKm = double.tryParse(order.value.startKitoMetersReading?.toString() ?? "0") ?? 0.0;
|
||||
double endKm = double.tryParse(order.value.endKitoMetersReading?.toString() ?? "0") ?? 0.0;
|
||||
if (endKm > startKm) {
|
||||
double totalKm = endKm - startKm;
|
||||
if (totalKm > double.parse(order.value.rentalPackageModel!.includedDistance!)) {
|
||||
totalKm = totalKm - double.parse(order.value.rentalPackageModel!.includedDistance!);
|
||||
double extraKmRate = double.tryParse(order.value.rentalPackageModel?.extraKmFare?.toString() ?? "0") ?? 0.0;
|
||||
extraKilometerCharge.value = totalKm * extraKmRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
subTotal.value = subTotal.value + extraKilometerCharge.value + extraMinutesCharge.value;
|
||||
|
||||
if (order.value.taxSetting != null) {
|
||||
for (var element in order.value.taxSetting!) {
|
||||
taxAmount.value += Constant.calculateTax(amount: (subTotal.value - discount.value).toString(), taxModel: element);
|
||||
}
|
||||
}
|
||||
|
||||
if (order.value.adminCommission!.isNotEmpty) {
|
||||
adminCommission.value = Constant.calculateAdminCommission(
|
||||
amount: (subTotal.value - discount.value).toString(),
|
||||
adminCommissionType: order.value.adminCommissionType.toString(),
|
||||
adminCommission: order.value.adminCommission ?? '0');
|
||||
}
|
||||
|
||||
totalAmount.value = (subTotal.value - discount.value) + taxAmount.value;
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Failed to calculate total: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchOrder(String orderId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
order.value = (await FireStoreUtils.getRentalOrderById(orderId))!;
|
||||
|
||||
calculateTotalAmount();
|
||||
fetchCustomerDetails();
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Failed to fetch order details: $e");
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchCustomerDetails() async {
|
||||
if (order.value.authorID != null) {
|
||||
final user = await FireStoreUtils.getUserProfile(order.value.authorID!);
|
||||
if (user != null) {
|
||||
userData.value = user;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
80
lib/controllers/rental_order_list_controller.dart
Normal file
80
lib/controllers/rental_order_list_controller.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'dart:async';
|
||||
import 'package:get/get.dart';
|
||||
import '../models/rental_order_model.dart';
|
||||
import '../utils/fire_store_utils.dart';
|
||||
|
||||
class RentalOrderListController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
RxList<RentalOrderModel> rentalOrders = <RentalOrderModel>[].obs;
|
||||
|
||||
RxString selectedTab = "On Going".obs;
|
||||
RxList<String> tabTitles = ["On Going", "Completed", "Cancelled"].obs;
|
||||
|
||||
StreamSubscription<List<RentalOrderModel>>? _rentalSubscription;
|
||||
final RxString selectedPaymentMethod = ''.obs;
|
||||
|
||||
RxString driverId = ''.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
driverId.value = args?['driverId'] ?? FireStoreUtils.getCurrentUid();
|
||||
listenRentalOrders();
|
||||
}
|
||||
|
||||
void selectTab(String tab) {
|
||||
selectedTab.value = tab;
|
||||
}
|
||||
|
||||
/// Start listening to rental orders live. Cancel previous subscription first.
|
||||
void listenRentalOrders() {
|
||||
isLoading.value = true;
|
||||
_rentalSubscription?.cancel();
|
||||
|
||||
_rentalSubscription = FireStoreUtils.getRentalOrders(driverId.value).listen(
|
||||
(orders) {
|
||||
rentalOrders.assignAll(orders);
|
||||
isLoading.value = false;
|
||||
},
|
||||
onError: (err) {
|
||||
isLoading.value = false;
|
||||
print("Error fetching rental orders: $err");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Return filtered list for a specific tab title
|
||||
List<RentalOrderModel> getOrdersForTab(String tab) {
|
||||
switch (tab) {
|
||||
case "On Going":
|
||||
return rentalOrders
|
||||
.where(
|
||||
(order) => [
|
||||
"Order Placed",
|
||||
"Order Accepted",
|
||||
"Driver Accepted",
|
||||
"Driver Pending",
|
||||
"Order Shipped",
|
||||
"In Transit",
|
||||
].contains(order.status),
|
||||
)
|
||||
.toList();
|
||||
|
||||
case "Completed":
|
||||
return rentalOrders.where((order) => ["Order Completed"].contains(order.status)).toList();
|
||||
|
||||
case "Cancelled":
|
||||
return rentalOrders.where((order) => ["Order Rejected", "Order Cancelled", "Driver Rejected"].contains(order.status)).toList();
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_rentalSubscription?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
132
lib/controllers/rental_review_controller.dart
Normal file
132
lib/controllers/rental_review_controller.dart
Normal file
@@ -0,0 +1,132 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../constant/collection_name.dart';
|
||||
import '../constant/show_toast_dialog.dart';
|
||||
import '../models/rating_model.dart';
|
||||
import '../models/rental_order_model.dart';
|
||||
import '../models/user_model.dart';
|
||||
import '../constant/constant.dart';
|
||||
import '../utils/fire_store_utils.dart';
|
||||
|
||||
class RentalReviewController extends GetxController {
|
||||
/// Order from arguments
|
||||
final Rx<RentalOrderModel?> order = Rx<RentalOrderModel?>(null);
|
||||
|
||||
/// Rating data
|
||||
final Rx<RatingModel?> ratingModel = Rx<RatingModel?>(null);
|
||||
final RxDouble ratings = 0.0.obs;
|
||||
final Rx<TextEditingController> comment = TextEditingController().obs;
|
||||
|
||||
/// Target user (customer in this case)
|
||||
final Rx<UserModel?> customerUser = Rx<UserModel?>(null);
|
||||
|
||||
/// Review stats
|
||||
final RxInt futureCount = 0.obs;
|
||||
final RxInt futureSum = 0.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
final args = Get.arguments;
|
||||
if (args != null && args['order'] != null) {
|
||||
order.value = args['order'] as RentalOrderModel;
|
||||
getReview();
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetch old review + customer stats
|
||||
Future<void> getReview() async {
|
||||
final existingRating = await FireStoreUtils.getReviewsbyID(order.value?.id ?? "");
|
||||
if (existingRating != null) {
|
||||
ratingModel.value = existingRating;
|
||||
ratings.value = existingRating.rating ?? 0;
|
||||
comment.value.text = existingRating.comment ?? "";
|
||||
}
|
||||
|
||||
final user = await FireStoreUtils.getUserProfile(order.value?.authorID ?? '');
|
||||
customerUser.value = user;
|
||||
|
||||
if (user != null) {
|
||||
final int userReviewsCount = int.tryParse(user.reviewsCount?.toString() ?? "0") ?? 0;
|
||||
final int userReviewsSum = int.tryParse(user.reviewsSum?.toString() ?? "0") ?? 0;
|
||||
|
||||
if (ratingModel.value != null) {
|
||||
final int oldRating = ratingModel.value?.rating?.toInt() ?? 0;
|
||||
futureCount.value = userReviewsCount - 1;
|
||||
futureSum.value = userReviewsSum - oldRating;
|
||||
} else {
|
||||
futureCount.value = userReviewsCount;
|
||||
futureSum.value = userReviewsSum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Save / update review (Driver → Customer)
|
||||
Future<void> submitReview() async {
|
||||
if (comment.value.text.trim().isEmpty || ratings.value == 0) {
|
||||
ShowToastDialog.showToast("Please provide rating and comment");
|
||||
return;
|
||||
}
|
||||
|
||||
ShowToastDialog.showLoader("Submitting...");
|
||||
|
||||
final user = await FireStoreUtils.getUserProfile(order.value?.authorID ?? '');
|
||||
|
||||
if (user != null) {
|
||||
user.reviewsCount = (futureCount.value + 1).toString();
|
||||
user.reviewsSum = (futureSum.value + ratings.value.toInt()).toString();
|
||||
}
|
||||
|
||||
if (ratingModel.value != null) {
|
||||
/// Update existing review
|
||||
final updatedRating = RatingModel(
|
||||
id: ratingModel.value!.id,
|
||||
comment: comment.value.text,
|
||||
photos: ratingModel.value?.photos ?? [],
|
||||
rating: ratings.value,
|
||||
orderId: ratingModel.value!.orderId,
|
||||
customerId: ratingModel.value!.customerId, // target is customer
|
||||
driverId: ratingModel.value!.driverId,
|
||||
vendorId: ratingModel.value?.vendorId,
|
||||
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
|
||||
profile: Constant.userModel?.profilePictureURL,
|
||||
createdAt: Timestamp.now(),
|
||||
);
|
||||
|
||||
await FireStoreUtils.updateReviewById(updatedRating);
|
||||
if (user != null) {
|
||||
await FireStoreUtils.updateUser(user);
|
||||
}
|
||||
} else {
|
||||
/// New review
|
||||
final docRef = FireStoreUtils.fireStore.collection(CollectionName.itemsReview).doc();
|
||||
final newRating = RatingModel(
|
||||
id: docRef.id,
|
||||
comment: comment.value.text,
|
||||
photos: [],
|
||||
rating: ratings.value,
|
||||
orderId: order.value?.id,
|
||||
customerId: order.value?.authorID, // target is customer
|
||||
driverId: order.value?.driverId, // reviewer (driver id)
|
||||
uname: "${Constant.userModel?.firstName ?? ''} ${Constant.userModel?.lastName ?? ''}",
|
||||
profile: Constant.userModel?.profilePictureURL,
|
||||
createdAt: Timestamp.now(),
|
||||
);
|
||||
|
||||
await FireStoreUtils.updateReviewById(newRating);
|
||||
if (user != null) {
|
||||
await FireStoreUtils.updateUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
Get.back(result: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
comment.value.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
312
lib/controllers/signup_controller.dart
Normal file
312
lib/controllers/signup_controller.dart
Normal file
@@ -0,0 +1,312 @@
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/app/auth_screen/login_screen.dart';
|
||||
import 'package:driver/app/cab_screen/cab_dashboard_screen.dart';
|
||||
import 'package:driver/app/dash_board_screen/dash_board_screen.dart';
|
||||
import 'package:driver/app/owner_screen/owner_dashboard_screen.dart';
|
||||
import 'package:driver/app/parcel_screen/parcel_dashboard_screen.dart';
|
||||
import 'package:driver/app/rental_service/rental_dashboard_screen.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/car_makes.dart';
|
||||
import 'package:driver/models/car_model.dart';
|
||||
import 'package:driver/models/section_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/vehicle_type.dart';
|
||||
import 'package:driver/models/zone_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/notification_service.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class SignupController extends GetxController {
|
||||
Rx<TextEditingController> firstNameEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> lastNameEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> emailEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> phoneNUmberEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> countryCodeEditingController = TextEditingController(text: Constant.defaultCountryCode).obs;
|
||||
Rx<TextEditingController> passwordEditingController = TextEditingController().obs;
|
||||
Rx<TextEditingController> conformPasswordEditingController = TextEditingController().obs;
|
||||
|
||||
Rx<TextEditingController> carPlatNumberEditingController = TextEditingController().obs;
|
||||
|
||||
RxBool passwordVisible = true.obs;
|
||||
RxBool conformPasswordVisible = true.obs;
|
||||
|
||||
RxString type = "".obs;
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
RxList<ZoneModel> zoneList = <ZoneModel>[].obs;
|
||||
Rx<ZoneModel> selectedZone = ZoneModel().obs;
|
||||
RxList<String> service = ['Delivery Service', 'Cab Service', 'Parcel Service', 'Rental Service'].obs; // Option 2
|
||||
RxString selectedService = 'Delivery Service'.obs; // Default selected option
|
||||
|
||||
RxList<SectionModel> sectionList = <SectionModel>[].obs;
|
||||
Rx<SectionModel> selectedSection = SectionModel().obs;
|
||||
|
||||
RxList<VehicleType> cabVehicleType = <VehicleType>[].obs;
|
||||
Rx<VehicleType> selectedVehicleType = VehicleType().obs;
|
||||
|
||||
// RxList<VehicleType> rentalVehicleType = <VehicleType>[].obs;
|
||||
// Rx<VehicleType> selectedRentalVehicleType = VehicleType().obs;
|
||||
|
||||
RxList<CarMakes> carMakesList = <CarMakes>[].obs;
|
||||
Rx<CarMakes> selectedCarMakes = CarMakes().obs;
|
||||
|
||||
RxList<CarModel> carModelList = <CarModel>[].obs;
|
||||
Rx<CarModel> selectedCarModel = CarModel().obs;
|
||||
|
||||
RxString selectedValue = "Individual".obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getArgument() async {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
type.value = argumentData['type'];
|
||||
userModel.value = argumentData['userModel'];
|
||||
if (type.value == "mobileNumber") {
|
||||
phoneNUmberEditingController.value.text = userModel.value.phoneNumber ?? "";
|
||||
countryCodeEditingController.value.text = userModel.value.countryCode ?? "+1";
|
||||
} else if (type.value == "google" || type.value == "apple") {
|
||||
emailEditingController.value.text = userModel.value.email ?? "";
|
||||
firstNameEditingController.value.text = userModel.value.firstName ?? "";
|
||||
lastNameEditingController.value.text = userModel.value.lastName ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
await FireStoreUtils.getZone().then((value) {
|
||||
if (value != null) {
|
||||
zoneList.value = value;
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.getCarMakes().then((value) {
|
||||
carMakesList.value = value;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getSection() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
await FireStoreUtils.getSections(selectedService.value == "Cab Service"
|
||||
? "cab-service"
|
||||
: selectedService.value == "Parcel Service"
|
||||
? "parcel_delivery"
|
||||
: selectedService.value == "Rental Service"
|
||||
? "rental-service"
|
||||
: "")
|
||||
.then((value) {
|
||||
sectionList.value = value;
|
||||
if (sectionList.isNotEmpty) {
|
||||
selectedSection.value = sectionList.first;
|
||||
}
|
||||
});
|
||||
await getVehicleType();
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> getVehicleType() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
cabVehicleType.clear();
|
||||
if (selectedService.value == "Cab Service") {
|
||||
await FireStoreUtils.getCabVehicleType(selectedSection.value.id.toString()).then((value) {
|
||||
cabVehicleType.value = value;
|
||||
if (cabVehicleType.isNotEmpty) {
|
||||
selectedVehicleType.value = cabVehicleType.first;
|
||||
}
|
||||
});
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
await FireStoreUtils.getRentalVehicleType(selectedSection.value.id.toString()).then((value) {
|
||||
cabVehicleType.value = value;
|
||||
if (cabVehicleType.isNotEmpty) {
|
||||
selectedVehicleType.value = cabVehicleType.first;
|
||||
}
|
||||
});
|
||||
}
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> getCarModel() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
carModelList.clear();
|
||||
selectedCarModel.value = CarModel();
|
||||
await FireStoreUtils.getCarModel(selectedCarMakes.value.name.toString()).then((value) {
|
||||
carModelList.value = value;
|
||||
});
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
|
||||
Future<void> signUpWithEmailAndPassword() async {
|
||||
signUp();
|
||||
}
|
||||
|
||||
Future<void> signUp() async {
|
||||
ShowToastDialog.showLoader("Please wait");
|
||||
if (type.value == "google" || type.value == "apple" || type.value == "mobileNumber") {
|
||||
userModel.value.firstName = firstNameEditingController.value.text.toString();
|
||||
userModel.value.lastName = lastNameEditingController.value.text.toString();
|
||||
userModel.value.email = emailEditingController.value.text.toString().toLowerCase();
|
||||
userModel.value.phoneNumber = phoneNUmberEditingController.value.text.toString();
|
||||
userModel.value.role = Constant.userRoleDriver;
|
||||
userModel.value.fcmToken = await NotificationService.getToken();
|
||||
userModel.value.active = Constant.autoApproveDriver == true ? true : false;
|
||||
userModel.value.isDocumentVerify = selectedValue.value == "Company"
|
||||
? Constant.isOwnerVerification == true
|
||||
? false
|
||||
: true
|
||||
: Constant.isDriverVerification == true
|
||||
? false
|
||||
: true;
|
||||
userModel.value.countryCode = countryCodeEditingController.value.text;
|
||||
userModel.value.createdAt = Timestamp.now();
|
||||
userModel.value.zoneId = selectedZone.value.id;
|
||||
userModel.value.appIdentifier = Platform.isAndroid ? 'android' : 'ios';
|
||||
userModel.value.provider = type.value;
|
||||
userModel.value.carNumber = carPlatNumberEditingController.value.text.toString();
|
||||
userModel.value.isOwner = selectedValue.value == "Company" ? true : false;
|
||||
userModel.value.serviceType = selectedService.value == "Cab Service"
|
||||
? "cab-service"
|
||||
: selectedService.value == "Parcel Service"
|
||||
? "parcel_delivery"
|
||||
: selectedService.value == "Rental Service"
|
||||
? "rental-service"
|
||||
: "delivery-service";
|
||||
|
||||
if (selectedService.value == "Cab Service") {
|
||||
userModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
userModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
userModel.value.carMakes = selectedCarMakes.value.name;
|
||||
userModel.value.carName = selectedCarModel.value.name;
|
||||
userModel.value.rideType = selectedSection.value.rideType;
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
userModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
userModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
userModel.value.carMakes = selectedCarMakes.value.name;
|
||||
userModel.value.carName = selectedCarModel.value.name;
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
} else if (selectedService.value == "Parcel Service") {
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
}
|
||||
|
||||
log(userModel.value.toJson().toString());
|
||||
|
||||
await FireStoreUtils.updateUser(userModel.value).then(
|
||||
(value) async {
|
||||
if (Constant.autoApproveDriver == true) {
|
||||
if (userModel.value.isOwner == true) {
|
||||
Get.offAll(OwnerDashboardScreen());
|
||||
} else {
|
||||
if (userModel.value.serviceType == "delivery-service") {
|
||||
Get.offAll(const DashBoardScreen());
|
||||
} else if (userModel.value.serviceType == "cab-service") {
|
||||
Get.offAll(const CabDashboardScreen());
|
||||
} else if (userModel.value.serviceType == "parcel_delivery") {
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
} else if (userModel.value.serviceType == "rental-service") {
|
||||
Get.offAll(const RentalDashboardScreen());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("Thank you for sign up, your application is under approval so please wait till that approve.".tr);
|
||||
Get.offAll(const LoginScreen());
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
final credential = await FirebaseAuth.instance.createUserWithEmailAndPassword(
|
||||
email: emailEditingController.value.text.trim(),
|
||||
password: passwordEditingController.value.text.trim(),
|
||||
);
|
||||
if (credential.user != null) {
|
||||
userModel.value.id = credential.user!.uid;
|
||||
userModel.value.firstName = firstNameEditingController.value.text.toString();
|
||||
userModel.value.lastName = lastNameEditingController.value.text.toString();
|
||||
userModel.value.email = emailEditingController.value.text.toString().toLowerCase();
|
||||
userModel.value.phoneNumber = phoneNUmberEditingController.value.text.toString();
|
||||
userModel.value.role = Constant.userRoleDriver;
|
||||
userModel.value.fcmToken = await NotificationService.getToken();
|
||||
userModel.value.active = Constant.autoApproveDriver == true ? true : false;
|
||||
userModel.value.isDocumentVerify = Constant.isDriverVerification == true ? false : true;
|
||||
userModel.value.countryCode = countryCodeEditingController.value.text;
|
||||
userModel.value.createdAt = Timestamp.now();
|
||||
userModel.value.zoneId = selectedZone.value.id;
|
||||
userModel.value.appIdentifier = Platform.isAndroid ? 'android' : 'ios';
|
||||
userModel.value.provider = 'email';
|
||||
userModel.value.carNumber = carPlatNumberEditingController.value.text.toString();
|
||||
userModel.value.isOwner = selectedValue.value == "Company" ? true : false;
|
||||
userModel.value.serviceType = selectedService.value == "Cab Service"
|
||||
? "cab-service"
|
||||
: selectedService.value == "Parcel Service"
|
||||
? "parcel_delivery"
|
||||
: selectedService.value == "Rental Service"
|
||||
? "rental-service"
|
||||
: "delivery-service";
|
||||
|
||||
if (selectedService.value == "Cab Service") {
|
||||
userModel.value.carMakes = selectedCarMakes.value.name;
|
||||
userModel.value.carName = selectedCarModel.value.name;
|
||||
userModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
userModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
userModel.value.rideType = "ride";
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
userModel.value.carMakes = selectedCarMakes.value.name;
|
||||
userModel.value.carName = selectedCarModel.value.name;
|
||||
userModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
userModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
} else if (selectedService.value == "Parcel Service") {
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
}
|
||||
|
||||
await FireStoreUtils.updateUser(userModel.value).then(
|
||||
(value) async {
|
||||
if (Constant.autoApproveDriver == true) {
|
||||
if (userModel.value.isOwner == true) {
|
||||
Get.offAll(OwnerDashboardScreen());
|
||||
} else {
|
||||
if (userModel.value.serviceType == "delivery-service") {
|
||||
Get.offAll(const DashBoardScreen());
|
||||
} else if (userModel.value.serviceType == "cab-service") {
|
||||
Get.offAll(const CabDashboardScreen());
|
||||
} else if (userModel.value.serviceType == "parcel_delivery") {
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
} else if (userModel.value.serviceType == "rental-service") {
|
||||
Get.offAll(const RentalDashboardScreen());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("Thank you for sign up, your application is under approval so please wait till that approve.".tr);
|
||||
Get.offAll(const LoginScreen());
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
} on FirebaseAuthException catch (e) {
|
||||
if (e.code == 'weak-password') {
|
||||
ShowToastDialog.showToast("The password provided is too weak.".tr);
|
||||
} else if (e.code == 'email-already-in-use') {
|
||||
ShowToastDialog.showToast("The account already exists for that email.".tr);
|
||||
} else if (e.code == 'invalid-email') {
|
||||
ShowToastDialog.showToast("Enter email is Invalid".tr);
|
||||
}
|
||||
print(e);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
ShowToastDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
}
|
||||
}
|
||||
74
lib/controllers/splash_controller.dart
Normal file
74
lib/controllers/splash_controller.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:driver/app/auth_screen/login_screen.dart';
|
||||
import 'package:driver/app/cab_screen/cab_dashboard_screen.dart';
|
||||
import 'package:driver/app/dash_board_screen/dash_board_screen.dart';
|
||||
import 'package:driver/app/maintenance_mode_screen/maintenance_mode_screen.dart';
|
||||
import 'package:driver/app/on_boarding_screen.dart';
|
||||
import 'package:driver/app/owner_screen/owner_dashboard_screen.dart';
|
||||
import 'package:driver/app/parcel_screen/parcel_dashboard_screen.dart';
|
||||
import 'package:driver/app/rental_service/rental_dashboard_screen.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/notification_service.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class SplashController extends GetxController {
|
||||
@override
|
||||
void onInit() {
|
||||
Timer(const Duration(seconds: 3), () => redirectScreen());
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> redirectScreen() async {
|
||||
if (Constant.isMaintenanceModeForDriver == true) {
|
||||
Get.offAll(const MaintenanceModeScreen());
|
||||
return;
|
||||
}
|
||||
if (Preferences.getBoolean(Preferences.isFinishOnBoardingKey) == false) {
|
||||
Get.offAll(const OnboardingScreen());
|
||||
} else {
|
||||
bool isLogin = await FireStoreUtils.isLogin();
|
||||
if (isLogin == true) {
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then((value) async {
|
||||
if (value != null) {
|
||||
UserModel userModel = value;
|
||||
log(userModel.toJson().toString());
|
||||
if (userModel.role == Constant.userRoleDriver) {
|
||||
if (userModel.active == true) {
|
||||
userModel.fcmToken = await NotificationService.getToken();
|
||||
await FireStoreUtils.updateUser(userModel);
|
||||
if (userModel.isOwner == true) {
|
||||
Get.offAll(OwnerDashboardScreen());
|
||||
} else {
|
||||
if (userModel.serviceType == "delivery-service") {
|
||||
Get.offAll(const DashBoardScreen());
|
||||
} else if (userModel.serviceType == "cab-service") {
|
||||
Get.offAll(const CabDashboardScreen());
|
||||
} else if (userModel.serviceType == "parcel_delivery") {
|
||||
Get.offAll(const ParcelDashboardScreen());
|
||||
} else if (userModel.serviceType == "rental-service") {
|
||||
Get.offAll(const RentalDashboardScreen());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
Get.offAll(const LoginScreen());
|
||||
}
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
Get.offAll(const LoginScreen());
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await FirebaseAuth.instance.signOut();
|
||||
Get.offAll(const LoginScreen());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
lib/controllers/vehicle_information_controller.dart
Normal file
229
lib/controllers/vehicle_information_controller.dart
Normal file
@@ -0,0 +1,229 @@
|
||||
import 'dart:developer';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/car_makes.dart';
|
||||
import 'package:driver/models/car_model.dart';
|
||||
import 'package:driver/models/section_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/vehicle_type.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class VehicleInformationController extends GetxController {
|
||||
Rx<TextEditingController> carPlatNumberEditingController = TextEditingController().obs;
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
|
||||
RxList<String> service = ['Delivery Service', 'Cab Service', 'Parcel Service', 'Rental Service'].obs;
|
||||
RxString selectedService = ''.obs;
|
||||
RxString selectedValue = 'ride'.obs;
|
||||
|
||||
RxList<SectionModel> sectionList = <SectionModel>[].obs;
|
||||
Rx<SectionModel> selectedSection = SectionModel().obs;
|
||||
|
||||
RxList<VehicleType> cabVehicleType = <VehicleType>[].obs;
|
||||
Rx<VehicleType> selectedVehicleType = VehicleType().obs;
|
||||
|
||||
RxList<CarMakes> carMakesList = <CarMakes>[].obs;
|
||||
Rx<CarMakes> selectedCarMakes = CarMakes().obs;
|
||||
|
||||
RxList<CarModel> carModelList = <CarModel>[].obs;
|
||||
Rx<CarModel> selectedCarModel = CarModel().obs;
|
||||
|
||||
RxBool isLoading = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadUserData();
|
||||
}
|
||||
|
||||
Future<void> loadUserData() async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
|
||||
UserModel? model = await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid());
|
||||
if (model != null) {
|
||||
userModel.value = model;
|
||||
carPlatNumberEditingController.value.text = userModel.value.carNumber ?? '';
|
||||
|
||||
selectedService.value = getReadableServiceType(userModel.value.serviceType!);
|
||||
|
||||
selectedValue.value = userModel.value.rideType ?? 'ride';
|
||||
await getSection();
|
||||
await getCarMakes();
|
||||
|
||||
if (userModel.value.sectionId != null) {
|
||||
selectedSection.value = sectionList.firstWhere(
|
||||
(e) => e.id == userModel.value.sectionId,
|
||||
orElse: () => sectionList.first,
|
||||
);
|
||||
}
|
||||
|
||||
await getVehicleType(selectedSection.value.id.toString());
|
||||
|
||||
if (userModel.value.vehicleId != null) {
|
||||
selectedVehicleType.value = cabVehicleType.firstWhere(
|
||||
(e) => e.id == userModel.value.vehicleId,
|
||||
orElse: () => cabVehicleType.first,
|
||||
);
|
||||
}
|
||||
|
||||
if (userModel.value.carMakes != null) {
|
||||
selectedCarMakes.value = carMakesList.firstWhere(
|
||||
(e) => e.name == userModel.value.carMakes,
|
||||
orElse: () => carMakesList.first,
|
||||
);
|
||||
await getCarModel();
|
||||
}
|
||||
|
||||
if (userModel.value.carName != null) {
|
||||
selectedCarModel.value = carModelList.firstWhere(
|
||||
(e) => e.name == userModel.value.carName,
|
||||
orElse: () => carModelList.first,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveVehicleInformation() async {
|
||||
if (userModel.value.isOwner == true) {
|
||||
ShowToastDialog.showToast("Update not allowed for Owner type users.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (carPlatNumberEditingController.value.text.trim().isEmpty) {
|
||||
ShowToastDialog.showToast("Please enter car plate number");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedVehicleType.value.id == null) {
|
||||
ShowToastDialog.showToast("Please select a vehicle type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCarMakes.value.id == null) {
|
||||
ShowToastDialog.showToast("Please select a car brand");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedCarModel.value.id == null) {
|
||||
ShowToastDialog.showToast("Please select a car model");
|
||||
return;
|
||||
}
|
||||
|
||||
ShowToastDialog.showLoader("Updating vehicle information...");
|
||||
|
||||
try {
|
||||
userModel.value.carNumber = carPlatNumberEditingController.value.text.trim();
|
||||
userModel.value.serviceType = getServiceTypeKey(selectedService.value);
|
||||
userModel.value.sectionId = selectedSection.value.id;
|
||||
userModel.value.vehicleType = selectedVehicleType.value.name;
|
||||
userModel.value.vehicleId = selectedVehicleType.value.id;
|
||||
userModel.value.carMakes = selectedCarMakes.value.name;
|
||||
userModel.value.carName = selectedCarModel.value.name;
|
||||
userModel.value.rideType = selectedValue.value;
|
||||
|
||||
bool success = await FireStoreUtils.updateUser(userModel.value);
|
||||
|
||||
ShowToastDialog.closeLoader();
|
||||
|
||||
if (success) {
|
||||
ShowToastDialog.showToast("Vehicle information updated successfully.");
|
||||
} else {
|
||||
ShowToastDialog.showToast("Failed to update. Please try again.");
|
||||
}
|
||||
} catch (e) {
|
||||
ShowToastDialog.closeLoader();
|
||||
ShowToastDialog.showToast("Error updating vehicle info: $e");
|
||||
log("Error updating vehicle info: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getSection() async {
|
||||
try {
|
||||
String key = getServiceTypeKey(selectedService.value);
|
||||
final value = await FireStoreUtils.getSections(key);
|
||||
sectionList.value = value;
|
||||
if (sectionList.isNotEmpty) {
|
||||
selectedSection.value = sectionList.first;
|
||||
}
|
||||
} catch (e) {
|
||||
log("Error loading sections: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getVehicleType(String sectionId) async {
|
||||
try {
|
||||
if (selectedService.value == "Cab Service") {
|
||||
cabVehicleType.value = await FireStoreUtils.getCabVehicleType(sectionId);
|
||||
} else if (selectedService.value == "Rental Service") {
|
||||
cabVehicleType.value = await FireStoreUtils.getRentalVehicleType(sectionId);
|
||||
}
|
||||
if (cabVehicleType.isNotEmpty) selectedVehicleType.value = cabVehicleType.first;
|
||||
} catch (e) {
|
||||
log("Error loading vehicle types: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getCarMakes() async {
|
||||
try {
|
||||
carMakesList.value = await FireStoreUtils.getCarMakes();
|
||||
if (carMakesList.isNotEmpty) selectedCarMakes.value = carMakesList.first;
|
||||
} catch (e) {
|
||||
log("Error loading car makes: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getCarModel() async {
|
||||
try {
|
||||
if (selectedCarMakes.value.name == null || selectedCarMakes.value.name!.isEmpty) {
|
||||
carModelList.clear();
|
||||
selectedCarModel.value = CarModel();
|
||||
return;
|
||||
}
|
||||
|
||||
carModelList.value = await FireStoreUtils.getCarModel(selectedCarMakes.value.name!);
|
||||
if (carModelList.isNotEmpty) {
|
||||
selectedCarModel.value = carModelList.first;
|
||||
} else {
|
||||
selectedCarModel.value = CarModel();
|
||||
}
|
||||
update();
|
||||
} catch (e) {
|
||||
log("Error loading car models: $e");
|
||||
}
|
||||
}
|
||||
|
||||
String getReadableServiceType(String key) {
|
||||
switch (key) {
|
||||
case 'cab-service':
|
||||
return 'Cab Service';
|
||||
case 'parcel_delivery':
|
||||
return 'Parcel Service';
|
||||
case 'rental-service':
|
||||
return 'Rental Service';
|
||||
default:
|
||||
return 'Delivery Service';
|
||||
}
|
||||
}
|
||||
|
||||
String getServiceTypeKey(String name) {
|
||||
switch (name) {
|
||||
case 'Cab Service':
|
||||
return 'cab-service';
|
||||
case 'Parcel Service':
|
||||
return 'parcel_delivery';
|
||||
case 'Rental Service':
|
||||
return 'rental-service';
|
||||
default:
|
||||
return 'delivery_service';
|
||||
}
|
||||
}
|
||||
}
|
||||
32
lib/controllers/verification_controller.dart
Normal file
32
lib/controllers/verification_controller.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/document_model.dart';
|
||||
import 'package:driver/models/driver_document_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class VerificationController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getDocument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
RxList documentList = <DocumentModel>[].obs;
|
||||
RxList driverDocumentList = <Documents>[].obs;
|
||||
|
||||
Future<void> getDocument() async {
|
||||
await FireStoreUtils.getDocumentList(Constant.userModel!.isOwner == true ? "owner" : "driver").then((value) {
|
||||
documentList.value = value;
|
||||
});
|
||||
await FireStoreUtils.getDocumentOfDriver().then((value) {
|
||||
if (value != null) {
|
||||
driverDocumentList.value = value.documents!;
|
||||
}
|
||||
});
|
||||
isLoading.value = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
100
lib/controllers/verification_details_upload_controller.dart
Normal file
100
lib/controllers/verification_details_upload_controller.dart
Normal file
@@ -0,0 +1,100 @@
|
||||
import 'dart:io';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/document_model.dart';
|
||||
import 'package:driver/models/driver_document_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
class DetailsUploadController extends GetxController {
|
||||
Rx<DocumentModel> documentModel = DocumentModel().obs;
|
||||
|
||||
Rx<DateTime?> selectedDate = DateTime.now().obs;
|
||||
|
||||
RxString frontImage = "".obs;
|
||||
RxString backImage = "".obs;
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getArgument();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getArgument() async {
|
||||
dynamic argumentData = Get.arguments;
|
||||
if (argumentData != null) {
|
||||
documentModel.value = argumentData['documentModel'];
|
||||
}
|
||||
getDocument();
|
||||
update();
|
||||
}
|
||||
|
||||
Rx<Documents> documents = Documents().obs;
|
||||
|
||||
Future<void> getDocument() async {
|
||||
await FireStoreUtils.getDocumentOfDriver().then((value) {
|
||||
isLoading.value = false;
|
||||
if (value != null) {
|
||||
var contain = value.documents!.where((element) => element.documentId == documentModel.value.id);
|
||||
if (contain.isNotEmpty) {
|
||||
documents.value = value.documents!.firstWhere((itemToCheck) => itemToCheck.documentId == documentModel.value.id);
|
||||
frontImage.value = documents.value.frontImage!;
|
||||
backImage.value = documents.value.backImage!;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final ImagePicker _imagePicker = ImagePicker();
|
||||
|
||||
Future pickFile({required ImageSource source, required String type}) async {
|
||||
try {
|
||||
XFile? image = await _imagePicker.pickImage(source: source);
|
||||
if (image == null) return;
|
||||
Get.back();
|
||||
|
||||
if (type == "front") {
|
||||
frontImage.value = image.path;
|
||||
} else {
|
||||
backImage.value = image.path;
|
||||
}
|
||||
} on PlatformException {
|
||||
ShowToastDialog.showToast(
|
||||
source == ImageSource.camera ? "Camera permission denied. Please allow access to take a photo." : "Gallery permission denied. Please allow access to select an image.");
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast("Something went wrong. Please try again.");
|
||||
print("Pick file error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> uploadDocument() async {
|
||||
String frontImageFileName = File(frontImage.value).path.split('/').last;
|
||||
String backImageFileName = File(backImage.value).path.split('/').last;
|
||||
|
||||
if (frontImage.value.isNotEmpty && Constant().hasValidUrl(frontImage.value) == false) {
|
||||
frontImage.value = await Constant.uploadUserImageToFireStorage(File(frontImage.value), "driverDocument/${FireStoreUtils.getCurrentUid()}", frontImageFileName);
|
||||
}
|
||||
|
||||
if (backImage.value.isNotEmpty && Constant().hasValidUrl(backImage.value) == false) {
|
||||
backImage.value = await Constant.uploadUserImageToFireStorage(File(backImage.value), "driverDocument/${FireStoreUtils.getCurrentUid()}", backImageFileName);
|
||||
}
|
||||
documents.value.frontImage = frontImage.value;
|
||||
documents.value.backImage = backImage.value;
|
||||
documents.value.documentId = documentModel.value.id;
|
||||
documents.value.status = "uploaded";
|
||||
|
||||
await FireStoreUtils.uploadDriverDocument(documents.value).then((value) {
|
||||
if (value) {
|
||||
ShowToastDialog.closeLoader();
|
||||
ShowToastDialog.showToast("Document upload successfully".tr);
|
||||
|
||||
Get.back(result: true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
988
lib/controllers/wallet_controller.dart
Normal file
988
lib/controllers/wallet_controller.dart
Normal file
@@ -0,0 +1,988 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:math' as maths;
|
||||
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/constant/show_toast_dialog.dart';
|
||||
import 'package:driver/models/cab_order_model.dart';
|
||||
import 'package:driver/models/order_model.dart';
|
||||
import 'package:driver/models/parcel_order_model.dart';
|
||||
import 'package:driver/models/payment_model/flutter_wave_model.dart';
|
||||
import 'package:driver/models/payment_model/mercado_pago_model.dart';
|
||||
import 'package:driver/models/payment_model/mid_trans.dart';
|
||||
import 'package:driver/models/payment_model/orange_money.dart';
|
||||
import 'package:driver/models/payment_model/pay_fast_model.dart';
|
||||
import 'package:driver/models/payment_model/pay_stack_model.dart';
|
||||
import 'package:driver/models/payment_model/paypal_model.dart';
|
||||
import 'package:driver/models/payment_model/razorpay_model.dart';
|
||||
import 'package:driver/models/payment_model/stripe_model.dart';
|
||||
import 'package:driver/models/payment_model/xendit.dart';
|
||||
import 'package:driver/models/rental_order_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/wallet_transaction_model.dart';
|
||||
import 'package:driver/models/withdraw_method_model.dart';
|
||||
import 'package:driver/models/withdrawal_model.dart';
|
||||
import 'package:driver/payment/MercadoPagoScreen.dart';
|
||||
import 'package:driver/payment/PayFastScreen.dart';
|
||||
import 'package:driver/payment/midtrans_screen.dart';
|
||||
import 'package:driver/payment/orangePayScreen.dart';
|
||||
import 'package:driver/payment/paystack/pay_stack_screen.dart';
|
||||
import 'package:driver/payment/paystack/pay_stack_url_model.dart';
|
||||
import 'package:driver/payment/paystack/paystack_url_genrater.dart';
|
||||
import 'package:driver/payment/stripe_failed_model.dart';
|
||||
import 'package:driver/payment/xenditModel.dart';
|
||||
import 'package:driver/payment/xenditScreen.dart';
|
||||
import 'package:driver/themes/app_them_data.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:driver/utils/preferences.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_paypal/flutter_paypal.dart';
|
||||
import 'package:flutter_stripe/flutter_stripe.dart' as flutterStipe;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:razorpay_flutter/razorpay_flutter.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class WalletController extends GetxController {
|
||||
RxBool isLoading = true.obs;
|
||||
|
||||
Rx<TextEditingController> topUpAmountController = TextEditingController().obs;
|
||||
RxString selectedPaymentMethod = "".obs;
|
||||
|
||||
Rx<TextEditingController> amountTextFieldController = TextEditingController().obs;
|
||||
Rx<TextEditingController> noteTextFieldController = TextEditingController().obs;
|
||||
|
||||
Rx<UserModel> userModel = UserModel().obs;
|
||||
RxList<WalletTransactionModel> walletTopTransactionList = <WalletTransactionModel>[].obs;
|
||||
RxList<WithdrawalModel> withdrawalList = <WithdrawalModel>[].obs;
|
||||
|
||||
RxList<OrderModel> dailyEarningList = <OrderModel>[].obs;
|
||||
RxList<OrderModel> monthlyEarningList = <OrderModel>[].obs;
|
||||
RxList<OrderModel> yearlyEarningList = <OrderModel>[].obs;
|
||||
|
||||
RxList<ParcelOrderModel> dailyParcelEarningList = <ParcelOrderModel>[].obs;
|
||||
RxList<ParcelOrderModel> monthlyParcelEarningList = <ParcelOrderModel>[].obs;
|
||||
RxList<ParcelOrderModel> yearlyParcelEarningList = <ParcelOrderModel>[].obs;
|
||||
|
||||
RxList<RentalOrderModel> dailyRentalEarningList = <RentalOrderModel>[].obs;
|
||||
RxList<RentalOrderModel> monthlyRentalEarningList = <RentalOrderModel>[].obs;
|
||||
RxList<RentalOrderModel> yearlyRentalEarningList = <RentalOrderModel>[].obs;
|
||||
|
||||
RxList<CabOrderModel> dailyCabEarningList = <CabOrderModel>[].obs;
|
||||
RxList<CabOrderModel> monthlyCabEarningList = <CabOrderModel>[].obs;
|
||||
RxList<CabOrderModel> yearlyCabEarningList = <CabOrderModel>[].obs;
|
||||
|
||||
RxList<String> dropdownValue = ["Daily", "Monthly", "Yearly"].obs;
|
||||
RxString selectedDropDownValue = "Daily".obs;
|
||||
|
||||
RxInt selectedTabIndex = 0.obs;
|
||||
RxInt selectedValue = 0.obs;
|
||||
|
||||
Rx<WithdrawMethodModel> withdrawMethodModel = WithdrawMethodModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getWalletTransaction();
|
||||
getPaymentSettings();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Rx<PayFastModel> payFastModel = PayFastModel().obs;
|
||||
Rx<MercadoPagoModel> mercadoPagoModel = MercadoPagoModel().obs;
|
||||
Rx<PayPalModel> payPalModel = PayPalModel().obs;
|
||||
Rx<StripeModel> stripeModel = StripeModel().obs;
|
||||
Rx<FlutterWaveModel> flutterWaveModel = FlutterWaveModel().obs;
|
||||
Rx<PayStackModel> payStackModel = PayStackModel().obs;
|
||||
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
|
||||
|
||||
Rx<MidTrans> midTransModel = MidTrans().obs;
|
||||
Rx<OrangeMoney> orangeMoneyModel = OrangeMoney().obs;
|
||||
Rx<Xendit> xenditModel = Xendit().obs;
|
||||
|
||||
Future<void> getPaymentSettings() async {
|
||||
await FireStoreUtils.getPaymentSettingsData().then(
|
||||
(value) {
|
||||
payFastModel.value = PayFastModel.fromJson(jsonDecode(Preferences.getString(Preferences.payFastSettings)));
|
||||
mercadoPagoModel.value = MercadoPagoModel.fromJson(jsonDecode(Preferences.getString(Preferences.mercadoPago)));
|
||||
payPalModel.value = PayPalModel.fromJson(jsonDecode(Preferences.getString(Preferences.paypalSettings)));
|
||||
stripeModel.value = StripeModel.fromJson(jsonDecode(Preferences.getString(Preferences.stripeSettings)));
|
||||
flutterWaveModel.value = FlutterWaveModel.fromJson(jsonDecode(Preferences.getString(Preferences.flutterWave)));
|
||||
payStackModel.value = PayStackModel.fromJson(jsonDecode(Preferences.getString(Preferences.payStack)));
|
||||
razorPayModel.value = RazorPayModel.fromJson(jsonDecode(Preferences.getString(Preferences.razorpaySettings)));
|
||||
|
||||
midTransModel.value = MidTrans.fromJson(jsonDecode(Preferences.getString(Preferences.midTransSettings)));
|
||||
orangeMoneyModel.value = OrangeMoney.fromJson(jsonDecode(Preferences.getString(Preferences.orangeMoneySettings)));
|
||||
xenditModel.value = Xendit.fromJson(jsonDecode(Preferences.getString(Preferences.xenditSettings)));
|
||||
|
||||
flutterStipe.Stripe.publishableKey = stripeModel.value.clientpublishableKey.toString();
|
||||
flutterStipe.Stripe.merchantIdentifier = 'eMart Driver';
|
||||
flutterStipe.Stripe.instance.applySettings();
|
||||
setRef();
|
||||
|
||||
razorPay.on(Razorpay.EVENT_PAYMENT_SUCCESS, handlePaymentSuccess);
|
||||
razorPay.on(Razorpay.EVENT_EXTERNAL_WALLET, handleExternalWaller);
|
||||
razorPay.on(Razorpay.EVENT_PAYMENT_ERROR, handlePaymentError);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> getWalletTransaction() async {
|
||||
await FireStoreUtils.getWalletTransaction().then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
walletTopTransactionList.value = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await FireStoreUtils.getWithdrawHistory().then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
withdrawalList.value = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
DateTime nowDate = DateTime.now();
|
||||
|
||||
await FireStoreUtils.getUserProfile(FireStoreUtils.getCurrentUid()).then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
userModel.value = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// if(userModel.value.serviceType == "delivery-service"){
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.vendorOrders)
|
||||
// .where('driverID', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month, nowDate.day)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// OrderModel dailyEarningModel = OrderModel.fromJson(element.data());
|
||||
// dailyEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.vendorOrders)
|
||||
// .where('driverID', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// OrderModel dailyEarningModel = OrderModel.fromJson(element.data());
|
||||
// monthlyEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.vendorOrders)
|
||||
// .where('driverID', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// OrderModel dailyEarningModel = OrderModel.fromJson(element.data());
|
||||
// yearlyEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
// }
|
||||
// else if(userModel.value.serviceType == "parcel_delivery"){
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.parcelOrders)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month, nowDate.day)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// ParcelOrderModel dailyEarningModel = ParcelOrderModel.fromJson(element.data());
|
||||
// dailyParcelEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.parcelOrders)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// ParcelOrderModel dailyEarningModel = ParcelOrderModel.fromJson(element.data());
|
||||
// monthlyParcelEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.parcelOrders)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// ParcelOrderModel dailyEarningModel = ParcelOrderModel.fromJson(element.data());
|
||||
// yearlyParcelEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
// }
|
||||
// else if(userModel.value.serviceType == "rental-service"){
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.rentalOrders)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month, nowDate.day)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// RentalOrderModel dailyEarningModel = RentalOrderModel.fromJson(element.data());
|
||||
// dailyRentalEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.rentalOrders)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// RentalOrderModel dailyEarningModel = RentalOrderModel.fromJson(element.data());
|
||||
// monthlyRentalEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.rentalOrders)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// RentalOrderModel dailyEarningModel = RentalOrderModel.fromJson(element.data());
|
||||
// yearlyRentalEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
// }
|
||||
// else if(userModel.value.serviceType == "cab-service"){
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.ridesBooking)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month, nowDate.day)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// CabOrderModel dailyEarningModel = CabOrderModel.fromJson(element.data());
|
||||
// dailyCabEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.ridesBooking)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year, nowDate.month)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// CabOrderModel dailyEarningModel = CabOrderModel.fromJson(element.data());
|
||||
// monthlyCabEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
//
|
||||
// await FireStoreUtils.fireStore
|
||||
// .collection(CollectionName.ridesBooking)
|
||||
// .where('driverId', isEqualTo: Constant.userModel!.id.toString())
|
||||
// .where('createdAt', isGreaterThanOrEqualTo: Timestamp.fromDate(DateTime(nowDate.year)))
|
||||
// .orderBy('createdAt', descending: true)
|
||||
// .get()
|
||||
// .then((value) {
|
||||
// for (var element in value.docs) {
|
||||
// CabOrderModel dailyEarningModel = CabOrderModel.fromJson(element.data());
|
||||
// yearlyCabEarningList.add(dailyEarningModel);
|
||||
// }
|
||||
// }).catchError((error) {
|
||||
// log(error.toString());
|
||||
// });
|
||||
// }
|
||||
await getPaymentMethod();
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> getPaymentMethod() async {
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("razorpaySettings").get().then((user) {
|
||||
try {
|
||||
razorPayModel.value = RazorPayModel.fromJson(user.data() ?? {});
|
||||
} catch (e) {
|
||||
debugPrint('FireStoreUtils.getUserByID failed to parse user object ${user.id}');
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("paypalSettings").get().then((paypalData) {
|
||||
try {
|
||||
payPalModel.value = PayPalModel.fromJson(paypalData.data() ?? {});
|
||||
} catch (error) {
|
||||
debugPrint(error.toString());
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("stripeSettings").get().then((paypalData) {
|
||||
try {
|
||||
stripeModel.value = StripeModel.fromJson(paypalData.data() ?? {});
|
||||
} catch (error) {
|
||||
debugPrint(error.toString());
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("flutterWave").get().then((paypalData) {
|
||||
try {
|
||||
flutterWaveModel.value = FlutterWaveModel.fromJson(paypalData.data() ?? {});
|
||||
} catch (error) {
|
||||
debugPrint(error.toString());
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.getWithdrawMethod().then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
withdrawMethodModel.value = value;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> walletTopUp() async {
|
||||
WalletTransactionModel transactionModel = WalletTransactionModel(
|
||||
id: Constant.getUuid(),
|
||||
amount: double.parse(topUpAmountController.value.text),
|
||||
date: Timestamp.now(),
|
||||
paymentMethod: selectedPaymentMethod.value,
|
||||
transactionUser: "user",
|
||||
userId: FireStoreUtils.getCurrentUid(),
|
||||
isTopup: true,
|
||||
note: "Wallet Top-up",
|
||||
paymentStatus: "success");
|
||||
|
||||
await FireStoreUtils.setWalletTransaction(transactionModel).then((value) async {
|
||||
if (value == true) {
|
||||
await FireStoreUtils.updateUserWallet(amount: topUpAmountController.value.text, userId: FireStoreUtils.getCurrentUid())
|
||||
.then((value) {
|
||||
getWalletTransaction();
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ShowToastDialog.showToast("Amount Top-up successfully".tr);
|
||||
}
|
||||
|
||||
// final _flutterPaypalNativePlugin = FlutterPaypalNative.instance;
|
||||
|
||||
// void initPayPal() async {
|
||||
// //set debugMode for error logging
|
||||
// FlutterPaypalNative.isDebugMode = paytmModel.value.isSandboxEnabled == true ? true : false;
|
||||
|
||||
// //initiate payPal plugin
|
||||
// await _flutterPaypalNativePlugin.init(
|
||||
// //your app id !!! No Underscore!!! see readme.md for help
|
||||
// returnUrl: "com.parkme://paypalpay",
|
||||
// //client id from developer dashboard
|
||||
// clientID: payPalModel.value.paypalClient.toString(),
|
||||
// //sandbox, staging, live etc
|
||||
// payPalEnvironment: payPalModel.value.isLive == false ? FPayPalEnvironment.sandbox : FPayPalEnvironment.live,
|
||||
// //what currency do you plan to use? default is US dollars
|
||||
// currencyCode: FPayPalCurrencyCode.usd,
|
||||
// //action paynow?
|
||||
// action: FPayPalUserAction.payNow,
|
||||
// );
|
||||
|
||||
// //call backs for payment
|
||||
// _flutterPaypalNativePlugin.setPayPalOrderCallback(
|
||||
// callback: FPayPalOrderCallback(
|
||||
// onCancel: () {
|
||||
// //user canceled the payment
|
||||
// ShowToastDialog.showToast("Payment canceled");
|
||||
// },
|
||||
// onSuccess: (data) {
|
||||
// //successfully paid
|
||||
// //remove all items from queue
|
||||
// // _flutterPaypalNativePlugin.removeAllPurchaseItems();
|
||||
// ShowToastDialog.showToast("Payment Successful!!");
|
||||
// walletTopUp();
|
||||
// },
|
||||
// onError: (data) {
|
||||
// //an error occured
|
||||
// ShowToastDialog.showToast("error: ${data.reason}");
|
||||
// },
|
||||
// onShippingChange: (data) {
|
||||
// //the user updated the shipping address
|
||||
// ShowToastDialog.showToast("shipping change: ${data.shippingChangeAddress?.adminArea1 ?? ""}");
|
||||
// },
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
//Paypal
|
||||
void paypalPaymentSheet(String amount, context) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (BuildContext context) => UsePaypal(
|
||||
sandboxMode: payPalModel.value.isLive == true ? false : true,
|
||||
clientId: payPalModel.value.paypalClient ?? '',
|
||||
secretKey: payPalModel.value.paypalSecret ?? '',
|
||||
returnURL: "com.parkme://paypalpay",
|
||||
cancelURL: "com.parkme://paypalpay",
|
||||
transactions: [
|
||||
{
|
||||
"amount": {
|
||||
"total": amount,
|
||||
"currency": "USD",
|
||||
"details": {"subtotal": amount}
|
||||
},
|
||||
}
|
||||
],
|
||||
note: "Contact us for any questions on your order.",
|
||||
onSuccess: (Map params) async {
|
||||
log("onSuccess: $params");
|
||||
getWalletTransaction();
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
},
|
||||
onError: (error) {
|
||||
log("onError: $error");
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
|
||||
},
|
||||
onCancel: (params) {
|
||||
log("onError: $params");
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Strip
|
||||
Future<void> stripeMakePayment({required String amount}) async {
|
||||
log(double.parse(amount).toStringAsFixed(0));
|
||||
try {
|
||||
Map<String, dynamic>? paymentIntentData = await createStripeIntent(amount: amount);
|
||||
log("stripe Responce====>$paymentIntentData");
|
||||
if (paymentIntentData!.containsKey("error")) {
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
|
||||
} else {
|
||||
await flutterStipe.Stripe.instance.initPaymentSheet(
|
||||
paymentSheetParameters: flutterStipe.SetupPaymentSheetParameters(
|
||||
paymentIntentClientSecret: paymentIntentData['client_secret'],
|
||||
allowsDelayedPaymentMethods: false,
|
||||
googlePay: const flutterStipe.PaymentSheetGooglePay(
|
||||
merchantCountryCode: 'US',
|
||||
testEnv: true,
|
||||
currencyCode: "USD",
|
||||
),
|
||||
customFlow: true,
|
||||
style: ThemeMode.system,
|
||||
appearance: flutterStipe.PaymentSheetAppearance(
|
||||
colors: flutterStipe.PaymentSheetAppearanceColors(
|
||||
primary: AppThemeData.primary300,
|
||||
),
|
||||
),
|
||||
merchantDisplayName: 'GoRide'));
|
||||
displayStripePaymentSheet(amount: amount);
|
||||
}
|
||||
} catch (e, s) {
|
||||
log("$e \n$s");
|
||||
ShowToastDialog.showToast("exception:$e \n$s");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> displayStripePaymentSheet({required String amount}) async {
|
||||
try {
|
||||
await flutterStipe.Stripe.instance.presentPaymentSheet().then((value) {
|
||||
ShowToastDialog.showToast("Payment successfully".tr);
|
||||
walletTopUp();
|
||||
});
|
||||
} on flutterStipe.StripeException catch (e) {
|
||||
var lo1 = jsonEncode(e);
|
||||
var lo2 = jsonDecode(lo1);
|
||||
StripePayFailedModel lom = StripePayFailedModel.fromJson(lo2);
|
||||
ShowToastDialog.showToast(lom.error.message);
|
||||
} catch (e) {
|
||||
ShowToastDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future createStripeIntent({required String amount}) async {
|
||||
try {
|
||||
Map<String, dynamic> body = {
|
||||
'amount': ((double.parse(amount) * 100).round()).toString(),
|
||||
'currency': "USD",
|
||||
'payment_method_types[]': 'card',
|
||||
"description": "Strip Payment",
|
||||
"shipping[name]": userModel.value.fullName(),
|
||||
"shipping[address][line1]": "510 Townsend St",
|
||||
"shipping[address][postal_code]": "98140",
|
||||
"shipping[address][city]": "San Francisco",
|
||||
"shipping[address][state]": "CA",
|
||||
"shipping[address][country]": "US",
|
||||
};
|
||||
var stripeSecret = stripeModel.value.stripeSecret;
|
||||
var response = await http.post(Uri.parse('https://api.stripe.com/v1/payment_intents'),
|
||||
body: body, headers: {'Authorization': 'Bearer $stripeSecret', 'Content-Type': 'application/x-www-form-urlencoded'});
|
||||
|
||||
return jsonDecode(response.body);
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
//mercadoo
|
||||
Future<Null> mercadoPagoMakePayment({required BuildContext context, required String amount}) async {
|
||||
final headers = {
|
||||
'Authorization': 'Bearer ${mercadoPagoModel.value.accessToken}',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
final body = jsonEncode({
|
||||
"items": [
|
||||
{
|
||||
"title": "Test",
|
||||
"description": "Test Payment",
|
||||
"quantity": 1,
|
||||
"currency_id": "BRL", // or your preferred currency
|
||||
"unit_price": double.parse(amount),
|
||||
}
|
||||
],
|
||||
"payer": {"email": userModel.value.email},
|
||||
"back_urls": {
|
||||
"failure": "${Constant.globalUrl}payment/failure",
|
||||
"pending": "${Constant.globalUrl}payment/pending",
|
||||
"success": "${Constant.globalUrl}payment/success",
|
||||
},
|
||||
"auto_return": "approved" // Automatically return after payment is approved
|
||||
});
|
||||
|
||||
final response = await http.post(
|
||||
Uri.parse("https://api.mercadopago.com/checkout/preferences"),
|
||||
headers: headers,
|
||||
body: body,
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final data = jsonDecode(response.body);
|
||||
Get.to(MercadoPagoScreen(initialURl: data['init_point']))!.then((value) {
|
||||
if (value) {
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.showToast("Something want wrong please contact administrator".tr);
|
||||
|
||||
print('Error creating preference: ${response.body}');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
///PayStack Payment Method
|
||||
Future<void> payStackPayment(String totalAmount) async {
|
||||
await PayStackURLGen.payStackURLGen(
|
||||
amount: (double.parse(totalAmount) * 100).toString(),
|
||||
currency: "ZAR",
|
||||
secretKey: payStackModel.value.secretKey.toString(),
|
||||
userModel: userModel.value)
|
||||
.then((value) async {
|
||||
if (value != null) {
|
||||
PayStackUrlModel payStackModel0 = value;
|
||||
Get.to(PayStackScreen(
|
||||
secretKey: payStackModel.value.secretKey.toString(),
|
||||
callBackUrl: payStackModel.value.callbackURL.toString(),
|
||||
initialURl: payStackModel0.data.authorizationUrl,
|
||||
amount: totalAmount,
|
||||
reference: payStackModel0.data.reference,
|
||||
))!
|
||||
.then((value) {
|
||||
if (value) {
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//flutter wave Payment Method
|
||||
Future<Null> flutterWaveInitiatePayment({required BuildContext context, required String amount}) async {
|
||||
final url = Uri.parse('https://api.flutterwave.com/v3/payments');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer ${flutterWaveModel.value.secretKey}',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
final body = jsonEncode({
|
||||
"tx_ref": _ref,
|
||||
"amount": amount,
|
||||
"currency": "NGN",
|
||||
"redirect_url": "${Constant.globalUrl}payment/success",
|
||||
"payment_options": "ussd, card, barter, payattitude",
|
||||
"customer": {
|
||||
"email": userModel.value.email.toString(),
|
||||
"phonenumber": userModel.value.phoneNumber, // Add a real phone number
|
||||
"name": userModel.value.fullName(), // Add a real customer name
|
||||
},
|
||||
"customizations": {
|
||||
"title": "Payment for Services",
|
||||
"description": "Payment for XYZ services",
|
||||
}
|
||||
});
|
||||
|
||||
final response = await http.post(url, headers: headers, body: body);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body);
|
||||
Get.to(MercadoPagoScreen(initialURl: data['data']['link']))!.then((value) {
|
||||
if (value) {
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Payment UnSuccessful!!".tr);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
print('Payment initialization failed: ${response.body}');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String? _ref;
|
||||
|
||||
void setRef() {
|
||||
maths.Random numRef = maths.Random();
|
||||
int year = DateTime.now().year;
|
||||
int refNumber = numRef.nextInt(20000);
|
||||
if (Platform.isAndroid) {
|
||||
_ref = "AndroidRef$year$refNumber";
|
||||
} else if (Platform.isIOS) {
|
||||
_ref = "IOSRef$year$refNumber";
|
||||
}
|
||||
}
|
||||
|
||||
// payFast
|
||||
void payFastPayment({required BuildContext context, required String amount}) {
|
||||
PayStackURLGen.getPayHTML(payFastSettingData: payFastModel.value, amount: amount.toString(), userModel: userModel.value)
|
||||
.then((String? value) async {
|
||||
bool isDone = await Get.to(PayFastScreen(htmlData: value!, payFastSettingData: payFastModel.value));
|
||||
if (isDone) {
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment successfully".tr);
|
||||
walletTopUp();
|
||||
} else {
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment Failed".tr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///RazorPay payment function
|
||||
final Razorpay razorPay = Razorpay();
|
||||
|
||||
void openCheckout({required amount, required orderId}) async {
|
||||
var options = {
|
||||
'key': razorPayModel.value.razorpayKey,
|
||||
'amount': amount * 100,
|
||||
'name': 'GoRide',
|
||||
'order_id': orderId,
|
||||
"currency": "INR",
|
||||
'description': 'wallet Topup',
|
||||
'retry': {'enabled': true, 'max_count': 1},
|
||||
'send_sms_hash': true,
|
||||
'prefill': {
|
||||
'contact': userModel.value.phoneNumber,
|
||||
'email': userModel.value.email,
|
||||
},
|
||||
'external': {
|
||||
'wallets': ['paytm']
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
razorPay.open(options);
|
||||
} catch (e) {
|
||||
debugPrint('Error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void handlePaymentSuccess(PaymentSuccessResponse response) {
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
}
|
||||
|
||||
void handleExternalWaller(ExternalWalletResponse response) {
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment Processing!! via".tr);
|
||||
}
|
||||
|
||||
void handlePaymentError(PaymentFailureResponse response) {
|
||||
Get.back();
|
||||
ShowToastDialog.showToast("Payment Failed!!".tr);
|
||||
}
|
||||
|
||||
//Midtrans payment
|
||||
Future<void> midtransMakePayment({required String amount, required BuildContext context}) async {
|
||||
await createPaymentLink(amount: amount).then((url) {
|
||||
ShowToastDialog.closeLoader();
|
||||
if (url != '') {
|
||||
Get.to(() => MidtransScreen(
|
||||
initialURl: url,
|
||||
))!
|
||||
.then((value) {
|
||||
if (value == true) {
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> createPaymentLink({required var amount}) async {
|
||||
var ordersId = const Uuid().v1();
|
||||
final url = Uri.parse(
|
||||
midTransModel.value.isSandbox! ? 'https://api.sandbox.midtrans.com/v1/payment-links' : 'https://api.midtrans.com/v1/payment-links');
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': generateBasicAuthHeader(midTransModel.value.serverKey!),
|
||||
},
|
||||
body: jsonEncode({
|
||||
'transaction_details': {
|
||||
'order_id': ordersId,
|
||||
'gross_amount': double.parse(amount.toString()).toInt(),
|
||||
},
|
||||
'usage_limit': 2,
|
||||
"callbacks": {"finish": "https://www.google.com?merchant_order_id=$ordersId"},
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final responseData = jsonDecode(response.body);
|
||||
return responseData['payment_url'];
|
||||
} else {
|
||||
ShowToastDialog.showToast("something went wrong, please contact admin.".tr);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
String generateBasicAuthHeader(String apiKey) {
|
||||
String credentials = '$apiKey:';
|
||||
String base64Encoded = base64Encode(utf8.encode(credentials));
|
||||
return 'Basic $base64Encoded';
|
||||
}
|
||||
|
||||
//Orangepay payment
|
||||
static String accessToken = '';
|
||||
static String payToken = '';
|
||||
static String orderId = '';
|
||||
static String amount = '';
|
||||
|
||||
Future<void> orangeMakePayment({required String amount, required BuildContext context}) async {
|
||||
reset();
|
||||
var id = const Uuid().v4();
|
||||
var paymentURL = await fetchToken(context: context, orderId: id, amount: amount, currency: 'USD');
|
||||
ShowToastDialog.closeLoader();
|
||||
if (paymentURL.toString() != '') {
|
||||
Get.to(() => OrangeMoneyScreen(
|
||||
initialURl: paymentURL,
|
||||
accessToken: accessToken,
|
||||
amount: amount,
|
||||
orangePay: orangeMoneyModel.value,
|
||||
orderId: orderId,
|
||||
payToken: payToken,
|
||||
))!
|
||||
.then((value) {
|
||||
if (value == true) {
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
|
||||
}
|
||||
}
|
||||
|
||||
Future fetchToken({required String orderId, required String currency, required BuildContext context, required String amount}) async {
|
||||
String apiUrl = 'https://api.orange.com/oauth/v3/token';
|
||||
Map<String, String> requestBody = {
|
||||
'grant_type': 'client_credentials',
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(apiUrl),
|
||||
headers: <String, String>{
|
||||
'Authorization': "Basic ${orangeMoneyModel.value.auth!}",
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: requestBody);
|
||||
|
||||
// Handle the response
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
Map<String, dynamic> responseData = jsonDecode(response.body);
|
||||
|
||||
accessToken = responseData['access_token'];
|
||||
// ignore: use_build_context_synchronously
|
||||
return await webpayment(context: context, amountData: amount, currency: currency, orderIdData: orderId);
|
||||
} else {
|
||||
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
Future webpayment(
|
||||
{required String orderIdData, required BuildContext context, required String currency, required String amountData}) async {
|
||||
orderId = orderIdData;
|
||||
amount = amountData;
|
||||
String apiUrl = orangeMoneyModel.value.isSandbox! == true
|
||||
? 'https://api.orange.com/orange-money-webpay/dev/v1/webpayment'
|
||||
: 'https://api.orange.com/orange-money-webpay/cm/v1/webpayment';
|
||||
Map<String, String> requestBody = {
|
||||
"merchant_key": orangeMoneyModel.value.merchantKey ?? '',
|
||||
"currency": orangeMoneyModel.value.isSandbox == true ? "OUV" : currency,
|
||||
"order_id": orderId,
|
||||
"amount": amount,
|
||||
"reference": 'Y-Note Test',
|
||||
"lang": "en",
|
||||
"return_url": orangeMoneyModel.value.returnUrl!.toString(),
|
||||
"cancel_url": orangeMoneyModel.value.cancelUrl!.toString(),
|
||||
"notif_url": orangeMoneyModel.value.notifUrl!.toString(),
|
||||
};
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse(apiUrl),
|
||||
headers: <String, String>{'Authorization': 'Bearer $accessToken', 'Content-Type': 'application/json', 'Accept': 'application/json'},
|
||||
body: json.encode(requestBody),
|
||||
);
|
||||
|
||||
// Handle the response
|
||||
if (response.statusCode == 201) {
|
||||
Map<String, dynamic> responseData = jsonDecode(response.body);
|
||||
if (responseData['message'] == 'OK') {
|
||||
payToken = responseData['pay_token'];
|
||||
return responseData['payment_url'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
ShowToastDialog.showToast("Something went wrong, please contact admin.".tr);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
static void reset() {
|
||||
accessToken = '';
|
||||
payToken = '';
|
||||
orderId = '';
|
||||
amount = '';
|
||||
}
|
||||
|
||||
//XenditPayment
|
||||
Future<void> xenditPayment(context, amount) async {
|
||||
await createXenditInvoice(amount: amount).then((model) {
|
||||
ShowToastDialog.closeLoader();
|
||||
if (model.id != null) {
|
||||
Get.to(() => XenditScreen(
|
||||
initialURl: model.invoiceUrl ?? '',
|
||||
transId: model.id ?? '',
|
||||
apiKey: xenditModel.value.apiKey!.toString(),
|
||||
))!
|
||||
.then((value) {
|
||||
if (value == true) {
|
||||
ShowToastDialog.showToast("Payment Successful!!".tr);
|
||||
walletTopUp();
|
||||
} else {
|
||||
ShowToastDialog.showToast("Payment Unsuccessful!!".tr);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<XenditModel> createXenditInvoice({required var amount}) async {
|
||||
const url = 'https://api.xendit.co/v2/invoices';
|
||||
var headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': generateBasicAuthHeader(xenditModel.value.apiKey!.toString()),
|
||||
// 'Cookie': '__cf_bm=yERkrx3xDITyFGiou0bbKY1bi7xEwovHNwxV1vCNbVc-1724155511-1.0.1.1-jekyYQmPCwY6vIJ524K0V6_CEw6O.dAwOmQnHtwmaXO_MfTrdnmZMka0KZvjukQgXu5B.K_6FJm47SGOPeWviQ',
|
||||
};
|
||||
|
||||
final body = jsonEncode({
|
||||
'external_id': const Uuid().v1(),
|
||||
'amount': amount,
|
||||
'payer_email': 'customer@domain.com',
|
||||
'description': 'Test - VA Successful invoice payment',
|
||||
'currency': 'IDR', //IDR, PHP, THB, VND, MYR
|
||||
});
|
||||
|
||||
try {
|
||||
final response = await http.post(Uri.parse(url), headers: headers, body: body);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
XenditModel model = XenditModel.fromJson(jsonDecode(response.body));
|
||||
return model;
|
||||
} else {
|
||||
return XenditModel();
|
||||
}
|
||||
} catch (e) {
|
||||
return XenditModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
112
lib/controllers/withdraw_method_setup_controller.dart
Normal file
112
lib/controllers/withdraw_method_setup_controller.dart
Normal file
@@ -0,0 +1,112 @@
|
||||
import 'package:driver/constant/collection_name.dart';
|
||||
import 'package:driver/constant/constant.dart';
|
||||
import 'package:driver/models/payment_model/flutter_wave_model.dart';
|
||||
import 'package:driver/models/payment_model/paypal_model.dart';
|
||||
import 'package:driver/models/payment_model/razorpay_model.dart';
|
||||
import 'package:driver/models/payment_model/stripe_model.dart';
|
||||
import 'package:driver/models/user_model.dart';
|
||||
import 'package:driver/models/withdraw_method_model.dart';
|
||||
import 'package:driver/utils/fire_store_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class WithdrawMethodSetupController extends GetxController {
|
||||
Rx<TextEditingController> accountNumberFlutterWave = TextEditingController().obs;
|
||||
Rx<TextEditingController> bankCodeFlutterWave = TextEditingController().obs;
|
||||
Rx<TextEditingController> emailPaypal = TextEditingController().obs;
|
||||
Rx<TextEditingController> accountIdRazorPay = TextEditingController().obs;
|
||||
Rx<TextEditingController> accountIdStripe = TextEditingController().obs;
|
||||
|
||||
Rx<UserBankDetails> userBankDetails = UserBankDetails().obs;
|
||||
Rx<WithdrawMethodModel> withdrawMethodModel = WithdrawMethodModel().obs;
|
||||
|
||||
RxBool isBankDetailsAdded = false.obs;
|
||||
|
||||
RxBool isLoading = true.obs;
|
||||
Rx<RazorPayModel> razorPayModel = RazorPayModel().obs;
|
||||
Rx<PayPalModel> paypalDataModel = PayPalModel().obs;
|
||||
Rx<StripeModel> stripeSettingData = StripeModel().obs;
|
||||
Rx<FlutterWaveModel> flutterWaveSettingData = FlutterWaveModel().obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
// TODO: implement onInit
|
||||
getPaymentMethod();
|
||||
getPaymentSettings();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future<void> getPaymentMethod() async {
|
||||
isLoading.value = true;
|
||||
accountNumberFlutterWave.value.clear();
|
||||
bankCodeFlutterWave.value.clear();
|
||||
emailPaypal.value.clear();
|
||||
accountIdRazorPay.value.clear();
|
||||
accountIdStripe.value.clear();
|
||||
|
||||
await FireStoreUtils.getWithdrawMethod().then(
|
||||
(value) {
|
||||
if (value != null) {
|
||||
withdrawMethodModel.value = value;
|
||||
|
||||
if (withdrawMethodModel.value.flutterWave != null) {
|
||||
accountNumberFlutterWave.value.text = withdrawMethodModel.value.flutterWave!.accountNumber.toString();
|
||||
bankCodeFlutterWave.value.text = withdrawMethodModel.value.flutterWave!.bankCode.toString();
|
||||
}
|
||||
|
||||
if (withdrawMethodModel.value.paypal != null) {
|
||||
emailPaypal.value.text = withdrawMethodModel.value.paypal!.email.toString();
|
||||
}
|
||||
|
||||
if (withdrawMethodModel.value.razorpay != null) {
|
||||
accountIdRazorPay.value.text = withdrawMethodModel.value.razorpay!.accountId.toString();
|
||||
}
|
||||
if (withdrawMethodModel.value.stripe != null) {
|
||||
accountIdStripe.value.text = withdrawMethodModel.value.stripe!.accountId.toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
Future<void> getPaymentSettings() async {
|
||||
if (Constant.userModel!.userBankDetails != null) {
|
||||
userBankDetails.value = Constant.userModel!.userBankDetails!;
|
||||
isBankDetailsAdded.value = userBankDetails.value.accountNumber.isNotEmpty;
|
||||
}
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("razorpaySettings").get().then((user) {
|
||||
try {
|
||||
razorPayModel.value = RazorPayModel.fromJson(user.data() ?? {});
|
||||
} catch (e) {
|
||||
debugPrint('FireStoreUtils.getUserByID failed to parse user object ${user.id}');
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("paypalSettings").get().then((paypalData) {
|
||||
try {
|
||||
paypalDataModel.value = PayPalModel.fromJson(paypalData.data() ?? {});
|
||||
} catch (error) {
|
||||
debugPrint(error.toString());
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("stripeSettings").get().then((paypalData) {
|
||||
try {
|
||||
stripeSettingData.value = StripeModel.fromJson(paypalData.data() ?? {});
|
||||
} catch (error) {
|
||||
debugPrint(error.toString());
|
||||
}
|
||||
});
|
||||
|
||||
await FireStoreUtils.fireStore.collection(CollectionName.settings).doc("flutterWave").get().then((paypalData) {
|
||||
try {
|
||||
flutterWaveSettingData.value = FlutterWaveModel.fromJson(paypalData.data() ?? {});
|
||||
} catch (error) {
|
||||
debugPrint(error.toString());
|
||||
}
|
||||
});
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user