You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

742 lines
15 KiB

1 month ago
import 'dart:async';
import 'dart:convert';
import 'dart:math';
6 months ago
import 'package:flutter/material.dart';
1 month ago
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:supplier_new/common/settings.dart';
3 weeks ago
import 'otp_start_unloading.dart';
import 'unloading_inprogress.dart';
import 'collect_money.dart';
6 months ago
class DeliveryUpdatesPage extends StatefulWidget {
3 weeks ago
var details;
6 months ago
final String orderId;
final String initialStatus;
1 month ago
3 weeks ago
DeliveryUpdatesPage({
1 month ago
super.key,
3 weeks ago
this.details,
1 month ago
required this.orderId,
required this.initialStatus,
});
6 months ago
@override
State<DeliveryUpdatesPage> createState() => _DeliveryUpdatesPageState();
}
class _DeliveryUpdatesPageState extends State<DeliveryUpdatesPage> {
3 weeks ago
1 month ago
GoogleMapController? _mapController;
Timer? _timer;
final PolylinePoints _polylinePoints = PolylinePoints();
3 weeks ago
static const String googleApiKey = "YOUR_GOOGLE_KEY";
1 month ago
3 weeks ago
bool isLoading=true;
bool mapReady=false;
1 month ago
3 weeks ago
String currentStatus='';
String etaText='--';
String distanceText='--';
String durationText='--';
1 month ago
3 weeks ago
String driverName='';
String driverPhone='';
String tankerName='';
String orderAddress='';
1 month ago
double? driverLat;
double? driverLng;
3 weeks ago
1 month ago
double? destinationLat;
double? destinationLng;
3 weeks ago
Set<Marker> markers={};
Set<Polyline> polylines={};
1 month ago
3 weeks ago
List<LatLng> routePoints=[];
LatLng? _lastDriverPosition;
bool firstCameraMove=true;
6 months ago
@override
3 weeks ago
void initState(){
6 months ago
super.initState();
1 month ago
3 weeks ago
currentStatus=_beautifyStatus(widget.initialStatus);
_loadTracking(showLoader:true);
_timer=Timer.periodic(
Duration(seconds:5),
(_){
_loadTracking();
});
6 months ago
}
1 month ago
@override
3 weeks ago
void dispose(){
1 month ago
_timer?.cancel();
_mapController?.dispose();
3 weeks ago
1 month ago
super.dispose();
}
3 weeks ago
Future<void> _loadTracking({bool showLoader=false}) async{
1 month ago
3 weeks ago
try{
final uri=Uri.parse(
'${AppSettings.host}trackOrder/${widget.orderId}'
1 month ago
);
3 weeks ago
final response=await http.get(uri);
1 month ago
3 weeks ago
final decoded=jsonDecode(response.body);
1 month ago
3 weeks ago
final data=decoded["data"]??decoded;
1 month ago
3 weeks ago
driverLat=_toDouble(data["driverLat"]);
driverLng=_toDouble(data["driverLng"]);
1 month ago
3 weeks ago
destinationLat=_toDouble(data["destinationLat"]);
destinationLng=_toDouble(data["destinationLng"]);
driverName=(data["driverName"]??"").toString();
driverPhone=(data["driverPhone"]??"").toString();
tankerName=(data["tankerName"]??"").toString();
1 month ago
3 weeks ago
orderAddress=(data["address"]??"").toString();
currentStatus=_beautifyStatus(
(data["status"]??widget.initialStatus).toString()
1 month ago
);
3 weeks ago
if(driverLat!=null &&
driverLng!=null &&
destinationLat!=null &&
destinationLng!=null){
1 month ago
await _buildMarkers();
3 weeks ago
if(currentStatus.toLowerCase().contains("out for delivery")){
await _fetchGoogleRouteAndEta();
}
1 month ago
await _moveCameraToBounds();
}
3 weeks ago
setState(() {
isLoading=false;
});
}
catch(e){
setState(() {
isLoading=false;
});
1 month ago
}
3 weeks ago
}
double _calculateDistanceKm(
double startLat,
double startLng,
double endLat,
double endLng){
const double earthRadius=6371;
final dLat=_degToRad(endLat-startLat);
final dLng=_degToRad(endLng-startLng);
final a=
sin(dLat/2)*sin(dLat/2)+
cos(_degToRad(startLat))*
cos(_degToRad(endLat))*
sin(dLng/2)*
sin(dLng/2);
final c=2*atan2(sqrt(a),sqrt(1-a));
return earthRadius*c;
}
double _degToRad(double deg){
return deg*pi/180;
6 months ago
}
3 weeks ago
Future<void> _buildMarkers() async{
if(driverLat==null ||
driverLng==null ||
destinationLat==null ||
destinationLng==null){
1 month ago
return;
}
3 weeks ago
LatLng newDriverPos=
LatLng(driverLat!,driverLng!);
/// CHECK MOVEMENT DISTANCE
bool moved=true;
if(_lastDriverPosition!=null){
double diff=
_calculateDistanceKm(
_lastDriverPosition!.latitude,
_lastDriverPosition!.longitude,
newDriverPos.latitude,
newDriverPos.longitude
);
/// UPDATE ROUTE ONLY IF DRIVER MOVED > 30m
moved=diff>0.03;
}
_lastDriverPosition=newDriverPos;
markers={
1 month ago
Marker(
3 weeks ago
markerId:MarkerId("driver"),
position:newDriverPos,
rotation:0,
icon:BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure
)
1 month ago
),
3 weeks ago
1 month ago
Marker(
3 weeks ago
markerId:MarkerId("destination"),
position:LatLng(destinationLat!,destinationLng!),
icon:BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueRed
)
)
1 month ago
};
3 weeks ago
/// FOLLOW DRIVER CAMERA
if(_mapController!=null){
1 month ago
3 weeks ago
if(firstCameraMove){
1 month ago
3 weeks ago
_mapController!.animateCamera(
CameraUpdate.newLatLngZoom(
newDriverPos,
15
)
);
firstCameraMove=false;
}
else{
_mapController!.animateCamera(
CameraUpdate.newLatLng(
newDriverPos
)
);
1 month ago
}
}
}
3 weeks ago
Future<void> _fetchGoogleRouteAndEta() async{
if(driverLat==null ||
driverLng==null ||
destinationLat==null ||
destinationLng==null){
1 month ago
return;
}
3 weeks ago
final origin='${driverLat!},${driverLng!}';
final destination='${destinationLat!},${destinationLng!}';
final url=Uri.parse(
"https://maps.googleapis.com/maps/api/directions/json?"
"origin=$origin"
"&destination=$destination"
"&departure_time=now"
"&traffic_model=best_guess"
"&mode=driving"
"&key=$googleApiKey"
1 month ago
);
3 weeks ago
try{
final res=await http.get(url);
final data=jsonDecode(res.body);
if(data["routes"]==null || data["routes"].isEmpty)return;
final route=data["routes"][0];
final leg=route["legs"][0];
/// DISTANCE
distanceText=
(leg["distance"]["text"]??"--").toString();
/// DURATION
durationText=
(leg["duration"]["text"]??"--").toString();
/// ETA WITH TRAFFIC
if(leg["duration_in_traffic"]!=null){
etaText=
leg["duration_in_traffic"]["text"].toString();
}
else{
etaText=durationText;
}
/// ROUTE POLYLINE
final poly=
route["overview_polyline"]["points"];
final decoded=
_polylinePoints.decodePolyline(poly);
routePoints=
decoded.map((e)=>
LatLng(e.latitude,e.longitude)
).toList();
polylines={
Polyline(
polylineId:PolylineId("route"),
points:routePoints,
width:5,
color:Color(0XFF8270DB)
)
};
1 month ago
}
3 weeks ago
catch(e){
print("Route error $e");
1 month ago
}
3 weeks ago
1 month ago
}
3 weeks ago
Future<void> _moveCameraToBounds() async{
if(_mapController==null)return;
LatLngBounds bounds=LatLngBounds(
southwest:LatLng(
min(driverLat!,destinationLat!),
min(driverLng!,destinationLng!)
),
northeast:LatLng(
max(driverLat!,destinationLat!),
max(driverLng!,destinationLng!)
)
1 month ago
);
3 weeks ago
_mapController!.animateCamera(
CameraUpdate.newLatLngBounds(bounds,70)
1 month ago
);
}
3 weeks ago
double? _toDouble(val){
1 month ago
3 weeks ago
if(val==null)return null;
1 month ago
3 weeks ago
return double.tryParse(val.toString());
1 month ago
}
3 weeks ago
String _beautifyStatus(String s){
return s.replaceAll("_"," ").toUpperCase();
1 month ago
}
3 weeks ago
/// STATUS FLOW BUTTON
Widget _buildActionButton(){
String status=currentStatus
.replaceAll("_"," ")
.toLowerCase()
.trim();
if(status.contains("arrived")){
return _actionButton(
"Start Unloading",
(){
Navigator.push(
context,
MaterialPageRoute(
builder:(_)=>UnloadArrivalScreen(
details:widget.details
)
)
);
});
1 month ago
}
3 weeks ago
if(status.contains("arrived")){
etaText="Reached destination";
1 month ago
}
3 weeks ago
if(status.contains("unloading started")){
return _actionButton(
"Unloading Progress",
(){
Navigator.push(
context,
MaterialPageRoute(
builder:(_)=>UnloadingInProgressScreen(
details:widget.details
)
)
);
});
1 month ago
}
3 weeks ago
if(status.contains("unloading stopped")){
return _actionButton(
"Collect Payment",
(){
Navigator.push(
context,
MaterialPageRoute(
builder:(_)=>CollectMoney(
details:widget.details
)
)
);
});
1 month ago
}
3 weeks ago
if(status.contains("payment pending")){
return _actionButton(
"Complete Delivery",
(){
Navigator.push(
context,
MaterialPageRoute(
builder:(_)=>CollectMoney(
details:widget.details
)
)
);
});
1 month ago
}
3 weeks ago
return SizedBox();
1 month ago
}
3 weeks ago
Widget _actionButton(
String text,
VoidCallback onTap){
1 month ago
3 weeks ago
return SizedBox(
1 month ago
3 weeks ago
width:double.infinity,
child:ElevatedButton(
style:ElevatedButton.styleFrom(
backgroundColor:Color(0XFF8270DB),
padding:EdgeInsets.symmetric(vertical:15),
shape:RoundedRectangleBorder(
borderRadius:BorderRadius.circular(20)
)
1 month ago
),
3 weeks ago
onPressed:onTap,
child:Text(
text,
style:TextStyle(
color:Colors.white,
fontSize:16,
fontWeight:FontWeight.w600
)
)
1 month ago
),
3 weeks ago
1 month ago
);
3 weeks ago
1 month ago
}
3 weeks ago
Future<void> _callDriver() async{
if(widget.details.delivery_agent_mobile.isEmpty)return;
final uri=Uri.parse("tel:${widget.details.delivery_agent_mobile}");
await launchUrl(uri);
1 month ago
}
3 weeks ago
@override
Widget build(BuildContext context){
return Scaffold(
backgroundColor:Color(0XFFF2F2F2),
appBar:AppBar(
title:Text("Track Delivery"),
backgroundColor:Colors.white,
1 month ago
),
3 weeks ago
body:isLoading?
Center(child:CircularProgressIndicator())
:
Column(
children:[
Expanded(
child:GoogleMap(
initialCameraPosition:
CameraPosition(
target:LatLng(
driverLat??17.385,
driverLng??78.486
1 month ago
),
3 weeks ago
zoom:14
1 month ago
),
3 weeks ago
markers:markers,
polylines:polylines,
onMapCreated:(c){
_mapController=c;
mapReady=true;
}
)
),
Container(
padding:EdgeInsets.all(16),
decoration:BoxDecoration(
color:Colors.white,
borderRadius:BorderRadius.vertical(
top:Radius.circular(25)
)
),
child:Column(
children:[
Row(
children:[
Expanded(
child:Text(
"${widget.details.delivery_agent_name}",
style:TextStyle(
fontSize:16,
fontWeight:FontWeight.bold
)
)
1 month ago
),
3 weeks ago
IconButton(
onPressed:_callDriver,
icon:Icon(Icons.call)
)
],
),
SizedBox(height:10),
Text(
"Status : $currentStatus"
),
SizedBox(height:8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
1 month ago
children: [
3 weeks ago
1 month ago
Text(
3 weeks ago
"ETA",
1 month ago
style: TextStyle(
3 weeks ago
fontSize:12,
color:Colors.grey
1 month ago
),
),
3 weeks ago
Text(
etaText,
style: TextStyle(
fontSize:14,
fontWeight:FontWeight.bold
),
)
1 month ago
],
),
6 months ago
3 weeks ago
Column(
children: [
1 month ago
3 weeks ago
Text(
"Distance",
style: TextStyle(
fontSize:12,
color:Colors.grey
),
),
1 month ago
3 weeks ago
Text(
distanceText,
style: TextStyle(
fontSize:14,
fontWeight:FontWeight.bold
),
)
1 month ago
3 weeks ago
],
1 month ago
),
3 weeks ago
Column(
children: [
Text(
"Duration",
style: TextStyle(
fontSize:12,
color:Colors.grey
),
),
Text(
durationText,
style: TextStyle(
fontSize:14,
fontWeight:FontWeight.bold
),
)
],
)
1 month ago
],
),
3 weeks ago
SizedBox(height:15),
SizedBox(height:20),
_buildActionButton(),
SizedBox(height:10),
1 month ago
],
3 weeks ago
),
)
1 month ago
],
3 weeks ago
1 month ago
),
3 weeks ago
1 month ago
);
3 weeks ago
1 month ago
}
3 weeks ago
1 month ago
}