25-7-28-2

This commit is contained in:
Hamza-Ayed
2025-07-28 12:21:28 +03:00
parent 660d60e1f5
commit 83a97baed1
549 changed files with 109870 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
// import 'package:flutter/material.dart';
// import 'package:ride/constant/api_key.dart';
// import 'package:ride/constant/box_name.dart';
// import 'package:ride/main.dart';
// import 'package:zego_uikit_prebuilt_call/zego_uikit_prebuilt_call.dart';
// class CallPage extends StatelessWidget {
// const CallPage({Key? key, required this.callID}) : super(key: key);
// final String callID;
// @override
// Widget build(BuildContext context) {
// return ZegoUIKitPrebuiltCall(
// appID: AK
// .zegoCloudAppID, // Fill in the appID that you get from ZEGOCLOUD Admin Console.
// appSign: AK
// .zegoCloudAppSIGN, // Fill in the appSign that you get from ZEGOCLOUD Admin Console.
// userID: box.read(BoxName.passengerID) ?? box.read(BoxName.driverID),
// userName: box.read(BoxName.name) ?? box.read(BoxName.nameDriver),
// callID: callID,
// // You can also use groupVideo/groupVoice/oneOnOneVoice to make more types of calls.
// config: ZegoUIKitPrebuiltCallConfig.oneOnOneVoiceCall(),
// );
// }
// }

View File

