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 '../Provider/RideProvider.dart'; import '../Provider/language_provider.dart'; import '../Provider/utils_singleton.dart'; import '../config/AppSnackbar.dart'; import '../config/constants/app_constant.dart'; import '../config/language/language_enum.dart'; import '../models/Ride.dart'; import '../utils/colors.dart'; import '../widgets/callPhone.dart'; import '../widgets/custom_button.dart'; import '../widgets/custom_text.dart'; class RideDetailScreen extends StatefulWidget { final Ride ride; const RideDetailScreen({super.key, required this.ride}); @override State createState() => _RideDetailScreenState(); } class _RideDetailScreenState extends State { late RideProvider _rideProvider; 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, ); } // Convert meters to kilometers return totalDistance / 1000; } @override void initState() { super.initState(); _rideProvider = context.read(); } void _onNavigationClicked(double lat, double lng) async { final uri = Uri.parse( 'https://www.google.com/maps/dir/?api=1&destination=$lat,$lng&travelmode=driving'); if (await canLaunchUrl(uri)) await launchUrl(uri); } @override Widget build(BuildContext context) { final languageProvider = context.watch(); bool isArabic = languageProvider.currentLangCode == Language.ar; return Scaffold( backgroundColor: Theme.of(context).brightness == Brightness.dark ? AppColors.blackBackgroundColor : AppColors.whiteBackgroundColor, body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), 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: AppColors.black.withOpacity(.1), blurRadius: 1, offset: const Offset(2, 1), ), ], ), child: const Icon( Icons.arrow_back, color: AppColors.black, size: 16, ), ), ), Expanded( child: Center( child: Txt( text: 'Ride details', style: TextStyle( color: Theme.of(context).brightness == Brightness.dark ? Colors.white : const Color(0xFF0A0C11), fontSize: 18, fontFamily: isArabic ? "Readex_Pro" : 'Inter', fontWeight: FontWeight.w600, height: 1.23, ), ), ), ), const SizedBox(width: 32), ], ), ), // Departure Section Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( // color: Theme.of(context).brightness == Brightness.dark // ? Colors.grey[800] // : Colors.grey[100], borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).brightness == Brightness.dark ? AppColors.whiteBackgroundColor : AppColors.darkCardColor, width: 0.5, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Txt( text: "Departure", style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black), ), const SizedBox(height: 20), // Pick up and Drop off locations in containers _locationItem("Pick up", widget.ride.startLocationName, widget.ride.startLocation), const SizedBox(height: 12), _locationItem("Drop off", widget.ride.endLocationName, widget.ride.endLocation), const SizedBox(height: 14), const Divider( thickness: 0.3, ), const SizedBox(height: 14), // Date, Depart, Arrival Section _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: 30), Container( padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).brightness == Brightness.dark ? AppColors.white : AppColors.detailsColors, width: 0.5, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(top: 16,bottom: 12), child: Txt(text: "User Details",style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black),), ), Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(30), child: CachedNetworkImage( imageUrl: widget.ride.userimageurl.startsWith('http') ? widget.ride.userimageurl : "${mediaUrl ?? ''}${widget.ride.userimageurl}", 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.ride.ridername, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Theme.of(context).brightness == Brightness.dark ? AppColors.white : AppColors.black, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), const SizedBox(height: 4), Txt( text: "+${widget.ride.riderphonenumber}", style: TextStyle( color:Theme.of(context).brightness == Brightness.dark ? AppColors.lightGrey : Color(0xFF5B616D), fontSize: 12, fontFamily: 'Inter', fontWeight: FontWeight.w600, height: 1.33, ), overflow: TextOverflow.ellipsis, maxLines: 1, ), ], ), ), ], ), const Padding( padding: EdgeInsets.only(top: 12.0), child: Divider(thickness: 1,color: Color(0xffEAEAEA),), ), Padding( padding: const EdgeInsets.symmetric(vertical:12.0), child: CustomButton( onClick: () => { if (widget.ride.riderphonenumber.isNotEmpty) { showContactOptions( context, widget.ride.riderphonenumber) } else { AppSnackbar.showError( "Error", "Phone number not available") } }, width:double.infinity, fontSize: 15, height: 40, borderRadius: 10, txtColor: AppColors.white, title: "Call him", backgroundColor: AppColors.primaryBtnColor, borderColor: AppColors.primaryBtnColor, //isloading: isCancelling, loadercolor: AppColors.white, ), ), ], ), ), const SizedBox(height: 30), // 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 ? AppColors.whiteBackgroundColor : AppColors.darkCardColor, width: 0.5, ), ), child: Column( children: [ _priceRow( "Ticket price", "${widget.ride.totalPrice?.toStringAsFixed(0) ?? '24'} TND", isMainPrice: true, ), _priceRow( "Reservation Fee (0%)", "0.0 TND", ), const SizedBox(height: 8), const Align( alignment: Alignment.centerLeft, child: Txt( text: "To make a reservation, 0% of the total\namount should be paid upfront", style: TextStyle( color: Color(0xff8C929C), fontSize: 13, ), ), ), const SizedBox(height: 16), _priceRow( "Coupon (${widget.ride.couponpercentage?.toStringAsFixed(2)}%)", "${widget.ride.couponvalue?.toStringAsFixed(2)} TND" ?? "0.0 TND", ), const SizedBox(height: 8), Row( children: [ const Txt( text: "Including taxes and fees. ", style: TextStyle( color: Color(0xff8C929C), fontSize: 13, ), ), GestureDetector( onTap: () { // View details action }, child: const Txt( text: "View details", style: TextStyle( color: AppColors.primaryColor, fontSize: 13, decoration: TextDecoration.underline, ), ), ), ], ), ], ), ), const SizedBox(height: 20), // Additional ride info if needed // if (widget.ride.ridername != null) ...[ // const SizedBox(height: 20), // _infoRow('Rider Name', widget.ride.ridername ?? '-'), // const SizedBox(height: 12), // _infoRow('Phone', widget.ride.riderphonenumber ?? '-'), // const SizedBox(height: 12), // _infoRow('Payment Method', widget.ride.paymenttype ?? 'N/A'), // ], ], ), ), ); } Widget _locationItem(String label, String location, String locationPos) { return Row( children: [ SizedBox( width: 55, child: Txt( text: label, style: TextStyle( color: Theme.of(context).brightness == Brightness.dark ? AppColors.lightGrey : AppColors.detailsColors, fontSize: 14, ), ), ), const SizedBox(width: 12), Expanded( child: GestureDetector( onTap: () => _onNavigationClicked( _rideProvider.getLatitude(locationPos), _rideProvider.getLongitude(locationPos)), child: Container( 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( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ const Icon( LucideIcons.map_pin, size: 20, ), const SizedBox(width: 8), Flexible( child: Txt( text: location, maxLines: 1, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w500, ), ), ), ], ), ), ), ), ], ); } Widget _timeInfoRow(IconData icon, String label, String value) { return Row( children: [ Icon( icon, color: Theme.of(context).brightness == Brightness.dark ? AppColors.lightGrey : AppColors.detailsColors, size: 20, ), const SizedBox(width: 12), Expanded( child: Txt( text: label, style: TextStyle( color:Theme.of(context).brightness == Brightness.dark ? AppColors.lightGrey : AppColors.detailsColors, fontSize: 16, ), ), ), Txt( text: value, style: TextStyle( fontSize: 16, color: Theme.of(context).brightness == Brightness.dark ? AppColors.whiteBackgroundColor : AppColors.blackBackgroundColor, fontWeight: FontWeight.w500, ), ), ], ); } Widget _priceRow(String label, String value, {bool isMainPrice = false}) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Txt( text: label, style: TextStyle( fontSize: isMainPrice ? 16 : 14, fontWeight: isMainPrice ? FontWeight.w600 : FontWeight.normal, color:Theme.of(context).brightness == Brightness.dark ? AppColors.lightGrey : AppColors.detailsSColors, ), ), Txt( text: value, style: TextStyle( fontSize: isMainPrice ? 18 : 14, fontWeight: FontWeight.w600, color:Theme.of(context).brightness == Brightness.dark ? AppColors.white : AppColors.black, ), ), ], ); } Widget _infoRow(String title, String value) { return GestureDetector( onTap: () { if (title != 'Phone' || value == null || value == "not_exist") { return; } showContactOptions(context, value); }, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Txt( text: title, style: const TextStyle(fontWeight: FontWeight.bold)), Text(value), ], ), ), ); } 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); } String _formatDateTime(DateTime? dateTime) { if (dateTime == null) return '-'; final now = DateTime.now(); final isToday = now.year == dateTime.year && now.month == dateTime.month && now.day == dateTime.day; final time = DateFormat('h:mm a').format(dateTime); return isToday ? 'Today $time' : DateFormat.yMMMd().add_jm().format(dateTime); } }