new changes maps

This commit is contained in:
2026-08-12 10:50:45 +05:30
parent df4e044d74
commit c65d78878a
25 changed files with 6120 additions and 3814 deletions

View File

@@ -5,12 +5,13 @@ import 'dart:async';
import 'dart:convert';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:lottie/lottie.dart' hide Marker;
import 'package:nearle/services/maps/map_marker_icons.dart';
import 'package:nearle/services/maps/nearle_map.dart';
import 'package:nearle/services/maps/routing_service.dart';
import 'package:nearle/views/helpers/constants/Colorconstants.dart';
import 'package:nearle/views/helpers/constants/Font_constant.dart';
import 'package:slider_button_lite/feature/presentation/slider_button/slider.dart';

View File

@@ -16,24 +16,18 @@ class _DeliveryMapScreen extends StatefulWidget {
}
class _DeliveryMapScreenState extends State<_DeliveryMapScreen> {
GoogleMapController? _mapController;
NearleMapController? _mapController;
late final LatLng _pickupLocation;
late final LatLng _dropLocation;
final Set<Marker> _markers = {};
final Set<Polyline> _polylines = {};
late final PolylinePoints _polylinePoints;
bool _isLoadingRoute = true;
List<NearleMapMarker> _markers = const [];
List<NearleMapPolyline> _polylines = const [];
bool _isNavigating = false; // Prevent multiple clicks
static const String _googleApiKey = 'AIzaSyBhkGfnq27sN0wV5y_S-M2KojpFTk_by-Q';
@override
void initState() {
super.initState();
_polylinePoints = PolylinePoints(apiKey: _googleApiKey);
_resolveLocationsFromDelivery();
_setMarkers();
_isLoadingRoute = false;
_createRealRoute();
}
@@ -73,113 +67,70 @@ class _DeliveryMapScreenState extends State<_DeliveryMapScreen> {
_dropLocation = drop;
}
void _setMarkers() {
_markers.addAll([
Marker(
markerId: const MarkerId('pickup'),
position: _pickupLocation,
infoWindow: const InfoWindow(title: 'Pickup Location'),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
),
Marker(
markerId: const MarkerId('drop'),
position: _dropLocation,
infoWindow: const InfoWindow(title: 'Drop Location'),
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
),
]);
Future<void> _setMarkers() async {
final pickupIcon = await MapMarkerIcons.pin(Colors.red);
final dropIcon = await MapMarkerIcons.pin(Colors.green);
if (!mounted) return;
setState(() {
_markers = [
NearleMapMarker(
id: 'pickup',
position: _pickupLocation,
icon: pickupIcon,
),
NearleMapMarker(id: 'drop', position: _dropLocation, icon: dropIcon),
];
});
}
Future<void> _createRealRoute() async {
try {
final request = PolylineRequest(
origin: PointLatLng(
_pickupLocation.latitude,
_pickupLocation.longitude,
),
destination: PointLatLng(
_dropLocation.latitude,
_dropLocation.longitude,
),
mode: TravelMode.driving,
);
final route = await RoutingService.getRoute(
origin: _pickupLocation,
destination: _dropLocation,
);
final result = await _polylinePoints.getRouteBetweenCoordinates(
request: request,
);
if (result.points.isNotEmpty) {
final routePoints = result.points
.map((e) => LatLng(e.latitude, e.longitude))
.toList();
if (mounted) {
setState(() {
_polylines.add(
Polyline(
polylineId: const PolylineId('real_route'),
color: ColorConstants.primaryColor,
width: 6,
points: routePoints,
),
);
_isLoadingRoute = false;
});
_fitMapToRoute();
}
} else {
debugPrint('[MAP_PREVIEW] No route found');
if (mounted) {
setState(() => _isLoadingRoute = false);
}
}
} catch (e) {
debugPrint('[MAP_PREVIEW] Error creating route: $e');
if (mounted) {
setState(() => _isLoadingRoute = false);
}
}
}
Future<void> _fitMapToRoute() async {
if (!mounted) return;
if (_mapController == null) return;
// Check if controller is still alive (important!)
try {
await _mapController!.getVisibleRegion();
} catch (e) {
debugPrint("❌ Map controller is dead. Skip animateCamera.");
return;
}
final bounds = LatLngBounds(
southwest: LatLng(
math.min(_pickupLocation.latitude, _dropLocation.latitude),
math.min(_pickupLocation.longitude, _dropLocation.longitude),
),
northeast: LatLng(
math.max(_pickupLocation.latitude, _dropLocation.latitude),
math.max(_pickupLocation.longitude, _dropLocation.longitude),
),
);
// Try animate safely
for (int i = 0; i < 10; i++) {
if (!mounted) return;
try {
await _mapController!.animateCamera(
CameraUpdate.newLatLngBounds(bounds, 80),
);
return;
} catch (e) {
await Future.delayed(const Duration(milliseconds: 150));
}
// Fall back to a straight line so the screen still shows the connection
// when routing is unavailable.
final points = (route != null && route.points.isNotEmpty)
? route.points
: <LatLng>[_pickupLocation, _dropLocation];
if (route == null) debugPrint('[MAP_PREVIEW] No route found');
setState(() {
_polylines = [
NearleMapPolyline(
id: 'real_route',
points: points,
color: ColorConstants.primaryColor,
width: 6,
),
];
});
_fitMapToRoute();
}
debugPrint("❌ animateCamera failed after retries (map probably disposed)");
}
Future<void> _fitMapToRoute() async {
final controller = _mapController;
if (!mounted || controller == null) return;
await controller.fitBounds(
LatLngBounds(
southwest: LatLng(
math.min(_pickupLocation.latitude, _dropLocation.latitude),
math.min(_pickupLocation.longitude, _dropLocation.longitude),
),
northeast: LatLng(
math.max(_pickupLocation.latitude, _dropLocation.latitude),
math.max(_pickupLocation.longitude, _dropLocation.longitude),
),
),
padding: 80,
);
}
@@ -188,27 +139,20 @@ Future<void> _fitMapToRoute() async {
return Scaffold(
body: Stack(
children: [
SizedBox.expand(
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: _pickupLocation,
zoom: 14.5,
),
onMapCreated: (controller) {
_mapController = controller;
Future.delayed(const Duration(milliseconds: 500), () {
if (!_isLoadingRoute && _polylines.isNotEmpty) {
_fitMapToRoute();
}
});
},
markers: _markers,
polylines: _polylines,
zoomControlsEnabled: false,
myLocationButtonEnabled: false,
),
),
SizedBox.expand(
child: NearleMap(
initialCameraPosition: CameraPosition(
target: _pickupLocation,
zoom: 14.5,
),
markers: _markers,
polylines: _polylines,
onMapReady: (controller) {
_mapController = controller;
_fitMapToRoute();
},
),
),
Positioned(
top: 50,
left: 16,

View File

@@ -24,15 +24,11 @@ class MultiCustomerMapScreen extends StatefulWidget {
}
class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
GoogleMapController? mapController;
Set<Marker> markers = {};
Set<Polyline> polylines = {};
NearleMapController? mapController;
List<NearleMapMarker> markers = const [];
List<NearleMapPolyline> polylines = const [];
LatLng? currentLocation;
bool _isLoading = true;
bool _mapReady = false;
final PolylinePoints _polylinePoints = PolylinePoints(
apiKey: 'AIzaSyBhkGfnq27sN0wV5y_S-M2KojpFTk_by-Q',
);
final Map<String, Map<String, dynamic>> _deliveryMap = {};
@override
@@ -199,7 +195,7 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
}
Future<void> _createDeliveryMarkers() async {
Set<Marker> tempMarkers = {};
final List<NearleMapMarker> tempMarkers = [];
List<_StepPoint> stepPoints = [];
_deliveryMap.clear();
@@ -231,8 +227,6 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
);
if (lat == 0 || lon == 0) continue;
final customerName = (delivery['deliverycustomer'] ?? 'Customer ${i + 1}')
.toString();
final orderId = (delivery['orderid'] ?? '').toString();
final status = (delivery['orderstatus']?.toString().toLowerCase() ?? '')
.trim();
@@ -242,23 +236,19 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
// Store delivery data for dialog
_deliveryMap['delivery_$orderId'] = delivery;
final icon = await _createCircularMarkerBitmap(
final icon = await MapMarkerIcons.numberedCircle(
displayStep,
primaryColor: ColorConstants.primaryColor,
isSkipped: isSkipped,
isNext: isNext,
);
tempMarkers.add(
Marker(
markerId: MarkerId('delivery_$orderId'),
NearleMapMarker(
id: 'delivery_$orderId',
position: LatLng(lat, lon),
icon: icon,
infoWindow: InfoWindow(
title: isSkipped
? 'Step $displayStep: $customerName (SKIPPED)'
: 'Step $displayStep: $customerName',
snippet: 'Order #$orderId',
),
anchor: MarkerAnchor.center,
onTap: () {
_showCustomerDetailsSheet(delivery, displayStep);
},
@@ -270,13 +260,11 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
if (currentLocation != null) {
tempMarkers.add(
Marker(
markerId: const MarkerId('current_location'),
NearleMapMarker(
id: 'current_location',
position: currentLocation!,
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
),
infoWindow: const InfoWindow(title: 'You are here'),
icon: await MapMarkerIcons.dot(Colors.blueAccent),
anchor: MarkerAnchor.center,
),
);
stepPoints.insert(0, _StepPoint(0, currentLocation!));
@@ -284,67 +272,7 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
stepPoints.sort((a, b) => a.step.compareTo(b.step));
Set<Polyline> newPolylines = {};
int segmentIndex = 0;
for (int i = 0; i < stepPoints.length - 1; i++) {
final start = stepPoints[i].position;
final end = stepPoints[i + 1].position;
try {
// ignore: deprecated_member_use
final request = PolylineRequest(
origin: PointLatLng(start.latitude, start.longitude),
destination: PointLatLng(end.latitude, end.longitude),
mode: TravelMode.driving,
);
final result = await _polylinePoints.getRouteBetweenCoordinates(
request: request,
);
if (result.points.isNotEmpty) {
final routePoints = result.points
.map((p) => LatLng(p.latitude, p.longitude))
.toList();
newPolylines.add(
Polyline(
polylineId: PolylineId('segment_$segmentIndex'),
points: routePoints,
width: 6,
color: Colors.blue,
startCap: Cap.roundCap,
endCap: Cap.roundCap,
jointType: JointType.round,
geodesic: true,
),
);
segmentIndex++;
} else {
newPolylines.add(
Polyline(
polylineId: PolylineId('segment_fallback_$segmentIndex'),
points: [start, end],
width: 4,
color: Colors.blue.shade200,
),
);
segmentIndex++;
}
} catch (e) {
debugPrint('[CUSTOMER_MAP] Directions error for segment $i: $e');
newPolylines.add(
Polyline(
polylineId: PolylineId('segment_error_$segmentIndex'),
points: [start, end],
width: 4,
color: Colors.blue.shade200,
),
);
segmentIndex++;
}
}
final newPolylines = await _buildRoutePolylines(stepPoints);
if (mounted) {
setState(() {
@@ -357,8 +285,50 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
_fitBoundsToMarkersAndPolylines();
}
/// Builds the route through every stop in one Directions call.
///
/// This used to issue one request per consecutive pair of stops, so opening
/// this screen with N stops cost N billable requests. OSRM takes every stop
/// as a coordinate in one request, so the whole route is a single call.
Future<List<NearleMapPolyline>> _buildRoutePolylines(
List<_StepPoint> stepPoints,
) async {
if (stepPoints.length < 2) return const [];
final route = await RoutingService.getRoute(
origin: stepPoints.first.position,
destination: stepPoints.last.position,
waypoints: stepPoints
.sublist(1, stepPoints.length - 1)
.map((s) => s.position)
.toList(),
);
if (route != null && route.points.isNotEmpty) {
return [
NearleMapPolyline(
id: 'route',
points: route.points,
width: 6,
color: Colors.blue,
),
];
}
debugPrint('[CUSTOMER_MAP] Routing unavailable, drawing direct segments');
return [
NearleMapPolyline(
id: 'route_fallback',
points: stepPoints.map((s) => s.position).toList(),
width: 4,
color: Colors.blue.shade200,
),
];
}
Future<void> _fitBoundsToMarkersAndPolylines() async {
if (mapController == null) return;
final controller = mapController;
if (controller == null) return;
double minLat = double.infinity;
double maxLat = -double.infinity;
@@ -367,8 +337,7 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
bool hasPoint = false;
for (final m in markers) {
final pos = m.position;
void include(LatLng pos) {
minLat = math.min(minLat, pos.latitude);
maxLat = math.max(maxLat, pos.latitude);
minLng = math.min(minLng, pos.longitude);
@@ -376,137 +345,24 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
hasPoint = true;
}
for (final m in markers) {
include(m.position);
}
for (final poly in polylines) {
for (final pos in poly.points) {
minLat = math.min(minLat, pos.latitude);
maxLat = math.max(maxLat, pos.latitude);
minLng = math.min(minLng, pos.longitude);
maxLng = math.max(maxLng, pos.longitude);
hasPoint = true;
include(pos);
}
}
if (!hasPoint) return;
final bounds = LatLngBounds(
southwest: LatLng(minLat, minLng),
northeast: LatLng(maxLat, maxLng),
);
WidgetsBinding.instance.addPostFrameCallback((_) async {
try {
await mapController!.animateCamera(
CameraUpdate.newLatLngBounds(bounds, 80),
);
} catch (e) {
Future.delayed(const Duration(milliseconds: 300), () async {
try {
await mapController!.animateCamera(
CameraUpdate.newLatLngBounds(bounds, 80),
);
} catch (e) {
debugPrint('[CUSTOMER_MAP] Retry failed: $e');
}
});
}
});
}
Future<BitmapDescriptor> _createCircularMarkerBitmap(
int number, {
bool isSkipped = false,
bool isNext = false,
}) async {
const double size = 70;
final pictureRecorder = ui.PictureRecorder();
final canvas = Canvas(pictureRecorder);
final center = Offset(size / 2, size / 2);
final paint = Paint()
..color = isSkipped ? Colors.orange : ColorConstants.primaryColor;
canvas.drawCircle(center, 15, paint);
final border = Paint()
..color = isSkipped ? Colors.orange.shade900 : Colors.white
..style = PaintingStyle.stroke
..strokeWidth = isSkipped ? 4 : 3;
canvas.drawCircle(center, 15, border);
final textPainter = TextPainter(
text: TextSpan(
text: number.toString(),
style: const TextStyle(
fontSize: 19,
color: Colors.white,
fontWeight: FontWeight.bold,
),
await controller.fitBounds(
LatLngBounds(
southwest: LatLng(minLat, minLng),
northeast: LatLng(maxLat, maxLng),
),
textDirection: TextDirection.ltr,
padding: 80,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
center.dx - textPainter.width / 2,
center.dy - textPainter.height / 2,
),
);
// Draw "NEXT" indicator (road sign with down arrow) on top
if (isNext) {
// Draw road sign background (rectangle)
final signPaint = Paint()
..color = Colors.green
..style = PaintingStyle.fill;
final signRect = RRect.fromRectAndRadius(
Rect.fromCenter(center: Offset(size / 2, 8), width: 45, height: 30),
const Radius.circular(4),
);
canvas.drawRRect(signRect, signPaint);
// Draw border
final signBorder = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 1.5;
canvas.drawRRect(signRect, signBorder);
// Draw "NEXT" text
final nextTextPainter = TextPainter(
text: const TextSpan(
text: 'NEXT',
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
nextTextPainter.layout();
nextTextPainter.paint(
canvas,
Offset(size / 2 - nextTextPainter.width / 2, 4),
);
// Draw down arrow
final arrowPath = Path();
arrowPath.moveTo(size / 2, 18);
arrowPath.lineTo(size / 2 - 4, 24);
arrowPath.lineTo(size / 2 + 4, 24);
arrowPath.close();
final arrowPaint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
canvas.drawPath(arrowPath, arrowPaint);
}
final img = await pictureRecorder.endRecording().toImage(
size.toInt(),
size.toInt(),
);
final data = await img.toByteData(format: ui.ImageByteFormat.png);
return BitmapDescriptor.bytes(data!.buffer.asUint8List());
}
void _showCustomerDetailsSheet(
@@ -872,21 +728,14 @@ class _MultiCustomerMapScreenState extends State<MultiCustomerMapScreen> {
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: GoogleMap(
: NearleMap(
initialCameraPosition: initialCameraPosition,
markers: markers,
polylines: polylines,
myLocationEnabled: true,
myLocationButtonEnabled: true,
onMapCreated: (controller) {
onMapReady: (controller) {
mapController = controller;
_mapReady = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(const Duration(milliseconds: 150), () {
if (_mapReady) _fitBoundsToMarkersAndPolylines();
});
});
_fitBoundsToMarkersAndPolylines();
},
),
);

View File

@@ -19,12 +19,15 @@ class _RiderNavigationScreen extends StatefulWidget {
class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
with WidgetsBindingObserver {
GoogleMapController? mapController;
NearleMapController? mapController;
bool _mapReady = false;
bool _isDisposed = false;
Position? _currentPosition;
StreamSubscription<Position>? _positionStream;
final Set<Marker> _markers = {};
List<NearleMapMarker> _markers = const [];
MarkerIcon? _riderIcon;
MarkerIcon? _dropIcon;
LatLng? _lastRiderPosition;
late final LatLng dropLocation;
bool _isInitializing = true;
bool _hasOpenedGoogleMaps = false;
@@ -46,6 +49,7 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
super.initState();
WidgetsBinding.instance.addObserver(this);
_resolveDropFromDelivery();
_loadMarkerIcons();
_initializeMap();
_initEtaForNav();
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -214,8 +218,8 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
if (lastPos != null && mounted) {
setState(() {
_currentPosition = lastPos;
_addMarkers(LatLng(lastPos.latitude, lastPos.longitude));
});
_addMarkers(LatLng(lastPos.latitude, lastPos.longitude));
_startLiveTracking();
}
});
@@ -338,8 +342,8 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
if (mounted) {
setState(() {
_currentPosition = position;
_addMarkers(LatLng(position.latitude, position.longitude));
});
_addMarkers(LatLng(position.latitude, position.longitude));
final distance = Geolocator.distanceBetween(
position.latitude,
@@ -362,27 +366,36 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
});
}
/// Marker bitmaps are drawn on a canvas, so they arrive asynchronously.
/// Once they land, rebuild whatever markers are already pending.
Future<void> _loadMarkerIcons() async {
final rider = await MapMarkerIcons.dot(Colors.blueAccent);
final drop = await MapMarkerIcons.pin(Colors.red);
if (!mounted || _isDisposed) return;
setState(() {
_riderIcon = rider;
_dropIcon = drop;
_addMarkers(_lastRiderPosition ?? dropLocation);
});
}
/// Mutates [_markers] only — callers own the [setState] so this can be
/// invoked from inside an existing one without nesting.
void _addMarkers(LatLng riderPos) {
_markers
..clear()
..add(
Marker(
markerId: const MarkerId('rider'),
position: riderPos,
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
),
infoWindow: const InfoWindow(title: 'You (Rider)'),
),
)
..add(
Marker(
markerId: const MarkerId('drop'),
position: dropLocation,
icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
infoWindow: const InfoWindow(title: 'Drop Location'),
),
);
_lastRiderPosition = riderPos;
final riderIcon = _riderIcon;
final dropIcon = _dropIcon;
if (riderIcon == null || dropIcon == null) return;
_markers = [
NearleMapMarker(
id: 'rider',
position: riderPos,
icon: riderIcon,
anchor: MarkerAnchor.center,
),
NearleMapMarker(id: 'drop', position: dropLocation, icon: dropIcon),
];
}
Future<void> _attemptAutoOpenNavigation({bool fromTimer = false}) async {
@@ -511,9 +524,8 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
return Scaffold(
body: Stack(
children: [
GoogleMap(
mapType: MapType.normal,
onMapCreated: (controller) {
NearleMap(
onMapReady: (controller) {
mapController = controller;
_mapReady = true;
},
@@ -527,10 +539,7 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
),
markers: _markers,
myLocationEnabled: true,
myLocationButtonEnabled: false,
trafficEnabled: true,
compassEnabled: true,
buildingsEnabled: true,
tiltGesturesEnabled: true,
),
if (!_hasOpenedGoogleMaps)
@@ -994,11 +1003,7 @@ class _RiderNavigationScreenState extends State<_RiderNavigationScreen>
}
void _safeAnimateCamera(CameraUpdate update) {
if (!_mapReady || _isDisposed || mapController == null) return;
try {
mapController?.animateCamera(update);
} catch (e) {
debugPrint('[NAVIGATION] animateCamera skipped: $e');
}
if (!_mapReady || _isDisposed) return;
mapController?.animateCamera(update);
}
}

View File

@@ -2,7 +2,6 @@
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'dart:async';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
@@ -22,8 +21,10 @@ import 'package:nearle/controllers/delivery.dart';
import 'package:nearle/controllers/deliveries_controller.dart';
import 'package:geolocator/geolocator.dart';
import 'package:slide_to_submit_button/slide_to_submit_button.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:geocoding/geocoding.dart';
import 'package:nearle/services/maps/map_marker_icons.dart';
import 'package:nearle/services/maps/nearle_map.dart';
import 'package:nearle/services/maps/routing_service.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:floating/floating.dart';
import 'dart:math' as math;
@@ -2624,7 +2625,6 @@ void _showmapDetailsSheet(
double pickupLat = parseD(pickupLatStr);
double pickupLng = parseD(pickupLngStr);
bool hasPickup = pickupLat != 0 && pickupLng != 0;
final Completer<GoogleMapController> mapController = Completer();
double riderLat = 0.0;
double riderLng = 0.0;
@@ -2661,36 +2661,46 @@ void _showmapDetailsSheet(
final bool hasAny = hasRiderLocation && hasPickup;
final List<LatLng> polylineCoords = [];
final Set<Polyline> polylines = {};
const String googleAPIKey = "AIzaSyBhkGfnq27sN0wV5y_S-M2KojpFTk_by-Q";
final List<NearleMapPolyline> polylines = [];
if (hasRiderLocation && hasPickup) {
final PolylinePoints polylinePoints = PolylinePoints(apiKey: googleAPIKey);
final PolylineResult result = await polylinePoints
.getRouteBetweenCoordinates(
request: PolylineRequest(
origin: PointLatLng(riderLat, riderLng),
destination: PointLatLng(pickupLat, pickupLng),
mode: TravelMode.driving,
),
);
if (result.points.isNotEmpty) {
for (var point in result.points) {
polylineCoords.add(LatLng(point.latitude, point.longitude));
}
final route = await RoutingService.getRoute(
origin: LatLng(riderLat, riderLng),
destination: LatLng(pickupLat, pickupLng),
);
if (route != null && route.points.isNotEmpty) {
polylines.add(
Polyline(
polylineId: const PolylineId("route"),
NearleMapPolyline(
id: 'route',
points: route.points,
color: Colors.blueAccent,
width: 5,
points: polylineCoords,
),
);
}
}
final List<NearleMapMarker> mapMarkers = [];
if (hasRiderLocation) {
mapMarkers.add(
NearleMapMarker(
id: 'rider',
position: LatLng(riderLat, riderLng),
icon: await MapMarkerIcons.dot(Colors.blueAccent),
anchor: MarkerAnchor.center,
),
);
}
if (hasPickup) {
mapMarkers.add(
NearleMapMarker(
id: 'pickup',
position: LatLng(pickupLat, pickupLng),
icon: await MapMarkerIcons.pin(Colors.red),
),
);
}
// ⭐ STEP 3 — Close loader
Navigator.pop(context);
@@ -2848,7 +2858,7 @@ void _showmapDetailsSheet(
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade200),
),
child: GoogleMap(
child: NearleMap(
initialCameraPosition: CameraPosition(
target: hasRiderLocation && hasPickup
? LatLng(
@@ -2862,52 +2872,22 @@ void _showmapDetailsSheet(
: const LatLng(0, 0),
zoom: hasRiderLocation && hasPickup ? 13 : 15,
),
markers: {
if (hasRiderLocation)
Marker(
markerId: const MarkerId("rider"),
position: LatLng(riderLat, riderLng),
infoWindow: const InfoWindow(
title: "Your Location",
),
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
),
),
if (hasPickup)
Marker(
markerId: const MarkerId("pickup"),
position: LatLng(pickupLat, pickupLng),
infoWindow: const InfoWindow(
title: "Pickup Location",
),
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueRed,
),
),
},
markers: mapMarkers,
polylines: polylines,
zoomGesturesEnabled: true,
scrollGesturesEnabled: true,
tiltGesturesEnabled: true,
rotateGesturesEnabled: true,
zoomControlsEnabled: false,
myLocationButtonEnabled: false,
onMapCreated: (GoogleMapController controller) async {
mapController.complete(controller);
onMapReady: (controller) async {
if (hasRiderLocation && hasPickup) {
final bounds = LatLngBounds(
southwest: LatLng(
riderLat < pickupLat ? riderLat : pickupLat,
riderLng < pickupLng ? riderLng : pickupLng,
await controller.fitBounds(
LatLngBounds(
southwest: LatLng(
riderLat < pickupLat ? riderLat : pickupLat,
riderLng < pickupLng ? riderLng : pickupLng,
),
northeast: LatLng(
riderLat > pickupLat ? riderLat : pickupLat,
riderLng > pickupLng ? riderLng : pickupLng,
),
),
northeast: LatLng(
riderLat > pickupLat ? riderLat : pickupLat,
riderLng > pickupLng ? riderLng : pickupLng,
),
);
await controller.animateCamera(
CameraUpdate.newLatLngBounds(bounds, 80),
padding: 80,
);
}
},

View File

@@ -45,7 +45,7 @@ class HelpCenter extends StatelessWidget {
),
const SizedBox(height: 8),
Text(
"We make sure your delivery experience is smooth and clear. Whether you’re on your first trip or your hundredth, we’ve got your back. Browse through frequently asked questions or reach out directly if you need further help.",
"We make sure your delivery experience is smooth and clear. Whether you're on your first trip or your hundredth, we've got your back. Browse through frequently asked questions or reach out directly if you need further help.",
style: TextStyle(fontSize: 18,color: Colors.grey.shade700, height: 1.4, fontFamily: FontConstants.fontFamily),
),
const SizedBox(height: 16),