@@ -0,0 +1,811 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import 'package:sefer_driver/constant/api_key.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/links.dart';
import '../../../../controller/firebase/local_notification.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../main.dart';
import '../../../../models/model/order_data.dart';
import '../../../../print.dart';
// === Enhanced Colors for Better Readability ===
class AppColors {
static const primary = Color(0xFF1A252F);
static const card = Color(0xFF2C3E50);
static const white = Colors.white;
static const gray = Color(0xFFBDC3C7);
static const lightGray = Color(0xFFECF0F1);
static const accent = Color(0xFF00BCD4);
static const accept = Color(0xFF4CAF50);
static const reject = Color(0xFFFF5722);
static const highlight = Color(0xFFFFC107);
static const priceHighlight = Color(0xFF00E676);
static const urgentRed = Color(0xFFD32F2F);
}
class OrderOverlay extends StatefulWidget {
const OrderOverlay({Key? key}) : super(key: key);
@override
State<OrderOverlay> createState() => _OrderOverlayState();
}
class _OrderOverlayState extends State<OrderOverlay>
with WidgetsBindingObserver {
// === State Variables ===
OrderData? orderData;
Timer? timer;
int remainingSeconds = 10;
final AudioPlayer audioPlayer = AudioPlayer();
bool buttonsEnabled = true;
final String mapApiKey = AK.mapAPIKEY;
final CRUD _crud = CRUD();
final NotificationController notificationController =
Get.put(NotificationController());
// === Getters ===
bool get canShowMap {
if (orderData == null || mapApiKey.isEmpty) return false;
final start = orderData!.startCoordinates;
final end = orderData!.endCoordinates;
return start?['lat'] != null &&
start?['lng'] != null &&
end?['lat'] != null &&
end?['lng'] != null;
}
String get staticMapUrl {
if (!canShowMap) return "";
final start = orderData!.startCoordinates!;
final end = orderData!.endCoordinates!;
final startMarker = Uri.encodeComponent("${start['lat']},${start['lng']}");
final endMarker = Uri.encodeComponent("${end['lat']},${end['lng']}");
return "https://maps.googleapis.com/maps/api/staticmap?"
"size=600x150&maptype=roadmap"
"&markers=color:green%7Clabel:S%7C$startMarker"
"&markers=color:red%7Clabel:D%7C$endMarker"
"&path=color:0x007bff%7Cweight:5%7C$startMarker%7C$endMarker"
"&key=$mapApiKey";
}
// === Lifecycle ===
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
FlutterOverlayWindow.overlayListener.listen((event) {
if (mounted) _processEventData(event);
});
}
@override
void dispose() {
timer?.cancel();
_stopAudio();
audioPlayer.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
_checkOverlayStatus();
}
}
List myList = [];
// === Setup & Listeners ===
void _setupOverlayListener() {
FlutterOverlayWindow.overlayListener.listen((event) {
if (mounted) _processEventData(event);
});
}
void _processEventData(dynamic event) {
_log("Received event: $event");
if (event is List<dynamic>) {
try {
myList = event;
final newOrder = OrderData.fromList(event);
_log("Parsed OrderData: ${newOrder.toMap()}");
setState(() {
orderData = newOrder;
});
_resetAndStartTimer();
} catch (e, s) {
_log("Error parsing OrderData: $e\nStackTrace: $s");
}
} else {
_log("Unexpected data format: $event");
}
}
void _checkOverlayStatus() async {
bool isActive = await FlutterOverlayWindow.isActive();
if (isActive && mounted && orderData != null) {
if (remainingSeconds > 0 && (timer == null || !timer!.isActive)) {
_resetAndStartTimer();
}
}
}
// === Timer Management ===
void _resetAndStartTimer() {
timer?.cancel();
audioPlayer.stop();
setState(() {
buttonsEnabled = true;
remainingSeconds = _calculateTimerDuration();
});
_playAudio();
_startTimer();
}
int _calculateTimerDuration() {
if (orderData?.durationToPassengerMinutes != null &&
orderData!.durationToPassengerMinutes > 0) {
int duration = orderData!.durationToPassengerMinutes * 60;
return duration > 10 ? 10 : duration;
}
return 10;
}
void _startTimer() {
if (orderData == null) return;
timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (!mounted) {
timer.cancel();
_stopAudio();
return;
}
setState(() {
if (remainingSeconds > 0) {
remainingSeconds--;
} else {
timer.cancel();
_stopAudio();
if (buttonsEnabled) _handleOrderTimeout();
}
});
});
}
// === Audio Management ===
void _playAudio() async {
try {
await audioPlayer.setAsset('assets/order.mp3', preload: true);
await audioPlayer.setLoopMode(LoopMode.one);
await audioPlayer.play();
} catch (e) {
_log('Error playing audio: $e');
}
}
void _stopAudio() {
audioPlayer.stop();
}
String _getData(int index, {String defaultValue = ''}) {
if (myList.length > index && myList[index] != null) {
return myList[index].toString();
}
return defaultValue;
}
// === Order Actions ===
Future<void> _acceptOrder() async {
if (!buttonsEnabled || orderData == null) return;
_disableButtonsAndProcess();
_log("Order ACCEPTED: ${orderData!.orderId}");
try {
final driverId = box.read(BoxName.driverID)?.toString();
if (driverId == null) {
_log("Error: Driver ID is null. Closing overlay.");
await _closeOverlay();
return;
}
var res = await CRUD().post(link: AppLink.updateStausFromSpeed, payload: {
'id': orderData!.orderId,
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': orderData!.orderId,
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
final payload = {
// بيانات أساسية
'driver_id': driverId,
'status': 'Apply',
'passengerLocation': _getData(0),
'passengerDestination': _getData(1),
'Duration': _getData(4),
'totalCost': _getData(26),
'Distance': _getData(5),
'name': _getData(8),
'phone': _getData(10),
'email': _getData(28),
'WalletChecked': _getData(13),
'tokenPassenger': _getData(9),
'direction': staticMapUrl.toString(),
'DurationToPassenger': _getData(15),
'rideId': orderData!.orderId,
'passengerId': _getData(7),
'durationOfRideValue': _getData(19),
'paymentAmount': _getData(2),
'paymentMethod': _getData(13) == 'true' ? 'visa' : 'cash',
'isHaveSteps': _getData(20),
'step0': myList[21].toString(),
'step1': myList[22].toString(),
'step2': myList[23].toString(),
'step3': myList[24].toString(),
'step4': myList[25].toString(),
'passengerWalletBurc': myList[26].toString(),
'carType': myList[31].toString(),
'kazan': myList[32].toString(),
'startNameLocation': myList[29].toString(),
'endNameLocation': myList[30].toString(),
// الحقول الإضافية التي يجب تضمينها
'timeOfOrder': DateTime.now().toIso8601String(),
'totalPassenger': _getData(2),
};
Log.print('myList: ${myList}');
Log.print('payload: ${payload}');
CRUD().post(
link: AppLink.addOverLayStatus,
payload: payload,
);
if (res != "failure") {
// Using rideId (_getData(16)) for order_id consistently
CRUD().post(link: AppLink.addDriverOrder, payload: {
'driver_id': driverId, // Driver ID from the order data
'order_id': orderData!.orderId,
'status': 'Apply'
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/driver_order/add.php",
payload: {
'driver_id': driverId,
'order_id': orderData!.orderId,
'status': 'Apply'
});
}
_log("Server update successful. Writing to storage.");
notificationController.showNotification(
"Order Accepted".tr,
"Open app and go to passenger".tr,
'ding',
'',
);
await _closeOverlay();
} else {
_log("Failed to update order status on server: $res");
notificationController.showNotification(
"Order Accepted by another driver".tr,
"Open app and go to passenger".tr,
'ding',
'',
);
await _closeOverlay();
}
} catch (e, s) {
_log(
"A critical error occurred during server update: $e\nStackTrace: $s");
if (mounted) setState(() => buttonsEnabled = true);
return;
}
}
// Your list parsing for 'customerToken' should be something like:
// customerToken: list.length > a_certain_index ? list[a_certain_index].toString() : null,
Future<void> _rejectOrder() async {
if (!buttonsEnabled || orderData == null) return;
_disableButtonsAndProcess();
_log("Order REJECTED: ${orderData!.orderId}");
box.write(BoxName.rideStatus, 'reject');
Log.print('rideStatus from overlay 303 : ${box.read(BoxName.rideStatus)}');
await _apiRefuseOrder(orderData!.orderId);
await _closeOverlay();
}
void _handleOrderTimeout() {
if (orderData == null) return;
_log("Order TIMED OUT: ${orderData!.orderId}");
_rejectOrder();
}
Future<void> _apiRefuseOrder(String orderID) async {
if (orderID == "N/A") {
_log("Cannot refuse order with N/A ID");
return;
}
try {
final driverId = box.read(BoxName.driverID)?.toString();
if (driverId == null) {
_log("Driver ID is null, cannot refuse order");
return;
}
await _crud.post(link: AppLink.addDriverOrder, payload: {
'driver_id': driverId,
'order_id': orderID,
'status': 'Refused'
});
await _crud.post(link: AppLink.updateRides, payload: {
'id': orderID,
'status': 'Refused',
'driver_id': driverId,
});
_log("Order $orderID refused successfully");
} catch (e) {
_log("Error in _apiRefuseOrder for $orderID: $e");
}
}
// === Helper Methods ===
void _disableButtonsAndProcess() {
setState(() => buttonsEnabled = false);
timer?.cancel();
_stopAudio();
}
Future<void> _closeOverlay() async {
_stopAudio();
timer?.cancel();
if (await FlutterOverlayWindow.isActive()) {
await FlutterOverlayWindow.closeOverlay();
}
}
void _log(String message) {
// A simple logger to distinguish overlay logs
print("OVERLAY_LOG: $message");
}
// === UI Build Methods ===
@override
Widget build(BuildContext context) {
// ... (Your entire UI build method remains unchanged) ...
// The UI code is excellent and doesn't need modification.
if (orderData == null) {
return const Material(
color: Colors.transparent,
child: Center(
child: CircularProgressIndicator(color: AppColors.accent)));
}
return Material(
color: Colors.black.withOpacity(0.4),
child: Center(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
padding: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: AppColors.card,
borderRadius: BorderRadius.circular(20.0),
border: Border.all(
color: AppColors.accent.withOpacity(0.3), width: 1.5),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.6),
blurRadius: 15,
spreadRadius: 2,
)
],
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildQuickHeader(),
const SizedBox(height: 12),
_buildPrimaryInfo(),
const SizedBox(height: 12),
if (canShowMap) _buildCompactMap(),
if (canShowMap) const SizedBox(height: 12),
_buildSecondaryInfo(),
const SizedBox(height: 16),
_buildEnhancedActionButtons(),
],
),
),
),
),
);
}
// All your _build... widget methods (_buildQuickHeader, _buildPrimaryInfo, etc.)
// are perfectly fine and do not need to be changed.
// ... Paste all your existing _build... methods here ...
Widget _buildQuickHeader() {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
remainingSeconds <= 3
? AppColors.urgentRed
: remainingSeconds <= 5
? AppColors.highlight
: AppColors.accent,
remainingSeconds <= 3
? AppColors.urgentRed.withOpacity(0.7)
: remainingSeconds <= 5
? AppColors.highlight.withOpacity(0.7)
: AppColors.accent.withOpacity(0.7),
],
),
borderRadius: BorderRadius.circular(15),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(Icons.drive_eta_rounded, color: AppColors.white, size: 24),
const SizedBox(width: 8),
Text(
"طلب جديد".tr,
style: const TextStyle(
color: AppColors.white,
fontSize: 18,
fontWeight: FontWeight.w600),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
decoration: BoxDecoration(
color: AppColors.white.withOpacity(0.9),
borderRadius: BorderRadius.circular(20),
),
child: Text(
"$remainingSeconds ث",
style: TextStyle(
color: remainingSeconds <= 3
? AppColors.urgentRed
: remainingSeconds <= 5
? AppColors.highlight
: AppColors.accent,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
);
}
Widget _buildPrimaryInfo() {
final order = orderData!;
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.6),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: AppColors.priceHighlight.withOpacity(0.3), width: 1),
),
child: Column(
children: [
// Price and Distance - Most Important Info
Row(
children: [
Expanded(
flex: 3,
child: _buildHighlightInfo("\$${order.price}", "السعر".tr,
Icons.monetization_on_rounded, AppColors.priceHighlight,
isLarge: true),
),
const SizedBox(width: 12),
Expanded(
flex: 2,
child: _buildHighlightInfo(
"${order.tripDistanceKm.toStringAsFixed(1)} كم",
"المسافة".tr,
Icons.straighten_rounded,
AppColors.accent,
),
),
],
),
const SizedBox(height: 12),
Divider(color: AppColors.gray.withOpacity(0.2), thickness: 1),
const SizedBox(height: 12),
// Passenger Info and ETA
Row(
children: [
Expanded(
flex: 2,
child: _buildPassengerQuickInfo(),
),
const SizedBox(width: 12),
Expanded(
child: _buildHighlightInfo(
"${order.durationToPassengerMinutes} د",
"للوصول".tr,
Icons.access_time_filled_rounded,
order.durationToPassengerMinutes <= 3
? AppColors.priceHighlight
: AppColors.gray,
),
),
],
),
],
),
);
}
Widget _buildHighlightInfo(
String value, String label, IconData icon, Color color,
{bool isLarge = false}) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: color.withOpacity(0.3), width: 1),
),
child: Column(
children: [
Icon(icon, color: color, size: isLarge ? 24 : 20),
const SizedBox(height: 4),
Text(
value,
style: TextStyle(
color: AppColors.white,
fontSize: isLarge ? 20 : 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
Text(
label,
style: TextStyle(
color: AppColors.lightGray.withOpacity(0.7),
fontSize: 11,
),
textAlign: TextAlign.center,
),
],
),
);
}
Widget _buildPassengerQuickInfo() {
final order = orderData!;
return Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
decoration: BoxDecoration(
color: AppColors.highlight.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
border:
Border.all(color: AppColors.highlight.withOpacity(0.3), width: 1),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.person_rounded, color: AppColors.highlight, size: 18),
const SizedBox(width: 4),
Expanded(
child: Text(
order.customerName,
style: const TextStyle(
color: AppColors.white,
fontSize: 14,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 2),
Text(
order.rideType,
style: TextStyle(
color: AppColors.lightGray.withOpacity(0.7),
fontSize: 11,
),
),
],
),
);
}
Widget _buildCompactMap() {
return ClipRRect(
borderRadius: BorderRadius.circular(12.0),
child: Image.network(
staticMapUrl,
height: 100, // Reduced from 110
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
height: 100,
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.3),
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child:
Icon(Icons.map_outlined, color: AppColors.gray, size: 32)),
);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return SizedBox(
height: 100,
child: Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
color: AppColors.accent,
strokeWidth: 2.0,
),
),
);
},
),
);
}
Widget _buildSecondaryInfo() {
final order = orderData!;
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColors.primary.withOpacity(0.4),
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
_buildLocationRow(
Icons.trip_origin_rounded,
"من".tr,
order.startLocationAddress,
Colors.green.shade300,
),
const SizedBox(height: 8),
_buildLocationRow(
Icons.flag_rounded,
"إلى".tr,
order.endLocationAddress,
Colors.red.shade300,
),
if (order.tripDurationMinutes > 0) ...[
const SizedBox(height: 8),
Divider(color: AppColors.gray.withOpacity(0.2), thickness: 0.5),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.timer_outlined, color: AppColors.accent, size: 16),
const SizedBox(width: 4),
Text(
"مدة الرحلة: ${order.tripDurationMinutes} دقيقة".tr,
style: const TextStyle(
color: AppColors.lightGray,
fontSize: 12,
),
),
],
),
]
],
),
);
}
Widget _buildLocationRow(
IconData icon, String label, String address, Color iconColor) {
return Row(
children: [
Icon(icon, color: iconColor, size: 16),
const SizedBox(width: 8),
Text(
"$label: ",
style: TextStyle(
color: AppColors.lightGray.withOpacity(0.8),
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
Expanded(
child: Text(
address,
style: const TextStyle(
color: AppColors.white,
fontSize: 12,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
],
);
}
Widget _buildEnhancedActionButtons() {
return Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: buttonsEnabled ? _rejectOrder : null,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.reject,
foregroundColor: AppColors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: AppColors.reject.withOpacity(0.3),
elevation: 3,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.close_rounded, size: 20),
const SizedBox(width: 6),
Text(
"رفض".tr,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.w600),
),
],
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton(
onPressed: buttonsEnabled ? _acceptOrder : null,
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.accept,
foregroundColor: AppColors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
disabledBackgroundColor: AppColors.accept.withOpacity(0.3),
elevation: 3,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.check_circle_rounded, size: 20),
const SizedBox(width: 6),
Text(
"قبول".tr,
style: const TextStyle(
fontSize: 16, fontWeight: FontWeight.w600),
),
],
),
),
),
],
);
}
}

