Initial commit for Intaleq Driver

This commit is contained in:
Hamza-Ayed
2025-09-01 19:04:50 +03:00
parent 889c67a691
commit 8c7f3e3a75
46 changed files with 4300 additions and 6192 deletions

View File

@@ -26,7 +26,7 @@ class AboutPage extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'SEFER LLC\n${box.read(BoxName.countryCode).toString().tr}',
'Intaleq LLC\n${'Syria'.tr}',
style: AppStyle.headTitle2,
textAlign: TextAlign.center,
),
@@ -34,7 +34,7 @@ class AboutPage extends StatelessWidget {
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
'SEFER is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:'
'Intaleq is a ride-sharing app designed with your safety and affordability in mind. We connect you with reliable drivers in your area, ensuring a convenient and stress-free travel experience.\n\nHere are some of the key features that set us apart:'
.tr,
style: AppStyle.title,
textAlign: TextAlign.center,

View File

@@ -10,6 +10,7 @@ import '../../Rate/rate_passenger.dart';
import '../../widgets/my_textField.dart';
import 'mapDriverWidgets/driver_end_ride_bar.dart';
import 'mapDriverWidgets/google_driver_map_page.dart';
import 'mapDriverWidgets/google_map_app.dart';
import 'mapDriverWidgets/passenger_info_window.dart';
import 'mapDriverWidgets/sos_connect.dart';
@@ -44,12 +45,13 @@ class PassengerLocationMapPage extends StatelessWidget {
// 2. شريط تعليمات الطريق في الأعلى
const InstructionsOfRoads(),
// 3. زر إلغاء الرحلة في الأعلى يسارًا
CancelWidget(mapDriverController: mapDriverController),
// 4. نافذة معلومات الراكب في الأسفل (تظهر قبل بدء الرحلة)
const PassengerInfoWindow(),
const PassengerInfoWindow(),
// 3. زر إلغاء الرحلة في الأعلى يسارًا
CancelWidget(mapDriverController: mapDriverController),
// Changed: تم تعديل تصميم زر الإلغاء ليكون أيقونة بسيطة في الأعلى
// 5. شريط معلومات وإنهاء الرحلة (يظهر بعد بدء الرحلة)
driverEndRideBar(),
@@ -58,7 +60,7 @@ class PassengerLocationMapPage extends StatelessWidget {
// 7. دائرة عرض السرعة
speedCircle(),
GoogleMapApp(),
// 8. نافذة عرض السعر النهائي (تظهر بعد انتهاء الرحلة)
const PricesWindow(),
],
@@ -131,7 +133,7 @@ class CancelWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Positioned(
top: 10,
top: 70,
left: 10,
child: GetBuilder<MapDriverController>(
builder: (controller) {

View File

@@ -1,12 +1,9 @@
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import '../../../../controller/auth/captin/history_captain.dart';
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import '../../../../controller/functions/encrypt_decrypt.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
class HistoryCaptain extends StatelessWidget {
const HistoryCaptain({super.key});
@@ -14,136 +11,221 @@ class HistoryCaptain extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(HistoryCaptainController());
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('History Page'.tr),
leading: CupertinoNavigationBarBackButton(
onPressed: () => Get.back(),
),
return Scaffold(
backgroundColor: Colors.grey[100], // A softer background color
appBar: AppBar(
title: Text('Ride History'.tr),
backgroundColor: Colors.white,
foregroundColor: Colors.black,
elevation: 1,
),
child: SafeArea(
child: GetBuilder<HistoryCaptainController>(
builder: (historyCaptainController) => historyCaptainController
.isloading
? const Center(child: CupertinoActivityIndicator())
: historyCaptainController.historyData['message'].length < 1
? Center(
child: Text(
'No ride Yet.'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
body: GetBuilder<HistoryCaptainController>(
builder: (controller) {
if (controller.isloading) {
return const Center(child: CircularProgressIndicator());
}
if (controller.historyData['message'].isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.history_toggle_off,
size: 80, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No Rides Yet'.tr,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: Colors.grey[600]),
),
],
),
);
}
// 动画: Wrap ListView with AnimationLimiter for staggered animations
return AnimationLimiter(
child: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: controller.historyData['message'].length,
itemBuilder: (BuildContext context, int index) {
var trip = controller.historyData['message'][index];
// 动画: Apply animation to each list item
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: _AnimatedHistoryCard(
trip: trip,
onTap: () {
// Your original logic is preserved here
if (trip['status'] != 'Cancel') {
controller.getHistoryDetails(trip['order_id']);
} else {
MyDialog().getDialog(
'This Trip Was Cancelled'.tr,
'This Trip Was Cancelled'.tr,
() => Get.back(),
);
}
},
),
)
: ListView.builder(
itemCount: historyCaptainController
.historyData['message'].length,
itemBuilder: (BuildContext context, int index) {
var list = historyCaptainController
.historyData['message'][index];
return Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: CupertinoColors.systemGrey, width: 1),
borderRadius:
const BorderRadius.all(Radius.circular(8.0)),
),
child: CupertinoButton(
onPressed: () {
if (list['status'] != 'Cancel') {
historyCaptainController
.getHistoryDetails(list['order_id']);
} else {
MyDialog().getDialog(
'This Trip Cancelled'.tr,
'This Trip Cancelled'.tr,
() => Get.back(),
);
}
},
child: Container(
margin: const EdgeInsets.all(8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'OrderId'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
Text(
EncryptionHelper.instance
.decryptData(list['order_id']),
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'created time'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
Text(
list['created_at'],
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
Text(
list['status'],
style: EncryptionHelper.instance
.decryptData(
list['status']) ==
'Apply'
? CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle
.copyWith(
color: CupertinoColors
.systemGreen)
: EncryptionHelper.instance.decryptData(
list['status']) ==
'Refused'
? CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle
.copyWith(
color: CupertinoColors
.systemRed)
: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle
.copyWith(
color: CupertinoColors
.systemYellow),
),
],
),
),
),
),
);
},
),
),
);
},
),
);
},
),
);
}
}
// 动画: A new stateful widget to handle the tap animation
class _AnimatedHistoryCard extends StatefulWidget {
final Map<String, dynamic> trip;
final VoidCallback onTap;
const _AnimatedHistoryCard({required this.trip, required this.onTap});
@override
__AnimatedHistoryCardState createState() => __AnimatedHistoryCardState();
}
class __AnimatedHistoryCardState extends State<_AnimatedHistoryCard>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 150),
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.95).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _onTapDown(TapDownDetails details) {
_controller.forward();
}
void _onTapUp(TapUpDetails details) {
_controller.reverse();
widget.onTap();
}
void _onTapCancel() {
_controller.reverse();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
child: ScaleTransition(
scale: _scaleAnimation,
child: Card(
elevation: 4,
shadowColor: Colors.black.withOpacity(0.1),
margin: const EdgeInsets.only(bottom: 16.0),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Icon(Icons.receipt_long,
color: Theme.of(context).primaryColor, size: 40),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'OrderId'.tr}: ${widget.trip['order_id']}',
style:
Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
widget.trip['created_at'],
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey[600],
),
),
],
),
),
const SizedBox(width: 16),
_buildStatusChip(widget.trip['status']),
],
),
),
),
),
);
}
}
// 🎨 A separate function for the status chip, slightly restyled for Material
Widget _buildStatusChip(String status) {
Color chipColor;
Color textColor;
String statusText = status;
IconData iconData;
switch (status) {
case 'Apply':
chipColor = Colors.green.shade50;
textColor = Colors.green.shade800;
iconData = Icons.check_circle;
break;
case 'Refused':
chipColor = Colors.red.shade50;
textColor = Colors.red.shade800;
iconData = Icons.cancel;
break;
case 'Cancel':
chipColor = Colors.orange.shade50;
textColor = Colors.orange.shade800;
iconData = Icons.info;
statusText = 'Cancelled';
break;
default:
chipColor = Colors.grey.shade200;
textColor = Colors.grey.shade800;
iconData = Icons.hourglass_empty;
}
return Chip(
avatar: Icon(iconData, color: textColor, size: 16),
label: Text(
statusText.tr,
style: TextStyle(color: textColor, fontWeight: FontWeight.w600),
),
backgroundColor: chipColor,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
side: BorderSide.none,
);
}

