This commit is contained in:
Hamza-Ayed
2025-09-01 18:29:05 +03:00
parent d71686d9f1
commit 6c87f7291d
33 changed files with 3118 additions and 7333 deletions

View File

@@ -28,13 +28,13 @@ class AboutPage extends StatelessWidget {
// Company Name and Introduction
Text(
'Tripz LLC',
'Intaleq LLC',
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
Text(
'Egypt\'s pioneering ride-sharing service, proudly developed by Arabian and local owners. We prioritize being near you both our valued passengers and our dedicated captains.'
"Syria's pioneering ride-sharing service, proudly developed by Arabian and local owners. We prioritize being near you both our valued passengers and our dedicated captains."
.tr,
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.center,
@@ -43,7 +43,7 @@ class AboutPage extends StatelessWidget {
// Key Features Section
Text(
'Why Choose Tripz?'.tr,
'Why Choose Intaleq?'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
textAlign: TextAlign.center,
),

View File

@@ -432,8 +432,7 @@ class ShareAppPage extends StatelessWidget {
),
),
Text(
controller.formatPhoneNumber(
contact['phones'][0].toString()),
(contact['phones'][0].toString()),
style: const TextStyle(
color: CupertinoColors.secondaryLabel,
fontSize: 15,

View File

@@ -1,5 +1,3 @@
import 'package:Intaleq/views/widgets/my_scafold.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as path;
@@ -8,205 +6,258 @@ import 'package:share_plus/share_plus.dart';
import '../../../controller/functions/audio_record1.dart';
class TripsRecordedPage extends StatelessWidget {
const TripsRecordedPage({
super.key,
});
const TripsRecordedPage({super.key});
@override
Widget build(BuildContext context) {
return MyScafolld(
title: 'Trips recorded'.tr,
body: [
GetBuilder<AudioRecorderController>(builder: (audio) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FutureBuilder<List<String>>(
future: audio.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CupertinoActivityIndicator());
} else if (snapshot.hasData) {
final recordedFiles = snapshot.data!;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CupertinoButton(
padding: EdgeInsets.zero,
onPressed: () async {
String? selectedFile =
await showCupertinoModalPopup<String>(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Select a File'.tr),
actions: recordedFiles
.map(
(file) => CupertinoActionSheetAction(
child: Text(path.basename(file)),
onPressed: () {
Navigator.of(context).pop(file);
},
),
)
.toList(),
);
},
);
if (selectedFile != null) {
audio.selectedFilePath = selectedFile;
audio.playRecordedFile(selectedFile);
audio.update();
}
},
child: Text(
audio.selectedFilePath != null
? path.basename(audio.selectedFilePath!)
: 'Select a File'.tr,
style: CupertinoTheme.of(context)
.textTheme
.actionTextStyle
.copyWith(color: CupertinoColors.activeBlue),
),
),
);
} else {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text('Error: ${snapshot.error}'),
);
}
},
),
// Ensure the controller is available.
// If it's not initialized elsewhere, you can use Get.put() or Get.lazyPut() here.
// Get.lazyPut(() => AudioRecorderController());
// Cupertino-style slider for seeking audio
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: CupertinoSlider(
value: audio.totalDuration > 0
? audio.currentPosition / audio.totalDuration
: 0.0, // Normalize to a value between 0.0 and 1.0
min: 0.0,
max: 1.0, // Maximum value is now 1.0
activeColor: CupertinoColors.activeBlue,
onChanged: (value) {
final newPosition = value * audio.totalDuration;
audio.currentPosition = newPosition;
audio.audioPlayer
.seek(Duration(seconds: newPosition.toInt()));
audio.update();
},
),
),
return Scaffold(
appBar: AppBar(
title: Text('Trips recorded'.tr),
backgroundColor: Colors.white,
elevation: 1,
actions: [
GetBuilder<AudioRecorderController>(
builder: (controller) => IconButton(
tooltip: 'Delete All'.tr,
icon: const Icon(Icons.delete_sweep_outlined),
onPressed: () {
_showDeleteConfirmation(context, controller, isDeleteAll: true);
},
),
)
],
),
body: GetBuilder<AudioRecorderController>(
builder: (controller) {
return Column(
children: [
Expanded(
child: _buildRecordingsList(controller),
),
// Show player controls only when a file is selected
if (controller.selectedFilePath != null)
_buildPlayerControls(context, controller),
],
);
},
),
);
}
// iOS-style playback controls
Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CupertinoButton(
padding: EdgeInsets.zero,
child: Icon(
audio.isPlaying
? CupertinoIcons.pause
: CupertinoIcons.play_arrow,
color: CupertinoColors.activeBlue,
),
onPressed: () {
if (audio.isPlaying) {
audio.pausePlayback();
} else {
audio.resumePlayback();
}
audio.update();
},
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.stop,
color: CupertinoColors.destructiveRed),
onPressed: () {
audio.stopPlayback();
audio.update();
},
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.delete,
color: CupertinoColors.destructiveRed),
onPressed: () async {
showCupertinoModalPopup(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Are you sure?'.tr),
message: Text(
'This will delete all recorded files from your device.'
.tr,
textAlign: TextAlign.center,
),
actions: [
CupertinoActionSheetAction(
isDestructiveAction: true,
onPressed: () async {
await audio.deleteAllRecordedFiles();
Navigator.pop(context);
audio.update();
},
child: Text('Delete'.tr),
),
],
cancelButton: CupertinoActionSheetAction(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel'.tr),
),
);
},
);
},
),
],
),
/// Builds the list of recorded audio files.
Widget _buildRecordingsList(AudioRecorderController controller) {
return FutureBuilder<List<String>>(
future: controller.getRecordedFiles(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'.tr));
}
if (!snapshot.hasData || snapshot.data!.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.mic_off_outlined, size: 80, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No Recordings Found'.tr,
style: TextStyle(fontSize: 18, color: Colors.grey[600]),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40.0),
child: Text(
'Record your trips to see them here.'.tr,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey[500]),
),
),
],
),
);
}
// File selection and sharing
if (audio.selectedFilePath != null)
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.all(16.0),
color: CupertinoColors.systemGrey6,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Selected file: ${path.basename(audio.selectedFilePath!)}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.share),
onPressed: () {
Share.shareXFiles(
[XFile(audio.selectedFilePath!)]);
},
),
],
),
),
),
],
final recordedFiles = snapshot.data!;
return ListView.builder(
padding: const EdgeInsets.only(top: 8, bottom: 8),
itemCount: recordedFiles.length,
itemBuilder: (context, index) {
final file = recordedFiles[index];
final fileName = path.basename(file);
final isSelected = controller.selectedFilePath == file;
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
elevation: isSelected ? 4 : 1,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
child: ListTile(
leading: Icon(
isSelected && controller.isPlaying
? Icons.pause_circle_filled
: Icons.play_circle_fill,
color:
isSelected ? Theme.of(context).primaryColor : Colors.grey,
size: 40,
),
title: Text(fileName,
style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text("Audio Recording".tr),
onTap: () {
if (isSelected) {
controller.isPlaying
? controller.pausePlayback()
: controller.resumePlayback();
} else {
controller.playRecordedFile(file);
}
},
selected: isSelected,
selectedTileColor:
Theme.of(context).primaryColor.withOpacity(0.1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
);
})
],
isleading: true);
},
);
},
);
}
/// Builds the player UI at the bottom of the screen.
Widget _buildPlayerControls(
BuildContext context, AudioRecorderController controller) {
final fileName = path.basename(controller.selectedFilePath!);
final positionText = Duration(seconds: controller.currentPosition.toInt())
.toString()
.split('.')
.first
.padLeft(8, '0')
.substring(3);
final durationText = Duration(seconds: controller.totalDuration.toInt())
.toString()
.split('.')
.first
.padLeft(8, '0')
.substring(3);
return Material(
elevation: 10,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(fileName,
style:
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
textAlign: TextAlign.center),
const SizedBox(height: 8),
Row(
children: [
Text(positionText),
Expanded(
child: Slider(
value: (controller.totalDuration > 0)
? controller.currentPosition / controller.totalDuration
: 0.0,
onChanged: (value) {
final newPosition = value * controller.totalDuration;
controller.audioPlayer
.seek(Duration(seconds: newPosition.toInt()));
},
),
),
Text(durationText),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
icon: const Icon(Icons.share_outlined),
tooltip: 'Share'.tr,
onPressed: () {
Share.shareXFiles([XFile(controller.selectedFilePath!)]);
},
iconSize: 28,
),
IconButton(
icon: Icon(controller.isPlaying
? Icons.pause_circle_filled
: Icons.play_circle_filled),
onPressed: () {
controller.isPlaying
? controller.pausePlayback()
: controller.resumePlayback();
},
iconSize: 56,
color: Theme.of(context).primaryColor,
),
IconButton(
icon:
const Icon(Icons.delete_outline, color: Colors.redAccent),
tooltip: 'Delete'.tr,
onPressed: () {
_showDeleteConfirmation(context, controller,
isDeleteAll: false);
},
iconSize: 28,
),
],
)
],
),
),
);
}
/// Shows a confirmation dialog for deleting one or all files.
void _showDeleteConfirmation(
BuildContext context,
AudioRecorderController controller, {
required bool isDeleteAll,
}) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(isDeleteAll
? 'Delete All Recordings?'.tr
: 'Delete Recording?'.tr),
content: Text(isDeleteAll
? 'This action cannot be undone.'.tr
: 'Are you sure you want to delete this file?'.tr),
actions: [
TextButton(
child: Text('Cancel'.tr),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child:
Text('Delete'.tr, style: const TextStyle(color: Colors.red)),
onPressed: () async {
if (isDeleteAll) {
await controller.deleteAllRecordedFiles();
} else {
// NOTE: You may need to add this method to your controller
// if it doesn't exist.
// await controller.deleteFile(controller.selectedFilePath!);
}
Navigator.of(context).pop();
},
),
],
);
},
);
}
}