View File

@@ -0,0 +1,431 @@
import 'dart:convert';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:math' as math;
import '../../../../constant/colors.dart';
import '../../../../constant/links.dart';
import '../../../../constant/style.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart';
import '../../../widgets/elevated_btn.dart';
class OrderRequestPage extends StatefulWidget {
const OrderRequestPage({super.key});
@override
State<OrderRequestPage> createState() => _OrderRequestPageState();
}
class _OrderRequestPageState extends State<OrderRequestPage> {
final OrderRequestController orderRequestController =
Get.put(OrderRequestController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Order Request'.tr),
centerTitle: true,
),
body: GetBuilder<OrderRequestController>(
builder: (controller) {
if (controller.myList == null) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
SizedBox(
height: Get.height * 0.3,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: CameraPosition(
target: LatLng(controller.latPassengerLocation,
controller.lngPassengerLocation),
zoom: 14.0,
),
myLocationButtonEnabled: true,
onMapCreated: controller.onMapCreated,
myLocationEnabled: true,
markers: {
Marker(
markerId: const MarkerId('startLocation'),
position: LatLng(controller.latPassengerLocation,
controller.lngPassengerLocation),
icon: controller.startIcon,
),
Marker(
markerId: const MarkerId('destinationLocation'),
position: LatLng(controller.latPassengerDestination,
controller.lngPassengerDestination),
icon: controller.endIcon,
),
},
polylines: {
Polyline(
polylineId: const PolylineId('route'),
color: AppColor.primaryColor,
width: 5,
points: controller.pointsDirection,
),
},
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
elevation: 4,
child: ListTile(
leading: Icon(
controller.myList[13].toString() == 'true'
? Icons.credit_card
: Icons.money,
color: controller.myList[13].toString() == 'true'
? AppColor.deepPurpleAccent
: AppColor.greenColor,
),
title: Text(
'Payment Method'.tr,
style: Theme.of(context).textTheme.titleMedium,
),
trailing: Text(
controller.myList[13].toString() == 'true'
? 'Visa'
: 'Cash',
style:
Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: ListTile(
leading: const Icon(Icons.account_circle,
color: AppColor.secondaryColor),
title: Text(
controller.myList[8],
style: Theme.of(context).textTheme.titleMedium,
),
subtitle: Row(
children: [
const Icon(Icons.star,
size: 16, color: Colors.amber),
Text(
controller.myList[33].toString(),
style: const TextStyle(color: Colors.amber),
),
],
),
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.location_on,
color: AppColor.greenColor),
const SizedBox(width: 8),
Expanded(
// Keep Expanded here for layout
child: Text(
controller.myList[29],
style:
Theme.of(context).textTheme.titleSmall,
maxLines: 2, // Allow up to 2 lines
overflow: TextOverflow
.ellipsis, // Handle overflow
),
),
],
),
const Divider(),
Row(
children: [
const Icon(Icons.flag,
color: AppColor.redColor),
const SizedBox(width: 8),
Expanded(
// Keep Expanded here for layout
child: Text(
controller.myList[30],
style:
Theme.of(context).textTheme.titleSmall,
maxLines: 2, // Allow up to 2 lines
overflow: TextOverflow
.ellipsis, // Handle overflow
),
),
],
),
],
),
),
),
const SizedBox(height: 10),
Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_InfoTile(
icon: Icons.timer,
label:
'${(double.parse(controller.myList[12]) / 60).toStringAsFixed(0)} ${'min'.tr}',
),
_InfoTile(
icon: Icons.directions_car,
label:
'${(double.parse(controller.myList[11]) / 1000).toStringAsFixed(1)} ${'km'.tr}',
),
_InfoTile(
icon: Icons.monetization_on,
label: '${controller.myList[2]}',
),
],
),
),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Accept Order'.tr,
onPressed: () async {
Get.put(HomeCaptainController()).changeRideId();
box.write(BoxName.statusDriverLocation, 'on');
controller.endTimer();
controller.changeApplied();
var res = await CRUD().post(
link: AppLink.updateStausFromSpeed,
payload: {
'id': (controller.myList[16]),
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': (controller.myList[16]),
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
if (res == 'failure') {
MyDialog().getDialog(
"This ride is already applied by another driver."
.tr,
'', () {
Get.back();
});
} else {
await CRUD().postFromDialogue(
link: AppLink.addDriverOrder,
payload: {
'driver_id':
(controller.myList[6].toString()),
'order_id':
(controller.myList[16].toString()),
'status': 'Apply'
});
if (AppLink.endPoint !=
AppLink.seferCairoServer) {
CRUD().postFromDialogue(
link:
'${AppLink.endPoint}/rides/driver_order/add.php',
payload: {
'driver_id':
(controller.myList[6].toString()),
'order_id':
(controller.myList[16].toString()),
'status': 'Apply'
});
}
List<String> bodyToPassenger = [
controller.myList[6].toString(),
controller.myList[8].toString(),
controller.myList[9].toString(),
];
FirebaseMessagesController()
.sendNotificationToPassengerToken(
"Accepted Ride".tr,
'your ride is Accepted'.tr,
controller.myList[9].toString(),
bodyToPassenger,
'start.wav');
Get.back();
box.write(BoxName.rideArguments, {
'passengerLocation':
controller.myList[0].toString(),
'passengerDestination':
controller.myList[1].toString(),
'Duration': controller.myList[4].toString(),
'totalCost': controller.myList[26].toString(),
'Distance': controller.myList[5].toString(),
'name': controller.myList[8].toString(),
'phone': controller.myList[10].toString(),
'email': controller.myList[28].toString(),
'WalletChecked':
controller.myList[13].toString(),
'tokenPassenger':
controller.myList[9].toString(),
'direction':
'https://www.google.com/maps/dir/${controller.myList[0]}/${controller.myList[1]}/',
'DurationToPassenger':
controller.myList[15].toString(),
'rideId': (controller.myList[16].toString()),
'passengerId':
(controller.myList[7].toString()),
'driverId': (controller.myList[18].toString()),
'durationOfRideValue':
controller.myList[19].toString(),
'paymentAmount':
controller.myList[2].toString(),
'paymentMethod':
controller.myList[13].toString() == 'true'
? 'visa'
: 'cash',
'isHaveSteps': controller.myList[20].toString(),
'step0': controller.myList[21].toString(),
'step1': controller.myList[22].toString(),
'step2': controller.myList[23].toString(),
'step3': controller.myList[24].toString(),
'step4': controller.myList[25].toString(),
'passengerWalletBurc':
controller.myList[26].toString(),
'timeOfOrder': DateTime.now().toString(),
'totalPassenger':
controller.myList[2].toString(),
'carType': controller.myList[31].toString(),
'kazan': controller.myList[32].toString(),
'startNameLocation':
controller.myList[29].toString(),
'endNameLocation':
controller.myList[30].toString(),
});
Get.to(() => PassengerLocationMapPage(),
arguments: box.read(BoxName.rideArguments));
}
},
),
GetBuilder<OrderRequestController>(
builder: (timerController) {
final isNearEnd = timerController.remainingTime <=
5; // Define a threshold for "near end"
return Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: timerController.progress,
// Set the color based on the "isNearEnd" condition
color: isNearEnd ? Colors.red : Colors.blue,
),
Text(
'${timerController.remainingTime}',
style: AppStyle.number,
),
],
);
},
),
MyElevatedButton(
title: 'Refuse Order'.tr,
onPressed: () async {
controller.endTimer();
List<String> bodyToPassenger = [
box.read(BoxName.driverID).toString(),
box.read(BoxName.nameDriver).toString(),
box.read(BoxName.tokenDriver).toString(),
];
FirebaseMessagesController()
.sendNotificationToPassengerToken(
'Order Under Review'.tr,
'${box.read(BoxName.nameDriver)} ${'is reviewing your order. They may need more information or a higher price.'.tr}',
controller.myList[9].toString(),
bodyToPassenger,
'notification.wav');
controller.refuseOrder(
EncryptionHelper.instance.encryptData(
controller.myList[16].toString()),
);
controller.addRideToNotificationDriverString(
controller.myList[16].toString(),
controller.myList[29].toString(),
controller.myList[30].toString(),
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
'${DateTime.now().hour}:${DateTime.now().minute}',
controller.myList[2].toString(),
controller.myList[7].toString(),
'wait',
controller.myList[31].toString(),
controller.myList[33].toString(),
controller.myList[2].toString(),
controller.myList[5].toString(),
controller.myList[4].toString());
},
kolor: AppColor.redColor,
),
],
),
],
),
),
],
);
},
),
);
}
}
class _InfoTile extends StatelessWidget {
final IconData icon;
final String label;
const _InfoTile({required this.icon, required this.label});
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(icon, color: AppColor.primaryColor),
const SizedBox(height: 4),
Text(
label,
style: Theme.of(context).textTheme.bodyMedium,
),
],
);
}
}