View File

@@ -1,252 +1,369 @@
import 'package:sefer_driver/controller/functions/location_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:sefer_driver/controller/auth/captin/history_captain.dart';
import 'package:sefer_driver/controller/functions/launch.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter/cupertino.dart';
class HistoryDetailsPage extends StatefulWidget {
const HistoryDetailsPage({super.key});
import '../../../../controller/functions/encrypt_decrypt.dart';
@override
State<HistoryDetailsPage> createState() => _HistoryDetailsPageState();
}
class HistoryDetailsPage extends StatelessWidget {
HistoryDetailsPage({super.key});
HistoryCaptainController historyCaptainController =
Get.put(HistoryCaptainController());
class _HistoryDetailsPageState extends State<HistoryDetailsPage> {
// Get the controller instance
final HistoryCaptainController controller =
Get.find<HistoryCaptainController>();
// Helper method to safely parse LatLng from a string 'lat,lng'
LatLng? _parseLatLng(String? latLngString) {
if (latLngString == null) return null;
final parts = latLngString.split(',');
if (parts.length != 2) return null;
final lat = double.tryParse(parts[0]);
final lng = double.tryParse(parts[1]);
if (lat == null || lng == null) return null;
return LatLng(lat, lng);
}
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('Trip Detail'.tr),
leading: CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.back),
onPressed: () => Navigator.pop(context),
),
return Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
title: Text('Trip Details'.tr),
backgroundColor: Colors.white,
elevation: 1,
),
child: GetBuilder<HistoryCaptainController>(
builder: (historyCaptainController) {
var res = historyCaptainController.historyDetailsData['data'];
return historyCaptainController.isloading
? const Center(
child: CupertinoActivityIndicator(),
)
: CupertinoScrollbar(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(
height: 20,
),
CupertinoButton(
onPressed: () {
String mapUrl =
'https://www.google.com/maps/dir/${EncryptionHelper.instance.decryptData(res['start_location'])}/${EncryptionHelper.instance.decryptData(res['end_location'])}/';
showInBrowser(mapUrl);
},
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.activeBlue,
width: 2),
),
child: Column(
children: [
const SizedBox(
height: 20,
),
SizedBox(
height: MediaQuery.of(context).size.height *
0.3,
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: Get.find<LocationController>()
.myLocation,
tilt: 80,
zoom: 13,
),
zoomControlsEnabled: true,
polylines: {
Polyline(
polylineId: const PolylineId('route'),
points: [
LatLng(
double.parse(EncryptionHelper
.instance
.decryptData(
res['start_location'])
.toString()
.split(',')[0]),
double.parse(EncryptionHelper
.instance
.decryptData(
res['start_location'])
.toString()
.split(',')[1]),
),
LatLng(
double.parse(EncryptionHelper
.instance
.decryptData(
res['end_location'])
.toString()
.split(',')[0]),
double.parse(EncryptionHelper
.instance
.decryptData(
res['end_location'])
.toString()
.split(',')[1]),
)
],
color: CupertinoColors.activeGreen,
width: 5,
),
},
),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Order ID'.tr} ${EncryptionHelper.instance.decryptData(res['id'])}',
style: CupertinoTheme.of(context)
.textTheme
.navActionTextStyle,
),
Text(
res['date'].toString(),
style: CupertinoTheme.of(context)
.textTheme
.navActionTextStyle,
),
],
),
],
),
),
),
const SizedBox(height: 20),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.activeGreen, width: 2),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Price is'.tr} ${EncryptionHelper.instance.decryptData(res['price_for_driver'])}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
Text(
'${'Distance is'.tr} ${EncryptionHelper.instance.decryptData(res['distance'])} KM',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
),
const SizedBox(height: 20),
Text(
'Times of Trip'.tr,
style: CupertinoTheme.of(context)
.textTheme
.navTitleTextStyle,
),
const SizedBox(height: 10),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.destructiveRed,
width: 2),
),
child: Column(
children: [
Text(
'${'Time to Passenger is'.tr} ${res['DriverIsGoingToPassenger']}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
Text(
'${'TimeStart is'.tr} ${res['rideTimeStart']}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
Text(
'${'Time Finish is'.tr} ${res['rideTimeFinish']}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
],
),
),
const SizedBox(height: 20),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.systemGreen, width: 2),
),
child: Center(
child: Text(
'${'Passenger Name is'.tr} ${EncryptionHelper.instance.decryptData(res['first_name'])} ${EncryptionHelper.instance.decryptData(res['last_name'])}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
),
),
const SizedBox(height: 20),
Container(
width: MediaQuery.of(context).size.width * 0.9,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12.0),
border: Border.all(
color: CupertinoColors.systemYellow,
width: 2),
),
child: Center(
child: Text(
'${'Status is'.tr} ${EncryptionHelper.instance.decryptData(res['status'])}',
style: CupertinoTheme.of(context)
.textTheme
.textStyle,
),
),
),
],
),
body: GetBuilder<HistoryCaptainController>(
builder: (controller) {
if (controller.isloading) {
return const Center(child: CircularProgressIndicator());
}
final res = controller.historyDetailsData['data'];
if (res == null) {
return Center(child: Text('Could not load trip details.'.tr));
}
final startLocation = _parseLatLng(res['start_location']);
final endLocation = _parseLatLng(res['end_location']);
// Create markers for the map
final Set<Marker> markers = {};
if (startLocation != null) {
markers.add(Marker(
markerId: const MarkerId('start'),
position: startLocation,
infoWindow: InfoWindow(title: 'Start'.tr)));
}
if (endLocation != null) {
markers.add(Marker(
markerId: const MarkerId('end'),
position: endLocation,
infoWindow: InfoWindow(title: 'End'.tr)));
}
return AnimationLimiter(
child: ListView(
padding: const EdgeInsets.all(16.0),
children: AnimationConfiguration.toStaggeredList(
duration: const Duration(milliseconds: 375),
childAnimationBuilder: (widget) => SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(child: widget),
),
children: [
// --- Map Card ---
_buildMapCard(context, startLocation, endLocation, markers),
const SizedBox(height: 16),
// --- Trip Info Card ---
_DetailCard(
icon: Icons.receipt_long,
title: 'Trip Info'.tr,
child: Column(
children: [
_InfoTile(
label: 'Order ID'.tr,
value: res['id']?.toString() ?? 'N/A'),
_InfoTile(
label: 'Date'.tr,
value: res['date']?.toString() ?? 'N/A'),
],
),
),
);
// --- Earnings Card ---
_DetailCard(
icon: Icons.account_balance_wallet,
title: 'Earnings & Distance'.tr,
child: Column(
children: [
_InfoTile(
label: 'Your Earnings'.tr,
value: '${res['price_for_driver']}'),
_InfoTile(
label: 'Distance'.tr,
value: '${res['distance']} KM'),
],
),
),
// --- Timeline Card ---
_DetailCard(
icon: Icons.timeline,
title: 'Trip Timeline'.tr,
child: Column(
children: [
_InfoTile(
label: 'Time to Passenger'.tr,
value: res['DriverIsGoingToPassenger'] ?? 'N/A'),
_InfoTile(
label: 'Trip Started'.tr,
value: res['rideTimeStart'] ?? 'N/A'),
_InfoTile(
label: 'Trip Finished'.tr,
value: res['rideTimeFinish'] ?? 'N/A'),
],
),
),
// --- Passenger & Status Card ---
_DetailCard(
icon: Icons.person,
title: 'Passenger & Status'.tr,
child: Column(
children: [
_InfoTile(
label: 'Passenger Name'.tr,
value:
'${res['passengerName']} ${res['last_name']}'),
_InfoTile(
label: 'Status'.tr,
value: res['status'] ?? 'N/A',
isStatus: true),
],
),
),
],
),
),
);
},
),
);
}
Widget _buildMapCard(BuildContext context, LatLng? startLocation,
LatLng? endLocation, Set<Marker> markers) {
// A fallback position if locations are not available
final initialCameraPosition = (startLocation != null)
? CameraPosition(target: startLocation, zoom: 14)
: const CameraPosition(
target: LatLng(31.96, 35.92), zoom: 12); // Fallback to Amman
return Card(
elevation: 4,
shadowColor: Colors.black.withOpacity(0.1),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
clipBehavior:
Clip.antiAlias, // Ensures the map respects the border radius
child: Stack(
children: [
SizedBox(
height: 250,
child: GoogleMap(
initialCameraPosition: initialCameraPosition,
markers: markers,
polylines: {
if (startLocation != null && endLocation != null)
Polyline(
polylineId: const PolylineId('route'),
points: [startLocation, endLocation],
color: Colors.deepPurple,
width: 5,
),
},
onMapCreated: (GoogleMapController mapController) {
// Animate camera to fit the route
if (startLocation != null && endLocation != null) {
LatLngBounds bounds = LatLngBounds(
southwest: LatLng(
startLocation.latitude < endLocation.latitude
? startLocation.latitude
: endLocation.latitude,
startLocation.longitude < endLocation.longitude
? startLocation.longitude
: endLocation.longitude,
),
northeast: LatLng(
startLocation.latitude > endLocation.latitude
? startLocation.latitude
: endLocation.latitude,
startLocation.longitude > endLocation.longitude
? startLocation.longitude
: endLocation.longitude,
),
);
mapController.animateCamera(
CameraUpdate.newLatLngBounds(bounds, 60.0));
}
},
),
),
Positioned(
top: 10,
right: 10,
child: FloatingActionButton.small(
heroTag: 'open_maps',
onPressed: () {
if (startLocation != null && endLocation != null) {
String mapUrl =
'https://www.google.com/maps/dir/${startLocation.latitude},${startLocation.longitude}/${endLocation.latitude},${endLocation.longitude}/';
showInBrowser(mapUrl);
}
},
child: const Icon(Icons.directions),
),
),
],
),
);
}
}
// A reusable widget for the main detail cards
class _DetailCard extends StatelessWidget {
final IconData icon;
final String title;
final Widget child;
const _DetailCard({
required this.icon,
required this.title,
required this.child,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
shadowColor: Colors.black.withOpacity(0.05),
margin: const EdgeInsets.only(bottom: 16.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(icon, color: Theme.of(context).primaryColor),
const SizedBox(width: 8),
Text(
title,
style: Theme.of(context)
.textTheme
.titleLarge
?.copyWith(fontWeight: FontWeight.bold),
),
],
),
const Divider(height: 24),
child,
],
),
),
);
}
}
// A reusable widget for a label-value pair inside a card
class _InfoTile extends StatelessWidget {
final String label;
final String value;
final bool isStatus;
const _InfoTile({
required this.label,
required this.value,
this.isStatus = false,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: Colors.grey[700])),
if (isStatus)
_buildStatusChip(value)
else
Flexible(
child: Text(
value,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
textAlign: TextAlign.end,
),
),
],
),
);
}
}
// Reusing the status chip from the previous page for consistency
Widget _buildStatusChip(String status) {
Color chipColor;
Color textColor;
IconData iconData;
switch (status.toLowerCase()) {
case 'apply':
case 'completed': // Assuming 'Apply' means completed
chipColor = Colors.green.shade50;
textColor = Colors.green.shade800;
iconData = Icons.check_circle;
status = 'Completed';
break;
case 'refused':
chipColor = Colors.red.shade50;
textColor = Colors.red.shade800;
iconData = Icons.cancel;
break;
case 'cancel':
chipColor = Colors.orange.shade50;
textColor = Colors.orange.shade800;
iconData = Icons.info;
status = 'Cancelled';
break;
default:
chipColor = Colors.grey.shade200;
textColor = Colors.grey.shade800;
iconData = Icons.hourglass_empty;
}
return Chip(
avatar: Icon(iconData, color: textColor, size: 16),
label: Text(
status.tr,
style: TextStyle(color: textColor, fontWeight: FontWeight.w600),
),
backgroundColor: chipColor,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
side: BorderSide.none,
);
}

