Fixes & Updates - 2026-06-01: Integrate Back-End v3 updates, fix call/connection issues across apps
This commit is contained in:
@@ -3,8 +3,13 @@ import 'package:get/get.dart';
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../controller/functions/crud.dart';
|
||||
import '../../controller/functions/package_info.dart';
|
||||
import '../../controller/home/map_passenger_controller.dart';
|
||||
import '../../controller/home/map/map_socket_controller.dart';
|
||||
import '../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../controller/home/map/location_search_controller.dart';
|
||||
import '../../controller/home/map/nearby_drivers_controller.dart';
|
||||
import '../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../controller/home/map/ui_interactions_controller.dart';
|
||||
import '../../controller/home/map/ride_state.dart';
|
||||
import '../../main.dart';
|
||||
import '../../views/home/map_widget.dart/ride_begin_passenger.dart';
|
||||
|
||||
@@ -17,7 +22,7 @@ import 'map_widget.dart/google_map_passenger_widget.dart';
|
||||
import 'map_widget.dart/left_main_menu_icons.dart';
|
||||
import 'map_widget.dart/main_bottom_menu_map.dart';
|
||||
import 'map_widget.dart/map_menu_widget.dart';
|
||||
import 'map_widget.dart/menu_map_page.dart';
|
||||
import '../../controller/functions/package_info.dart';
|
||||
import 'map_widget.dart/passengerRideLoctionWidget.dart';
|
||||
import 'map_widget.dart/payment_method.page.dart';
|
||||
import 'map_widget.dart/points_page_for_rider.dart';
|
||||
@@ -30,9 +35,14 @@ class MapPagePassenger extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(MapPassengerController());
|
||||
Get.put(MyMenuController());
|
||||
Get.put(CRUD());
|
||||
Get.find<MapSocketController>();
|
||||
Get.find<MapEngineController>();
|
||||
Get.find<LocationSearchController>();
|
||||
Get.find<NearbyDriversController>();
|
||||
Get.find<RideLifecycleController>();
|
||||
Get.find<UiInteractionsController>();
|
||||
Get.find<MyMenuController>();
|
||||
Get.find<CRUD>();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
checkForUpdate(context);
|
||||
});
|
||||
@@ -118,7 +128,7 @@ class CancelRidePageShow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) {
|
||||
// نستخدم RideState Enum لأنه أدق، أو نصلح المنطق النصي
|
||||
// الشرط:
|
||||
@@ -175,7 +185,7 @@ class PickerIconOnMap extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) => controller.isPickerShown
|
||||
? Positioned(
|
||||
bottom: Get.height * .2,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:Intaleq/constant/colors.dart';
|
||||
import 'package:Intaleq/constant/links.dart';
|
||||
import 'package:Intaleq/constant/style.dart';
|
||||
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
|
||||
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:Intaleq/controller/home/map/ride_state.dart';
|
||||
import 'package:Intaleq/controller/voice_call_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -10,6 +12,7 @@ import 'package:intl/intl.dart';
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../controller/firebase/notification_service.dart';
|
||||
import '../../../controller/functions/launch.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
class ApplyOrderWidget extends StatelessWidget {
|
||||
@@ -29,7 +32,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
return Obx(() {
|
||||
final controller = Get.find<MapPassengerController>();
|
||||
final controller = Get.find<RideLifecycleController>();
|
||||
|
||||
final bool isVisible =
|
||||
controller.currentRideState.value == RideState.driverApplied ||
|
||||
@@ -57,7 +60,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
),
|
||||
// تغيير: تقليل الحواف الخارجية بشكل كبير
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
||||
child: GetBuilder<MapPassengerController>(
|
||||
child: GetBuilder<RideLifecycleController>(
|
||||
builder: (c) {
|
||||
return Column(
|
||||
mainAxisSize:
|
||||
@@ -106,15 +109,18 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
// [NEW] 1. صف الرأس المضغوط (يحتوي الحالة + الإحصائيات + السعر)
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildCompactHeaderRow(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
BuildContext context, RideLifecycleController controller) {
|
||||
// تنسيق السعر
|
||||
final formatter = NumberFormat("#,###");
|
||||
String formattedPrice = formatter.format(controller.totalPassenger);
|
||||
|
||||
// حساب الدقائق
|
||||
int minutes =
|
||||
(controller.timeToPassengerFromDriverAfterApplied / 60).ceil();
|
||||
if (minutes < 1) minutes = 1;
|
||||
// حساب الدقائق من الوقت المتبقي الحي، وليس ETA الأصلي فقط.
|
||||
final int secondsToPassenger =
|
||||
controller.remainingTimeToPassengerFromDriverAfterApplied > 0
|
||||
? controller.remainingTimeToPassengerFromDriverAfterApplied
|
||||
: controller.timeToPassengerFromDriverAfterApplied;
|
||||
final int minutes =
|
||||
secondsToPassenger <= 0 ? 0 : (secondsToPassenger / 60).ceil();
|
||||
|
||||
// تنسيق المسافة
|
||||
String distanceDisplay = "";
|
||||
@@ -151,7 +157,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
children: [
|
||||
_buildMiniStatChip(
|
||||
icon: Icons.access_time_filled_rounded,
|
||||
text: "$minutes ${'min'.tr}",
|
||||
text: minutes > 0 ? "$minutes ${'min'.tr}" : "--",
|
||||
color: AppColor.primaryColor,
|
||||
bgColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
),
|
||||
@@ -229,7 +235,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
// [MODIFIED] 2. كرت المعلومات المضغوط جداً
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildCompactInfoCard(BuildContext context,
|
||||
MapPassengerController controller, Color Function(String) parseColor) {
|
||||
RideLifecycleController controller, Color Function(String) parseColor) {
|
||||
return Container(
|
||||
// تقليل الحواف الداخلية للكرت
|
||||
padding: const EdgeInsets.all(10),
|
||||
@@ -312,7 +318,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildMicroCarIcon(
|
||||
MapPassengerController controller, Color Function(String) parseColor) {
|
||||
RideLifecycleController controller, Color Function(String) parseColor) {
|
||||
Color carColor = parseColor(controller.colorHex);
|
||||
return Container(
|
||||
height: 40, // تصغير من 50
|
||||
@@ -343,9 +349,8 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
color: Get.isDarkMode ? Colors.grey[850] : const Color(0xFFF5F5F5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(
|
||||
color: Get.isDarkMode
|
||||
? Colors.white10
|
||||
: Colors.grey.withOpacity(0.3)),
|
||||
color:
|
||||
Get.isDarkMode ? Colors.white10 : Colors.grey.withOpacity(0.3)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -374,7 +379,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
// [MODIFIED] 3. أزرار الاتصال (Slim Buttons)
|
||||
// ---------------------------------------------------------------------------
|
||||
Widget _buildCompactButtonsRow(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
BuildContext context, RideLifecycleController controller) {
|
||||
return SizedBox(
|
||||
height: 40, // تحديد ارتفاع ثابت وصغير للأزرار
|
||||
child: Row(
|
||||
@@ -397,7 +402,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
bgColor: AppColor.greenColor,
|
||||
onTap: () {
|
||||
HapticFeedback.heavyImpact();
|
||||
makePhoneCall(controller.driverPhone);
|
||||
_showCallSelectionDialog(context, controller);
|
||||
},
|
||||
isPrimary: true,
|
||||
),
|
||||
@@ -407,6 +412,73 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _showCallSelectionDialog(
|
||||
BuildContext context, RideLifecycleController controller) {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Call Options'.tr,
|
||||
style: AppStyle.title
|
||||
.copyWith(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'Choose how you want to call the driver'.tr,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 14),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: AppColor.greenColor.withOpacity(0.1),
|
||||
child: Icon(Icons.phone_android_rounded,
|
||||
color: AppColor.greenColor),
|
||||
),
|
||||
title: Text('Standard Call'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text('Uses cellular network'.tr,
|
||||
style: const TextStyle(fontSize: 12)),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
makePhoneCall(controller.driverPhone);
|
||||
},
|
||||
),
|
||||
const Divider(),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
child: Icon(Icons.wifi_calling_3_rounded,
|
||||
color: AppColor.primaryColor),
|
||||
),
|
||||
title: Text('Free Call'.tr,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text('Voice call over internet'.tr,
|
||||
style: const TextStyle(fontSize: 12)),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
final voiceCtrl = Get.find<VoiceCallController>();
|
||||
final passengerId = box.read(BoxName.passengerID).toString();
|
||||
voiceCtrl.startCall(
|
||||
rideIdVal: controller.rideId,
|
||||
driverId: controller.driverId,
|
||||
passengerId: passengerId,
|
||||
remoteNameVal: controller.driverName,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSlimButton({
|
||||
required String label,
|
||||
required IconData icon,
|
||||
@@ -444,7 +516,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
|
||||
// --- النوافذ المنبثقة للرسائل (نفس الكود السابق مع تحسين بسيط) ---
|
||||
void _showContactOptionsDialog(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
BuildContext context, RideLifecycleController controller) {
|
||||
Get.bottomSheet(
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -470,7 +542,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildPredefinedMessages(MapPassengerController controller) {
|
||||
List<Widget> _buildPredefinedMessages(RideLifecycleController controller) {
|
||||
const messages = [
|
||||
'Hello, I\'m at the agreed-upon location',
|
||||
'I\'m waiting for you',
|
||||
@@ -510,7 +582,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildCustomMessageInput(
|
||||
MapPassengerController controller, BuildContext context) {
|
||||
RideLifecycleController controller, BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@@ -555,7 +627,22 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
void _sendMessage(MapPassengerController controller, String text) {
|
||||
void _sendMessage(RideLifecycleController controller, String text) async {
|
||||
try {
|
||||
await CRUD().post(
|
||||
link: AppLink.sendChatMessage,
|
||||
payload: {
|
||||
'ride_id': controller.rideId.toString(),
|
||||
'sender_id': box.read(BoxName.passengerID).toString(),
|
||||
'receiver_id': controller.driverId.toString(),
|
||||
'sender_type': 'passenger',
|
||||
'message_content': text.tr,
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
// Ignore or log error
|
||||
}
|
||||
|
||||
NotificationService.sendNotification(
|
||||
category: 'MSG_FROM_PASSENGER',
|
||||
target: controller.driverToken.toString(),
|
||||
@@ -577,7 +664,7 @@ class DriverArrivePassengerAndWaitMinute extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
@@ -619,7 +706,7 @@ class TimeDriverToPassenger extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
if (controller.timeToPassengerFromDriverAfterApplied <= 0) {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ import 'package:get/get.dart';
|
||||
import 'package:Intaleq/controller/payment/payment_controller.dart';
|
||||
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
|
||||
GetBuilder<MapPassengerController> buttomSheetMapPage() {
|
||||
GetBuilder<RideLifecycleController> buttomSheetMapPage() {
|
||||
Get.put(PaymentController());
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) =>
|
||||
controller.isBottomSheetShown && controller.rideConfirm == false
|
||||
? const Positioned(
|
||||
@@ -508,7 +508,7 @@ class Details extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) => Column(
|
||||
children: [
|
||||
Row(
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:Intaleq/constant/colors.dart';
|
||||
import 'package:Intaleq/constant/style.dart';
|
||||
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
|
||||
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
|
||||
// دالة لإظهار الشيت
|
||||
@@ -21,7 +21,7 @@ class CancelRidePageWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// تأكد من وجود الكنترولر
|
||||
final controller = Get.find<MapPassengerController>();
|
||||
final controller = Get.find<RideLifecycleController>();
|
||||
|
||||
final List<String> reasons = [
|
||||
"Changed my mind".tr,
|
||||
@@ -39,7 +39,7 @@ class CancelRidePageWidget extends StatelessWidget {
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(25)),
|
||||
),
|
||||
child: GetBuilder<MapPassengerController>(
|
||||
child: GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) => Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
|
||||
@@ -12,7 +12,7 @@ import 'dart:ui';
|
||||
|
||||
import '../../../constant/info.dart';
|
||||
import '../../../controller/functions/tts.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../widgets/mydialoug.dart';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -62,7 +62,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
CarDetailsTypeToChoose({super.key});
|
||||
final textToSpeechController = Get.find<TextToSpeechController>();
|
||||
|
||||
void _prepareCarTypes(MapPassengerController controller) {
|
||||
void _prepareCarTypes(RideLifecycleController controller) {
|
||||
if (controller.distance > 23) {
|
||||
if (!carTypes.any((car) => car.carType == 'Rayeh Gai')) {
|
||||
carTypes.add(CarType(
|
||||
@@ -77,7 +77,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
_prepareCarTypes(controller);
|
||||
|
||||
if (!(controller.isBottomSheetShown) && controller.rideConfirm == false) {
|
||||
@@ -170,7 +170,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// HEADER
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
Widget _buildHeader(MapPassengerController controller) {
|
||||
Widget _buildHeader(RideLifecycleController controller) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(22, 4, 22, 8),
|
||||
child: Column(
|
||||
@@ -290,7 +290,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// CAR CARD
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
Widget _buildCarCard(BuildContext context, MapPassengerController controller,
|
||||
Widget _buildCarCard(BuildContext context, RideLifecycleController controller,
|
||||
CarType carType, bool isSelected, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
@@ -437,7 +437,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// PROMO BUTTON
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
Widget _buildPromoButton(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
BuildContext context, RideLifecycleController controller) {
|
||||
if (controller.promoTaken) return const SizedBox.shrink();
|
||||
|
||||
return Padding(
|
||||
@@ -511,7 +511,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// NEGATIVE BALANCE WARNING
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
Widget _buildNegativeBalanceWarning(MapPassengerController controller) {
|
||||
Widget _buildNegativeBalanceWarning(RideLifecycleController controller) {
|
||||
final passengerWallet =
|
||||
double.tryParse(box.read(BoxName.passengerWalletTotal) ?? '0.0') ?? 0.0;
|
||||
if (passengerWallet < 0.0) {
|
||||
@@ -556,7 +556,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// PRICING HELPERS (Unchanged logic)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
String _getPassengerPriceText(
|
||||
CarType carType, MapPassengerController mapPassengerController) {
|
||||
CarType carType, RideLifecycleController mapPassengerController) {
|
||||
double rawPrice;
|
||||
switch (carType.carType) {
|
||||
case 'Comfort':
|
||||
@@ -596,7 +596,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// DIALOGS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
void _showPromoCodeDialog(
|
||||
BuildContext context, MapPassengerController controller) {
|
||||
BuildContext context, RideLifecycleController controller) {
|
||||
Get.dialog(
|
||||
Dialog(
|
||||
shape:
|
||||
@@ -671,7 +671,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
|
||||
void _showCarDetailsDialog(
|
||||
BuildContext context,
|
||||
MapPassengerController mapPassengerController,
|
||||
RideLifecycleController mapPassengerController,
|
||||
CarType carType,
|
||||
TextToSpeechController textToSpeechController) {
|
||||
Get.dialog(
|
||||
@@ -843,7 +843,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// LOGIC HELPERS (Unchanged)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
String _getCarDescription(
|
||||
MapPassengerController mapPassengerController, CarType carType) {
|
||||
RideLifecycleController mapPassengerController, CarType carType) {
|
||||
switch (carType.carType) {
|
||||
case 'Comfort':
|
||||
return mapPassengerController.endNameAddress
|
||||
@@ -881,7 +881,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
}
|
||||
|
||||
void _handleCarSelection(BuildContext context,
|
||||
MapPassengerController mapPassengerController, CarType carType) {
|
||||
RideLifecycleController mapPassengerController, CarType carType) {
|
||||
box.write(BoxName.carType, carType.carType);
|
||||
mapPassengerController.totalPassenger =
|
||||
_getOriginalPrice(carType, mapPassengerController);
|
||||
@@ -932,7 +932,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
}
|
||||
|
||||
double _getOriginalPrice(
|
||||
CarType carType, MapPassengerController mapPassengerController) {
|
||||
CarType carType, RideLifecycleController mapPassengerController) {
|
||||
switch (carType.carType) {
|
||||
case 'Comfort':
|
||||
return mapPassengerController.totalPassengerComfort;
|
||||
@@ -953,7 +953,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
|
||||
Widget _buildRayehGaiOption(
|
||||
BuildContext context,
|
||||
MapPassengerController mapPassengerController,
|
||||
RideLifecycleController mapPassengerController,
|
||||
String carTypeName,
|
||||
double price) {
|
||||
return GestureDetector(
|
||||
@@ -983,7 +983,7 @@ class BurcMoney extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (mapPassengerController) {
|
||||
final passengerWallet =
|
||||
double.tryParse(box.read(BoxName.passengerWalletTotal) ?? '0.0') ??
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'package:Intaleq/views/home/my_wallet/passenger_wallet.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/info.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../controller/payment/payment_controller.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
@@ -17,7 +17,7 @@ class CashConfirmPageShown extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
// شرط الإظهار الرئيسي لم يتغير
|
||||
return Positioned(
|
||||
bottom: 0,
|
||||
|
||||
@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'hexegone_clipper.dart';
|
||||
|
||||
GetBuilder<MapPassengerController> hexagonClipper() {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
GetBuilder<RideLifecycleController> hexagonClipper() {
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: ((controller) => controller.rideConfirm
|
||||
? Positioned(
|
||||
top: Get.height * .1,
|
||||
|
||||
@@ -4,14 +4,14 @@ import 'package:intl/intl.dart';
|
||||
// import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
|
||||
class DriverTimeArrivePassengerPage extends StatelessWidget {
|
||||
const DriverTimeArrivePassengerPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) {
|
||||
return controller.remainingTime == 0
|
||||
? Positioned(
|
||||
|
||||
@@ -12,54 +12,51 @@ import 'package:Intaleq/views/widgets/elevated_btn.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../controller/home/map/ride_state.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
// ---------------------------------------------------
|
||||
// -- Widget for Destination Point Search (Optimized) --
|
||||
// ---------------------------------------------------
|
||||
|
||||
/// A more optimized and cleaner implementation of the destination search form.
|
||||
///
|
||||
/// Improvements:
|
||||
/// 1. **Widget Refactoring**: The UI is broken down into smaller, focused widgets
|
||||
/// (_SearchField, _QuickActions, _SearchResults) to prevent unnecessary rebuilds.
|
||||
/// 2. **State Management Scoping**: `GetBuilder` is used only on widgets that
|
||||
/// actually need to update, not the entire form.
|
||||
/// 3. **Reduced Build Logic**: Logic like reading from `box` is done once.
|
||||
/// 4. **Readability**: Code is cleaner and easier to follow.
|
||||
GetBuilder<MapPassengerController> formSearchPlacesDestenation() {
|
||||
// --- [تحسين] قراءة القيم مرة واحدة في بداية البناء ---
|
||||
// Store box values in local variables to avoid repeated calls inside the build method.
|
||||
GetBuilder<LocationSearchController> formSearchPlacesDestenation() {
|
||||
final String addWorkValue =
|
||||
box.read(BoxName.addWork)?.toString() ?? 'addWork';
|
||||
final String addHomeValue =
|
||||
box.read(BoxName.addHome)?.toString() ?? 'addHome';
|
||||
|
||||
// --- [ملاحظة] تأكد من أن القيم الأولية موجودة ---
|
||||
// This initialization can be moved to your app's startup logic or a splash screen controller.
|
||||
if (addWorkValue.isEmpty || addHomeValue.isEmpty) {
|
||||
box.write(BoxName.addWork, 'addWork');
|
||||
box.write(BoxName.addHome, 'addHome');
|
||||
}
|
||||
|
||||
return GetBuilder<MapPassengerController>(
|
||||
id: 'destination_form', // Use an ID to allow targeted updates
|
||||
return GetBuilder<LocationSearchController>(
|
||||
id: 'destination_form',
|
||||
builder: (controller) {
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
return Column(
|
||||
children: [
|
||||
// --- Widget for the search text field ---
|
||||
_SearchField(controller: controller),
|
||||
|
||||
// --- Widget for "Add Work" and "Add Home" buttons ---
|
||||
_SearchField(
|
||||
controller: controller,
|
||||
mapEngine: mapEngine,
|
||||
rideLifecycle: rideLifecycle,
|
||||
),
|
||||
_QuickActions(
|
||||
controller: controller,
|
||||
mapEngine: mapEngine,
|
||||
rideLifecycle: rideLifecycle,
|
||||
addWorkValue: addWorkValue,
|
||||
addHomeValue: addHomeValue,
|
||||
),
|
||||
|
||||
// --- Widget for displaying search results, wrapped in its own GetBuilder ---
|
||||
_SearchResults(),
|
||||
_SearchResults(
|
||||
controller: controller,
|
||||
mapEngine: mapEngine,
|
||||
rideLifecycle: rideLifecycle,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -70,11 +67,16 @@ GetBuilder<MapPassengerController> formSearchPlacesDestenation() {
|
||||
// -- Private Helper Widgets for Cleaner Code --
|
||||
// ---------------------------------------------------
|
||||
|
||||
/// A dedicated widget for the search input field.
|
||||
class _SearchField extends StatefulWidget {
|
||||
final MapPassengerController controller;
|
||||
final LocationSearchController controller;
|
||||
final MapEngineController mapEngine;
|
||||
final RideLifecycleController rideLifecycle;
|
||||
|
||||
const _SearchField({required this.controller});
|
||||
const _SearchField({
|
||||
required this.controller,
|
||||
required this.mapEngine,
|
||||
required this.rideLifecycle,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_SearchField> createState() => _SearchFieldState();
|
||||
@@ -83,7 +85,6 @@ class _SearchField extends StatefulWidget {
|
||||
class _SearchFieldState extends State<_SearchField> {
|
||||
Timer? _debounce;
|
||||
|
||||
// --- [إصلاح] Listener لتحديث الواجهة عند تغيير النص لإظهار/إخفاء زر المسح ---
|
||||
void _onTextChanged() {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
@@ -93,20 +94,18 @@ class _SearchFieldState extends State<_SearchField> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Add listener to update the suffix icon when text changes
|
||||
widget.controller.placeDestinationController.addListener(_onTextChanged);
|
||||
}
|
||||
|
||||
// --- [تحسين] إضافة Debouncer لتأخير البحث أثناء الكتابة ---
|
||||
void _onSearchChanged(String query) {
|
||||
if (_debounce?.isActive ?? false) _debounce!.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||
if (query.length > 2) {
|
||||
widget.controller.getPlaces();
|
||||
widget.controller.changeHeightPlaces();
|
||||
widget.mapEngine.changeHeightPlaces();
|
||||
} else if (query.isEmpty) {
|
||||
widget.controller.clearPlacesDestination();
|
||||
widget.controller.changeHeightPlaces();
|
||||
widget.mapEngine.changeHeightPlaces();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -114,7 +113,6 @@ class _SearchFieldState extends State<_SearchField> {
|
||||
@override
|
||||
void dispose() {
|
||||
_debounce?.cancel();
|
||||
// Remove the listener to prevent memory leaks
|
||||
widget.controller.placeDestinationController.removeListener(_onTextChanged);
|
||||
super.dispose();
|
||||
}
|
||||
@@ -133,18 +131,15 @@ class _SearchFieldState extends State<_SearchField> {
|
||||
hintText: widget.controller.hintTextDestinationPoint,
|
||||
hintStyle: AppStyle.subtitle.copyWith(color: Colors.grey[600]),
|
||||
prefixIcon: Icon(Icons.search, color: AppColor.primaryColor),
|
||||
// --- [إصلاح] تم استبدال Obx بشرط بسيط لأن `setState` يعيد بناء الواجهة الآن ---
|
||||
suffixIcon: widget
|
||||
.controller.placeDestinationController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.grey[400]),
|
||||
onPressed: () {
|
||||
widget.controller.placeDestinationController.clear();
|
||||
// The listener will automatically handle the UI update
|
||||
// And _onSearchChanged will handle clearing the results
|
||||
},
|
||||
)
|
||||
: null, // Use null instead of SizedBox for better practice
|
||||
: null,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
border: OutlineInputBorder(
|
||||
@@ -163,12 +158,12 @@ class _SearchFieldState extends State<_SearchField> {
|
||||
const SizedBox(width: 8.0),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
widget.controller.changeMainBottomMenuMap();
|
||||
widget.controller.changePickerShown();
|
||||
widget.mapEngine.changeMainBottomMenuMap();
|
||||
widget.mapEngine.changePickerShown();
|
||||
},
|
||||
icon: Icon(Icons.location_on_outlined,
|
||||
color: AppColor.accentColor, size: 30),
|
||||
tooltip: widget.controller.isAnotherOreder
|
||||
tooltip: widget.rideLifecycle.isAnotherOreder
|
||||
? 'Pick destination on map'.tr
|
||||
: 'Pick on map'.tr,
|
||||
),
|
||||
@@ -178,14 +173,17 @@ class _SearchFieldState extends State<_SearchField> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A dedicated widget for the quick action buttons (Work/Home).
|
||||
class _QuickActions extends StatelessWidget {
|
||||
final MapPassengerController controller;
|
||||
final LocationSearchController controller;
|
||||
final MapEngineController mapEngine;
|
||||
final RideLifecycleController rideLifecycle;
|
||||
final String addWorkValue;
|
||||
final String addHomeValue;
|
||||
|
||||
const _QuickActions({
|
||||
required this.controller,
|
||||
required this.mapEngine,
|
||||
required this.rideLifecycle,
|
||||
required this.addWorkValue,
|
||||
required this.addHomeValue,
|
||||
});
|
||||
@@ -203,13 +201,20 @@ class _QuickActions extends StatelessWidget {
|
||||
onTap: () {
|
||||
if (addWorkValue == 'addWork') {
|
||||
controller.workLocationFromMap = true;
|
||||
controller.changeMainBottomMenuMap();
|
||||
controller.changePickerShown();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
mapEngine.changePickerShown();
|
||||
} else {
|
||||
_handleQuickAction(controller, BoxName.addWork, 'To Work');
|
||||
_handleQuickAction(
|
||||
controller,
|
||||
mapEngine,
|
||||
rideLifecycle,
|
||||
BoxName.addWork,
|
||||
'To Work',
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () => _showChangeLocationDialog(controller, 'Work'),
|
||||
onLongPress: () =>
|
||||
_showChangeLocationDialog(controller, mapEngine, 'Work'),
|
||||
),
|
||||
_buildQuickActionButton(
|
||||
icon: Icons.home_outlined,
|
||||
@@ -217,13 +222,20 @@ class _QuickActions extends StatelessWidget {
|
||||
onTap: () {
|
||||
if (addHomeValue == 'addHome') {
|
||||
controller.homeLocationFromMap = true;
|
||||
controller.changeMainBottomMenuMap();
|
||||
controller.changePickerShown();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
mapEngine.changePickerShown();
|
||||
} else {
|
||||
_handleQuickAction(controller, BoxName.addHome, 'To Home');
|
||||
_handleQuickAction(
|
||||
controller,
|
||||
mapEngine,
|
||||
rideLifecycle,
|
||||
BoxName.addHome,
|
||||
'To Home',
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () => _showChangeLocationDialog(controller, 'Home'),
|
||||
onLongPress: () =>
|
||||
_showChangeLocationDialog(controller, mapEngine, 'Home'),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -231,17 +243,25 @@ class _QuickActions extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// A dedicated widget for the search results list.
|
||||
/// It uses its own `GetBuilder` to only rebuild when the list of places changes.
|
||||
class _SearchResults extends StatelessWidget {
|
||||
final LocationSearchController controller;
|
||||
final MapEngineController mapEngine;
|
||||
final RideLifecycleController rideLifecycle;
|
||||
|
||||
const _SearchResults({
|
||||
required this.controller,
|
||||
required this.mapEngine,
|
||||
required this.rideLifecycle,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
id: 'places_list', // Use a specific ID for targeted updates
|
||||
builder: (controller) {
|
||||
return GetBuilder<LocationSearchController>(
|
||||
id: 'places_list',
|
||||
builder: (locCtrl) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
height: controller.placesDestination.isNotEmpty ? 300 : 0,
|
||||
height: locCtrl.placesDestination.isNotEmpty ? 300 : 0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
@@ -250,11 +270,11 @@ class _SearchResults extends StatelessWidget {
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
itemCount: controller.placesDestination.length,
|
||||
itemCount: locCtrl.placesDestination.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const Divider(height: 1, color: Colors.grey),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final res = controller.placesDestination[index];
|
||||
final res = locCtrl.placesDestination[index];
|
||||
final title = res['name_ar'] ?? res['name'] ?? 'Unknown Place';
|
||||
final address = res['address'] ?? 'Details not available';
|
||||
final latitude = res['latitude'];
|
||||
@@ -277,7 +297,14 @@ class _SearchResults extends StatelessWidget {
|
||||
context, latitude, longitude, title),
|
||||
),
|
||||
onTap: () => _handlePlaceSelection(
|
||||
controller, latitude, longitude, title, index),
|
||||
controller,
|
||||
mapEngine,
|
||||
rideLifecycle,
|
||||
latitude,
|
||||
longitude,
|
||||
title,
|
||||
index,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
@@ -286,7 +313,6 @@ class _SearchResults extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// --- [تحسين] استخراج المنطق المعقد إلى دوال مساعدة ---
|
||||
Future<void> _handleAddToFavorites(BuildContext context, dynamic latitude,
|
||||
dynamic longitude, String title) async {
|
||||
if (latitude != null && longitude != null) {
|
||||
@@ -311,14 +337,19 @@ class _SearchResults extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handlePlaceSelection(MapPassengerController controller,
|
||||
dynamic latitude, dynamic longitude, String title, int index) async {
|
||||
Future<void> _handlePlaceSelection(
|
||||
LocationSearchController controller,
|
||||
MapEngineController mapEngine,
|
||||
RideLifecycleController rideLifecycle,
|
||||
dynamic latitude,
|
||||
dynamic longitude,
|
||||
String title,
|
||||
int index) async {
|
||||
if (latitude == null || longitude == null) {
|
||||
Toast.show(Get.context!, 'Invalid location data', AppColor.redColor);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save to recent locations
|
||||
await sql.insertMapLocation({
|
||||
'latitude': latitude,
|
||||
'longitude': longitude,
|
||||
@@ -330,53 +361,55 @@ class _SearchResults extends StatelessWidget {
|
||||
final destLatLng = LatLng(
|
||||
double.parse(latitude.toString()), double.parse(longitude.toString()));
|
||||
|
||||
if (controller.isAnotherOreder) {
|
||||
// **Another Order Flow**
|
||||
await _handleAnotherOrderSelection(controller, destLatLng);
|
||||
if (rideLifecycle.isAnotherOreder) {
|
||||
await _handleAnotherOrderSelection(
|
||||
controller, mapEngine, rideLifecycle, destLatLng);
|
||||
} else {
|
||||
// **Regular Order Flow**
|
||||
_handleRegularOrderSelection(controller, destLatLng, index);
|
||||
_handleRegularOrderSelection(
|
||||
controller, mapEngine, rideLifecycle, destLatLng, index);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleAnotherOrderSelection(
|
||||
MapPassengerController controller, LatLng destination) async {
|
||||
LocationSearchController controller,
|
||||
MapEngineController mapEngine,
|
||||
RideLifecycleController rideLifecycle,
|
||||
LatLng destination) async {
|
||||
controller.myDestination = destination;
|
||||
controller.clearPlacesDestination(); // Helper method in controller
|
||||
controller.clearPlacesDestination();
|
||||
|
||||
await controller.getDirectionMap(
|
||||
await rideLifecycle.getDirectionMap(
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
|
||||
'${controller.myDestination.latitude},${controller.myDestination.longitude}');
|
||||
|
||||
controller.isPickerShown = false;
|
||||
mapEngine.isPickerShown = false;
|
||||
controller.passengerStartLocationFromMap = false;
|
||||
controller.changeMainBottomMenuMap();
|
||||
controller.showBottomSheet1();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
rideLifecycle.showBottomSheet1();
|
||||
}
|
||||
|
||||
void _handleRegularOrderSelection(
|
||||
MapPassengerController controller, LatLng destination, int index) {
|
||||
LocationSearchController controller,
|
||||
MapEngineController mapEngine,
|
||||
RideLifecycleController rideLifecycle,
|
||||
LatLng destination,
|
||||
int index) {
|
||||
controller.passengerLocation = controller.newMyLocation;
|
||||
controller.myDestination = destination;
|
||||
controller.convertHintTextDestinationNewPlaces(index);
|
||||
|
||||
controller.clearPlacesDestination(); // Helper method in controller
|
||||
controller.clearPlacesDestination();
|
||||
|
||||
controller.changeMainBottomMenuMap();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
controller.passengerStartLocationFromMap = true;
|
||||
controller.isPickerShown = true;
|
||||
mapEngine.isPickerShown = true;
|
||||
|
||||
// ✅ FIX: Draw the route after setting destination (matching the "Another Order" flow)
|
||||
controller.getDirectionMap(
|
||||
rideLifecycle.getDirectionMap(
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
|
||||
'${controller.myDestination.latitude},${controller.myDestination.longitude}');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// -- Helper Functions (kept from original code) --
|
||||
// ---------------------------------------------------
|
||||
|
||||
Widget _buildQuickActionButton({
|
||||
required IconData icon,
|
||||
required String text,
|
||||
@@ -410,10 +443,12 @@ Widget _buildQuickActionButton({
|
||||
);
|
||||
}
|
||||
|
||||
void _showChangeLocationDialog(
|
||||
MapPassengerController controller, String locationType) {
|
||||
void _showChangeLocationDialog(LocationSearchController controller,
|
||||
MapEngineController mapEngine, String locationType) {
|
||||
MyDialog().getDialog(
|
||||
locationType == 'Work' ? 'Change Work location ?'.tr : 'Change Home location ?'.tr,
|
||||
locationType == 'Work'
|
||||
? 'Change Work location ?'.tr
|
||||
: 'Change Home location ?'.tr,
|
||||
'',
|
||||
() {
|
||||
if (locationType == 'Work') {
|
||||
@@ -421,15 +456,18 @@ void _showChangeLocationDialog(
|
||||
} else {
|
||||
controller.homeLocationFromMap = true;
|
||||
}
|
||||
controller.changeMainBottomMenuMap();
|
||||
controller.changePickerShown();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
mapEngine.changePickerShown();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _handleQuickAction(
|
||||
MapPassengerController controller, String boxName, String hintText) async {
|
||||
// --- [تحسين] قراءة وتحويل الإحداثيات بأمان أكبر ---
|
||||
LocationSearchController controller,
|
||||
MapEngineController mapEngine,
|
||||
RideLifecycleController rideLifecycle,
|
||||
String boxName,
|
||||
String hintText) async {
|
||||
try {
|
||||
final locationString = box.read(boxName).toString();
|
||||
final parts = locationString.split(',');
|
||||
@@ -439,20 +477,19 @@ void _handleQuickAction(
|
||||
);
|
||||
|
||||
controller.hintTextDestinationPoint = hintText;
|
||||
controller.changeMainBottomMenuMap();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
|
||||
await controller.getDirectionMap(
|
||||
await rideLifecycle.getDirectionMap(
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
|
||||
'${latLng.latitude},${latLng.longitude}',
|
||||
);
|
||||
|
||||
controller.currentLocationToFormPlaces = false;
|
||||
controller.clearPlacesDestination(); // Helper method in controller
|
||||
controller.clearPlacesDestination();
|
||||
controller.passengerStartLocationFromMap = false;
|
||||
controller.isPickerShown = false;
|
||||
controller.showBottomSheet1();
|
||||
mapEngine.isPickerShown = false;
|
||||
rideLifecycle.showBottomSheet1();
|
||||
} catch (e) {
|
||||
// Handle error if parsing fails
|
||||
Log.print("Error handling quick action: $e");
|
||||
Toast.show(Get.context!, "Failed to get location".tr, AppColor.redColor);
|
||||
}
|
||||
|
||||
@@ -4,135 +4,122 @@ import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
|
||||
// ---------------------------------------------------
|
||||
// -- Widget for Start Point Search (Updated) --
|
||||
// ---------------------------------------------------
|
||||
|
||||
GetBuilder<MapPassengerController> formSearchPlacesStart() {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
id: 'start_point_form', // إضافة معرف لتحديث هذا الجزء فقط عند الحاجة
|
||||
builder: (controller) => Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
// --- حقل البحث النصي ---
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: controller.placeStartController,
|
||||
onChanged: (value) {
|
||||
if (controller.placeStartController.text.length > 2) {
|
||||
controller.getPlacesStart();
|
||||
} else if (controller.placeStartController.text.isEmpty) {
|
||||
controller.clearPlacesStart();
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search for a starting point'.tr,
|
||||
hintStyle:
|
||||
AppStyle.subtitle.copyWith(color: Colors.grey[600]),
|
||||
prefixIcon:
|
||||
Icon(Icons.search, color: AppColor.primaryColor),
|
||||
suffixIcon: controller.placeStartController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.grey[400]),
|
||||
onPressed: () {
|
||||
controller.placeStartController.clear();
|
||||
controller.clearPlacesStart();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide.none,
|
||||
GetBuilder<LocationSearchController> formSearchPlacesStart() {
|
||||
return GetBuilder<LocationSearchController>(
|
||||
id: 'start_point_form',
|
||||
builder: (controller) {
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: controller.placeStartController,
|
||||
onChanged: (value) {
|
||||
if (controller.placeStartController.text.length > 2) {
|
||||
controller.getPlacesStart();
|
||||
} else if (controller.placeStartController.text.isEmpty) {
|
||||
controller.clearPlacesStart();
|
||||
}
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Search for a starting point'.tr,
|
||||
hintStyle:
|
||||
AppStyle.subtitle.copyWith(color: Colors.grey[600]),
|
||||
prefixIcon:
|
||||
Icon(Icons.search, color: AppColor.primaryColor),
|
||||
suffixIcon: controller.placeStartController.text.isNotEmpty
|
||||
? IconButton(
|
||||
icon: Icon(Icons.clear, color: Colors.grey[400]),
|
||||
onPressed: () {
|
||||
controller.placeStartController.clear();
|
||||
controller.clearPlacesStart();
|
||||
},
|
||||
)
|
||||
: null,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0, vertical: 10.0),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(color: AppColor.primaryColor),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey[50],
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(color: AppColor.primaryColor),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.grey[50],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 8.0),
|
||||
|
||||
// --- أيقونة اختيار الموقع من الخريطة (الجزء المضاف) ---
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
// هذا السطر مهم جداً: نخبر الكونترولر أننا نحدد نقطة البداية الآن
|
||||
controller.passengerStartLocationFromMap = true;
|
||||
|
||||
// إخفاء القائمة السفلية وفتح مؤشر الخريطة (Picker)
|
||||
controller.changeMainBottomMenuMap();
|
||||
controller.changePickerShown();
|
||||
},
|
||||
icon: Icon(Icons.location_on_outlined,
|
||||
color: AppColor.accentColor, size: 30),
|
||||
tooltip: 'Pick start point on map'.tr,
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 8.0),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
controller.passengerStartLocationFromMap = true;
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
mapEngine.changePickerShown();
|
||||
},
|
||||
icon: Icon(Icons.location_on_outlined,
|
||||
color: AppColor.accentColor, size: 30),
|
||||
tooltip: 'Pick start point on map'.tr,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
height: controller.placesStart.isNotEmpty ? 300 : 0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
itemCount: controller.placesStart.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const Divider(height: 1, color: Colors.grey),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var res = controller.placesStart[index];
|
||||
var title = res['name_ar'] ?? res['name'] ?? 'Unknown Place';
|
||||
var address = res['address'] ?? 'Details not available';
|
||||
|
||||
// --- قائمة نتائج البحث ---
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
height: controller.placesStart.isNotEmpty ? 300 : 0,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.place, size: 30, color: Colors.grey),
|
||||
title: Text(title,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(fontWeight: FontWeight.w500)),
|
||||
subtitle: Text(address,
|
||||
style: TextStyle(color: Colors.grey[600], fontSize: 12)),
|
||||
onTap: () {
|
||||
var latitude = res['latitude'];
|
||||
var longitude = res['longitude'];
|
||||
if (latitude != null && longitude != null) {
|
||||
controller.passengerLocation =
|
||||
LatLng(double.parse(latitude), double.parse(longitude));
|
||||
controller.placeStartController.text = title;
|
||||
controller.clearPlacesStart();
|
||||
mapEngine.changeMainBottomMenuMap();
|
||||
controller.update();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
itemCount: controller.placesStart.length,
|
||||
separatorBuilder: (context, index) =>
|
||||
const Divider(height: 1, color: Colors.grey),
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
var res = controller.placesStart[index];
|
||||
var title = res['name_ar'] ?? res['name'] ?? 'Unknown Place';
|
||||
var address = res['address'] ?? 'Details not available';
|
||||
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.place, size: 30, color: Colors.grey),
|
||||
title: Text(title,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(fontWeight: FontWeight.w500)),
|
||||
subtitle: Text(address,
|
||||
style: TextStyle(color: Colors.grey[600], fontSize: 12)),
|
||||
onTap: () {
|
||||
var latitude = res['latitude'];
|
||||
var longitude = res['longitude'];
|
||||
if (latitude != null && longitude != null) {
|
||||
// تحديث موقع الراكب (نقطة الانطلاق) بناءً على الاختيار
|
||||
controller.passengerLocation =
|
||||
LatLng(double.parse(latitude), double.parse(longitude));
|
||||
|
||||
// تحديث النص في الحقل
|
||||
controller.placeStartController.text = title;
|
||||
|
||||
// مسح النتائج
|
||||
controller.clearPlacesStart();
|
||||
|
||||
// إغلاق القائمة والعودة للخريطة لرؤية النتيجة (اختياري حسب منطق تطبيقك)
|
||||
controller.changeMainBottomMenuMap();
|
||||
|
||||
controller.update();
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,183 +6,181 @@ import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
GetBuilder<MapPassengerController> formSearchPlaces(int index) {
|
||||
// DbSql sql = DbSql.instance;
|
||||
return GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: AppColor.secondaryColor),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.only(),
|
||||
gapPadding: 4,
|
||||
borderSide: BorderSide(
|
||||
color: AppColor.redColor,
|
||||
width: 2,
|
||||
)),
|
||||
suffixIcon: const Icon(Icons.search),
|
||||
hintText: controller.hintTextwayPoint0.tr,
|
||||
hintStyle: AppStyle.title,
|
||||
hintMaxLines: 1,
|
||||
prefixIcon: IconButton(
|
||||
onPressed: () {
|
||||
controller.allTextEditingPlaces[index].clear();
|
||||
controller.clearPlaces(index);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.clear,
|
||||
color: Colors.red[300],
|
||||
),
|
||||
),
|
||||
),
|
||||
controller: controller.allTextEditingPlaces[index],
|
||||
onChanged: (value) {
|
||||
if (controller.allTextEditingPlaces[index].text.length >
|
||||
5) {
|
||||
controller.getPlacesListsWayPoint(index);
|
||||
controller.changeHeightPlacesAll(index);
|
||||
}
|
||||
GetBuilder<LocationSearchController> formSearchPlaces(int index) {
|
||||
return GetBuilder<LocationSearchController>(
|
||||
builder: (controller) {
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: AppColor.secondaryColor),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.only(),
|
||||
gapPadding: 4,
|
||||
borderSide: BorderSide(
|
||||
color: AppColor.redColor,
|
||||
width: 2,
|
||||
)),
|
||||
suffixIcon: const Icon(Icons.search),
|
||||
hintText: controller.hintTextwayPoint0.tr,
|
||||
hintStyle: AppStyle.title,
|
||||
hintMaxLines: 1,
|
||||
prefixIcon: IconButton(
|
||||
onPressed: () {
|
||||
controller.allTextEditingPlaces[index].clear();
|
||||
controller.clearPlaces(index);
|
||||
},
|
||||
// onEditingComplete: () => controller.changeHeight(),
|
||||
icon: Icon(
|
||||
Icons.clear,
|
||||
color: Colors.red[300],
|
||||
),
|
||||
),
|
||||
),
|
||||
controller: controller.allTextEditingPlaces[index],
|
||||
onChanged: (value) {
|
||||
if (controller.allTextEditingPlaces[index].text.length > 5) {
|
||||
controller.getPlacesListsWayPoint(index);
|
||||
mapEngine.changeHeightPlacesAll(index);
|
||||
}
|
||||
},
|
||||
),
|
||||
controller.placeListResponseAll[index].isEmpty
|
||||
? InkWell(
|
||||
onTap: () {
|
||||
controller.startLocationFromMapAll[index] = true;
|
||||
controller.wayPointIndex = index;
|
||||
Get.back();
|
||||
// controller.changeMainBottomMenuMap();
|
||||
controller.changeWayPointStopsSheet();
|
||||
controller.changePickerShown();
|
||||
},
|
||||
child: Text(
|
||||
'Choose from Map'.tr + ' $index'.tr,
|
||||
style:
|
||||
AppStyle.title.copyWith(color: AppColor.blueColor),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
Container(
|
||||
height: controller.placeListResponseAll[index].isNotEmpty
|
||||
? controller.height
|
||||
: 0,
|
||||
color: AppColor.secondaryColor,
|
||||
child: ListView.builder(
|
||||
itemCount: controller.placeListResponseAll[index].length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
var res = controller.placeListResponseAll[index][i];
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
// ── Extract selected location ──
|
||||
final double lat = res['geometry']['location']['lat'];
|
||||
final double lng = res['geometry']['location']['lng'];
|
||||
final String placeName = res['name'].toString();
|
||||
final selectedLatLng = LatLng(lat, lng);
|
||||
),
|
||||
),
|
||||
controller.placeListResponseAll[index].isEmpty
|
||||
? InkWell(
|
||||
onTap: () {
|
||||
controller.startLocationFromMapAll[index] = true;
|
||||
controller.wayPointIndex = index;
|
||||
Get.back();
|
||||
mapEngine.changeWayPointStopsSheet();
|
||||
mapEngine.changePickerShown();
|
||||
},
|
||||
child: Text(
|
||||
'Choose from Map'.tr + ' $index'.tr,
|
||||
style:
|
||||
AppStyle.title.copyWith(color: AppColor.blueColor),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
Container(
|
||||
height: controller.placeListResponseAll[index].isNotEmpty
|
||||
? mapEngine.height
|
||||
: 0,
|
||||
color: AppColor.secondaryColor,
|
||||
child: ListView.builder(
|
||||
itemCount: controller.placeListResponseAll[index].length,
|
||||
itemBuilder: (BuildContext context, int i) {
|
||||
var res = controller.placeListResponseAll[index][i];
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
final double lat = res['geometry']['location']['lat'];
|
||||
final double lng = res['geometry']['location']['lng'];
|
||||
final String placeName = res['name'].toString();
|
||||
final selectedLatLng = LatLng(lat, lng);
|
||||
|
||||
controller.changeHeightPlaces();
|
||||
mapEngine.changeHeightPlaces();
|
||||
|
||||
// ── Update start/end based on context ──
|
||||
if (controller.currentLocationToFormPlacesAll[index] ==
|
||||
true) {
|
||||
controller.newStartPointLocation =
|
||||
controller.passengerLocation;
|
||||
} else {
|
||||
controller.passengerLocation =
|
||||
controller.newStartPointLocation;
|
||||
}
|
||||
if (controller.currentLocationToFormPlacesAll[index] ==
|
||||
true) {
|
||||
controller.newStartPointLocation =
|
||||
rideLifecycle.passengerLocation;
|
||||
} else {
|
||||
rideLifecycle.passengerLocation =
|
||||
controller.newStartPointLocation;
|
||||
}
|
||||
|
||||
// ✅ FIX: Set the waypoint to the selected location
|
||||
controller.menuWaypoints[index] = selectedLatLng;
|
||||
controller.menuWaypointNames[index] = placeName;
|
||||
controller.menuWaypoints[index] = selectedLatLng;
|
||||
controller.menuWaypointNames[index] = placeName;
|
||||
|
||||
// ✅ FIX: Update hint text and coordinates
|
||||
controller.convertHintTextPlaces(index, res);
|
||||
controller.convertHintTextPlaces(index, res);
|
||||
|
||||
// ✅ FIX: Draw the route with the updated waypoint
|
||||
final String start =
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}';
|
||||
final String dest =
|
||||
'${controller.myDestination.latitude},${controller.myDestination.longitude}';
|
||||
final String start =
|
||||
'${rideLifecycle.passengerLocation.latitude},${rideLifecycle.passengerLocation.longitude}';
|
||||
final String dest =
|
||||
'${rideLifecycle.myDestination.latitude},${rideLifecycle.myDestination.longitude}';
|
||||
|
||||
await controller.getDirectionMap(start, dest);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
await rideLifecycle.getDirectionMap(start, dest);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
Column(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Image.network(
|
||||
res['icon'],
|
||||
width: 20,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await sql.insertMapLocation({
|
||||
'latitude': res['geometry']
|
||||
['location']['lat'],
|
||||
'longitude': res['geometry']
|
||||
['location']['lng'],
|
||||
'name': res['name'].toString(),
|
||||
'rate': res['rating'].toString(),
|
||||
}, TableName.placesFavorite);
|
||||
Toast.show(
|
||||
context,
|
||||
'${res['name']} ${'Saved Sucssefully'.tr}',
|
||||
AppColor.primaryColor);
|
||||
},
|
||||
icon: const Icon(Icons.favorite_border),
|
||||
),
|
||||
],
|
||||
Image.network(
|
||||
res['icon'],
|
||||
width: 20,
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
res['name'].toString(),
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Text(
|
||||
res['vicinity'].toString(),
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'rate',
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
Text(
|
||||
res['rating'].toString(),
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
],
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await sql.insertMapLocation({
|
||||
'latitude': res['geometry']
|
||||
['location']['lat'],
|
||||
'longitude': res['geometry']
|
||||
['location']['lng'],
|
||||
'name': res['name'].toString(),
|
||||
'rate': res['rating'].toString(),
|
||||
}, TableName.placesFavorite);
|
||||
Toast.show(
|
||||
context,
|
||||
'${res['name']} ${'Saved Sucssefully'.tr}',
|
||||
AppColor.primaryColor);
|
||||
},
|
||||
icon: const Icon(Icons.favorite_border),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
res['name'].toString(),
|
||||
style: AppStyle.title,
|
||||
),
|
||||
Text(
|
||||
res['vicinity'].toString(),
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'rate',
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
Text(
|
||||
res['rating'].toString(),
|
||||
style: AppStyle.subtitle,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(
|
||||
thickness: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
const Divider(
|
||||
thickness: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,19 +6,26 @@ import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
|
||||
import 'package:Intaleq/services/offline_map_service.dart';
|
||||
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/map/nearby_drivers_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../widgets/mycircular.dart';
|
||||
import '../../widgets/mydialoug.dart';
|
||||
|
||||
class GoogleMapPassengerWidget extends StatelessWidget {
|
||||
GoogleMapPassengerWidget({super.key});
|
||||
|
||||
final WayPointController wayPointController = Get.put(WayPointController());
|
||||
final WayPointController wayPointController = Get.find<WayPointController>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => controller.isLoading
|
||||
final locationSearch = Get.find<LocationSearchController>();
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
final nearbyDrivers = Get.find<NearbyDriversController>();
|
||||
|
||||
return GetBuilder<MapEngineController>(
|
||||
builder: (controller) => rideLifecycle.isLoading
|
||||
? const MyCircularProgressIndicator()
|
||||
: Positioned(
|
||||
bottom: Get.height * .2,
|
||||
@@ -32,13 +39,13 @@ class GoogleMapPassengerWidget extends StatelessWidget {
|
||||
: 'assets/style.json',
|
||||
onMapCreated: controller.onMapCreated,
|
||||
onStyleLoaded: controller.onStyleLoaded,
|
||||
onCameraMove: controller.onCameraMoveThrottled,
|
||||
onCameraMove: locationSearch.onCameraMoveThrottled,
|
||||
onCameraIdle: () {
|
||||
if (controller.mapController != null) {
|
||||
final position = controller.mapController!.cameraPosition;
|
||||
if (position != null) {
|
||||
Log.print('✅ onCameraIdle targeted: ${position.target}');
|
||||
controller
|
||||
locationSearch
|
||||
.updateCurrentLocationFromCamera(position.target);
|
||||
OfflineMapService.instance
|
||||
.downloadRegion(position.target, radiusKm: 1.0);
|
||||
@@ -54,8 +61,8 @@ class GoogleMapPassengerWidget extends StatelessWidget {
|
||||
polygons: controller.polygons,
|
||||
circles: controller.circles,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: controller.passengerLocation,
|
||||
zoom: controller.lowPerf ? 14.5 : 15,
|
||||
target: locationSearch.passengerLocation,
|
||||
zoom: nearbyDrivers.lowPerf ? 14.5 : 15,
|
||||
),
|
||||
myLocationEnabled: true,
|
||||
onTap: (latlng) => controller.hidePlaces(),
|
||||
@@ -63,11 +70,11 @@ class GoogleMapPassengerWidget extends StatelessWidget {
|
||||
MyDialog().getDialog('Are you want to go to this site'.tr, '',
|
||||
() async {
|
||||
controller.clearPolyline();
|
||||
controller.getDirectionMap(
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
|
||||
rideLifecycle.getDirectionMap(
|
||||
'${locationSearch.passengerLocation.latitude},${locationSearch.passengerLocation.longitude}',
|
||||
'${latlng.latitude},${latlng.longitude}',
|
||||
);
|
||||
controller.showBottomSheet1();
|
||||
rideLifecycle.showBottomSheet1();
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,95 +1,82 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:Intaleq/views/widgets/elevated_btn.dart';
|
||||
import 'package:Intaleq/views/widgets/error_snakbar.dart';
|
||||
import 'package:Intaleq/views/widgets/mycircular.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
|
||||
import 'dart:ui';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/functions/tts.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/vip_waitting_page.dart';
|
||||
import '../navigation/navigation_view.dart';
|
||||
|
||||
// --- الدالة الرئيسية بالتصميم الجديد ---
|
||||
GetBuilder<MapPassengerController> leftMainMenuIcons() {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
builder: (controller) => Positioned(
|
||||
// تم تعديل الموضع ليتناسب مع التصميم الجديد
|
||||
top: Get.height * .01,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0), // لإنشاء شكل الكبسولة
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(
|
||||
sigmaX: 8.0, sigmaY: 8.0), // تأثير الزجاج المصنفر
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor.withOpacity(0.4), // لون شبه شفاف
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
border: Border.all(color: AppColor.secondaryColor),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min, // ليأخذ الشريط حجم الأزرار فقط
|
||||
children: [
|
||||
// --- تم استخدام دالة مساعدة جديدة للزر ---
|
||||
_buildMapActionButton(
|
||||
icon: Icons.near_me_outlined,
|
||||
tooltip: 'Toggle Map Type',
|
||||
onPressed: () => Get.to(() => NavigationView()),
|
||||
),
|
||||
// _buildVerticalDivider(),
|
||||
// _buildMapActionButton(
|
||||
// icon: Icons.traffic_outlined,
|
||||
// tooltip: 'Toggle Traffic',
|
||||
// onPressed: () => controller.changeMapTraffic(),
|
||||
// ),
|
||||
_buildVerticalDivider(),
|
||||
_buildMapActionButton(
|
||||
icon: Icons.my_location_rounded,
|
||||
tooltip: 'Go to My Location',
|
||||
onPressed: () {
|
||||
controller.mapController?.animateCamera(
|
||||
CameraUpdate.newLatLng(
|
||||
LatLng(
|
||||
controller.passengerLocation.latitude,
|
||||
controller.passengerLocation.longitude,
|
||||
GetBuilder<MapEngineController> leftMainMenuIcons() {
|
||||
return GetBuilder<MapEngineController>(
|
||||
builder: (controller) {
|
||||
final locationSearch = Get.find<LocationSearchController>();
|
||||
return Positioned(
|
||||
top: Get.height * .01,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor.withValues(alpha: 0.4),
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
border: Border.all(color: AppColor.secondaryColor),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildMapActionButton(
|
||||
icon: Icons.near_me_outlined,
|
||||
tooltip: 'Toggle Map Type',
|
||||
onPressed: () => Get.to(() => NavigationView()),
|
||||
),
|
||||
_buildVerticalDivider(),
|
||||
_buildMapActionButton(
|
||||
icon: Icons.my_location_rounded,
|
||||
tooltip: 'Go to My Location',
|
||||
onPressed: () {
|
||||
controller.mapController?.animateCamera(
|
||||
CameraUpdate.newLatLng(
|
||||
LatLng(
|
||||
locationSearch.passengerLocation.latitude,
|
||||
locationSearch.passengerLocation.longitude,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
_buildVerticalDivider(),
|
||||
_buildMapActionButton(
|
||||
icon: Octicons.watch,
|
||||
tooltip: 'VIP Waiting Page',
|
||||
onPressed: () => Get.to(() => VipWaittingPage()),
|
||||
),
|
||||
// _buildMapActionButton(
|
||||
// icon: Octicons.ellipsis,
|
||||
// tooltip: 'test',
|
||||
// onPressed: () => Get.to(() => TestPage()),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
_buildVerticalDivider(),
|
||||
_buildMapActionButton(
|
||||
icon: Octicons.watch,
|
||||
tooltip: 'VIP Waiting Page',
|
||||
onPressed: () => Get.to(() => VipWaittingPage()),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// --- دالة مساعدة جديدة لإنشاء الأزرار بشكل أنيق ---
|
||||
Widget _buildMapActionButton({
|
||||
required IconData icon,
|
||||
required String tooltip,
|
||||
@@ -101,28 +88,23 @@ Widget _buildMapActionButton({
|
||||
tooltip: tooltip,
|
||||
splashRadius: 22,
|
||||
padding: const EdgeInsets.all(12),
|
||||
constraints: const BoxConstraints(), // لإزالة المساحات الافتراضية
|
||||
constraints: const BoxConstraints(),
|
||||
);
|
||||
}
|
||||
|
||||
// --- ويدجت للفاصل الرأسي بين الأزرار ---
|
||||
Widget _buildVerticalDivider() {
|
||||
return Container(
|
||||
height: 20,
|
||||
width: 1,
|
||||
color: AppColor.writeColor.withOpacity(0.2),
|
||||
color: AppColor.writeColor.withValues(alpha: 0.2),
|
||||
);
|
||||
}
|
||||
|
||||
// --- باقي الكود الخاص بك يبقى كما هو بدون تغيير ---
|
||||
|
||||
class TestPage extends StatelessWidget {
|
||||
const TestPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final random = Random();
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('iOS Live Activity Test'),
|
||||
@@ -137,7 +119,6 @@ class TestPage extends StatelessWidget {
|
||||
title: 'title',
|
||||
onPressed: () {},
|
||||
),
|
||||
// زر الإنهاء
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../notification/notification_page.dart';
|
||||
import '../HomePage/contact_us.dart';
|
||||
import '../HomePage/share_app_page.dart';
|
||||
@@ -28,9 +28,8 @@ Color get _kBg =>
|
||||
Get.isDarkMode ? const Color(0xFF060B18) : AppColor.secondaryColor;
|
||||
Color get _kBgSurface => Get.isDarkMode
|
||||
? const Color(0xFF0D1525)
|
||||
: AppColor.secondaryColor.withOpacity(0.9);
|
||||
: AppColor.secondaryColor.withValues(alpha: 0.9);
|
||||
const _kAmber = Color(0xFFFFB700);
|
||||
Color get _kBorder => _kCyan.withOpacity(0.15);
|
||||
Color get _kText => AppColor.writeColor;
|
||||
Color get _kTextMuted => AppColor.grayColor;
|
||||
|
||||
@@ -39,16 +38,14 @@ class MapMenuWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.lazyPut(() => MapPassengerController());
|
||||
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<MapEngineController>(
|
||||
builder: (controller) => Stack(
|
||||
children: [
|
||||
// ── تعتيم الخلفية ───────────────────────────────────────────────
|
||||
if (controller.widthMenu > 0)
|
||||
GestureDetector(
|
||||
onTap: controller.getDrawerMenu,
|
||||
child: Container(color: Colors.black.withOpacity(0.55)),
|
||||
child: Container(color: Colors.black.withValues(alpha: 0.55)),
|
||||
),
|
||||
|
||||
_buildSideMenu(controller),
|
||||
@@ -59,7 +56,7 @@ class MapMenuWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
// ── زر القائمة العائم ────────────────────────────────────────────────────
|
||||
Widget _buildMenuButton(MapPassengerController controller) {
|
||||
Widget _buildMenuButton(MapEngineController controller) {
|
||||
return Positioned(
|
||||
top: 45,
|
||||
left: 16,
|
||||
@@ -76,12 +73,12 @@ class MapMenuWidget extends StatelessWidget {
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: _kBg.withOpacity(0.88),
|
||||
color: _kBg.withValues(alpha: 0.88),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: _kCyan.withOpacity(0.25), width: 1),
|
||||
border: Border.all(color: _kCyan.withValues(alpha: 0.25), width: 1),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: _kCyan.withOpacity(0.12),
|
||||
color: _kCyan.withValues(alpha: 0.12),
|
||||
blurRadius: 16,
|
||||
),
|
||||
],
|
||||
@@ -106,7 +103,7 @@ class MapMenuWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
// ── القائمة الجانبية ─────────────────────────────────────────────────────
|
||||
Widget _buildSideMenu(MapPassengerController controller) {
|
||||
Widget _buildSideMenu(MapEngineController controller) {
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 420),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
@@ -120,13 +117,13 @@ class MapMenuWidget extends StatelessWidget {
|
||||
width: Get.width * 0.8,
|
||||
constraints: const BoxConstraints(maxWidth: 320),
|
||||
decoration: BoxDecoration(
|
||||
color: _kBg.withOpacity(0.97),
|
||||
color: _kBg.withValues(alpha: 0.97),
|
||||
border: Border(
|
||||
right: BorderSide(color: _kCyan.withOpacity(0.12), width: 1),
|
||||
right: BorderSide(color: _kCyan.withValues(alpha: 0.12), width: 1),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.5),
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
blurRadius: 32,
|
||||
),
|
||||
],
|
||||
@@ -239,7 +236,7 @@ class MapMenuWidget extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: _kBgSurface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: _kCyan.withOpacity(0.15), width: 1),
|
||||
border: Border.all(color: _kCyan.withValues(alpha: 0.15), width: 1),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
@@ -255,12 +252,12 @@ class MapMenuWidget extends StatelessWidget {
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
_kCyan.withOpacity(0.2),
|
||||
_kAmber.withOpacity(0.12),
|
||||
_kCyan.withValues(alpha: 0.2),
|
||||
_kAmber.withValues(alpha: 0.12),
|
||||
],
|
||||
),
|
||||
border:
|
||||
Border.all(color: _kCyan.withOpacity(0.35), width: 1.5),
|
||||
Border.all(color: _kCyan.withValues(alpha: 0.35), width: 1.5),
|
||||
),
|
||||
child: Icon(Icons.person_rounded, color: _kCyan, size: 28),
|
||||
),
|
||||
@@ -277,7 +274,7 @@ class MapMenuWidget extends StatelessWidget {
|
||||
border: Border.all(color: _kBg, width: 2),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: const Color(0xFF00E676).withOpacity(0.5),
|
||||
color: const Color(0xFF00E676).withValues(alpha: 0.5),
|
||||
blurRadius: 6,
|
||||
),
|
||||
],
|
||||
@@ -365,7 +362,7 @@ class MapMenuWidget extends StatelessWidget {
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Colors.transparent,
|
||||
_kCyan.withOpacity(0.15),
|
||||
_kCyan.withValues(alpha: 0.15),
|
||||
Colors.transparent,
|
||||
],
|
||||
),
|
||||
@@ -419,7 +416,7 @@ class _QuickBtn extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: _kBgSurface,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: _kCyan.withOpacity(0.12), width: 1),
|
||||
border: Border.all(color: _kCyan.withValues(alpha: 0.12), width: 1),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -463,7 +460,7 @@ class MenuListItem extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final iconColor = isDestructive
|
||||
? const Color(0xFFFF5252)
|
||||
: (color ?? _kCyan.withOpacity(0.80));
|
||||
: (color ?? _kCyan.withValues(alpha: 0.80));
|
||||
final textColor =
|
||||
isDestructive ? const Color(0xFFFF5252) : (color ?? _kText);
|
||||
|
||||
@@ -472,8 +469,8 @@ class MenuListItem extends StatelessWidget {
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
splashColor: _kCyan.withOpacity(0.07),
|
||||
highlightColor: _kCyan.withOpacity(0.04),
|
||||
splashColor: _kCyan.withValues(alpha: 0.07),
|
||||
highlightColor: _kCyan.withValues(alpha: 0.04),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: Row(
|
||||
@@ -484,8 +481,8 @@ class MenuListItem extends StatelessWidget {
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: isDestructive
|
||||
? const Color(0xFFFF5252).withOpacity(0.08)
|
||||
: _kCyan.withOpacity(0.07),
|
||||
? const Color(0xFFFF5252).withValues(alpha: 0.08)
|
||||
: _kCyan.withValues(alpha: 0.07),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Icon(icon, size: 19, color: iconColor),
|
||||
@@ -504,7 +501,7 @@ class MenuListItem extends StatelessWidget {
|
||||
),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
color: _kTextMuted.withOpacity(0.4),
|
||||
color: _kTextMuted.withValues(alpha: 0.4),
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
@@ -520,7 +517,7 @@ class _MenuGridPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final paint = Paint()
|
||||
..color = AppColor.cyanBlue.withOpacity(0.04)
|
||||
..color = AppColor.cyanBlue.withValues(alpha: 0.04)
|
||||
..strokeWidth = 0.5;
|
||||
const spacing = 36.0;
|
||||
for (double y = 0; y < size.height; y += spacing) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
class MenuIconMapPageWidget extends StatelessWidget {
|
||||
@@ -13,7 +13,7 @@ class MenuIconMapPageWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<MapEngineController>(
|
||||
builder: (controller) => Positioned(
|
||||
top: Get.height * .008,
|
||||
left: box.read(BoxName.lang) != 'ar' ? 5 : null,
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'dart:ui'; // مهم لإضافة تأثير الضبابية
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
|
||||
// --- الويدجت الرئيسية بالتصميم الجديد ---
|
||||
class PassengerRideLocationWidget extends StatefulWidget {
|
||||
@@ -43,7 +43,7 @@ class _PassengerRideLocationWidgetState
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<LocationSearchController>(builder: (controller) {
|
||||
// --- نفس شرط الإظهار الخاص بك ---
|
||||
return AnimatedPositioned(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
@@ -60,9 +60,9 @@ class _PassengerRideLocationWidgetState
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor.withOpacity(0.85),
|
||||
color: AppColor.secondaryColor.withValues(alpha: 0.85),
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
border: Border.all(color: AppColor.writeColor.withOpacity(0.2)),
|
||||
border: Border.all(color: AppColor.writeColor.withValues(alpha: 0.2)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -89,7 +89,7 @@ class _PassengerRideLocationWidgetState
|
||||
Text(
|
||||
"Move the map to adjust the pin".tr,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: AppColor.writeColor.withOpacity(0.7),
|
||||
color: AppColor.writeColor.withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,8 +8,7 @@ import 'package:Intaleq/views/widgets/elevated_btn.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/digit_obsecur_formate.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
|
||||
class PaymentMethodPage extends StatelessWidget {
|
||||
const PaymentMethodPage({
|
||||
@@ -18,7 +17,7 @@ class PaymentMethodPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
return GetBuilder<MapEngineController>(
|
||||
builder: (controller) => Positioned(
|
||||
right: 5,
|
||||
bottom: 5,
|
||||
|
||||
@@ -4,20 +4,23 @@ import 'package:Intaleq/constant/table_names.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
import 'form_search_places_destenation.dart';
|
||||
|
||||
class PickerAnimtionContainerFormPlaces extends StatelessWidget {
|
||||
PickerAnimtionContainerFormPlaces({
|
||||
super.key,
|
||||
});
|
||||
final controller = MapPassengerController();
|
||||
const PickerAnimtionContainerFormPlaces({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// DbSql sql = DbSql.instance;
|
||||
return GetBuilder<MapPassengerController>(
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
final locationSearch = Get.find<LocationSearchController>();
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
|
||||
return GetBuilder<MapEngineController>(
|
||||
builder: (controller) => Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
@@ -101,8 +104,7 @@ class PickerAnimtionContainerFormPlaces extends StatelessWidget {
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.center,
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons
|
||||
@@ -132,9 +134,9 @@ class PickerAnimtionContainerFormPlaces extends StatelessWidget {
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await controller
|
||||
await rideLifecycle
|
||||
.getDirectionMap(
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
|
||||
'${locationSearch.passengerLocation.latitude},${locationSearch.passengerLocation.longitude}',
|
||||
'${favoritePlaces[index]['latitude']},${favoritePlaces[index]['longitude']}',
|
||||
);
|
||||
controller
|
||||
@@ -143,7 +145,7 @@ class PickerAnimtionContainerFormPlaces extends StatelessWidget {
|
||||
.changeBottomSheetShown(
|
||||
forceValue:
|
||||
true);
|
||||
controller
|
||||
rideLifecycle
|
||||
.bottomSheet();
|
||||
Get.back();
|
||||
},
|
||||
@@ -189,24 +191,22 @@ class PickerAnimtionContainerFormPlaces extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
if (controller.isPickerShown &&
|
||||
controller.placesDestination.isEmpty)
|
||||
locationSearch.placesDestination.isEmpty)
|
||||
MyElevatedButton(
|
||||
title: 'Go to this Target'.tr,
|
||||
onPressed: () async {
|
||||
await controller.getDirectionMap(
|
||||
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
|
||||
'${controller.newMyLocation.latitude},${controller.newMyLocation.longitude}',
|
||||
await rideLifecycle.getDirectionMap(
|
||||
'${locationSearch.passengerLocation.latitude},${locationSearch.passengerLocation.longitude}',
|
||||
'${locationSearch.newMyLocation.latitude},${locationSearch.newMyLocation.longitude}',
|
||||
);
|
||||
controller.changePickerShown();
|
||||
controller.changeBottomSheetShown(
|
||||
forceValue: true);
|
||||
controller.bottomSheet();
|
||||
// await sql
|
||||
// .getAllData(TableName.placesFavorite)
|
||||
rideLifecycle.bottomSheet();
|
||||
},
|
||||
),
|
||||
if (controller.isPickerShown &&
|
||||
controller.placesDestination.isEmpty)
|
||||
locationSearch.placesDestination.isEmpty)
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -3,21 +3,25 @@ import 'package:get/get.dart';
|
||||
import 'package:Intaleq/constant/style.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../controller/home/points_for_rider_controller.dart';
|
||||
|
||||
class PointsPageForRider extends StatelessWidget {
|
||||
PointsPageForRider({
|
||||
super.key,
|
||||
});
|
||||
MapPassengerController mapPassengerController =
|
||||
Get.put(MapPassengerController());
|
||||
|
||||
final locationSearch = Get.find<LocationSearchController>();
|
||||
final mapEngine = Get.find<MapEngineController>();
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(WayPointController());
|
||||
Get.find<WayPointController>();
|
||||
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
return Positioned(
|
||||
bottom: 2,
|
||||
left: 2,
|
||||
@@ -34,7 +38,7 @@ class PointsPageForRider extends StatelessWidget {
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
mapPassengerController.downPoints();
|
||||
mapEngine.downPoints();
|
||||
},
|
||||
icon: const Icon(Icons.arrow_drop_down_circle_outlined),
|
||||
),
|
||||
@@ -52,7 +56,7 @@ class PointsPageForRider extends StatelessWidget {
|
||||
wayPointController.wayPoints.length > 1
|
||||
? ElevatedButton(
|
||||
onPressed: () async {
|
||||
mapPassengerController
|
||||
locationSearch
|
||||
.getMapPointsForAllMethods();
|
||||
},
|
||||
child: const Text('Get Direction'),
|
||||
@@ -74,7 +78,6 @@ class PointsPageForRider extends StatelessWidget {
|
||||
.entries
|
||||
.map((entry) {
|
||||
final index = entry.key;
|
||||
final wayPoint = entry.value;
|
||||
return Padding(
|
||||
key: ValueKey(index),
|
||||
padding: const EdgeInsets.all(1),
|
||||
@@ -98,7 +101,7 @@ class PointsPageForRider extends StatelessWidget {
|
||||
content: SizedBox(
|
||||
width: Get.width,
|
||||
height: 400,
|
||||
child: mapPassengerController
|
||||
child: locationSearch
|
||||
.placeListResponse[index]),
|
||||
);
|
||||
},
|
||||
@@ -106,13 +109,13 @@ class PointsPageForRider extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(),
|
||||
color:
|
||||
AppColor.accentColor.withOpacity(.5)),
|
||||
AppColor.accentColor.withValues(alpha: 0.5)),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(index > 0
|
||||
? mapPassengerController
|
||||
? locationSearch
|
||||
.currentLocationStringAll[index]
|
||||
.toString()
|
||||
: ''),
|
||||
@@ -239,7 +242,6 @@ class PointsPageForRider extends StatelessWidget {
|
||||
}
|
||||
|
||||
void showAddLocationDialog(BuildContext context, int index) {
|
||||
final TextEditingController locationController = TextEditingController();
|
||||
// Get.put(WayPointController());
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -298,26 +300,24 @@ class AppBarPointsPageForRider extends StatelessWidget {
|
||||
color: AppColor.primaryColor,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
maxRadius: 15,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: AppColor.secondaryColor,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
maxRadius: 15,
|
||||
child: Icon(
|
||||
Icons.person,
|
||||
color: AppColor.secondaryColor,
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
"Switch Rider".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {},
|
||||
child: Text(
|
||||
"Switch Rider".tr,
|
||||
style: AppStyle.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Icon(
|
||||
Icons.clear,
|
||||
|
||||
@@ -11,7 +11,9 @@ import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/audio_record1.dart';
|
||||
import '../../../controller/functions/launch.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../controller/home/map/ui_interactions_controller.dart';
|
||||
import '../../../controller/home/map/ride_state.dart';
|
||||
import '../../../controller/profile/profile_controller.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../../views/home/profile/complaint_page.dart';
|
||||
@@ -24,9 +26,10 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
final ProfileController profileController = Get.put(ProfileController());
|
||||
final AudioRecorderController audioController =
|
||||
Get.put(AudioRecorderController());
|
||||
final uiController = Get.find<UiInteractionsController>();
|
||||
|
||||
return Obx(() {
|
||||
final controller = Get.find<MapPassengerController>();
|
||||
final controller = Get.find<RideLifecycleController>();
|
||||
|
||||
// شرط الإظهار
|
||||
final bool isVisible =
|
||||
@@ -50,8 +53,8 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Get.isDarkMode
|
||||
? Colors.black.withOpacity(0.4)
|
||||
: Colors.black.withOpacity(0.1),
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.black.withValues(alpha: 0.1),
|
||||
blurRadius: 20,
|
||||
spreadRadius: 2,
|
||||
offset: const Offset(0, -3),
|
||||
@@ -69,7 +72,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
width: 40,
|
||||
height: 4,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.grayColor.withOpacity(0.3),
|
||||
color: AppColor.grayColor.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
@@ -85,7 +88,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 0.5,
|
||||
color: AppColor.grayColor.withOpacity(0.2)),
|
||||
color: AppColor.grayColor.withValues(alpha: 0.2)),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
@@ -104,7 +107,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
}
|
||||
|
||||
// --- الهيدر (بدون تغيير، ممتاز) ---
|
||||
Widget _buildCompactHeader(MapPassengerController controller) {
|
||||
Widget _buildCompactHeader(RideLifecycleController controller) {
|
||||
return Row(
|
||||
children: [
|
||||
// صورة السائق
|
||||
@@ -112,7 +115,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: AppColor.primaryColor.withOpacity(0.5), width: 1.5),
|
||||
color: AppColor.primaryColor.withValues(alpha: 0.5), width: 1.5),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 24,
|
||||
@@ -166,9 +169,9 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.writeColor.withOpacity(0.05),
|
||||
color: AppColor.writeColor.withValues(alpha: 0.05),
|
||||
border: Border.all(
|
||||
color: AppColor.grayColor.withOpacity(0.2)),
|
||||
color: AppColor.grayColor.withValues(alpha: 0.2)),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
@@ -190,7 +193,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryColor.withOpacity(0.08),
|
||||
color: AppColor.primaryColor.withValues(alpha: 0.08),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
@@ -216,9 +219,10 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
// --- الأزرار (بدون تغيير) ---
|
||||
Widget _buildCompactActionButtons(
|
||||
BuildContext context,
|
||||
MapPassengerController controller,
|
||||
RideLifecycleController controller,
|
||||
ProfileController profileController,
|
||||
AudioRecorderController audioController) {
|
||||
final uiController = Get.find<UiInteractionsController>();
|
||||
return SizedBox(
|
||||
height: 60,
|
||||
child: Row(
|
||||
@@ -228,7 +232,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
icon: Icons.sos_rounded,
|
||||
label: 'SOS'.tr,
|
||||
color: AppColor.redColor,
|
||||
bgColor: AppColor.redColor.withOpacity(0.1),
|
||||
bgColor: AppColor.redColor.withValues(alpha: 0.1),
|
||||
onTap: () async {
|
||||
if (box.read(BoxName.sosPhonePassenger) == null) {
|
||||
await profileController.updatField(
|
||||
@@ -244,24 +248,26 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
icon: FontAwesome.whatsapp,
|
||||
label: 'WhatsApp'.tr,
|
||||
color: const Color(0xFF25D366),
|
||||
bgColor: const Color(0xFF25D366).withOpacity(0.1),
|
||||
bgColor: const Color(0xFF25D366).withValues(alpha: 0.1),
|
||||
onTap: () async {
|
||||
if (box.read(BoxName.sosPhonePassenger) == null) {
|
||||
await profileController.updatField(
|
||||
'sosPhone', TextInputType.phone);
|
||||
final phone = box.read(BoxName.sosPhonePassenger);
|
||||
if (phone == null || phone.toString().isEmpty) {
|
||||
// لا يوجد رقم طوارئ — نعرض الديالوج لإدخاله
|
||||
await uiController.shareTripWithFamily();
|
||||
} else {
|
||||
final phone = controller.formatSyrianPhoneNumber(
|
||||
box.read(BoxName.sosPhonePassenger).toString());
|
||||
controller.sendWhatsapp(phone);
|
||||
final formattedPhone = uiController.formatSyrianPhoneNumber(
|
||||
phone.toString());
|
||||
uiController.sendWhatsapp(formattedPhone);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
_compactBtn(
|
||||
icon: Icons.share,
|
||||
label: 'Share'.tr,
|
||||
color: AppColor.primaryColor,
|
||||
bgColor: AppColor.primaryColor.withOpacity(0.1),
|
||||
onTap: () async => await controller.shareTripWithFamily(),
|
||||
bgColor: AppColor.primaryColor.withValues(alpha: 0.1),
|
||||
onTap: () async => await uiController.shareTripWithFamily(),
|
||||
),
|
||||
GetBuilder<AudioRecorderController>(
|
||||
init: audioController,
|
||||
@@ -275,15 +281,19 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
? AppColor.redColor
|
||||
: AppColor.primaryColor,
|
||||
bgColor: audioCtx.isRecording
|
||||
? AppColor.redColor.withOpacity(0.1)
|
||||
: AppColor.primaryColor.withOpacity(0.1),
|
||||
? AppColor.redColor.withValues(alpha: 0.1)
|
||||
: AppColor.primaryColor.withValues(alpha: 0.1),
|
||||
onTap: () async {
|
||||
if (!audioCtx.isRecording) {
|
||||
await audioCtx.startRecording();
|
||||
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
|
||||
await audioCtx.startRecording(rideId: controller.rideId);
|
||||
if (context.mounted) {
|
||||
Toast.show(context, 'Start Record'.tr, AppColor.greenColor);
|
||||
}
|
||||
} else {
|
||||
await audioCtx.stopRecording();
|
||||
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
|
||||
if (context.mounted) {
|
||||
Toast.show(context, 'Record saved'.tr, AppColor.greenColor);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -293,7 +303,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
icon: Icons.info_outline_rounded,
|
||||
label: 'Report'.tr,
|
||||
color: AppColor.grayColor,
|
||||
bgColor: AppColor.writeColor.withOpacity(0.1),
|
||||
bgColor: AppColor.writeColor.withValues(alpha: 0.1),
|
||||
onTap: () => Get.to(() => ComplaintPage()),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,7 +8,9 @@ import '../../../constant/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../controller/home/map/ui_interactions_controller.dart';
|
||||
import '../../../controller/home/map/ride_state.dart';
|
||||
import '../../../controller/profile/profile_controller.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
@@ -18,8 +20,10 @@ class RideFromStartApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final profileController = Get.put(ProfileController());
|
||||
final MapPassengerController controller =
|
||||
Get.find<MapPassengerController>();
|
||||
final RideLifecycleController controller =
|
||||
Get.find<RideLifecycleController>();
|
||||
final UiInteractionsController uiController =
|
||||
Get.find<UiInteractionsController>();
|
||||
|
||||
return Obx(() {
|
||||
final bool isRideActive =
|
||||
@@ -59,7 +63,7 @@ class RideFromStartApp extends StatelessWidget {
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Get.isDarkMode
|
||||
? Colors.black.withOpacity(0.4)
|
||||
? Colors.black.withValues(alpha: 0.4)
|
||||
: Colors.black12,
|
||||
blurRadius: 15.0,
|
||||
spreadRadius: 5.0,
|
||||
@@ -78,7 +82,7 @@ class RideFromStartApp extends StatelessWidget {
|
||||
height: 4,
|
||||
margin: const EdgeInsets.only(bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.grayColor.withOpacity(0.3),
|
||||
color: AppColor.grayColor.withValues(alpha: 0.3),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
@@ -134,7 +138,7 @@ class RideFromStartApp extends StatelessWidget {
|
||||
Container(
|
||||
width: 1,
|
||||
height: 12,
|
||||
color: AppColor.grayColor.withOpacity(0.3)),
|
||||
color: AppColor.grayColor.withValues(alpha: 0.3)),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
"$carType - $carModel",
|
||||
@@ -160,7 +164,7 @@ class RideFromStartApp extends StatelessWidget {
|
||||
const EdgeInsets.symmetric(vertical: 12, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.grayColor
|
||||
.withOpacity(0.1), // خلفية رمادية خفيفة جداً
|
||||
.withValues(alpha: 0.1), // خلفية رمادية خفيفة جداً
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Row(
|
||||
@@ -188,7 +192,7 @@ class RideFromStartApp extends StatelessWidget {
|
||||
flex: 2,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => _checkAndCall(
|
||||
controller.sendWhatsapp, profileController),
|
||||
uiController.sendWhatsapp, profileController),
|
||||
icon:
|
||||
const Icon(FontAwesome.whatsapp, color: Colors.white),
|
||||
label: Text("Share Trip".tr,
|
||||
@@ -252,9 +256,9 @@ class RideFromStartApp extends StatelessWidget {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
color: color.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: color.withOpacity(0.5)),
|
||||
border: Border.all(color: color.withValues(alpha: 0.5)),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
@@ -297,7 +301,7 @@ class RideFromStartApp extends StatelessWidget {
|
||||
return Container(
|
||||
height: 30,
|
||||
width: 1,
|
||||
color: AppColor.grayColor.withOpacity(0.2),
|
||||
color: AppColor.grayColor.withValues(alpha: 0.2),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:Intaleq/constant/colors.dart';
|
||||
import 'package:Intaleq/constant/style.dart';
|
||||
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
|
||||
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:Intaleq/controller/home/map/ride_state.dart';
|
||||
|
||||
// --- الويدجت الرئيسية بالتصميم الجديد ---
|
||||
class SearchingCaptainWindow extends StatefulWidget {
|
||||
@@ -36,7 +37,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
// [تعديل 1] نستخدم Obx للاستماع إلى التغييرات في حالة الرحلة
|
||||
return Obx(() {
|
||||
// ابحث عن الكنترولر مرة واحدة
|
||||
final controller = Get.find<MapPassengerController>();
|
||||
final controller = Get.find<RideLifecycleController>();
|
||||
|
||||
// [تعديل 2] شرط الإظهار يعتمد الآن على حالة الرحلة مباشرة
|
||||
final bool isVisible =
|
||||
@@ -58,7 +59,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2),
|
||||
color: Colors.black.withValues(alpha: 0.2),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, -5),
|
||||
),
|
||||
@@ -83,7 +84,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColor.writeColor,
|
||||
side:
|
||||
BorderSide(color: AppColor.writeColor.withOpacity(0.3)),
|
||||
BorderSide(color: AppColor.writeColor.withValues(alpha: 0.3)),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
@@ -99,7 +100,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
}
|
||||
|
||||
// --- ويدجت بناء أنيميشن الرادار ---
|
||||
Widget _buildRadarAnimation(MapPassengerController controller) {
|
||||
Widget _buildRadarAnimation(RideLifecycleController controller) {
|
||||
return SizedBox(
|
||||
height: 180, // ارتفاع ثابت لمنطقة الأنيميشن
|
||||
child: Stack(
|
||||
@@ -125,7 +126,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: AppColor.primaryColor.withOpacity(0.7),
|
||||
color: AppColor.primaryColor.withValues(alpha: 0.7),
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -147,7 +148,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
Text(
|
||||
'Searching for the nearest captain...'.tr,
|
||||
style: AppStyle.subtitle
|
||||
.copyWith(color: AppColor.writeColor.withOpacity(0.7)),
|
||||
.copyWith(color: AppColor.writeColor.withValues(alpha: 0.7)),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -166,12 +167,12 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
CircularProgressIndicator(
|
||||
strokeWidth: 3,
|
||||
color: AppColor.primaryColor,
|
||||
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
|
||||
backgroundColor: AppColor.primaryColor.withValues(alpha: 0.2),
|
||||
),
|
||||
Center(
|
||||
child: Icon(
|
||||
Icons.search,
|
||||
color: AppColor.writeColor.withOpacity(0.8),
|
||||
color: AppColor.writeColor.withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:Intaleq/constant/colors.dart';
|
||||
import 'package:Intaleq/constant/style.dart';
|
||||
import 'package:Intaleq/controller/home/map_passenger_controller.dart';
|
||||
import 'package:Intaleq/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:Intaleq/views/widgets/elevated_btn.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -10,8 +10,10 @@ import '../../../constant/links.dart';
|
||||
import '../../../print.dart';
|
||||
|
||||
class CupertinoDriverListWidget extends StatelessWidget {
|
||||
MapPassengerController mapPassengerController =
|
||||
Get.put(MapPassengerController());
|
||||
CupertinoDriverListWidget({super.key});
|
||||
|
||||
final RideLifecycleController mapPassengerController =
|
||||
Get.find<RideLifecycleController>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CupertinoPageScaffold(
|
||||
@@ -158,7 +160,7 @@ class CupertinoDriverListWidget extends StatelessWidget {
|
||||
),
|
||||
onTap: () {
|
||||
Log.print(' driver["id"]: ${driver['driver_id']}');
|
||||
Get.find<MapPassengerController>().driverIdVip =
|
||||
Get.find<RideLifecycleController>().driverIdVip =
|
||||
driver['driver_id'];
|
||||
|
||||
// Handle driver selection
|
||||
@@ -266,8 +268,6 @@ class CupertinoDriverListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
void showDateTimePickerDialog(Map<String, dynamic> driver) {
|
||||
DateTime selectedDateTime = DateTime.now();
|
||||
|
||||
Get.defaultDialog(
|
||||
barrierDismissible: false,
|
||||
title: "Select date and time of trip".tr,
|
||||
@@ -302,7 +302,9 @@ class CupertinoDriverListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
class DateTimePickerWidget extends StatelessWidget {
|
||||
final MapPassengerController controller = Get.put(MapPassengerController());
|
||||
DateTimePickerWidget({super.key});
|
||||
|
||||
final RideLifecycleController controller = Get.find<RideLifecycleController>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -2,10 +2,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
|
||||
GetBuilder<MapPassengerController> timerForCancelTripFromPassenger() {
|
||||
return GetBuilder<MapPassengerController>(
|
||||
GetBuilder<RideLifecycleController> timerForCancelTripFromPassenger() {
|
||||
return GetBuilder<RideLifecycleController>(
|
||||
builder: (controller) {
|
||||
final isNearEnd =
|
||||
controller.remainingTime <= 5; // Define a threshold for "near end"
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:Intaleq/views/widgets/elevated_btn.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'ride_begin_passenger.dart';
|
||||
|
||||
class TimerToPassengerFromDriver extends StatelessWidget {
|
||||
@@ -14,7 +14,7 @@ class TimerToPassengerFromDriver extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
if (controller.remainingTime == 0 &&
|
||||
(controller.isDriverInPassengerWay == true ||
|
||||
controller.timeToPassengerFromDriverAfterApplied > 0)) {
|
||||
|
||||
@@ -12,7 +12,8 @@ import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/audio_record1.dart';
|
||||
import '../../../controller/functions/launch.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
import '../../../controller/home/map_passenger_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../controller/home/map/ui_interactions_controller.dart';
|
||||
|
||||
class VipRideBeginPassenger extends StatelessWidget {
|
||||
const VipRideBeginPassenger({
|
||||
@@ -24,8 +25,8 @@ class VipRideBeginPassenger extends StatelessWidget {
|
||||
ProfileController profileController = Get.put(ProfileController());
|
||||
AudioRecorderController audioController =
|
||||
Get.put(AudioRecorderController());
|
||||
// Get.put(MapPassengerController());
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
final uiController = Get.find<UiInteractionsController>();
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
if (controller.statusRideVip == 'Begin' ||
|
||||
!controller.statusRideFromStart) {
|
||||
return Positioned(
|
||||
@@ -148,9 +149,11 @@ class VipRideBeginPassenger extends StatelessWidget {
|
||||
child: audioController.isRecording == false
|
||||
? IconButton(
|
||||
onPressed: () async {
|
||||
await audioController.startRecording();
|
||||
Toast.show(context, 'Start Record'.tr,
|
||||
AppColor.greenColor);
|
||||
await audioController.startRecording(rideId: controller.rideId);
|
||||
if (context.mounted) {
|
||||
Toast.show(context, 'Start Record'.tr,
|
||||
AppColor.greenColor);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.play_circle_fill_outlined,
|
||||
@@ -162,8 +165,10 @@ class VipRideBeginPassenger extends StatelessWidget {
|
||||
: IconButton(
|
||||
onPressed: () async {
|
||||
await audioController.stopRecording();
|
||||
Toast.show(context, 'Record saved'.tr,
|
||||
AppColor.greenColor);
|
||||
if (context.mounted) {
|
||||
Toast.show(context, 'Record saved'.tr,
|
||||
AppColor.greenColor);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.stop_circle,
|
||||
@@ -215,15 +220,11 @@ class VipRideBeginPassenger extends StatelessWidget {
|
||||
profileController.prfoileData['sosPhone']);
|
||||
}
|
||||
} else {
|
||||
String phoneNumber = box
|
||||
.read(BoxName.sosPhonePassenger)
|
||||
.toString();
|
||||
// phoneNumber = phoneNumber.replaceAll('0', '');
|
||||
var phone = box.read(BoxName.countryCode) ==
|
||||
'Egypt'
|
||||
? '+2${box.read(BoxName.sosPhonePassenger)}'
|
||||
: '+962${box.read(BoxName.sosPhonePassenger)}';
|
||||
controller.sendWhatsapp(phone);
|
||||
uiController.sendWhatsapp(phone);
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
@@ -237,7 +238,7 @@ class VipRideBeginPassenger extends StatelessWidget {
|
||||
width: Get.width * .15,
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
await controller.shareTripWithFamily();
|
||||
await uiController.shareTripWithFamily();
|
||||
},
|
||||
icon: const Icon(
|
||||
AntDesign.Safety,
|
||||
@@ -283,13 +284,12 @@ class VipRideBeginPassenger extends StatelessWidget {
|
||||
}
|
||||
|
||||
class StreamCounter extends StatelessWidget {
|
||||
const StreamCounter({Key? key}) : super(key: key);
|
||||
const StreamCounter({super.key});
|
||||
|
||||
@override
|
||||
// Build the UI based on the timer value
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(MapPassengerController());
|
||||
return GetBuilder<MapPassengerController>(builder: (controller) {
|
||||
return GetBuilder<RideLifecycleController>(builder: (controller) {
|
||||
return StreamBuilder<int>(
|
||||
initialData: 0,
|
||||
stream: controller.timerController.stream,
|
||||
|
||||
@@ -268,9 +268,6 @@ class NavigationController extends GetxController
|
||||
},
|
||||
];
|
||||
|
||||
static final String _routeApiBaseUrl =
|
||||
"${AppLink.routesOsm}/route/v1/driving";
|
||||
|
||||
IconData get currentManeuverIcon {
|
||||
switch (currentManeuverModifier) {
|
||||
case 4: // Arrive
|
||||
@@ -378,7 +375,6 @@ class NavigationController extends GetxController
|
||||
void onMapCreated(IntaleqMapController controller) async {
|
||||
Log.print("DEBUG: NavigationController.onMapCreated called");
|
||||
mapController = controller;
|
||||
await onStyleLoaded();
|
||||
}
|
||||
|
||||
Future<void> onStyleLoaded() async {
|
||||
@@ -577,7 +573,7 @@ class NavigationController extends GetxController
|
||||
}
|
||||
|
||||
void _checkOffRoute(LatLng pos) {
|
||||
if (_autoRecalcInProgress || isLoading) return;
|
||||
if (!isNavigating || _autoRecalcInProgress || isLoading) return;
|
||||
if (_fullRouteCoordinates.isEmpty) return;
|
||||
|
||||
const int searchWindow = 80;
|
||||
@@ -604,7 +600,6 @@ class NavigationController extends GetxController
|
||||
if (elapsed >= _offRouteTriggerSeconds) {
|
||||
_offRouteStartTime = null;
|
||||
_autoRecalcInProgress = true;
|
||||
// Smart reroute: check if we have alternative routes available
|
||||
_smartRecalculateRoute(pos);
|
||||
}
|
||||
}
|
||||
@@ -613,40 +608,11 @@ class NavigationController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
/// الحل الذكي: إذا كان هناك مسارات بديلة متاحة، اختر الأقرب.
|
||||
/// وإلا فاطلب مسار جديد من الموقع الحالي إلى الوجهة.
|
||||
/// Recalculate immediately from the latest GPS point to the destination.
|
||||
Future<void> _smartRecalculateRoute(LatLng currentPos) async {
|
||||
try {
|
||||
// Check if we have alternative routes
|
||||
if (routes.isNotEmpty && selectedRouteIndex < routes.length - 1) {
|
||||
// Try using the next alternative route
|
||||
final nextIndex = selectedRouteIndex + 1;
|
||||
final nextRoute = routes[nextIndex];
|
||||
|
||||
// Calculate distance from current position to this alternative route's start
|
||||
double minDist = double.infinity;
|
||||
for (var coord in nextRoute.coordinates) {
|
||||
final d = Geolocator.distanceBetween(
|
||||
currentPos.latitude,
|
||||
currentPos.longitude,
|
||||
coord.latitude,
|
||||
coord.longitude,
|
||||
);
|
||||
if (d < minDist) minDist = d;
|
||||
}
|
||||
|
||||
// If this alternative is reasonable, switch to it
|
||||
if (minDist < 100) {
|
||||
selectRoute(nextIndex);
|
||||
Log.print("DEBUG: Switched to alternative route due to deviation");
|
||||
_autoRecalcInProgress = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No good alternative, recalculate from current position to destination
|
||||
if (_finalDestination != null) {
|
||||
await recalculateRoute();
|
||||
await recalculateRoute(origin: currentPos, keepNavigationActive: true);
|
||||
}
|
||||
_autoRecalcInProgress = false;
|
||||
} catch (e) {
|
||||
@@ -906,7 +872,8 @@ class NavigationController extends GetxController
|
||||
return const LatLng(31.7225, 35.9933); // Queen Alia Airport (JO)
|
||||
}
|
||||
|
||||
Future<void> getRoute(LatLng origin, LatLng destination) async {
|
||||
Future<void> getRoute(LatLng origin, LatLng destination,
|
||||
{bool keepNavigationActive = false}) async {
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
@@ -1007,9 +974,8 @@ class NavigationController extends GetxController
|
||||
currentStepIndex = 0;
|
||||
_nextInstructionSpoken = false;
|
||||
|
||||
// Don't start navigating immediately, wait for user to press Start
|
||||
isNavigating = false;
|
||||
_cameraLockedToUser = false;
|
||||
isNavigating = keepNavigationActive;
|
||||
_cameraLockedToUser = keepNavigationActive;
|
||||
_offRouteStartTime = null;
|
||||
isLoading = false;
|
||||
|
||||
@@ -1032,7 +998,10 @@ class NavigationController extends GetxController
|
||||
// Re-add car marker after polyline updates (ensures it stays on top)
|
||||
if (isStyleLoaded) _updateCarMarker();
|
||||
|
||||
if (_fullRouteCoordinates.length >= 2) {
|
||||
if (keepNavigationActive && myLocation != null) {
|
||||
animateCameraToPosition(myLocation!,
|
||||
bearing: _smoothedHeading, zoom: _targetZoom, tilt: _targetTilt);
|
||||
} else if (_fullRouteCoordinates.length >= 2) {
|
||||
final bounds =
|
||||
data['bbox'] != null && (data['bbox'] as List).length == 4
|
||||
? LatLngBounds(
|
||||
@@ -1117,12 +1086,23 @@ class NavigationController extends GetxController
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> recalculateRoute() async {
|
||||
if (myLocation == null || _finalDestination == null || isLoading) return;
|
||||
Future<void> recalculateRoute(
|
||||
{LatLng? origin, bool keepNavigationActive = false}) async {
|
||||
final LatLng? routeOrigin = origin ?? myLocation;
|
||||
if (routeOrigin == null || _finalDestination == null || isLoading) return;
|
||||
|
||||
isLoading = true;
|
||||
update();
|
||||
mySnackbarInfo('جاري حساب مسار جديد...');
|
||||
await getRoute(myLocation!, _finalDestination!);
|
||||
|
||||
markers = markers.where((m) => m.markerId.value != 'origin').toSet();
|
||||
markers.add(Marker(
|
||||
markerId: const MarkerId('origin'),
|
||||
position: routeOrigin,
|
||||
icon: InlqBitmap.fromStyleImage('start_icon'),
|
||||
));
|
||||
|
||||
await getRoute(routeOrigin, _finalDestination!,
|
||||
keepNavigationActive: keepNavigationActive);
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
@@ -1290,17 +1270,19 @@ class NavigationController extends GetxController
|
||||
try {
|
||||
// ✅ Use searchPlaces from intaleq_maps SDK
|
||||
final results = await mapController!.searchPlaces(q);
|
||||
|
||||
|
||||
if (myLocation != null) {
|
||||
for (final p in results) {
|
||||
final plat = double.tryParse(p['latitude']?.toString() ?? '0') ?? 0.0;
|
||||
final plng = double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0;
|
||||
p['distanceKm'] = _haversineKm(myLocation!.latitude, myLocation!.longitude, plat, plng);
|
||||
final plng =
|
||||
double.tryParse(p['longitude']?.toString() ?? '0') ?? 0.0;
|
||||
p['distanceKm'] = _haversineKm(
|
||||
myLocation!.latitude, myLocation!.longitude, plat, plng);
|
||||
}
|
||||
results.sort((a, b) =>
|
||||
(a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
|
||||
}
|
||||
|
||||
|
||||
placesDestination = results;
|
||||
update();
|
||||
} catch (e) {
|
||||
|
||||
@@ -41,6 +41,7 @@ class NavigationView extends StatelessWidget {
|
||||
IntaleqMap(
|
||||
apiKey: Env.mapSaasKey,
|
||||
onMapCreated: c.onMapCreated,
|
||||
onStyleLoaded: c.onStyleLoaded,
|
||||
onLongPress: (pos) => c.onMapLongPressed(Point(0, 0), pos),
|
||||
onTap: (pos) => c.onMapTapped(Point(0, 0), pos),
|
||||
markers: c.markers,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user