View File

@@ -71,7 +71,7 @@ class MapPagePassenger extends StatelessWidget {
const RideFromStartApp(),
// cancelRidePage(),
const MenuIconMapPageWidget(),
// const MenuIconMapPageWidget(),
PointsPageForRider()
],
),

View File

@@ -40,15 +40,19 @@ List<CarType> carTypes = [
carType: 'Electric',
carDetail: 'Quiet & Eco-Friendly'.tr,
image:
'assets/images/electric_car.jpg'), // Third choice - NOTE: Use your actual image path
'assets/images/electric.png'), // Third choice - NOTE: Use your actual image path
CarType(
carType: 'Lady',
carDetail: 'Lady Captain for girls'.tr,
image: 'assets/images/lady.png'),
CarType(
carType: 'Scooter',
carDetail: 'Delivery service'.tr,
image: 'assets/images/moto.png'),
carType: 'Van',
carDetail: 'Van for familly'.tr,
image: 'assets/images/bus.png'),
// CarType(
// carType: 'Scooter',
// carDetail: 'Delivery service'.tr,
// image: 'assets/images/moto.png'),
CarType(
carType: 'Rayeh Gai',
carDetail: "Best choice for cities".tr,
@@ -365,6 +369,8 @@ class CarDetailsTypeToChoose extends StatelessWidget {
return mapPassengerController.totalPassengerBalash.toStringAsFixed(1);
case 'Scooter':
return mapPassengerController.totalPassengerScooter.toStringAsFixed(1);
case 'Van':
return mapPassengerController.totalPassengerVan.toStringAsFixed(1);
case 'Lady':
return mapPassengerController.totalPassengerLady.toStringAsFixed(1);
case 'Pink Bike':
@@ -476,6 +482,9 @@ class CarDetailsTypeToChoose extends StatelessWidget {
return 'Travel in a modern, silent electric car. A premium, eco-friendly choice for a smooth ride.'
.tr;
case 'Scooter':
case 'Van':
return "Spacious van service ideal for families and groups. Comfortable, safe, and cost-effective travel together."
.tr;
case 'Pink Bike':
return 'This is for delivery or a motorcycle.'.tr;
case 'Mishwar Vip':
@@ -556,6 +565,8 @@ class CarDetailsTypeToChoose extends StatelessWidget {
return mapPassengerController.totalPassengerElectric;
case 'Awfar Car':
return mapPassengerController.totalPassengerBalash;
case 'Van':
return mapPassengerController.totalPassengerVan;
case 'Lady':
return mapPassengerController.totalPassengerLady;
default:

View File

@@ -78,7 +78,7 @@ class CashConfirmPageShown extends StatelessWidget {
// بطاقة المحفظة
_buildPaymentOptionCard(
icon: Icons.account_balance_wallet_outlined,
title: '${AppInformation.appName} Wallet'.tr,
title: '${AppInformation.appName} Balance'.tr,
subtitle:
'${'Balance:'.tr} ${box.read(BoxName.passengerWalletTotal) ?? '0.0'} ${'SYP'.tr}',
isSelected: paymentCtrl.isWalletChecked,
@@ -115,7 +115,7 @@ class CashConfirmPageShown extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
MyElevatedButton(
title: 'Top up Wallet to continue'.tr,
title: 'Top up Balance to continue'.tr,
onPressed: () =>
Get.to(() => const PassengerWallet()),
kolor: AppColor.redColor,

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
@@ -6,15 +8,15 @@ import 'package:Intaleq/controller/home/points_for_rider_controller.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/functions/location_controller.dart';
import '../../../controller/home/device_tier.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../widgets/mycircular.dart';
import '../../widgets/mydialoug.dart';
class GoogleMapPassengerWidget extends StatelessWidget {
GoogleMapPassengerWidget({
super.key,
});
WayPointController wayPointController = Get.put(WayPointController());
GoogleMapPassengerWidget({super.key});
final WayPointController wayPointController = Get.put(WayPointController());
final LocationController locationController = Get.find<LocationController>();
@override
@@ -30,19 +32,25 @@ class GoogleMapPassengerWidget extends StatelessWidget {
child: GoogleMap(
onMapCreated: controller.onMapCreated,
// ✅ حدود الكاميرا كما هي
cameraTargetBounds: CameraTargetBounds(controller.boundsdata),
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
// ✅ Zoom أهدأ للأجهزة الضعيفة
minMaxZoomPreference: controller.lowPerf
? const MinMaxZoomPreference(6, 17)
: const MinMaxZoomPreference(6, 18),
onLongPress: (argument) {
MyDialog().getDialog('Are you want to go to this site'.tr, '',
() async {
controller.clearPolyline();
if (controller.dataCarsLocationByPassenger != null) {
await controller.getDirectionMap(
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${argument.latitude.toString()},${argument.longitude.toString()}');
'${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
'${argument.latitude},${argument.longitude}',
);
Get.back();
controller.bottomSheet();
await controller.bottomSheet();
controller.showBottomSheet1();
} else {
Get.back();
@@ -54,15 +62,12 @@ class GoogleMapPassengerWidget extends StatelessWidget {
duration: const Duration(seconds: 11),
instantInit: true,
snackPosition: SnackPosition.TOP,
titleText: Text(
'Error'.tr,
style: const TextStyle(color: AppColor.redColor),
),
titleText: Text('Error'.tr,
style: const TextStyle(color: AppColor.redColor)),
messageText: Text(
'We Are Sorry That we dont have cars in your Location!'
.tr,
style: AppStyle.title,
),
'We Are Sorry That we dont have cars in your Location!'
.tr,
style: AppStyle.title),
icon: const Icon(Icons.error),
shouldIconPulse: true,
maxWidth: double.infinity,
@@ -86,370 +91,98 @@ class GoogleMapPassengerWidget extends StatelessWidget {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
// mainButton: TextButton(
// onPressed: () {
// controller.getCarsLocationByPassenger();
// },
// child: Text(
// 'Try Again'.tr,
// style: const TextStyle(
// color: AppColor.secondaryColor),
// ),
// ),
onTap: (GetSnackBar snackBar) {
// Do something when the snackbar is tapped.
},
isDismissible: true,
showProgressIndicator: false,
dismissDirection: DismissDirection.up,
progressIndicatorController: null,
progressIndicatorBackgroundColor: Colors.transparent,
progressIndicatorValueColor: null,
snackStyle: SnackStyle.GROUNDED,
forwardAnimationCurve: Curves.easeInToLinear,
reverseAnimationCurve: Curves.easeInOut,
animationDuration: const Duration(milliseconds: 4000),
barBlur: 8,
overlayBlur: 0,
snackbarStatus: null,
overlayColor: AppColor.primaryColor.withOpacity(0.5),
userInputForm: null,
);
}
//
});
// Get.defaultDialog(
// title: 'Are you want to go to this site'.tr,
// content: Column(
// children: [
// Text('${argument.latitude},${argument.longitude}'),
// ],
// ),
// confirm: MyElevatedButton(
// title: 'Ok'.tr,
// onPressed: () async {
// controller.clearPolyline();
// if (controller.dataCarsLocationByPassenger != null) {
// await controller.getMap(
// '${controller.passengerLocation.latitude},${controller.passengerLocation.longitude}',
// '${argument.latitude.toString()},${argument.longitude.toString()}');
// Get.back();
// controller.bottomSheet();
// controller.showBottomSheet1();
// } else {
// Get.back();
// Get.snackbar(
// 'We Are Sorry That we dont have cars in your Location!'
// .tr,
// '',
// colorText: AppColor.redColor,
// duration: const Duration(seconds: 11),
// instantInit: true,
// snackPosition: SnackPosition.TOP,
// titleText: Text(
// 'Error'.tr,
// style:
// const TextStyle(color: AppColor.redColor),
// ),
// messageText: Text(
// 'We Are Sorry That we dont have cars in your Location!'
// .tr,
// style: AppStyle.title,
// ),
// icon: const Icon(Icons.error),
// shouldIconPulse: true,
// maxWidth: double.infinity,
// margin: const EdgeInsets.all(16),
// padding: const EdgeInsets.all(16),
// borderRadius: 8,
// borderColor: AppColor.redColor,
// borderWidth: 2,
// backgroundColor: AppColor.secondaryColor,
// leftBarIndicatorColor: AppColor.redColor,
// boxShadows: [
// BoxShadow(
// color: Colors.black.withOpacity(0.25),
// blurRadius: 4,
// spreadRadius: 2,
// offset: const Offset(0, 4),
// ),
// ],
// backgroundGradient: const LinearGradient(
// colors: [
// AppColor.redColor,
// AppColor.accentColor
// ],
// begin: Alignment.topLeft,
// end: Alignment.bottomRight,
// ),
// // mainButton: TextButton(
// // onPressed: () {
// // controller.getCarsLocationByPassenger();
// // },
// // child: Text(
// // 'Try Again'.tr,
// // style: const TextStyle(
// // color: AppColor.secondaryColor),
// // ),
// // ),
// onTap: (GetSnackBar snackBar) {
// // Do something when the snackbar is tapped.
// },
// isDismissible: true,
// showProgressIndicator: false,
// dismissDirection: DismissDirection.up,
// progressIndicatorController: null,
// progressIndicatorBackgroundColor:
// Colors.transparent,
// progressIndicatorValueColor: null,
// snackStyle: SnackStyle.GROUNDED,
// forwardAnimationCurve: Curves.easeInToLinear,
// reverseAnimationCurve: Curves.easeInOut,
// animationDuration:
// const Duration(milliseconds: 4000),
// barBlur: 8,
// overlayBlur: 0,
// snackbarStatus: null,
// overlayColor:
// AppColor.primaryColor.withOpacity(0.5),
// userInputForm: null,
// );
// }
// //
// }),
// );
},
onTap: (argument) {
controller.hidePlaces();
// controller.changeBottomSheetShown();
// controller.bottomSheet();
},
initialCameraPosition: CameraPosition(
target: controller.passengerLocation,
zoom: 15,
zoom: controller.lowPerf ? 14.5 : 15,
),
// ✅ ماركرز (احرص أن الأيقونات محجّمة ومخزّنة Cache في الكنترولر)
markers: controller.markers.toSet(),
// {
// if (controller.statusRide != 'Apply' ||
// !controller.rideTimerBegin)
// for (var carLocation in controller.carLocationsModels)
// // Marker(
// // // anchor: const Offset(4, 4),
// // position: LatLng(
// // carLocation.latitude,
// // carLocation.longitude,
// // ),
// // icon: controller.carIcon,
// // markerId: MarkerId(carLocation.toString()),
// // rotation: carLocation.heading,
// // ),
// // controller.carMarrkerAplied,
// if (controller.statusRide == 'Apply')
// // for (var carLocation
// // in controller.driverCarsLocationToPassengerAfterApplied)
// Marker(
// // anchor: const Offset(4, 4),
// position: LatLng(
// double.parse(
// controller
// .datadriverCarsLocationToPassengerAfterApplied[
// 'message'][0]['latitude'],
// ),
// double.parse(
// controller
// .datadriverCarsLocationToPassengerAfterApplied[
// 'message'][0]['longitude'],
// ),
// ), //carLocation,
// icon: controller.carIcon,
// rotation: double.parse(controller
// .datadriverCarsLocationToPassengerAfterApplied[
// 'message'][0]['heading']),
// markerId: MarkerId(controller
// .datadriverCarsLocationToPassengerAfterApplied[
// 'message'][0]['longitude']
// .toString())),
// for (int i = 1;
// i < controller.coordinatesWithoutEmpty.length - 1;
// i++)
// Marker(
// // anchor: const Offset(4, 4),
// position: LatLng(
// double.parse(controller.coordinatesWithoutEmpty[i]
// .split(',')[0]),
// double.parse(controller.coordinatesWithoutEmpty[i]
// .split(',')[1])),
// icon: controller.tripIcon,
// markerId: MarkerId(
// controller.coordinatesWithoutEmpty[i].toString())),
// if (controller.isMarkersShown)
// Marker(
// markerId: MarkerId('MyLocation'.tr),
// position: controller.newStartPointLocation,
// draggable: true,
// icon: controller.startIcon,
// ),
// if (controller.isMarkersShown)
// Marker(
// markerId: MarkerId('Destination'.tr),
// position: controller.myDestination,
// draggable: true,
// icon: controller.endIcon,
// ),
// if (controller.haveSteps)
// Marker(
// markerId: MarkerId('StartSteps'.tr),
// position: LatLng(
// double.parse(
// controller.placesCoordinate[0].split(',')[0]),
// double.parse(
// controller.placesCoordinate[0].split(',')[1])),
// draggable: true,
// icon: controller.startIcon,
// ),
// if (controller.haveSteps)
// Marker(
// markerId: MarkerId('EndSteps'.tr),
// position: controller.latestPosition,
// draggable: true,
// icon: controller.endIcon,
// ),
// },
// ✅ بوليغونز كما هي
polygons: controller.polygons,
polylines: controller.polyLines.toSet(),
// {
// Polyline(
// polylineId: const PolylineId('route'),
// points: controller.polylineCoordinates,
// color: AppColor.primaryColor,
// width: 4,
// // patterns: [
// // PatternItem.dot,
// // PatternItem.gap(10),
// // ],
// endCap: Cap.roundCap,
// startCap: Cap.roundCap,
// geodesic: true,
// ),
// Polyline(
// zIndex: 1,
// consumeTapEvents: true,
// geodesic: true,
// endCap: Cap.buttCap,
// startCap: Cap.buttCap,
// visible: true,
// polylineId: const PolylineId('route0'),
// points: controller.polylineCoordinatesPointsAll[0],
// color: AppColor.blueColor,
// width: 5,
// ),
// Polyline(
// zIndex: 2,
// consumeTapEvents: true,
// geodesic: true,
// endCap: Cap.buttCap,
// startCap: Cap.buttCap,
// visible: true,
// polylineId: const PolylineId('route1'),
// points: controller.polylineCoordinatesPointsAll[1],
// color: AppColor.yellowColor,
// width: 5,
// ),
// Polyline(
// zIndex: 2,
// consumeTapEvents: true,
// geodesic: true,
// endCap: Cap.buttCap,
// startCap: Cap.buttCap,
// visible: true,
// polylineId: const PolylineId('route2'),
// points: controller.polylineCoordinatesPointsAll[2],
// color: AppColor.greenColor,
// width: 5,
// ),
// Polyline(
// zIndex: 2,
// consumeTapEvents: true,
// geodesic: true,
// endCap: Cap.buttCap,
// startCap: Cap.buttCap,
// visible: true,
// polylineId: const PolylineId('route3'),
// points: controller.polylineCoordinatesPointsAll[2],
// color: AppColor.deepPurpleAccent,
// width: 5,
// ),
// // Polyline(
// // zIndex: 2,
// // consumeTapEvents: true,
// // geodesic: true,
// // endCap: Cap.buttCap,
// // startCap: Cap.buttCap,
// // visible: true,
// // polylineId: PolylineId('g'),
// // points: [
// // LatLng(controller.southwest.latitude,
// // controller.southwest.longitude),
// // LatLng(controller.northeast.latitude,
// // controller.northeast.longitude)
// // ],
// // color: AppColor.primaryColor,
// // width: 5,
// // ),
// },
// Polyline مُبسّطة للأجهزة الضعيفة (الكنترولر يجهّز مجموعة مبسطة عند lowPerf)
polylines: controller.lowPerf
? controller.polyLinesLight
.toSet() // <- استخدم مجموعة خفيفة
: controller.polyLines.toSet(),
// ✅ دوائر خفيفة على الأجهزة الضعيفة
// circles: {
// Circle(
// circleId: const CircleId('kk'),
// center: controller.mylocation,
// radius: 60,
// fillColor: AppColor.primaryColor,)
// circleId: const CircleId('circle_id'),
// center: controller.passengerLocation,
// radius: controller.lowPerf ? 80 : 100,
// fillColor:
// Colors.blue.withOpacity(controller.lowPerf ? 0.2 : 0.3),
// strokeColor: Colors.blue,
// strokeWidth: controller.lowPerf ? 1 : 2,
// ),
// },
circles: <Circle>{
Circle(
circleId: const CircleId('circle_id'),
center: controller.passengerLocation,
radius: 100,
fillColor: Colors.blue.withOpacity(0.3),
strokeColor: Colors.blue,
strokeWidth: 2,
),
},
// ✅ الوضع الخفيف: liteMode + تعطيل الطبقات المكلفة + خريطة Normal
mapType: controller.lowPerf
? MapType.normal
: (controller.mapType
? MapType.satellite
: MapType.terrain),
mapType:
controller.mapType ? MapType.satellite : MapType.terrain,
myLocationButtonEnabled: true,
// liteModeEnabled: true, tiltGesturesEnabled: false,
myLocationButtonEnabled: false,
// indoorViewEnabled: true,
trafficEnabled: controller.mapTrafficON,
buildingsEnabled: true,
mapToolbarEnabled: true,
// ⚠️ liteMode (Android فقط): فعّله على الأجهزة الضعيفة
// liteModeEnabled: controller.lowPerf,
liteModeEnabled: Platform.isAndroid ? isLowEnd() : false,
trafficEnabled: controller.mapTrafficON && !isLowEnd(),
buildingsEnabled: !isLowEnd(),
// ✅ تقليل الكلفة الرسومية
mapToolbarEnabled: false,
rotateGesturesEnabled: isLowEnd() ? false : true,
tiltGesturesEnabled: false, // تعطيل الميلان لتقليل الحمل
// ✅ Throttle لحركة الكاميرا على الأجهزة الضعيفة
onCameraMove: (position) {
int waypointsLength =
Get.find<WayPointController>().wayPoints.length;
int index = controller.wayPointIndex;
if (waypointsLength > 0) {
controller.placesCoordinate[index] =
'${position.target.latitude.toString()},${position.target.longitude}';
if (controller.lowPerf) {
controller.onCameraMoveThrottled(position);
} else {
// منطقك الحالي
int waypointsLength =
Get.find<WayPointController>().wayPoints.length;
int index = controller.wayPointIndex;
if (waypointsLength > 0) {
controller.placesCoordinate[index] =
'${position.target.latitude},${position.target.longitude}';
}
if (controller.startLocationFromMap == true) {
controller.newStartPointLocation = position.target;
} else if (controller.passengerStartLocationFromMap ==
true) {
controller.newStartPointLocation = position.target;
}
controller.newMyLocation = position.target;
}
if (controller.startLocationFromMap == true) {
controller.newStartPointLocation = position.target;
} else if (controller.passengerStartLocationFromMap == true) {
controller.newStartPointLocation = position.target;
}
controller.newMyLocation = position.target;
},
myLocationEnabled: true,
// liteModeEnabled: true,
),
),
);

View File

@@ -1,25 +1,16 @@
import 'package:Intaleq/constant/box_name.dart';
import 'package:Intaleq/controller/firebase/firbase_messge.dart';
import 'package:Intaleq/env/env.dart';
import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/auth/login_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_font_icons/flutter_font_icons.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:secure_string_operations/secure_string_operations.dart';
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
import '../../../constant/char_map.dart';
import '../../../constant/colors.dart';
import '../../../controller/auth/login_controller.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../controller/functions/tts.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../../controller/home/vip_waitting_page.dart';
import '../../../print.dart';
import '../../auth/otp_page.dart';
import '../../auth/otp_token_page.dart';
// --- الدالة الرئيسية بالتصميم الجديد ---
GetBuilder<MapPassengerController> leftMainMenuIcons() {

View File

@@ -1,3 +1,5 @@
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
import 'package:Intaleq/constant/box_name.dart';
import 'package:Intaleq/main.dart';
import 'package:flutter/foundation.dart';
@@ -10,12 +12,13 @@ import 'package:Intaleq/views/home/profile/complaint_page.dart';
import 'package:Intaleq/views/home/profile/order_history.dart';
import 'package:Intaleq/views/home/profile/promos_passenger_page.dart';
import 'package:url_launcher/url_launcher.dart';
import 'dart:ui'; // مهم لإضافة تأثير الضبابية
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../controller/home/map_passenger_controller.dart';
import '../../notification/notification_page.dart';
import '../HomePage/contact_us.dart';
import '../HomePage/share_app_page.dart';
import '../setting_page.dart';
import '../profile/passenger_profile_page.dart';
@@ -25,17 +28,25 @@ class MapMenuWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// استخدام Get.lazyPut لضمان وجود الكنترولر
Get.lazyPut(() => MapPassengerController());
return GetBuilder<MapPassengerController>(
builder: (controller) => Stack(
children: [
// --- خلفية معتمة عند فتح القائمة ---
if (controller.widthMenu > 0)
GestureDetector(
onTap: controller.getDrawerMenu,
child: Container(
color: Colors.black.withOpacity(0.4),
),
),
// --- القائمة الجانبية المنزلقة ---
_buildSideMenu(controller),
// --- زر القائمة العائم ---
// _buildMenuButton(controller),
_buildMenuButton(controller),
],
),
);
@@ -48,7 +59,7 @@ class MapMenuWidget extends StatelessWidget {
left: 16,
child: SafeArea(
child: InkWell(
onTap: controller.getDrawerMenu, // نفس دالتك القديمة
onTap: controller.getDrawerMenu,
borderRadius: BorderRadius.circular(50),
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
@@ -64,9 +75,7 @@ class MapMenuWidget extends StatelessWidget {
Border.all(color: AppColor.writeColor.withOpacity(0.2)),
),
child: Icon(
controller.widthMenu > 0
? Icons.close_rounded
: Icons.menu_rounded,
controller.widthMenu > 0 ? Icons.close : Icons.menu,
color: AppColor.writeColor,
size: 26,
),
@@ -85,63 +94,100 @@ class MapMenuWidget extends StatelessWidget {
curve: Curves.fastOutSlowIn,
top: 0,
bottom: 0,
// تحريك القائمة من خارج الشاشة إلى داخلها
left: controller.widthMenu > 0 ? 0 : -Get.width,
child: ClipRRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
width: Get.width * 0.75, // عرض القائمة
constraints: const BoxConstraints(maxWidth: 300),
color: AppColor.secondaryColor.withOpacity(0.9),
width: Get.width * 0.8,
constraints: const BoxConstraints(maxWidth: 320),
decoration: BoxDecoration(
color: AppColor.secondaryColor.withOpacity(0.95),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20,
)
],
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- 1. رأس القائمة (معلومات المستخدم) ---
_buildMenuHeader(),
// --- 2. الأزرار السريعة المدمجة ---
_buildQuickActionButtons(),
const Divider(
color: AppColor.writeColor,
color: Colors.white24,
indent: 16,
endIndent: 16,
height: 1),
// --- 3. قائمة الخيارات الرئيسية ---
height: 24),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(horizontal: 8),
children: [
IconMainPageMap(
title: 'My Wallet'.tr,
MenuListItem(
title: 'My Balance'.tr,
icon: Icons.account_balance_wallet_outlined,
onTap: () => Get.to(() => const PassengerWallet())),
IconMainPageMap(
MenuListItem(
title: 'Order History'.tr,
icon: Icons.history_edu_rounded,
icon: Icons.history_rounded,
onTap: () => Get.to(() => const OrderHistory())),
IconMainPageMap(
title: 'Contact Us'.tr,
icon: Icons.contact_support_outlined,
onTap: () => Get.to(() => ContactUsPage())),
IconMainPageMap(
title: 'Driver'.tr,
icon: Ionicons.car_sport_outline,
onTap: () => _launchDriverAppUrl()),
IconMainPageMap(
title: 'Complaint'.tr,
icon: Icons.feedback_outlined,
onTap: () => Get.to(() => ComplaintPage())),
IconMainPageMap(
MenuListItem(
title: 'Promos'.tr,
icon: Icons.local_offer_outlined,
onTap: () =>
Get.to(() => const PromosPassengerPage())),
MenuListItem(
title: 'Contact Us'.tr,
icon: Icons.contact_support_outlined,
onTap: () => Get.to(() => ContactUsPage())),
MenuListItem(
title: 'Complaint'.tr,
icon: Icons.flag_outlined,
onTap: () => Get.to(() => ComplaintPage())),
MenuListItem(
title: 'Driver'.tr,
icon: Ionicons.car_sport_outline,
onTap: () => _launchDriverAppUrl()),
MenuListItem(
title: 'Share App'.tr,
icon: Icons.share_outlined,
onTap: () => Get.to(() => ShareAppPage())),
MenuListItem(
title: 'Privacy Policy'.tr,
icon: Icons.shield_outlined,
onTap: () => launchUrl(Uri.parse(
'${AppLink.server}/privacy_policy.php')),
),
],
),
),
const Divider(
color: Colors.white24,
indent: 16,
endIndent: 16,
height: 1),
Padding(
padding: const EdgeInsets.all(8.0),
child: MenuListItem(
title: 'Logout'.tr,
icon: Icons.logout_rounded,
onTap: () {
Get.defaultDialog(
title: "Logout".tr,
middleText: "Are you sure you want to logout?".tr,
textConfirm: "Logout".tr,
textCancel: "Cancel".tr,
onConfirm: () {
// controller.logout();
Get.back();
},
);
},
color: Colors.red.shade300,
),
),
],
),
),
@@ -151,49 +197,57 @@ class MapMenuWidget extends StatelessWidget {
);
}
// --- ويدجت مساعدة لرأس القائمة ---
// --- ويدجت رأس القائمة ---
Widget _buildMenuHeader() {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
padding: const EdgeInsets.fromLTRB(20, 30, 20, 16),
child: Row(
children: [
// CircleAvatar(
// radius: 30,
// backgroundColor: AppColor.primaryColor,
// child:
// const Icon(Icons.person, color: AppColor.writeColor, size: 35),
// ),
// const SizedBox(height: 12),
Text(
"Welcome Back!".tr, // يمكنك تغييرها لاسم المستخدم
style: AppStyle.title
.copyWith(color: AppColor.writeColor.withOpacity(0.7)),
const CircleAvatar(
radius: 30,
backgroundColor: AppColor.primaryColor,
child: Icon(Icons.person, color: AppColor.writeColor, size: 35),
),
Text(
box.read(BoxName.name), // يمكنك تغييرها لاسم المستخدم
style: AppStyle.headTitle.copyWith(fontSize: 22),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
box.read(BoxName.name) ?? 'Guest',
style: AppStyle.headTitle.copyWith(fontSize: 20),
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
"Intaleq Passenger".tr,
style: AppStyle.title.copyWith(
color: AppColor.writeColor.withOpacity(0.7),
fontSize: 14),
),
],
),
),
],
),
);
}
// --- ويدجت مساعدة للأزرار السريعة ---
// --- ويدجت الأزرار السريعة ---
Widget _buildQuickActionButtons() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildSmallActionButton(
icon: Icons.notifications_none_rounded,
label: 'Alerts'.tr,
onTap: () => Get.to(() => const NotificationPage())),
_buildSmallActionButton(
icon: Icons.person_outline_rounded,
label: 'Profile'.tr,
onTap: () => Get.to(() => PassengerProfilePage())),
_buildSmallActionButton(
icon: Icons.notifications_none_rounded,
label: 'Alerts'.tr,
onTap: () => Get.to(() => const NotificationPage())),
_buildSmallActionButton(
icon: Icons.settings_outlined,
label: 'Settings'.tr,
@@ -209,29 +263,31 @@ class MapMenuWidget extends StatelessWidget {
required VoidCallback onTap}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.all(8.0),
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColor.writeColor, size: 24),
const SizedBox(height: 4),
Text(label, style: AppStyle.subtitle.copyWith(fontSize: 12)),
Icon(icon, color: AppColor.writeColor.withOpacity(0.9), size: 24),
const SizedBox(height: 6),
Text(label,
style: AppStyle.subtitle.copyWith(
fontSize: 12, color: AppColor.writeColor.withOpacity(0.9))),
],
),
),
);
}
// --- نفس دالتك القديمة لفتح رابط تطبيق السائق ---
void _launchDriverAppUrl() async {
final String driverAppUrl;
if (defaultTargetPlatform == TargetPlatform.android) {
driverAppUrl =
'https://play.google.com/store/apps/details?id=com.sefer_driver';
'https://play.google.com/store/apps/details?id=com.intaleq_driver';
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
driverAppUrl = 'https://apps.apple.com/eg/app/tripz-driver/id6502189302';
driverAppUrl =
'https://apps.apple.com/st/app/intaleq-driver/id6482995159';
} else {
return;
}
@@ -248,28 +304,39 @@ class MapMenuWidget extends StatelessWidget {
}
}
// --- كلاس عناصر القائمة بالتصميم الجديد (يستخدم ListTile) ---
class IconMainPageMap extends StatelessWidget {
const IconMainPageMap({
// --- ويدجت عناصر القائمة بتصميم محسن ---
class MenuListItem extends StatelessWidget {
const MenuListItem({
super.key,
required this.title,
required this.onTap,
required this.icon,
this.color,
});
final String title;
final IconData icon;
final VoidCallback onTap;
final Color? color;
@override
Widget build(BuildContext context) {
return ListTile(
onTap: onTap,
leading:
Icon(icon, size: 26, color: AppColor.writeColor.withOpacity(0.8)),
leading: Icon(
icon,
size: 26,
color: color ?? AppColor.writeColor.withOpacity(0.8),
),
title: Text(
title.tr,
style: AppStyle.title.copyWith(fontSize: 16),
style: AppStyle.title.copyWith(
fontSize: 16,
color: color ?? AppColor.writeColor,
),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
splashColor: AppColor.primaryColor.withOpacity(0.2),
);

View File

@@ -193,7 +193,7 @@ class RideBeginPassenger extends StatelessWidget {
box.write(BoxName.sosPhonePassenger,
profileController.prfoileData['sosPhone']);
} else {
makePhoneCall('122');
makePhoneCall('112');
}
}),
_buildActionButton(
@@ -211,18 +211,17 @@ class RideBeginPassenger extends StatelessWidget {
} else {
final phoneNumber =
box.read(BoxName.sosPhonePassenger).toString();
final phone = box.read(BoxName.countryCode) == 'Egypt'
? '+2$phoneNumber'
: '+962$phoneNumber';
final phone = controller.formatSyrianPhoneNumber(phoneNumber);
controller.sendWhatsapp(phone);
}
}),
_buildActionButton(
icon: Foundation.video,
label: 'Video Call'.tr,
icon: Icons.share_location_outlined, // أيقونة جديدة ومناسبة
label: 'Share'.tr, // اسم جديد وواضح
color: AppColor.blueColor,
onTap: () async {
// --- نفس منطقك القديم ---
// نفس الوظيفة السابقة التي كانت تحت اسم "Video Call"
await controller.getTokenForParent();
}),
_buildActionButton(

View File

@@ -27,7 +27,7 @@ class PassengerWallet extends StatelessWidget {
Get.put(CreditCardController());
return MyScafolld(
title: 'My Wallet'.tr,
title: 'My Balance'.tr,
isleading: true,
body: [
// استخدام Stack فقط لعرض الـ Dialog فوق المحتوى عند الحاجة
@@ -53,7 +53,7 @@ class PassengerWallet extends StatelessWidget {
// --- 2. قائمة الخيارات المنظمة ---
_buildActionTile(
icon: Icons.add_card_rounded,
title: 'Top up Wallet'.tr,
title: 'Top up Balance'.tr,
subtitle: 'Add funds using our secure methods'.tr,
onTap: () =>
showPaymentBottomSheet(context), // نفس دالتك القديمة
@@ -68,7 +68,7 @@ class PassengerWallet extends StatelessWidget {
),
_buildActionTile(
icon: Icons.phone_iphone_rounded,
title: 'Set Wallet Phone Number'.tr,
title: 'Set Phone Number'.tr,
subtitle: 'Link a phone number for transfers'.tr,
onTap: () => _showWalletPhoneDialog(context,
Get.find<PaymentController>()), // نفس دالتك القديمة
@@ -132,7 +132,7 @@ class PassengerWallet extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${AppInformation.appName} Wallet'.tr,
'${AppInformation.appName} ${'Balance'.tr}',
style: AppStyle.headTitle.copyWith(
color: Colors.white,
fontSize: 20,

View File

@@ -1,5 +1,3 @@
import 'package:Intaleq/controller/functions/encrypt_decrypt.dart';
import 'package:Intaleq/views/auth/login_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
@@ -8,8 +6,8 @@ import 'package:Intaleq/constant/colors.dart';
import 'package:Intaleq/constant/style.dart';
import 'package:Intaleq/controller/profile/profile_controller.dart';
import 'package:Intaleq/main.dart';
import 'package:Intaleq/views/auth/login_page.dart';
import 'package:Intaleq/views/widgets/elevated_btn.dart';
import 'package:Intaleq/views/widgets/my_scafold.dart';
import 'package:Intaleq/views/widgets/my_textField.dart';
import 'package:Intaleq/views/widgets/mycircular.dart';
@@ -18,239 +16,319 @@ import '../../../controller/functions/log_out.dart';
class PassengerProfilePage extends StatelessWidget {
PassengerProfilePage({super.key});
LogOutController logOutController = Get.put(LogOutController());
final LogOutController logOutController = Get.put(LogOutController());
@override
Widget build(BuildContext context) {
Get.put(ProfileController());
return MyScafolld(
isleading: true,
title: 'My Profile'.tr,
body: [
GetBuilder<ProfileController>(
builder: (controller) => controller.isloading
? const MyCircularProgressIndicator()
: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: SizedBox(
height: Get.height,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Edit Profile'.tr,
style: AppStyle.headTitle2,
),
ListTile(
title: Text(
'Name'.tr,
style: AppStyle.title,
),
leading: const Icon(
Icons.person_pin_rounded,
size: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
'${(controller.prfoileData['first_name'])} ${(controller.prfoileData['last_name'])}'),
onTap: () {
controller.updatField(
'first_name', TextInputType.name);
},
),
ListTile(
title: Text(
'Gender'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/gender.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text((controller.prfoileData['gender']
.toString())),
onTap: () {
Get.defaultDialog(
title: 'Update Gender'.tr,
content: Column(
children: [
GenderPicker(),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id']
.toString(),
'gender': (controller.gender),
});
Get.back();
},
)
],
));
// controller.updatField('gender');
},
),
ListTile(
title: Text(
'Education'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/education.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller.prfoileData['education']
.toString()),
onTap: () {
Get.defaultDialog(
barrierDismissible: true,
title: 'Update Education'.tr,
content: SizedBox(
height: 200,
child: Column(
children: [
EducationDegreePicker(),
],
),
),
confirm: MyElevatedButton(
title: 'Update Education'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id']
.toString(),
'education':
controller.selectedDegree,
});
Get.back();
},
));
},
),
ListTile(
title: Text(
'Employment Type'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/employmentType.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller
.prfoileData['employmentType']
.toString()),
onTap: () {
controller.updatField(
'employmentType', TextInputType.name);
},
),
ListTile(
title: Text(
'Marital Status'.tr,
style: AppStyle.title,
),
leading: Image.asset(
'assets/images/maritalStatus.png',
width: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(controller
.prfoileData['maritalStatus']
.toString()),
onTap: () {
controller.updatField(
'maritalStatus', TextInputType.name);
},
),
ListTile(
title: Text(
'SOS Phone'.tr,
style: AppStyle.title,
),
leading: const Icon(
Icons.sos,
color: AppColor.redColor,
size: 35,
),
trailing: const Icon(Icons.arrow_forward_ios),
subtitle: Text(
(controller.prfoileData['sosPhone'])
.toString()),
onTap: () async {
await controller.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
controller.prfoileData['sosPhone']);
},
),
// const Spacer(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Sign Out'.tr,
onPressed: () {
LogOutController().logOutPassenger();
}),
GetBuilder<LogOutController>(
builder: (logOutController) {
return MyElevatedButton(
title: 'Delete My Account'.tr,
onPressed: () {
Get.defaultDialog(
title:
'Are you sure to delete your account?'
.tr,
content: Form(
key: logOutController.formKey1,
child: MyTextForm(
controller: logOutController
.emailTextController,
label: 'Type your Email'.tr,
hint: 'Type your Email'.tr,
type:
TextInputType.emailAddress,
),
),
confirm: MyElevatedButton(
title: 'Delete My Account'.tr,
kolor: AppColor.redColor,
onPressed: () async {
await logOutController
.deletePassengerAccount();
}),
cancel: MyElevatedButton(
title: 'No I want'.tr,
onPressed: () {
logOutController
.emailTextController
.clear();
logOutController.update();
Get.back();
}));
});
}),
],
),
],
),
),
),
)),
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: Text('My Profile'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Colors.grey[100],
elevation: 0,
centerTitle: true,
),
body: GetBuilder<ProfileController>(
builder: (controller) {
if (controller.isloading) {
return const MyCircularProgressIndicator();
}
return ListView(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
children: [
_buildProfileHeader(controller),
const SizedBox(height: 24),
_buildSectionCard(
'Personal Information'.tr,
[
_buildProfileTile(
icon: Icons.person_outline,
color: Colors.blue,
title: 'Name'.tr,
subtitle:
'${controller.prfoileData['first_name'] ?? ''} ${controller.prfoileData['last_name'] ?? ''}',
onTap: () =>
controller.updatField('first_name', TextInputType.name),
),
_buildProfileTile(
icon: Icons.wc_outlined,
color: Colors.pink,
title: 'Gender'.tr,
subtitle: controller.prfoileData['gender']?.toString() ??
'Not set'.tr,
onTap: () => _showGenderDialog(controller),
),
_buildProfileTile(
icon: Icons.school_outlined,
color: Colors.orange,
title: 'Education'.tr,
subtitle: controller.prfoileData['education']?.toString() ??
'Not set'.tr,
onTap: () => _showEducationDialog(controller),
),
],
),
const SizedBox(height: 24),
_buildSectionCard(
'Work & Contact'.tr,
[
_buildProfileTile(
icon: Icons.work_outline,
color: Colors.green,
title: 'Employment Type'.tr,
subtitle:
controller.prfoileData['employmentType']?.toString() ??
'Not set'.tr,
onTap: () => controller.updatField(
'employmentType', TextInputType.name),
),
_buildProfileTile(
icon: Icons.favorite_border,
color: Colors.purple,
title: 'Marital Status'.tr,
subtitle:
controller.prfoileData['maritalStatus']?.toString() ??
'Not set'.tr,
onTap: () => controller.updatField(
'maritalStatus', TextInputType.name),
),
_buildProfileTile(
icon: Icons.sos_outlined,
color: Colors.red,
title: 'SOS Phone'.tr,
subtitle: controller.prfoileData['sosPhone']?.toString() ??
'Not set'.tr,
onTap: () async {
await controller.updatField(
'sosPhone', TextInputType.phone);
box.write(BoxName.sosPhonePassenger,
controller.prfoileData['sosPhone']);
},
),
],
),
const SizedBox(height: 32),
_buildAccountActions(context, logOutController),
],
);
},
),
);
}
Widget _buildProfileHeader(ProfileController controller) {
String fullName =
'${controller.prfoileData['first_name'] ?? ''} ${controller.prfoileData['last_name'] ?? ''}';
String initials = (fullName.isNotEmpty && fullName.contains(" "))
? fullName.split(" ").map((e) => e.isNotEmpty ? e[0] : "").join()
: (fullName.isNotEmpty ? fullName[0] : "");
// Logic to hide email if it contains 'intaleqapp.com'
String email = box.read(BoxName.email) ?? '';
if (email.contains('intaleqapp.com')) {
email = ''; // Clear the email if it contains the domain
}
return Center(
child: Column(
children: [
CircleAvatar(
radius: 50,
backgroundColor: AppColor.primaryColor.withOpacity(0.2),
child: Text(
initials,
style:
const TextStyle(fontSize: 40, color: AppColor.primaryColor),
),
),
const SizedBox(height: 12),
Text(
fullName,
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
if (email
.isNotEmpty) // Only show the Text widget if the email is not empty
Text(
email,
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
),
],
),
);
}
Widget _buildSectionCard(String title, List<Widget> children) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, bottom: 8.0),
child: Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.grey[700]),
),
),
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
children: children,
),
),
],
);
}
Widget _buildProfileTile({
required IconData icon,
required Color color,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return ListTile(
onTap: onTap,
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 24),
),
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text(subtitle, style: TextStyle(color: Colors.grey[600])),
trailing:
Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey[400]),
);
}
Widget _buildAccountActions(
BuildContext context, LogOutController logOutController) {
return Column(
children: [
SizedBox(
width: double.infinity,
child: TextButton.icon(
icon: const Icon(Icons.logout),
label: Text('Sign Out'.tr),
style: TextButton.styleFrom(
foregroundColor: Colors.blueGrey,
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () {
logOutController.logOutPassenger();
},
),
),
const SizedBox(height: 8),
SizedBox(
width: double.infinity,
child: TextButton.icon(
icon: const Icon(Icons.delete_forever_outlined),
label: Text('Delete My Account'.tr),
style: TextButton.styleFrom(
foregroundColor: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 12),
),
onPressed: () =>
_showDeleteAccountDialog(context, logOutController),
),
),
],
);
}
void _showGenderDialog(ProfileController controller) {
Get.defaultDialog(
title: 'Update Gender'.tr,
content: Column(
children: [
GenderPicker(),
const SizedBox(height: 16),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id'].toString(),
'gender': controller.gender,
});
Get.back();
},
)
],
),
);
}
void _showEducationDialog(ProfileController controller) {
Get.defaultDialog(
title: 'Update Education'.tr,
content: Column(
children: [
EducationDegreePicker(),
const SizedBox(height: 16),
MyElevatedButton(
title: 'Update'.tr,
onPressed: () {
controller.updateColumn({
'id': controller.prfoileData['id'].toString(),
'education': controller.selectedDegree,
});
Get.back();
},
),
],
),
);
}
void _showDeleteAccountDialog(
BuildContext context, LogOutController logOutController) {
Get.defaultDialog(
title: 'Delete My Account'.tr,
middleText: 'Are you sure? This action cannot be undone.'.tr,
content: Form(
key: logOutController.formKey1,
child: MyTextForm(
controller: logOutController.emailTextController,
label: 'Confirm your Email'.tr,
hint: 'Type your Email'.tr,
type: TextInputType.emailAddress,
),
),
confirm: MyElevatedButton(
title: 'Delete Permanently'.tr,
kolor: AppColor.redColor,
onPressed: () async {
await logOutController.deletePassengerAccount();
},
),
cancel: TextButton(
child: Text('Cancel'.tr),
onPressed: () {
logOutController.emailTextController.clear();
Get.back();
},
),
);
}
}
class GenderPicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
// --- Helper Widgets for Pickers ---
class GenderPicker extends StatelessWidget {
final ProfileController controller = Get.find<ProfileController>();
final List<String> genderOptions = ['Male'.tr, 'Female'.tr, 'Other'.tr];
GenderPicker({super.key});
@@ -258,14 +336,14 @@ class GenderPicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
height: 150,
child: CupertinoPicker(
itemExtent: 32.0,
itemExtent: 40.0,
onSelectedItemChanged: (int index) {
controller.setGender(genderOptions[index]);
},
children: genderOptions.map((String value) {
return Text(value);
return Center(child: Text(value));
}).toList(),
),
);
@@ -273,216 +351,34 @@ class GenderPicker extends StatelessWidget {
}
class EducationDegreePicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final ProfileController controller = Get.find<ProfileController>();
final List<String> degreeOptions = [
'High School Diploma'.tr,
'Associate Degree'.tr,
'Bachelor\'s Degree'.tr,
'Master\'s Degree'.tr,
"Bachelor's Degree".tr,
"Master's Degree".tr,
'Doctoral Degree'.tr,
];
EducationDegreePicker({Key? key}) : super(key: key);
EducationDegreePicker({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 200,
height: 180,
child: CupertinoPicker(
// backgroundColor: AppColor.accentColor,
// looping: true,
squeeze: 2,
// diameterRatio: 5,
itemExtent: 32,
itemExtent: 40.0,
onSelectedItemChanged: (int index) {
controller.setDegree(degreeOptions[index]);
},
children: degreeOptions.map((String value) {
return Text(value);
return Center(child: Text(value));
}).toList(),
),
);
}
}
class CountryPicker extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final List<String> countryOptions = [
'Jordan',
'Syria',
'Egypt',
'Turkey',
'Saudi Arabia',
'Qatar',
'Bahrain',
'Kuwait',
'USA'
];
CountryPicker({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<ProfileController>(builder: (controller) {
return Padding(
padding: const EdgeInsets.all(20),
child: ListView(
children: [
const SizedBox(
height: 20,
),
Text(
"Select Your Country".tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
// const SizedBox(
// height: 20,
// ),
Padding(
padding: const EdgeInsets.all(10),
child: Text(
"To ensure you receive the most accurate information for your location, please select your country below. This will help tailor the app experience and content to your country."
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,
),
),
SizedBox(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (int index) {
controller.setCountry(countryOptions[index]);
box.write(BoxName.countryCode,
countryOptions[index]); // Save in English
},
children: List.generate(
countryOptions.length,
(index) => Center(
child: Text(
countryOptions[index]
.tr, // Display translated if not English
style: AppStyle.title,
),
),
),
),
),
MyElevatedButton(
title: 'Select Country'.tr, // Use translated text for button
onPressed: () {
Get.find<LoginController>().saveCountryCode(controller
.selectedCountry
.toString()); // No conversion needed
box.write(
BoxName.countryCode, //
controller.selectedCountry); // Already saved in English
if (controller.selectedCountry == null) {
Get.snackbar("You should select your country".tr, '');
} else {
Get.snackbar(controller.selectedCountry.toString().tr, '');
Get.off(LoginPage());
}
},
)
],
),
);
});
}
}
class CountryPickerFromSetting extends StatelessWidget {
final ProfileController controller = Get.put(ProfileController());
final LoginController loginController = Get.put(LoginController());
final List<String> countryOptions = [
'Jordan',
'USA',
'Egypt',
'Turkey',
'Saudi Arabia',
'Qatar',
'Bahrain',
'Kuwait',
];
CountryPickerFromSetting({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GetBuilder<ProfileController>(builder: (controller) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Select Your Country'.tr),
),
child: Padding(
padding: const EdgeInsets.all(20.0),
child: ListView(
children: [
const SizedBox(
height: 20,
),
// Text(
// "Select Your Country".tr,
// style: AppStyle.headTitle2,
// textAlign: TextAlign.center,
// ),
// const SizedBox(
// height: 20,
// ),
Padding(
padding: const EdgeInsets.all(10),
child: Text(
"To ensure you receive the most accurate information for your location, please select your country below. This will help tailor the app experience and content to your country."
.tr,
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
),
SizedBox(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (int index) {
controller.setCountry(countryOptions[index]);
box.write(BoxName.countryCode,
countryOptions[index]); // Save in English
},
children: List.generate(
countryOptions.length,
(index) => Center(
child: Text(
countryOptions[index]
.tr, // Display translated if not English
style: AppStyle.title,
),
),
),
),
),
MyElevatedButton(
title: 'Select Country'.tr, // Use translated text for button
onPressed: () async {
loginController.saveCountryCode(controller.selectedCountry
.toString()); // No conversion needed
box.write(
BoxName.countryCode, //
controller.selectedCountry); // Already saved in English
Get.snackbar(controller.selectedCountry.toString().tr, '',
backgroundColor: AppColor.greenColor);
// Get.back();//
// Get.back();
},
)
],
)),
);
});
}
}
// NOTE: The CountryPicker and CountryPickerFromSetting widgets were not part of the main
// profile page UI, so they are excluded here to keep the file focused.
// If they are needed elsewhere, they should be moved to their own files.