View File

@@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@@ -20,6 +22,7 @@ import 'package:sefer_driver/views/home/Captin/About Us/settings_captain.dart';
import 'package:sefer_driver/views/home/my_wallet/walet_captain.dart';
import 'package:sefer_driver/views/home/profile/profile_captain.dart';
import 'package:sefer_driver/views/notification/notification_captain.dart';
import 'package:url_launcher/url_launcher.dart';
import '../About Us/video_page.dart';
import '../assurance_health_page.dart';
import '../maintain_center_page.dart';
@@ -47,7 +50,7 @@ class AppDrawer extends StatelessWidget {
// 2. تعريف بيانات القائمة بشكل مركزي ومنظم
final List<DrawerItem> drawerItems = [
DrawerItem(
title: 'Wallet'.tr,
title: 'Balance'.tr,
icon: Icons.account_balance_wallet,
color: Colors.green,
onTap: () => Get.to(() => WalletCaptainRefactored())),
@@ -81,16 +84,16 @@ class AppDrawer extends StatelessWidget {
icon: Icons.share,
color: Colors.indigo,
onTap: () => Get.to(() => InviteScreen())),
DrawerItem(
title: 'Maintenance Center'.tr,
icon: Icons.build,
color: Colors.brown,
onTap: () => Get.to(() => MaintainCenterPage())),
DrawerItem(
title: 'Health Insurance'.tr,
icon: Icons.favorite,
color: Colors.pink,
onTap: () => Get.to(() => AssuranceHealthPage())),
// DrawerItem(
// title: 'Maintenance Center'.tr,
// icon: Icons.build,
// color: Colors.brown,
// onTap: () => Get.to(() => MaintainCenterPage())),
// DrawerItem(
// title: 'Health Insurance'.tr,
// icon: Icons.favorite,
// color: Colors.pink,
// onTap: () => Get.to(() => AssuranceHealthPage())),
DrawerItem(
title: 'Contact Us'.tr,
icon: Icons.email,
@@ -111,6 +114,12 @@ class AppDrawer extends StatelessWidget {
icon: Icons.memory,
color: Colors.greenAccent,
onTap: () => Get.to(() => DeviceCompatibilityPage())),
DrawerItem(
title: 'Privacy Policy'.tr,
icon: Icons.memory,
color: Colors.greenAccent,
onTap: () =>
launchUrl(Uri.parse('${AppLink.server}/privacy_policy.php'))),
DrawerItem(
title: 'Settings'.tr,
icon: Icons.settings,

View File

@@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:ui';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:sefer_driver/constant/box_name.dart';
@@ -26,18 +27,19 @@ import 'widget/connect.dart';
import 'widget/left_menu_map_captain.dart';
import '../../../../main.dart';
// الويدجت الرئيسية للصفحة بعد تنظيمها
// ==================================================================
// Redesigned Main Widget (V3)
// ==================================================================
class HomeCaptain extends StatelessWidget {
HomeCaptain({super.key});
// تم الإبقاء على تعريف الـ Controllers كما هو في الكود الأصلي
final LocationController locationController = Get.put(LocationController());
final HomeCaptainController homeCaptainController =
Get.put(HomeCaptainController());
@override
Widget build(BuildContext context) {
// لم يتم تغيير أي شيء في هذه الأوامر
// Initial calls remain the same.
Get.put(HomeCaptainController());
WidgetsBinding.instance.addPostFrameCallback((_) async {
closeOverlayIfFound();
@@ -46,19 +48,20 @@ class HomeCaptain extends StatelessWidget {
showDriverGiftClaim(context);
});
// التصميم الجديد: أصبح الـ build الرئيسي نظيفاً وواضحاً جداً
// The stack is now even simpler.
return Scaffold(
appBar: const _HomeAppBar(), // 1. تم فصل الـ AppBar في ويدجت خاصة
appBar: const _HomeAppBar(),
drawer: AppDrawer(),
body: Stack(
children: [
// كل جزء من الواجهة أصبح ويدجت منفصلة
const _MapView(), // 2. تم فصل الخريطة
const _DriverStatsOverlay(), // 3. تم فصل كارت الإحصائيات العلوي
const _DriverDurationOverlay(), // 4. تم فصل كارت مدة العمل
const _FloatingActionButtons(), // 5. تم فصل الأزرار الجانبية العائمة
const _ConnectButtonOverlay(), // 6. تم فصل زر الاتصال السفلي
leftMainMenuCaptainIcons(), // هذه بقيت كما هي
// 1. The Map View is the base layer.
const _MapView(),
// 2. The new floating "Status Pod" at the bottom.
const _StatusPodOverlay(),
// This widget from the original code remains.
leftMainMenuCaptainIcons(),
],
),
);
@@ -66,64 +69,51 @@ class HomeCaptain extends StatelessWidget {
}
// ==================================================================
// الأجزاء الصغيرة التي تم فصلها (Helper Widgets)
// هذه الويدجتس تحتوي على نفس كود التصميم الأصلي الخاص بك تماماً، ولكنها منظمة بشكل أفضل
// Redesigned Helper Widgets (V3)
// ==================================================================
/// 1. ويدجت الـ AppBar
/// 1. The AppBar now contains the map actions in a PopupMenuButton.
class _HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
const _HomeAppBar();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي للـ AppBar
final homeCaptainController = Get.find<HomeCaptainController>();
return AppBar(
elevation: 2,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.white, Colors.grey.shade50],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
),
],
),
),
backgroundColor: Colors.white,
elevation: 1,
shadowColor: Colors.black.withOpacity(0.1),
title: Row(
children: [
Image.asset(
'assets/images/logo.gif',
height: 32,
width: 35,
height: 35,
),
const SizedBox(width: 8),
const SizedBox(width: 10),
Text(
AppInformation.appName.split(' ')[0].toString().tr,
style: AppStyle.title.copyWith(
fontSize: 22,
fontWeight: FontWeight.w600,
fontSize: 24,
fontWeight: FontWeight.bold,
color: AppColor.blueColor,
),
),
],
),
actions: [
// Refuse count indicator
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: MyCircleContainer(
child: Text(
homeCaptainController.countRefuse.toString(),
style: AppStyle.title,
padding: const EdgeInsets.only(right: 8.0),
child: Center(
child: MyCircleContainer(
child: Text(
homeCaptainController.countRefuse.toString(),
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
),
),
),
),
// The new PopupMenuButton for all map and ride actions.
Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
@@ -165,28 +155,45 @@ class _HomeAppBar extends StatelessWidget implements PreferredSizeWidget {
],
),
),
const SizedBox(width: 8),
],
);
}
PopupMenuItem<String> _buildPopupMenuItem({
required String value,
IconData? icon,
Widget? iconWidget,
required String text,
Color? iconColor,
}) {
return PopupMenuItem<String>(
value: value,
child: Row(
children: [
iconWidget ?? Icon(icon, color: iconColor ?? Colors.grey.shade600),
const SizedBox(width: 16),
Text(text),
],
),
);
}
@override
Size get preferredSize => const Size.fromHeight(kToolbarHeight);
}
/// 2. ويدجت الخريطة
/// 2. The Map View is unchanged functionally.
class _MapView extends StatelessWidget {
const _MapView();
@override
Widget build(BuildContext context) {
final locationController = Get.find<LocationController>();
// نفس الكود الأصلي للخريطة
return GetBuilder<HomeCaptainController>(builder: (controller) {
return controller.isLoading
? const MyCircularProgressIndicator()
: GoogleMap(
padding: EdgeInsets.only(bottom: 50, top: 300),
padding: const EdgeInsets.only(bottom: 110, top: 300),
fortyFiveDegreeImageryEnabled: true,
onMapCreated: controller.onMapCreated,
minMaxZoomPreference: const MinMaxZoomPreference(6, 18),
@@ -217,7 +224,7 @@ class _MapView extends StatelessWidget {
myLocationEnabled: false,
trafficEnabled: controller.mapTrafficON,
buildingsEnabled: true,
mapToolbarEnabled: true,
mapToolbarEnabled: false,
compassEnabled: true,
zoomControlsEnabled: false,
);
@@ -225,353 +232,213 @@ class _MapView extends StatelessWidget {
}
}
/// 3. ويدجت كارت الإحصائيات العلوي
class _DriverStatsOverlay extends StatelessWidget {
const _DriverStatsOverlay();
/// 3. The floating "Status Pod" at the bottom of the screen.
class _StatusPodOverlay extends StatelessWidget {
const _StatusPodOverlay();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي لكارت الإحصائيات
return Positioned(
top: 5,
right: Get.width * .05,
left: Get.width * .05,
child: GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) {
return Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.white, Colors.white70],
),
borderRadius: BorderRadius.circular(15),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
width: Get.width * .8,
height: 120,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.greenColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
const Icon(
Entypo.wallet,
color: AppColor.greenColor,
size: 20,
),
const SizedBox(width: 8),
Text(
'${"Today".tr}: ${(homeCaptainController.totalMoneyToday)}',
style: AppStyle.title.copyWith(
color: AppColor.greenColor,
fontWeight: FontWeight.bold,
),
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColor.yellowColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Row(
children: [
const Icon(
Entypo.wallet,
color: AppColor.yellowColor,
size: 20,
),
const SizedBox(width: 8),
Text(
'${AppInformation.appName}: ${(homeCaptainController.totalMoneyInSEFER)}',
style: AppStyle.title.copyWith(
color: AppColor.yellowColor,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Total Points is'.tr}: ${(homeCaptainController.totalPoints)}',
style: AppStyle.title.copyWith(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: int.parse(
(homeCaptainController.countRideToday)) <
5
? AppColor.accentColor
: int.parse((homeCaptainController
.countRideToday)) >
5 &&
int.parse((homeCaptainController
.countRideToday)) <
10
? AppColor.yellowColor
: AppColor.greenColor,
),
child: Row(
children: [
const Icon(
Icons.directions_car_rounded,
color: Colors.white,
size: 18,
),
const SizedBox(width: 4),
Text(
'${"Ride Today : ".tr}: ${(homeCaptainController.countRideToday)}',
style: AppStyle.title.copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
],
),
),
],
),
]),
);
},
),
void _showDetailsDialog(BuildContext context) {
Get.dialog(
const _DriverDetailsDialog(),
barrierColor: Colors.black.withOpacity(0.3),
);
}
}
/// 4. ويدجت كارت مدة العمل
class _DriverDurationOverlay extends StatelessWidget {
const _DriverDurationOverlay();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي لكارت المدة
final homeCaptainController = Get.find<HomeCaptainController>();
return Positioned(
bottom: 65,
right: Get.width * .1,
left: Get.width * .1,
child: GetBuilder<HomeCaptainController>(
builder: (homeCaptainController) => Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
spreadRadius: 2,
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.timer_outlined, color: AppColor.greenColor),
const SizedBox(width: 8),
Text(
'Active Duration:'.tr,
style: AppStyle.title,
),
const SizedBox(width: 4),
Text(
(homeCaptainController.stringActiveDuration),
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.greenColor,
),
),
bottom: 16,
left: 16,
right: 16,
child: GestureDetector(
onTap: () => _showDetailsDialog(context),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.85),
borderRadius: BorderRadius.circular(24),
border: Border.all(color: Colors.white.withOpacity(0.5)),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
spreadRadius: -5,
)
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
child: Row(
children: [
const Icon(Icons.access_time, color: AppColor.accentColor),
const ConnectWidget(),
const Spacer(),
_buildQuickStat(
icon: Icons.directions_car_rounded,
value: homeCaptainController.countRideToday,
label: 'Rides'.tr,
color: AppColor.blueColor,
),
const SizedBox(width: 16),
_buildQuickStat(
icon: Entypo.wallet,
value: homeCaptainController.totalMoneyToday.toString(),
label: 'Today'.tr,
color: AppColor.greenColor,
),
const SizedBox(width: 8),
Text(
'Total Connection Duration:'.tr,
style: AppStyle.title,
),
const SizedBox(width: 4),
Text(
(homeCaptainController.totalDurationToday),
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.accentColor,
),
),
],
),
],
),
),
),
),
);
}
}
/// 5. ويدجت الأزرار الجانبية العائمة
class _FloatingActionButtons extends StatelessWidget {
const _FloatingActionButtons();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي للأزرار
return Positioned(
bottom: Get.height * .2,
right: 6,
child:
GetBuilder<HomeCaptainController>(builder: (homeCaptainController) {
return Column(
Widget _buildQuickStat(
{required IconData icon,
required String value,
required String label,
required Color color}) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Platform.isAndroid
? AnimatedContainer(
duration: const Duration(microseconds: 200),
width: homeCaptainController.widthMapTypeAndTraffic,
decoration: BoxDecoration(
border: Border.all(color: AppColor.blueColor),
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),
child: IconButton(
onPressed: () async {
Bubble().startBubbleHead(sendAppToBackground: true);
},
icon: Image.asset(
'assets/images/logo1.png',
fit: BoxFit.cover,
width: 35,
height: 35,
),
),
)
: const SizedBox(),
const SizedBox(
height: 5,
),
AnimatedContainer(
duration: const Duration(microseconds: 200),
width: homeCaptainController.widthMapTypeAndTraffic,
decoration: BoxDecoration(
border: Border.all(color: AppColor.blueColor),
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),
child: IconButton(
onPressed: () {
Get.to(() => const AvailableRidesPage());
},
icon: const Icon(
Icons.train_sharp,
size: 29,
color: AppColor.blueColor,
),
),
),
const SizedBox(
height: 5,
),
box.read(BoxName.rideStatus) == 'Applied' ||
box.read(BoxName.rideStatus) == 'Begin'
? Positioned(
bottom: Get.height * .2,
right: 6,
child: AnimatedContainer(
duration: const Duration(microseconds: 200),
width: homeCaptainController.widthMapTypeAndTraffic,
decoration: BoxDecoration(
border: Border.all(color: AppColor.blueColor),
color: AppColor.secondaryColor,
borderRadius: BorderRadius.circular(15)),
child: GestureDetector(
onLongPress: () {
box.write(BoxName.rideStatus, 'delete');
homeCaptainController.update();
},
child: IconButton(
onPressed: () {
box.read(BoxName.rideStatus) == 'Applied'
? {
Get.to(() => PassengerLocationMapPage(),
arguments:
box.read(BoxName.rideArguments)),
Get.put(MapDriverController())
.changeRideToBeginToPassenger()
}
: {
Get.to(() => PassengerLocationMapPage(),
arguments:
box.read(BoxName.rideArguments)),
Get.put(MapDriverController())
.startRideFromStartApp()
};
},
icon: const Icon(
Icons.directions_rounded,
size: 29,
color: AppColor.blueColor,
),
),
),
),
)
: const SizedBox()
Icon(icon, color: color, size: 20),
const SizedBox(width: 4),
Text(value,
style: AppStyle.title
.copyWith(fontSize: 16, fontWeight: FontWeight.bold)),
],
);
}),
),
Text(label,
style: AppStyle.title
.copyWith(fontSize: 12, color: Colors.grey.shade700)),
],
);
}
}
/// 6. ويدجت زر الاتصال السفلي
class _ConnectButtonOverlay extends StatelessWidget {
const _ConnectButtonOverlay();
/// 4. The Dialog that shows detailed driver stats.
class _DriverDetailsDialog extends StatelessWidget {
const _DriverDetailsDialog();
@override
Widget build(BuildContext context) {
// نفس الكود الأصلي لزر الاتصال
return Positioned(
bottom: 10,
right: Get.width * .1,
left: Get.width * .1,
child: const ConnectWidget());
final homeCaptainController = Get.find<HomeCaptainController>();
return BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: AlertDialog(
backgroundColor: Colors.white.withOpacity(0.95),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
titlePadding: const EdgeInsets.only(top: 20),
title: Center(
child: Text(
'Your Activity'.tr,
style: AppStyle.title
.copyWith(fontSize: 20, fontWeight: FontWeight.bold),
),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Divider(height: 20),
_buildStatRow(
icon: Entypo.wallet,
color: AppColor.greenColor,
label: 'Today'.tr,
value: homeCaptainController.totalMoneyToday.toString(),
),
const SizedBox(height: 12),
_buildStatRow(
icon: Entypo.wallet,
color: AppColor.yellowColor,
label: AppInformation.appName,
value: homeCaptainController.totalMoneyInSEFER.toString(),
),
const Divider(height: 24),
_buildDurationRow(
icon: Icons.timer_outlined,
label: 'Active Duration:'.tr,
value: homeCaptainController.stringActiveDuration,
color: AppColor.greenColor,
),
const SizedBox(height: 12),
_buildDurationRow(
icon: Icons.access_time,
label: 'Total Connection Duration:'.tr,
value: homeCaptainController.totalDurationToday,
color: AppColor.accentColor,
),
const Divider(height: 24),
_buildStatRow(
icon: Icons.star_border_rounded,
color: AppColor.blueColor,
label: 'Total Points'.tr,
value: homeCaptainController.totalPoints.toString(),
),
],
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('Close'.tr,
style: AppStyle.title.copyWith(
color: AppColor.blueColor, fontWeight: FontWeight.bold)),
)
],
),
);
}
Widget _buildStatRow(
{required IconData icon,
required Color color,
required String label,
required String value}) {
return Row(
children: [
Icon(icon, color: color, size: 22),
const SizedBox(width: 12),
Text('$label:', style: AppStyle.title),
const Spacer(),
Text(
value,
style: AppStyle.title.copyWith(
color: color, fontWeight: FontWeight.bold, fontSize: 18),
),
],
);
}
Widget _buildDurationRow(
{required IconData icon,
required String label,
required String value,
required Color color}) {
return Row(
children: [
Icon(icon, color: color, size: 20),
const SizedBox(width: 12),
Text(label, style: AppStyle.title),
const Spacer(),
Text(
value,
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold, color: color, fontSize: 16),
),
],
);
}
}
// هذه الويدجت المساعدة بقيت كما هي
class _MapControlButton extends StatelessWidget {
final IconData icon;
final VoidCallback onPressed;
@@ -606,7 +473,44 @@ class _MapControlButton extends StatelessWidget {
}
}
// الدوال المساعدة الأخرى تبقى كما هي في ملفك...
// showFirstTimeOfferNotification(BuildContext context) async { ... }
// bool _checkIfFirstTime() { ... }
// void _markAsNotFirstTime() { ... }
/// NOTE: The _FloatingActionButtons and _MapControlButton widgets have been removed
/// as their functionality is now integrated into the _HomeAppBar.
///
/// You will still need to modify your existing `ConnectWidget`
/// to accept an `isCompact` boolean flag as mentioned in the previous design.
/*
class ConnectWidget extends StatelessWidget {
final bool isCompact;
const ConnectWidget({super.key, this.isCompact = false});
@override
Widget build(BuildContext context) {
// ... your existing controller logic
if (isCompact) {
// Return a smaller version for the pod
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: controller.isConnect ? AppColor.greenColor : AppColor.accentColor,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(controller.isConnect ? Icons.wifi_tethering_rounded : Icons.wifi_tethering_off_rounded, color: Colors.white, size: 20),
const SizedBox(width: 8),
Text(
controller.isConnect ? 'Online'.tr : 'Offline'.tr,
style: AppStyle.title.copyWith(color: Colors.white, fontSize: 14),
),
],
),
);
}
// Return the original, larger button
return ElevatedButton.icon(...)
}
}
*/