View File

@@ -0,0 +1,603 @@
import 'dart:convert'; // Though not directly used in this version's UI logic, often kept for model interactions.
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/firbase_messge.dart';
import 'package:sefer_driver/main.dart'; // For `box`
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../../../constant/colors.dart'; // Your AppColor
import '../../../../constant/links.dart'; // Your AppLink
import '../../../../constant/style.dart'; // Your AppStyle
import '../../../../controller/functions/crud.dart';
import '../../../../controller/functions/launch.dart';
import '../../../../controller/home/captin/order_request_controller.dart';
import '../../../widgets/elevated_btn.dart'; // Your MyElevatedButton
class OrderSpeedRequest extends StatelessWidget {
OrderSpeedRequest({super.key});
final OrderRequestController orderRequestController =
Get.put(OrderRequestController());
// Helper to make myList access more readable and safer
String _getData(int index, {String defaultValue = ''}) {
if (orderRequestController.myList.length > index &&
orderRequestController.myList[index] != null) {
return orderRequestController.myList[index].toString();
}
return defaultValue;
}
@override
Widget build(BuildContext context) {
// Define AppBar first to get its height for body calculations
final appBar = AppBar(
title: Text('Speed Order'.tr),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => Get.back(),
),
backgroundColor: AppColor.primaryColor, // Example color, adjust as needed
elevation: 2.0,
);
final double appBarHeight = appBar.preferredSize.height;
final MediaQueryData mediaQueryData = MediaQuery.of(context);
final double screenHeight = mediaQueryData.size.height;
final double statusBarHeight = mediaQueryData.padding.top;
final double bottomSystemPadding = mediaQueryData.padding.bottom;
// Calculate available height for the Scaffold's body content
// Subtracting status bar, app bar, and bottom system padding (like navigation bar)
final double availableBodyHeight =
screenHeight - appBarHeight - statusBarHeight - bottomSystemPadding;
// Define overall padding for the body content
const EdgeInsets bodyContentPadding =
EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0);
// Calculate the height for the main content Column, considering the body's own padding
double mainColumnHeight = availableBodyHeight - bodyContentPadding.vertical;
if (mainColumnHeight < 0) mainColumnHeight = 0;
return GetBuilder<OrderRequestController>(
builder: (controller) {
// Pre-extract data for readability and safety
final String price =
double.tryParse(_getData(2))?.toStringAsFixed(2) ?? 'N/A';
final bool isComfortTrip = _getData(31) == 'Comfort';
final String carType = _getData(31).tr;
final String pickupName = _getData(12);
final String pickupDetails = '(${_getData(11)})';
final String pickupFullAddress = _getData(29);
final String dropoffName = _getData(5);
final String dropoffDetails = '(${_getData(4)})';
final String dropoffFullAddress = _getData(30);
final String passengerName = _getData(8);
final String passengerRating = _getData(33);
final bool isVisaPayment = _getData(13) == 'true';
final bool hasSteps = _getData(20) == 'haveSteps';
final String mapUrl =
'https://www.google.com/maps/dir/${_getData(0)}/${_getData(1)}/';
final String rideId = _getData(16); // Commonly used ID
return Scaffold(
appBar: appBar,
backgroundColor: AppColor.secondaryColor ??
Colors.grey[100], // Background for the page
body: SafeArea(
// Ensures content is not obscured by system UI
child: Padding(
padding:
bodyContentPadding, // Apply overall padding to the body's content area
child: SizedBox(
// Constrain the height of the main layout Column
height: mainColumnHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// --- MAP SECTION ---
SizedBox(
height:
Get.height * 0.28, // Relative to total screen height
child: ClipRRect(
borderRadius: BorderRadius.circular(15.0),
child: GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 12,
target:
Get.find<HomeCaptainController>().myLocation),
cameraTargetBounds:
CameraTargetBounds(controller.bounds),
myLocationButtonEnabled: false,
trafficEnabled: false,
buildingsEnabled: false,
mapToolbarEnabled: false,
myLocationEnabled: true,
markers: {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: LatLng(
controller.latPassengerLocation,
controller.lngPassengerLocation),
icon: controller.startIcon),
Marker(
markerId: MarkerId('Destination'.tr),
position: LatLng(
controller.latPassengerDestination,
controller.lngPassengerDestination),
icon: controller.endIcon),
},
polylines: {
Polyline(
zIndex: 1,
consumeTapEvents: true,
geodesic: true,
endCap: Cap.buttCap,
startCap: Cap.buttCap,
visible: true,
polylineId: const PolylineId('routeOrder'),
points: controller.pointsDirection,
color: AppColor.primaryColor,
width: 3,
),
},
),
),
),
const SizedBox(height: 8),
// --- PRICE & TRIP TYPE SECTION ---
Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 12.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
price,
style: AppStyle.headTitle.copyWith(
color: AppColor.primaryColor,
fontWeight: FontWeight.bold,
fontSize: 28),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
carType,
style: AppStyle.title.copyWith(
color: AppColor.greenColor,
fontWeight: FontWeight.bold),
),
if (isComfortTrip)
Row(
children: [
const Icon(Icons.ac_unit,
color: AppColor.blueColor, size: 18),
const SizedBox(width: 4),
Text('Air condition Trip'.tr,
style: AppStyle.subtitle
.copyWith(fontSize: 13)),
],
),
],
),
],
),
),
),
const SizedBox(height: 8),
// --- EXPANDED SECTION FOR SCROLLABLE (BUT NOT USER-SCROLLABLE) CONTENT ---
Expanded(
child: SingleChildScrollView(
physics:
const NeverScrollableScrollPhysics(), // Prevents user scrolling
child: Column(
mainAxisSize: MainAxisSize
.min, // Takes minimum vertical space needed
children: [
_buildLocationCard(
icon: Icons.arrow_circle_up,
iconColor: AppColor.greenColor,
title: pickupName,
subtitle: pickupDetails,
fullAddress: pickupFullAddress,
),
const SizedBox(height: 8),
_buildLocationCard(
icon: Icons.arrow_circle_down,
iconColor: AppColor.redColor,
title: dropoffName,
subtitle: dropoffDetails,
fullAddress: dropoffFullAddress,
),
const SizedBox(height: 8),
// --- PAYMENT, STEPS & DIRECTIONS INFO ---
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
isVisaPayment
? Icons.credit_card
: Icons
.payments_outlined, // Using payments_outlined for cash
color: isVisaPayment
? AppColor.deepPurpleAccent
: AppColor.greenColor,
size: 24,
),
const SizedBox(width: 8),
Text(
isVisaPayment ? 'Visa'.tr : 'Cash'.tr,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.w600),
),
],
),
if (hasSteps)
Expanded(
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
const Icon(
Icons
.format_list_numbered_rtl_outlined,
color: AppColor.bronze,
size: 24),
const SizedBox(width: 4),
Flexible(
child: Text(
'Trip has Steps'.tr,
style: AppStyle.title.copyWith(
color: AppColor.bronze,
fontSize: 13),
overflow: TextOverflow.ellipsis,
)),
],
),
),
TextButton.icon(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
tapTargetSize:
MaterialTapTargetSize.shrinkWrap,
alignment: Alignment.centerRight,
),
onPressed: () => showInBrowser(mapUrl),
icon: const Icon(
Icons.directions_outlined,
color: AppColor.blueColor,
size: 20),
label: Text("Directions".tr,
style: AppStyle.subtitle.copyWith(
color: AppColor.blueColor,
fontSize: 13)),
),
],
),
),
),
const SizedBox(height: 8),
// --- PASSENGER INFO ---
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 10.0),
child: Row(
children: [
const Icon(Icons.person_outline,
color: AppColor.greyColor, size: 22),
const SizedBox(width: 10),
Expanded(
child: Text(
passengerName,
style: AppStyle.title,
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 10),
const Icon(Icons.star_rounded,
color: Colors.amber, size: 20),
Text(
passengerRating,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold),
),
],
),
),
),
],
),
),
),
// const SizedBox(height: 8), // Spacer before action buttons if needed
// --- ACTION BUTTONS & TIMER ---
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: MyElevatedButton(
kolor: AppColor.greenColor,
title: 'Accept Order'.tr,
onPressed: () async {
Get.put(HomeCaptainController()).changeRideId();
box.write(BoxName.statusDriverLocation, 'on');
var res = await CRUD().post(
link: AppLink.updateStausFromSpeed,
payload: {
'id': rideId,
'rideTimeStart':
DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint !=
AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': rideId,
'rideTimeStart':
DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
if (res != "failure") {
box.write(BoxName.statusDriverLocation, 'on');
controller.changeApplied();
List<String> bodyToPassenger = [
box.read(BoxName.driverID).toString(),
box.read(BoxName.nameDriver).toString(),
box.read(BoxName.tokenDriver).toString(),
rideId.toString(),
];
Get.put(FirebaseMessagesController())
.sendNotificationToDriverMAP(
'Accepted Ride',
'your ride is applied'.tr,
controller.arguments?['DriverList']
?[9]
?.toString() ??
_getData(9), // Safer access
bodyToPassenger,
'start.wav');
// Using rideId (_getData(16)) for order_id consistently
CRUD().postFromDialogue(
link: AppLink.addDriverOrder,
payload: {
'driver_id': _getData(
6), // Driver ID from the order data
'order_id': rideId,
'status': 'Apply'
});
if (AppLink.endPoint !=
AppLink.seferCairoServer) {
CRUD().post(
link:
"${AppLink.endPoint}/ride/driver_order/add.php",
payload: {
'driver_id': _getData(6),
'order_id': rideId,
'status': 'Apply'
});
}
Get.back(); // Go back from order request screen
box.write(BoxName.rideArguments, {
'passengerLocation': _getData(0),
'passengerDestination': _getData(1),
'Duration': _getData(4),
'totalCost': _getData(26),
'Distance': _getData(5),
'name': _getData(8),
'phone': _getData(10),
'email': _getData(28),
'WalletChecked': _getData(13),
'tokenPassenger': _getData(9),
'direction': mapUrl,
'DurationToPassenger': _getData(15),
'rideId': rideId,
'passengerId': _getData(7),
'driverId': box
.read(BoxName.driverID)
.toString(), // Current driver accepting
'durationOfRideValue': _getData(19),
'paymentAmount': _getData(2),
'paymentMethod': _getData(13) == 'true'
? 'visa'
: 'cash',
'isHaveSteps': _getData(20),
'step0': _getData(21),
'step1': _getData(22),
'step2': _getData(23),
'step3': _getData(24),
'step4': _getData(25),
'passengerWalletBurc': _getData(26),
'timeOfOrder': DateTime.now().toString(),
'totalPassenger': _getData(
2), // This is likely trip cost for passenger
'carType': _getData(31),
'kazan':
_getData(32), // Driver's commission/cut
'startNameLocation': _getData(29),
'endNameLocation': _getData(30),
});
Get.to(() => PassengerLocationMapPage(),
arguments:
box.read(BoxName.rideArguments));
} else {
Get.defaultDialog(
title:
"This ride is already taken by another driver."
.tr,
middleText: '',
titleStyle: AppStyle.title,
middleTextStyle: AppStyle.title,
confirm: MyElevatedButton(
title: 'Ok'.tr,
onPressed: () {
Get.back(); // Close dialog
Get.back(); // Close order request screen
}));
}
},
),
),
const SizedBox(width: 10),
// --- TIMER ---
GetBuilder<OrderRequestController>(
id: 'timerUpdate', // Ensure controller calls update(['timerUpdate']) for this
builder: (timerCtrl) {
final isNearEnd =
timerCtrl.remainingTimeSpeed <= 5;
return SizedBox(
width: 60,
height: 60,
child: Stack(
alignment: Alignment.center,
children: [
CircularProgressIndicator(
value: timerCtrl.progressSpeed,
color: isNearEnd
? Colors.redAccent
: AppColor.primaryColor,
strokeWidth: 5,
backgroundColor: Colors.grey.shade300,
),
Text('${timerCtrl.remainingTimeSpeed}',
style: AppStyle.headTitle2.copyWith(
color: isNearEnd
? Colors.redAccent
: AppColor.writeColor ??
Colors.black,
)),
],
),
);
},
),
const SizedBox(width: 10),
Expanded(
child: MyElevatedButton(
title: 'Refuse Order'.tr,
onPressed: () async {
controller.endTimer();
controller.refuseOrder(rideId);
controller.addRideToNotificationDriverString(
rideId,
_getData(29),
_getData(30),
'${DateTime.now().year}-${DateTime.now().month}-${DateTime.now().day}',
'${DateTime.now().hour}:${DateTime.now().minute}',
_getData(2),
_getData(7),
'wait',
_getData(31),
_getData(33),
_getData(2),
_getData(5),
_getData(4));
// Get.back(); // refuseOrder or endTimer should handle navigation if needed.
// If not, add Get.back(); here.
},
kolor: AppColor.redColor,
),
),
],
),
),
],
),
),
),
),
);
},
);
}
// Helper widget for location cards to reduce repetition and improve readability
Widget _buildLocationCard(
{required IconData icon,
required Color iconColor,
required String title,
required String subtitle,
required String fullAddress}) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
margin: const EdgeInsets.symmetric(
vertical: 4), // Add a little vertical margin between cards
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, color: iconColor, size: 28),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"$title $subtitle"
.trim(), // Trim to avoid extra spaces if subtitle is empty
style: AppStyle.title.copyWith(fontWeight: FontWeight.w600),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (fullAddress.isNotEmpty) ...[
const SizedBox(height: 3),
Text(
fullAddress,
style: AppStyle.subtitle
.copyWith(fontSize: 13, color: AppColor.greyColor),
maxLines: 2, // Allow up to 2 lines for address
overflow: TextOverflow.ellipsis,
),
]
],
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,230 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:math' as math;
import '../../../../constant/colors.dart';
import '../../../../controller/home/captin/home_captain_controller.dart';
class OrderRequestPageTest extends StatefulWidget {
const OrderRequestPageTest({super.key});
@override
State<OrderRequestPageTest> createState() => _OrderRequestPageTestState();
}
class _OrderRequestPageTestState extends State<OrderRequestPageTest> {
late OrderRequestController _orderRequestController;
@override
void initState() {
super.initState();
// Initialize the controller and process arguments
_initializeController();
}
void _initializeController() {
// Get the controller or create a new one if not exists
_orderRequestController = Get.put(OrderRequestController());
// Process arguments passed to the page
final arguments = Get.arguments;
final myListString = arguments['myListString'];
var myList =
arguments['DriverList'] == null || arguments['DriverList'].isEmpty
? jsonDecode(myListString)
: arguments['DriverList'];
// Parse coordinates and prepare map data
_orderRequestController.parseCoordinates(myList);
// Start timer and calculate fuel consumption
_orderRequestController.startTimer(
myList[6].toString(),
myList[16].toString(),
);
_orderRequestController.calculateConsumptionFuel();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Container(
color: const Color.fromARGB(255, 241, 238, 238),
child: ListView(
children: [
SizedBox(
height: Get.height * .33,
child: Obx(() => GoogleMap(
initialCameraPosition: CameraPosition(
zoom: 12,
target:
Get.find<HomeCaptainController>().myLocation,
),
cameraTargetBounds: CameraTargetBounds(
_orderRequestController.mapBounds.value),
myLocationButtonEnabled: true,
trafficEnabled: false,
buildingsEnabled: false,
mapToolbarEnabled: true,
myLocationEnabled: true,
markers: _orderRequestController.markers.value,
polylines: _orderRequestController.polylines.value,
onMapCreated: (GoogleMapController controller) {
_orderRequestController.mapController.value =
controller;
},
onCameraMove: (CameraPosition position) {
_orderRequestController
.updateCameraPosition(position);
},
)),
),
// Rest of your UI components
],
),
),
),
],
),
);
}
}
class OrderRequestController extends GetxController {
// Reactive variables for map-related data
Rx<LatLngBounds> mapBounds = Rx<LatLngBounds>(LatLngBounds(
southwest: const LatLng(0, 0), northeast: const LatLng(0, 0)));
Rx<Set<Marker>> markers = Rx<Set<Marker>>({});
Rx<Set<Polyline>> polylines = Rx<Set<Polyline>>({});
Rx<GoogleMapController?> mapController = Rx<GoogleMapController?>(null);
// Icons for start and end markers
late BitmapDescriptor startIcon;
late BitmapDescriptor endIcon;
// Coordinates for passenger location and destination
Rx<LatLng?> passengerLocation = Rx<LatLng?>(null);
Rx<LatLng?> passengerDestination = Rx<LatLng?>(null);
@override
void onInit() {
super.onInit();
// Initialize marker icons
_initializeMarkerIcons();
}
void _initializeMarkerIcons() async {
// Load custom marker icons
startIcon = await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(48, 48)),
'assets/start_marker.png');
endIcon = await BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(48, 48)), 'assets/end_marker.png');
}
void parseCoordinates(List myList) {
// Parse coordinates from the input list
var cords = myList[0].split(',');
var cordDestination = myList[1].split(',');
double latPassengerLocation = double.parse(cords[0]);
double lngPassengerLocation = double.parse(cords[1]);
double latPassengerDestination = double.parse(cordDestination[0]);
double lngPassengerDestination = double.parse(cordDestination[1]);
// Update passenger location and destination
passengerLocation.value =
LatLng(latPassengerLocation, lngPassengerLocation);
passengerDestination.value =
LatLng(latPassengerDestination, lngPassengerDestination);
// Create markers
_createMarkers();
// Create polyline
_createPolyline();
// Calculate map bounds
_calculateMapBounds();
}
void _createMarkers() {
if (passengerLocation.value == null || passengerDestination.value == null)
return;
markers.value = {
Marker(
markerId: MarkerId('MyLocation'.tr),
position: passengerLocation.value!,
draggable: true,
icon: startIcon,
),
Marker(
markerId: MarkerId('Destination'.tr),
position: passengerDestination.value!,
draggable: true,
icon: endIcon,
),
};
}
void _createPolyline() {
if (passengerLocation.value == null || passengerDestination.value == null)
return;
polylines.value = {
Polyline(
zIndex: 1,
consumeTapEvents: true,
geodesic: true,
endCap: Cap.buttCap,
startCap: Cap.buttCap,
visible: true,
polylineId: const PolylineId('routeOrder'),
points: [passengerLocation.value!, passengerDestination.value!],
color: AppColor.primaryColor,
width: 2,
),
};
}
void _calculateMapBounds() {
if (passengerLocation.value == null || passengerDestination.value == null)
return;
double minLatitude = math.min(passengerLocation.value!.latitude,
passengerDestination.value!.latitude);
double maxLatitude = math.max(passengerLocation.value!.latitude,
passengerDestination.value!.latitude);
double minLongitude = math.min(passengerLocation.value!.longitude,
passengerDestination.value!.longitude);
double maxLongitude = math.max(passengerLocation.value!.longitude,
passengerDestination.value!.longitude);
mapBounds.value = LatLngBounds(
southwest: LatLng(minLatitude, minLongitude),
northeast: LatLng(maxLatitude, maxLongitude),
);
}
void updateCameraPosition(CameraPosition position) {
// Implement any specific logic for camera position updates
}
void startTimer(String param1, String param2) {
// Implement timer start logic
}
void calculateConsumptionFuel() {
// Implement fuel consumption calculation
}
}

