Initial commit

This commit is contained in:
2025-12-08 23:25:00 +05:00
commit ee5cb4ac1a
851 changed files with 115172 additions and 0 deletions

View 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;
}
}

View 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);
}
}
}

View 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 (dont 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();
}
}
}

View 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();
}
}
}

View 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);
}
}

View 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;
}
}

View 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");
// }
// }
}

View 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);
}
}
}

View 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);
}
}

View 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();
}
}

View 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),
// );
// }
// }
// }
// }

View 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'];
}
}
}
}

View 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");
}
}
}

View 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);
}
}
}
}

View 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);
}
});
}
});
}
}

View 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}");
}
}
}

View 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);
}
}

View 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;
}
}

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}
}

View 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);
});
}
}

View 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);
}
}

View 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);
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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();
}
}

View 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;
}
}
}

View 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);
// }
// }

View 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);
});
}
}

View 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();
}
}

View 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();
}
}

View 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);
}
}
}

View 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();
// Drivers 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());
}
});
}
}

View 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;
}
}
}
}

View 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();
}
}

View 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();
}
}

View 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();
}
}

View 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());
}
}
}
}

View 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';
}
}
}

View 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();
}
}

View 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);
}
});
}
}

View 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();
}
}
}

View 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;
}
}