import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_lucide/flutter_lucide.dart'; import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import 'package:geolocator/geolocator.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_svg/svg.dart'; import '../../config/AppSnackbar.dart'; import '../../config/constants/app_constant.dart'; import '../../config/custom_text.dart'; import '../../config/language/language_enum.dart'; import '../../models/ride_model.dart'; import '../../providers/language_provider.dart'; import '../../providers/utils_singleton.dart'; import '../../utils/colors.dart'; import '../../widgets/custom_button.dart'; import '../../widgets/trip_rating_card.dart'; class RideDetailClientScreen extends StatefulWidget { final RideDto ride; final String driverName; final String driverImage; final Function(int) onRatingSelected; final Function(int) onRateRider; final bool isBottomSheet; final double systembottompadding; final VoidCallback? onClose; const RideDetailClientScreen({ super.key, required this.ride, required this.driverName, required this.driverImage, required this.onRatingSelected, required this.onRateRider, required this.systembottompadding, this.isBottomSheet = false, this.onClose, }); @override State createState() => _RideDetailClientScreenState(); } class _RideDetailClientScreenState extends State { bool _showRatingCard = false; // Future calculateDistanceFromPolyline(String encodedPolyline) async { // PolylinePoints polylinePoints = PolylinePoints(); // List points = polylinePoints.decodePolyline(encodedPolyline); // double totalDistance = 0.0; // // for (int i = 0; i < points.length - 1; i++) { // final start = points[i]; // final end = points[i + 1]; // totalDistance += Geolocator.distanceBetween( // start.latitude, // start.longitude, // end.latitude, // end.longitude, // ); // } // // return totalDistance / 1000; // } @override Widget build(BuildContext context) { final languageProvider = context.watch(); bool isArabic = languageProvider.currentLangCode == Language.ar; // If rating card is shown, only show the rating card if (_showRatingCard) { return Container( decoration: BoxDecoration( color: widget.isBottomSheet ? Colors.black.withOpacity(0.5) : Colors.white, borderRadius: widget.isBottomSheet ? const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ) : null, ), child: Center( child: TripRatingCard( systembottompadding: widget.systembottompadding, driverName: widget.driverName, driverImage: widget.driverImage, onRatingSelected: widget.onRatingSelected, onRateRider: (rating) async { await widget.onRateRider(rating); setState(() { _showRatingCard = false; }); if (widget.onClose != null) { widget.onClose!(); } else if (!widget.isBottomSheet) { Navigator.pop(context); } }, ), ), ); } // Main content (only shown when rating card is not visible) Widget content = Container( decoration: BoxDecoration( color: Colors.white, borderRadius: widget.isBottomSheet ? const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ) : null, ), child: SingleChildScrollView( padding: EdgeInsets.all(widget.isBottomSheet ? 20 : 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header - only show if not bottom sheet if (!widget.isBottomSheet) Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( onTap: () => Navigator.pop(context), child: Container( width: 32, height: 32, margin: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( color: const Color(0xffF5F5F5), borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: AppNewColors.detailsColors.withOpacity(.1), blurRadius: 1, offset: const Offset(2, 1), ), ], ), child: const Icon( Icons.arrow_back, color: AppNewColors.detailsColors, size: 16, ), ), ), Expanded( child: Center( child: Txt( text: 'Ride details', style: TextStyle( color: const Color(0xFF0A0C11), fontSize: 18, fontFamily: isArabic ? "Readex_Pro" : 'Inter', fontWeight: FontWeight.w600, height: 1.23, ), ), ), ), const SizedBox(width: 32), ], ), ), // Bottom sheet header if (widget.isBottomSheet) Column( children: [ Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(2), ), ), const SizedBox(height: 8), const Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox(width: 8), Txt( text: "Ride summary!", style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black, ), ), ], ), const SizedBox(height: 20), ], ), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).brightness == Brightness.dark ? AppNewColors.white : AppNewColors.detailsColors, width: 0.5, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), // Pick up and Drop off locations _locationItem( "Pick up", widget.ride.startLocationName ?? '', ), const SizedBox(height: 12), _locationItem( "Drop off", widget.ride.endLocationName ?? '', ), const SizedBox(height: 10), const Divider(thickness: 0.3), const SizedBox(height: 10), // Trip timing info _timeInfoRow( LucideIcons.calendar, "Date", _formatDate(widget.ride.rideStartTime), ), const SizedBox(height: 16), _timeInfoRow( Icons.access_time, "Depart", _formatTime(widget.ride.rideStartTime), ), const SizedBox(height: 16), _timeInfoRow( Icons.access_time, "Arrival", _formatTime( widget.ride.rideStartTime?.add( Duration( minutes: widget.ride.durationInMinutes?.round() ?? 0, ), ), ), ), ], ), ), const SizedBox(height: 10), // Driver Info Section Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).brightness == Brightness.dark ? AppNewColors.white : AppNewColors.detailsColors, width: 0.5, ), ), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(30), child: CachedNetworkImage( imageUrl: widget.driverImage.startsWith('http') ? widget.driverImage : "${mediaUrl ?? ''}${widget.driverImage}", width: 50, height: 50, fit: BoxFit.cover, placeholder: (context, url) => Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(25), ), child: const Icon(Icons.person, color: Colors.grey, size: 24), ), errorWidget: (context, url, error) => Container( width: 50, height: 50, decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(25), ), child: const Icon(Icons.person, color: Colors.grey, size: 24), ), ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Txt( text: widget.driverName, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), const SizedBox(height: 4), Txt( text: "${widget.ride.drivercarmodel ?? 'Car'} • ${widget.ride.licenseplate ?? 'License'}", style: const TextStyle( fontSize: 13, color: Colors.grey, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), const SizedBox(height: 6), Row( children: [ ...List.generate(5, (index) { return Icon( Icons.star, size: 14, color: index < (widget.ride.averagedriverrating ?? 4).floor() ? Colors.amber : Colors.grey[300], ); }), const SizedBox(width: 6), Txt( text: widget.ride.averagedriverrating?.toStringAsFixed(1) ?? '4.0', style: const TextStyle( fontSize: 13, color: Colors.grey, fontWeight: FontWeight.w500, ), ), ], ), ], ), ), ], ), ), const SizedBox(height: 10), // Pricing Section Container( width: double.infinity, padding: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), border: Border.all( color: Theme.of(context).brightness == Brightness.dark ? AppNewColors.white : AppNewColors.detailsColors, width: 0.5, ), ), child: Column( children: [ _priceRow( "Total Amount", "${widget.ride.totalPrice?.toStringAsFixed(2) ?? '24.00'} TND", isMainPrice: true, ), const SizedBox(height: 16), _priceRow( "Coupon", "${widget.ride.couponvalue!=null ? widget.ride.couponvalue!.toStringAsFixed(2) : '0.0'} TND", ), const SizedBox(height: 8), _priceRow( "Distance", "${widget.ride.distancekm.toStringAsFixed(1) ?? '0.0'} km", ), const SizedBox(height: 8), _priceRow( "Duration", "${widget.ride.durationInMinutes?.round() ?? 0} min", ), const SizedBox(height: 8), _priceRow( "Payment Method", widget.ride.paymenttype ?? "Cash", ), if (widget.ride.couponpercentage != null && widget.ride.couponpercentage! > 0) ...[ const SizedBox(height: 8), _priceRow( "Discount (${widget.ride.couponpercentage?.toStringAsFixed(0)}%)", "-${widget.ride.couponvalue?.toStringAsFixed(2)} TND", ), ], ], ), ), const SizedBox(height: 18), // Rate Driver Button CustomButton( onClick: () { setState(() { _showRatingCard = true; }); }, fontSize: 16, width: double.infinity, height: 50, borderRadius: 12, txtColor: Colors.white, title: 'Rate Driver', backgroundColor: AppNewColors.primaryBtnColor, borderColor: AppNewColors.primaryBtnColor, ), SizedBox(height: 20+ widget.systembottompadding), ], ), ), ); if (widget.isBottomSheet) { return content; } else { return SafeArea( child: Scaffold( backgroundColor: Colors.white, body: content, ), ); } } Widget _locationItem(String label, String location) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Txt( text: label, style: const TextStyle( color: AppNewColors.detailsColors, fontSize: 12, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), decoration: BoxDecoration( color: Theme.of(context).brightness == Brightness.dark ? Colors.grey[800] : const Color(0xffF9F9FA), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Icon( LucideIcons.map_pin, size: 18, color: AppNewColors.detailsColors, ), const SizedBox(width: 8), Expanded( child: Txt( text: location, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ), ], ), ), ], ); } Widget _timeInfoRow(IconData icon, String label, String value) { return Row( children: [ Icon( icon, color: AppNewColors.detailsColors, size: 18, ), const SizedBox(width: 10), Expanded( flex: 2, child: Txt( text: label, style: const TextStyle( color: AppNewColors.detailsColors, fontSize: 14, ), ), ), Expanded( flex: 3, child: Txt( text: value, style: TextStyle( fontSize: 14, color: Theme.of(context).brightness == Brightness.dark ? AppNewColors.white : AppNewColors.black, fontWeight: FontWeight.w600, ), textAlign: TextAlign.end, overflow: TextOverflow.ellipsis, ), ), ], ); } Widget _priceRow(String label, String value, {bool isMainPrice = false}) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, child: Txt( text: label, style: TextStyle( fontSize: isMainPrice ? 15 : 13, fontWeight: isMainPrice ? FontWeight.w600 : FontWeight.normal, color: AppNewColors.detailsSColors, ), overflow: TextOverflow.ellipsis, maxLines: 2, ), ), const SizedBox(width: 8), Expanded( child: Txt( text: value, style: TextStyle( fontSize: isMainPrice ? 16 : 13, fontWeight: FontWeight.w600, color: AppNewColors.detailsSColors, ), textAlign: TextAlign.end, overflow: TextOverflow.ellipsis, maxLines: 1, ), ), ], ); } String _formatDate(DateTime? dateTime) { if (dateTime == null) return 'Fri 18 Aug'; return DateFormat('E d MMM').format(dateTime); } String _formatTime(DateTime? dateTime) { if (dateTime == null) return '06:00 AM'; return DateFormat('hh:mm a').format(dateTime); } }