View File

@@ -0,0 +1,237 @@
import 'dart:convert';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/constant/links.dart';
import 'package:sefer_driver/controller/functions/crud.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../../main.dart';
import '../../../../print.dart';
class VipOrderPage extends StatelessWidget {
const VipOrderPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(VipOrderController());
return MyScafolld(
title: 'VIP Order'.tr,
body: [
GetBuilder<VipOrderController>(builder: (vipOrderController) {
if (vipOrderController.isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (vipOrderController.tripData.isEmpty) {
return Center(
child: Text('No orders available'.tr),
);
}
final order = vipOrderController.tripData[0];
Log.print('order: ${order}');
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Passenger Section
_buildSectionTitle('Passenger Information'.tr),
_buildInfoCard(
children: [
_buildDetailRow('Name'.tr,
'${order['passengerName'] ?? 'Unknown'} ${order['passengerLastName'] ?? ''}'),
_buildDetailRow(
'Phone'.tr, order['passengerPhone'] ?? 'Unknown'),
_buildDetailRow(
'Gender'.tr, order['passengergender'] ?? 'Unknown'),
_buildDetailRow('time Selected'.tr,
order['timeSelected'] ?? 'Unknown'),
_buildDetailRow(
'Ride Status'.tr, order['status'] ?? 'Unknown'),
// _buildDetailRow('Ride Status'.tr,
// vipOrderController.myList[4] ?? 'Unknown'),
IconButton(
onPressed: () {
// print(vipOrderController.myList);
},
icon: const Icon(Icons.add),
),
],
),
const SizedBox(height: 24),
// Action Buttons
_buildActionButtons(context),
],
),
),
);
})
],
isleading: true,
);
}
}
Widget _buildSectionTitle(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
);
}
Widget _buildInfoCard({required List<Widget> children}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
),
);
}
Widget _buildDetailRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
flex: 2,
child: Text(
label,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
),
Expanded(
flex: 3,
child: Text(
value,
style: const TextStyle(color: Colors.black54),
),
),
],
),
);
}
Widget _buildActionButtons(BuildContext context) {
return Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => _onReject(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
'Reject'.tr,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () => _onApply(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
'Apply'.tr,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
),
],
);
}
void _onReject(BuildContext context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Ride Rejected'),
backgroundColor: Colors.red,
),
);
}
void _onApply(BuildContext context) {
// TODO: Implement application logic
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Ride Applied'),
backgroundColor: Colors.green,
),
);
}
class VipOrderController extends GetxController {
bool isLoading = false;
List tripData = [];
fetchOrder() async {
isLoading = true; // Set loading state
update(); // Notify listeners
var res = await CRUD().get(link: AppLink.getMishwariDriver, payload: {
'driverId': box.read(BoxName.driverID).toString(),
});
if (res != 'failure') {
tripData = jsonDecode(res)['message'];
} else {
tripData = [];
}
isLoading = false; // Loading complete
update(); // Notify listeners
}
@override
void onInit() async {
fetchOrder();
super.onInit();
}
}