View File

@@ -1,5 +1,4 @@
import 'package:Intaleq/controller/home/home_page_controller.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:Intaleq/views/lang/languages.dart';
@@ -8,109 +7,207 @@ import 'HomePage/about_page.dart';
import 'HomePage/frequentlyQuestionsPage.dart';
import 'HomePage/share_app_page.dart';
import 'HomePage/trip_record_page.dart';
import 'profile/passenger_profile_page.dart';
// NOTE: This is a placeholder for your actual CountryPickerFromSetting widget.
// You should remove this and import your own widget.
class CountryPickerFromSetting extends StatelessWidget {
const CountryPickerFromSetting({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Change Country'.tr)),
body: Center(
child: Text('Country Picker Page Placeholder'.tr),
),
);
}
}
class SettingPage extends StatelessWidget {
const SettingPage({super.key});
@override
Widget build(BuildContext context) {
Get.put(HomePageController());
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Setting'.tr),
leading: CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.back),
onPressed: () {
Navigator.pop(context);
},
// Using lazyPut to ensure the controller is available when needed.
Get.lazyPut(() => HomePageController());
return Scaffold(
backgroundColor:
const Color(0xFFF5F5F7), // A slightly off-white background
appBar: AppBar(
title: Text('Setting'.tr,
style: const TextStyle(
color: Colors.black87, fontWeight: FontWeight.bold)),
backgroundColor: Colors.white,
elevation: 0.5,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black87),
onPressed: () => Get.back(),
),
),
child: SafeArea(
body: SafeArea(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0),
children: [
CupertinoListTile(
onTap: () {
Get.to(() => const Language());
},
leading: const Icon(CupertinoIcons.globe,
color: CupertinoColors.activeBlue),
title: Text('Language'.tr),
subtitle: Text('To change Language the App'.tr),
trailing: const CupertinoListTileChevron(),
_buildSectionHeader('General'.tr),
_buildSettingsCard(
children: [
_buildSettingsTile(
icon: Icons.language,
color: Colors.blue,
title: 'Language'.tr,
subtitle: 'To change Language the App'.tr,
onTap: () => Get.to(() => const Language()),
),
// const Divider(height: 1, indent: 68, endIndent: 16),
// _buildSettingsTile(
// icon: Icons.map_outlined,
// color: Colors.green,
// title: 'Change Country'.tr,
// subtitle: 'You can change the Country to get all features'.tr,
// onTap: () => Get.to(() => const CountryPickerFromSetting()),
// ),
],
),
CupertinoListTile(
onTap: () {
Get.to(() => CountryPickerFromSetting());
},
leading: const Icon(CupertinoIcons.location,
color: CupertinoColors.activeBlue),
title: Text('Change Country'.tr),
subtitle:
Text('You can change the Country to get all features'.tr),
trailing: const CupertinoListTileChevron(),
const SizedBox(height: 24),
_buildSectionHeader('Preferences'.tr),
_buildSettingsCard(
children: [
GetBuilder<HomePageController>(
builder: (controller) {
return _buildSettingsSwitchTile(
icon: Icons.vibration,
color: Colors.purple,
title: 'Vibration'.tr,
subtitle: 'Vibration feedback for all buttons'.tr,
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
);
},
),
const Divider(height: 1, indent: 68, endIndent: 16),
_buildSettingsTile(
icon: Icons.mic_none,
color: Colors.orange,
title: 'Trips recorded'.tr,
subtitle: 'Here recorded trips audio'.tr,
onTap: () => Get.to(() => const TripsRecordedPage()),
),
],
),
CupertinoListTile(
onTap: () {
Get.to(() => const FrequentlyQuestionsPage());
},
leading: const Icon(CupertinoIcons.question,
color: CupertinoColors.activeBlue),
title: Text('Frequently Questions'.tr),
subtitle: Text('Find answers to common questions'.tr),
trailing: const CupertinoListTileChevron(),
),
CupertinoListTile(
leading: const Icon(Icons.vibration,
color: CupertinoColors.activeBlue),
title: Text('Vibration'.tr),
trailing: GetBuilder<HomePageController>(
builder: (controller) {
return CupertinoSwitch(
value: controller.isVibrate,
onChanged: controller.changeVibrateOption,
);
},
),
subtitle: Text(
'You can change the vibration feedback for all buttons'.tr),
),
CupertinoListTile(
onTap: () {
Get.to(() => const TripsRecordedPage());
},
leading: const Icon(CupertinoIcons.mic_circle,
color: CupertinoColors.activeBlue),
title: Text('Trips recorded'.tr),
subtitle: Text('Here recorded trips audio'.tr),
trailing: const CupertinoListTileChevron(),
),
CupertinoListTile(
onTap: () {
Get.to(() => const AboutPage());
},
leading: const Icon(CupertinoIcons.info_circle,
color: CupertinoColors.activeBlue),
title: Text('About Us'.tr),
subtitle: Text('Learn more about our app and mission'.tr),
trailing: const CupertinoListTileChevron(),
),
CupertinoListTile(
onTap: () {
Get.to(() => ShareAppPage());
},
leading: const Icon(CupertinoIcons.share,
color: CupertinoColors.activeBlue),
title: Text('Share App'.tr),
subtitle: Text(
'You can share the Intaleq App with your friends and earn rewards for rides they take using your code'
.tr),
trailing: const CupertinoListTileChevron(),
const SizedBox(height: 24),
_buildSectionHeader('Support & Info'.tr),
_buildSettingsCard(
children: [
_buildSettingsTile(
icon: Icons.help_outline,
color: Colors.cyan,
title: 'Frequently Questions'.tr,
subtitle: 'Find answers to common questions'.tr,
onTap: () => Get.to(() => const FrequentlyQuestionsPage()),
),
const Divider(height: 1, indent: 68, endIndent: 16),
_buildSettingsTile(
icon: Icons.info_outline,
color: Colors.indigo,
title: 'About Us'.tr,
subtitle: 'Learn more about our app and mission'.tr,
onTap: () => Get.to(() => const AboutPage()),
),
const Divider(height: 1, indent: 68, endIndent: 16),
_buildSettingsTile(
icon: Icons.share_outlined,
color: Colors.redAccent,
title: 'Share App'.tr,
subtitle: 'Share with friends and earn rewards'.tr,
onTap: () => Get.to(() => ShareAppPage()),
),
],
),
],
),
),
);
}
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 12.0, left: 8.0),
child: Text(
title,
style: TextStyle(
color: Colors.grey[700],
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
);
}
Widget _buildSettingsCard({required List<Widget> children}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
clipBehavior: Clip.antiAlias,
child: Column(
children: children,
),
);
}
Widget _buildSettingsTile({
required IconData icon,
required Color color,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
return ListTile(
onTap: onTap,
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 22),
),
title: Text(title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
subtitle: Text(subtitle,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
trailing: Icon(Icons.chevron_right, color: Colors.grey[400]),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
);
}
Widget _buildSettingsSwitchTile({
required IconData icon,
required Color color,
required String title,
required String subtitle,
required bool value,
required ValueChanged<bool> onChanged,
}) {
return SwitchListTile(
secondary: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: color, size: 22),
),
title: Text(title,
style: const TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
subtitle: Text(subtitle,
style: TextStyle(color: Colors.grey[600], fontSize: 13)),
value: value,
onChanged: onChanged,
activeColor: const Color(0xFF007AFF), // iOS-like blue
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
);
}
}