View File

@@ -1,6 +1,7 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/local_notification.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/auth/captin/login_captin.dart';
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
import 'package:sefer_driver/views/home/Captin/orderCaptin/vip_order_page.dart';
import 'package:flutter/material.dart';
@@ -14,6 +15,7 @@ import '../../../../../constant/links.dart';
import '../../../../../controller/firebase/firbase_messge.dart';
import '../../../../../controller/functions/crud.dart';
import '../../../../../controller/home/captin/order_request_controller.dart';
import '../../../../../controller/home/navigation/navigation_view.dart';
import '../../../../Rate/ride_calculate_driver.dart';
import '../../../../auth/syria/registration_view.dart';
@@ -156,18 +158,42 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
child: Builder(builder: (context) {
return IconButton(
onPressed: () async {
Get.to(() => const RegistrationView());
box.remove(BoxName.agreeTerms);
Get.to(() => const NavigationView());
// box.write(BoxName.statusDriverLocation, 'off');
},
icon: const Icon(
FontAwesome5.grin_tears,
FontAwesome5.map,
size: 29,
color: AppColor.blueColor,
),
);
}),
),
// AnimatedContainer(
// duration: const Duration(microseconds: 200),
// width: controller.widthMapTypeAndTraffic,
// decoration: BoxDecoration(
// color: AppColor.secondaryColor,
// border: Border.all(color: AppColor.blueColor),
// borderRadius: BorderRadius.circular(15)),
// child: Builder(builder: (context) {
// return IconButton(
// onPressed: () async {
// box.remove(BoxName.agreeTerms);
// Get.to(() => const NavigationView());
// // box.write(BoxName.statusDriverLocation, 'off');
// },
// icon: const Icon(
// FontAwesome5.grin_tears,
// size: 29,
// color: AppColor.blueColor,
// ),
// );
// }),
// ),
const SizedBox(
height: 5,

View File

@@ -204,7 +204,7 @@ GetBuilder<MapDriverController> speedCircle() {
? Positioned(
// New: تم وضع دائرة السرعة في الأسفل يمينًا
bottom: 25,
left: 16,
left: 3,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,

View File

@@ -1,117 +1,3 @@
// import 'package:flutter/material.dart';
// import 'package:get/get.dart';
// import 'package:google_maps_flutter/google_maps_flutter.dart';
// import '../../../../controller/functions/location_controller.dart';
// import '../../../../controller/home/captin/map_driver_controller.dart';
// class GoogleDriverMap extends StatelessWidget {
// const GoogleDriverMap({
// super.key,
// required this.locationController,
// });
// final LocationController locationController;
// @override
// Widget build(BuildContext context) {
// Get.put(MapDriverController());
// return Padding(
// padding: const EdgeInsets.all(8.0),
// child: GetBuilder<MapDriverController>(
// builder: (controller) => Column(
// children: [
// SizedBox(
// height: Get.height * .92,
// child: GoogleMap(
// onMapCreated: controller.onMapCreated,
// zoomControlsEnabled: true,
// // initialCameraPosition: CameraPosition(
// // target: locationController.myLocation,
// // zoom: 13,
// // bearing: locationController.heading,
// // tilt: 40,
// // ),
// initialCameraPosition: CameraPosition(
// target: locationController.myLocation,
// zoom: 17,
// bearing: locationController.heading, // استخدام اتجاه السائق
// tilt: 60, // زاوية ميل
// ),
// cameraTargetBounds:
// CameraTargetBounds.unbounded, // Allow unrestricted movement
// onCameraMove: (position) {
// CameraPosition(
// target: locationController.myLocation,
// zoom: 13,
// bearing: locationController.heading,
// tilt: 40,
// );
// //todo
// // locationController.myLocation = position.target;
// //
// // controller.mapController
// // ?.animateCamera(CameraUpdate.newCameraPosition(position));
// },
// minMaxZoomPreference: const MinMaxZoomPreference(8, 15),
// myLocationEnabled: true,
// myLocationButtonEnabled: true,
// compassEnabled: true,
// mapType: MapType.terrain,
// rotateGesturesEnabled: true,
// scrollGesturesEnabled: true,
// trafficEnabled: false,
// buildingsEnabled: true,
// mapToolbarEnabled: true,
// fortyFiveDegreeImageryEnabled: true,
// zoomGesturesEnabled: true,
// polylines: {
// Polyline(
// zIndex: 2,
// geodesic: true,
// polylineId: const PolylineId('route1'),
// points: controller.polylineCoordinates,
// color: const Color.fromARGB(255, 163, 81, 246),
// width: 5,
// ),
// Polyline(
// zIndex: 2,
// geodesic: true,
// polylineId: const PolylineId('route'),
// points: controller.polylineCoordinatesDestination,
// color: const Color.fromARGB(255, 10, 29, 126),
// width: 5,
// ),
// },
// markers: {
// Marker(
// markerId: MarkerId('MyLocation'.tr),
// position: locationController.myLocation,
// draggable: true,
// icon: controller.carIcon,
// rotation: locationController.heading,
// ),
// Marker(
// markerId: MarkerId('start'.tr),
// position: controller.latLngPassengerLocation,
// draggable: true,
// icon: controller.startIcon,
// ),
// Marker(
// markerId: MarkerId('end'.tr),
// position: controller.latLngPassengerDestination,
// draggable: true,
// icon: controller.endIcon,
// ),
// },
// ),
// ),
// ],
// ),
// ),
// );
// }
// }
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
@@ -179,15 +65,30 @@ class GoogleDriverMap extends StatelessWidget {
startCap: Cap.roundCap,
endCap: Cap.roundCap,
),
Polyline(
zIndex: 2,
// Polyline(
// zIndex: 2,
polylineId: const PolylineId('route'),
points: controller.polylineCoordinatesDestination,
color: const Color.fromARGB(255, 10, 29, 126),
width: 6, // Changed: زيادة عرض الخط
startCap: Cap.roundCap,
endCap: Cap.roundCap,
// polylineId: const PolylineId('route'),
// points: controller.polylineCoordinatesDestination,
// color: const Color.fromARGB(255, 10, 29, 126),
// width: 6, // Changed: زيادة عرض الخط
// startCap: Cap.roundCap,
// endCap: Cap.roundCap,
// ),
Polyline(
polylineId: const PolylineId('upcoming_route'),
points: controller.upcomingPathPoints,
color: Colors.blue, // أو أي لون آخر تختاره للمسار
width: 8,
zIndex: 2,
),
// 2. الخط المقطوع (تحت)
Polyline(
polylineId: const PolylineId('traveled_route'),
points: controller.traveledPathPoints,
color: Colors.grey.withOpacity(0.8),
width: 7,
zIndex: 1,
),
},
markers: {

View File

@@ -14,7 +14,7 @@ class GoogleMapApp extends StatelessWidget {
return GetBuilder<MapDriverController>(
builder: (mapDriverController) => mapDriverController.isRideStarted
? Positioned(
left: 150,
right: 3,
bottom: 20,
child: Container(
decoration: AppStyle.boxDecoration,

View File

@@ -144,6 +144,11 @@ class PassengerInfoWindow extends StatelessWidget {
borderRadius: BorderRadius.circular(10)),
),
onPressed: () async {
controller.getRoute(
origin: controller.latLngPassengerLocation,
destination: controller.latLngPassengerDestination,
routeColor: Colors.blue // أو أي لون
);
if (await controller
.calculateDistanceBetweenDriverAndPassengerLocation() <
140) {

View File

@@ -274,13 +274,18 @@ class _OrderRequestPageState extends State<OrderRequestPage> {
controller.myList[8].toString(),
controller.myList[9].toString(),
];
FirebaseMessagesController()
.sendNotificationToPassengerToken(
"Accepted Ride".tr,
'your ride is Accepted'.tr,
controller.myList[9].toString(),
bodyToPassenger,
'start.wav');
final fmc =
Get.isRegistered<FirebaseMessagesController>()
? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController());
fmc.sendNotificationToDriverMAP(
"Accepted Ride".tr,
'your ride is Accepted'.tr,
controller.myList[9].toString(),
bodyToPassenger,
'start.wav',
);
Get.back();
box.write(BoxName.rideArguments, {
'passengerLocation':

View File

@@ -95,7 +95,7 @@ class CardSeferWalletDriver extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'محفظة انطلق',
'رصيد انطلق',
style: AppStyle.headTitle.copyWith(
fontFamily: 'Amiri', // خط يوحي بالفخامة
color: Colors.white,

View File

@@ -1,9 +1,9 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:sefer_driver/constant/style.dart'; // Assuming this has your text styles
import 'package:sefer_driver/views/widgets/mycircular.dart'; // Assuming this is your loading widget
import '../../../controller/payment/driver_payment_controller.dart';
@@ -12,43 +12,133 @@ class PaymentHistoryDriverPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Initialize your controller
Get.put(DriverWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<DriverWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: ListView.builder(
itemCount: controller.archive.length,
itemBuilder: (BuildContext context, int index) {
var list = controller.archive[index];
return Padding(
padding: const EdgeInsets.all(4),
child: Container(
decoration: BoxDecoration(
color: double.parse(list['amount']) < 0
? AppColor.redColor.withOpacity(.4)
: AppColor.greenColor.withOpacity(.4)),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
list['amount'],
style: AppStyle.title,
),
Text(
list['created_at'],
style: AppStyle.title,
),
],
),
),
);
},
return Scaffold(
appBar: AppBar(
title: Text('Payment History'.tr),
backgroundColor: Colors.white,
elevation: 1,
),
backgroundColor: Colors.grey[100],
body: GetBuilder<DriverWalletHistoryController>(
builder: (controller) {
if (controller.isLoading) {
// Using your custom loading indicator
return const Center(child: MyCircularProgressIndicator());
}
if (controller.archive.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.account_balance_wallet_outlined,
size: 80, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No transactions yet'.tr,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: Colors.grey[600]),
),
)
],
isleading: true);
],
),
);
}
return AnimationLimiter(
child: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: controller.archive.length,
itemBuilder: (BuildContext context, int index) {
var transaction = controller.archive[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: _TransactionCard(transaction: transaction),
),
),
);
},
),
);
},
),
);
}
}
// A dedicated widget for displaying a single transaction with a modern UI.
class _TransactionCard extends StatelessWidget {
final Map<String, dynamic> transaction;
const _TransactionCard({required this.transaction});
@override
Widget build(BuildContext context) {
// Safely parse the amount to avoid errors
final double amount =
double.tryParse(transaction['amount']?.toString() ?? '0') ?? 0;
final bool isCredit = amount >= 0;
final Color indicatorColor =
isCredit ? AppColor.greenColor : AppColor.redColor;
final IconData iconData =
isCredit ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded;
final String transactionType = (isCredit ? 'Credit'.tr : 'Debit'.tr).tr;
return Card(
elevation: 2,
shadowColor: Colors.black.withOpacity(0.05),
margin: const EdgeInsets.only(bottom: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
clipBehavior: Clip.antiAlias, // Ensures the color bar is clipped neatly
child: IntrinsicHeight(
// Ensures the color bar and content have the same height
child: Row(
children: [
// Left-side color indicator bar
Container(width: 6, color: indicatorColor),
Expanded(
child: ListTile(
leading: Icon(iconData, color: indicatorColor, size: 30),
title: Text(
// Use .abs() to remove the negative sign from the display
'${amount.abs().toStringAsFixed(2)} ${'SYP'.tr}',
style: AppStyle.title.copyWith(
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
subtitle: Text(
transaction['created_at'] ?? 'No date',
style: AppStyle.title.copyWith(
fontSize: 12,
color: Colors.grey[600],
),
),
trailing: Text(
transactionType,
style: AppStyle.title.copyWith(
fontSize: 14,
color: indicatorColor,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
);
}
}

View File

@@ -47,7 +47,7 @@ class WalletCaptainRefactored extends StatelessWidget {
Widget build(BuildContext context) {
captainWalletController.refreshCaptainWallet();
return MyScafolld(
title: 'Driver Wallet'.tr,
title: 'Driver Balance'.tr,
isleading: true,
action: IconButton(
icon: const Icon(Icons.refresh),
@@ -173,7 +173,7 @@ class WalletCaptainRefactored extends StatelessWidget {
'This amount for all trip I get from Passengers'
.tr),
child: const Icon(Icons.headphones)),
'${'Total Amount:'.tr} ${controller.totalAmount} ${'S.P'.tr}',
'${'Total Amount:'.tr} ${controller.totalAmount} ${'SYP'.tr}',
'This amount for all trip I get from Passengers'.tr,
duration: const Duration(seconds: 6),
backgroundColor: AppColor.yellowColor,
@@ -195,7 +195,7 @@ class WalletCaptainRefactored extends StatelessWidget {
' Intaleq Wallet'.tr),
child: const Icon(Icons.headphones),
),
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'S.P'.tr}',
'${'Total Amount:'.tr} ${controller.totalAmountVisa} ${'SYP'.tr}',
'This amount for all trip I get from Passengers and Collected For me in'
.tr +
' ${AppInformation.appName} Wallet'.tr,

View File

@@ -1,11 +1,11 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/views/widgets/my_scafold.dart';
import 'package:sefer_driver/views/widgets/mycircular.dart';
import 'package:intl/intl.dart';
import '../../../controller/payment/driver_payment_controller.dart';
class WeeklyPaymentPage extends StatelessWidget {
@@ -14,128 +14,210 @@ class WeeklyPaymentPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
Get.put(DriverWalletHistoryController());
return MyScafolld(
title: 'Payment History'.tr,
body: [
GetBuilder<DriverWalletHistoryController>(
builder: (controller) => controller.isLoading
? const MyCircularProgressIndicator()
: Column(
children: [
Container(
width: Get.width * .8,
decoration: AppStyle.boxDecoration1,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
controller.weeklyList.isEmpty
? '0'
: controller.weeklyList[0]
['totalAmount']
.toString(),
style: AppStyle.number,
),
return Scaffold(
appBar: AppBar(
title: Text('Weekly Summary'.tr),
backgroundColor: Colors.white,
elevation: 1,
),
backgroundColor: Colors.grey[100],
body: GetBuilder<DriverWalletHistoryController>(
builder: (controller) {
if (controller.isLoading) {
return const Center(child: MyCircularProgressIndicator());
}
return Column(
children: [
// 1. Prominent Summary Card at the top
_buildSummaryCard(controller),
// 2. A title for the transactions list
Padding(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 10),
child: Row(
children: [
Icon(Icons.list_alt, color: Colors.grey[600]),
const SizedBox(width: 8),
Text(
'Transactions this week'.tr,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.grey[700],
),
),
],
),
),
// 3. The animated list of transactions
Expanded(
child: controller.weeklyList.isEmpty
? _buildEmptyState(context)
: AnimationLimiter(
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
itemCount: controller.weeklyList.length,
itemBuilder: (BuildContext context, int index) {
var transaction = controller.weeklyList[index];
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 375),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: _TransactionListItem(
transaction: transaction),
),
),
),
Text(
' Total weekly is '.tr,
style: AppStyle.title,
),
],
);
},
),
),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
child: SizedBox(
height: Get.height * .75,
child: controller.weeklyList.isNotEmpty
? ListView.builder(
itemCount: controller.weeklyList.length,
itemBuilder:
(BuildContext context, int index) {
var list = controller.weeklyList[index];
return Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: AppStyle.boxDecoration1,
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
children: [
Card(
elevation: 2,
color: list['paymentMethod'] ==
'visa'
? AppColor.blueColor
: AppColor.secondaryColor,
child: Padding(
padding:
const EdgeInsets.all(8.0),
child: Text(
list['paymentMethod'] ==
'Remainder'
? 'Remainder'.tr
: list['paymentMethod'] ==
'fromBudget'
? 'fromBudget'.tr
: list[
'paymentMethod'],
style: AppStyle.title,
),
),
),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Card(
child: Padding(
padding:
const EdgeInsets.all(
8.0),
child: Text(
list['amount'],
style: AppStyle.number,
),
),
),
Text(
DateFormat(
'yyyy-MM-dd hh:mm a')
.format(DateTime.parse(
list[
'dateUpdated'])),
style: AppStyle.number,
),
],
),
],
),
),
),
);
},
)
: const SizedBox(),
),
),
],
),
],
);
},
),
);
}
// A widget for the top summary card.
Widget _buildSummaryCard(DriverWalletHistoryController controller) {
final String totalAmount = controller.weeklyList.isEmpty
? '0.00'
: controller.weeklyList[0]['totalAmount']?.toString() ?? '0.00';
return Card(
margin: const EdgeInsets.all(16.0),
elevation: 4,
shadowColor: AppColor.primaryColor.withOpacity(0.2),
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Container(
padding: const EdgeInsets.all(20.0),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColor.primaryColor, AppColor.greenColor],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Row(
children: [
const Icon(Icons.account_balance_wallet,
color: Colors.white, size: 40),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Total Weekly Earnings'.tr,
style: AppStyle.title
.copyWith(color: Colors.white70, fontSize: 16),
),
)
const SizedBox(height: 4),
Text(
'$totalAmount ${'SYP'.tr}',
style: AppStyle.number
.copyWith(color: Colors.white, fontSize: 32),
),
],
),
),
],
),
),
);
}
// A dedicated widget for the list item.
Widget _TransactionListItem({required Map<String, dynamic> transaction}) {
final String paymentMethod = transaction['paymentMethod'] ?? 'Unknown';
final String amount = transaction['amount']?.toString() ?? '0';
final DateTime? date = DateTime.tryParse(transaction['dateUpdated'] ?? '');
return Card(
elevation: 2,
shadowColor: Colors.black.withOpacity(0.05),
margin: const EdgeInsets.only(bottom: 12.0),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: ListTile(
leading: CircleAvatar(
backgroundColor: AppColor.primaryColor.withOpacity(0.1),
child: Icon(_getPaymentIcon(paymentMethod),
color: AppColor.primaryColor, size: 22),
),
title: Text(
'$amount ${'SYP'.tr}',
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
),
subtitle: Text(
date != null
? DateFormat('EEEE, hh:mm a', Get.locale?.toString())
.format(date) // e.g., Tuesday, 10:11 AM
: 'Invalid Date',
style: AppStyle.title.copyWith(fontSize: 12, color: Colors.grey[600]),
),
trailing: Chip(
label: Text(
_getTranslatedPaymentMethod(paymentMethod),
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600),
),
backgroundColor: Colors.grey.shade200,
padding: const EdgeInsets.symmetric(horizontal: 6),
side: BorderSide.none,
),
),
);
}
Widget _buildEmptyState(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.receipt_long_outlined, size: 80, color: Colors.grey[400]),
const SizedBox(height: 16),
Text(
'No transactions this week'.tr,
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: Colors.grey[600]),
),
],
isleading: true);
),
);
}
// Helper to get a specific icon for each payment method.
IconData _getPaymentIcon(String paymentMethod) {
switch (paymentMethod.toLowerCase()) {
case 'visa':
return Icons.credit_card;
case 'frombudget':
return Icons.account_balance_wallet_outlined;
case 'remainder':
return Icons.receipt_long;
case 'cash':
return Icons.money;
default:
return Icons.payment;
}
}
// Helper to get translated or formatted payment method names.
String _getTranslatedPaymentMethod(String paymentMethod) {
switch (paymentMethod) {
case 'Remainder':
return 'Remainder'.tr;
case 'fromBudget':
return 'From Budget'.tr;
default:
return paymentMethod;
}
}
}