209 lines
6.5 KiB
Dart
Executable File
209 lines
6.5 KiB
Dart
Executable File
import 'dart:convert';
|
|
import 'dart:math';
|
|
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter/widgets.dart'; // Import for WidgetsBinding
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
|
|
|
import '../../constant/links.dart';
|
|
import '../functions/crud.dart';
|
|
import '../functions/location_controller.dart';
|
|
|
|
class RideAvailableController extends GetxController {
|
|
bool isLoading = false;
|
|
// FIX 1: Initialize the map with a default structure.
|
|
// This prevents `rideAvailableMap['message']` from ever being null in the UI.
|
|
Map rideAvailableMap = {'message': []};
|
|
late LatLng southwest;
|
|
late LatLng northeast;
|
|
|
|
LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) {
|
|
const double earthRadius = 6378137.0; // Earth's radius in meters
|
|
|
|
double latDelta = (radiusInMeters / earthRadius) * (180 / pi);
|
|
double lngDelta =
|
|
(radiusInMeters / (earthRadius * cos(pi * lat / 180))) * (180 / pi);
|
|
|
|
double minLat = lat - latDelta;
|
|
double maxLat = lat + latDelta;
|
|
double minLng = lng - lngDelta;
|
|
double maxLng = lng + lngDelta;
|
|
|
|
minLat = max(-90.0, minLat);
|
|
maxLat = min(90.0, maxLat);
|
|
|
|
minLng = (minLng + 180) % 360 - 180;
|
|
maxLng = (maxLng + 180) % 360 - 180;
|
|
|
|
if (minLng > maxLng) {
|
|
double temp = minLng;
|
|
minLng = maxLng;
|
|
maxLng = temp;
|
|
}
|
|
|
|
return LatLngBounds(
|
|
southwest: LatLng(minLat, minLng),
|
|
northeast: LatLng(maxLat, maxLng),
|
|
);
|
|
}
|
|
|
|
double calculateDistance(String startLocation) {
|
|
List<String> startLocationParts = startLocation.split(',');
|
|
double startLatitude = double.parse(startLocationParts[0]);
|
|
double startLongitude = double.parse(startLocationParts[1]);
|
|
|
|
double currentLatitude = Get.find<LocationController>().myLocation.latitude;
|
|
double currentLongitude =
|
|
Get.find<LocationController>().myLocation.longitude;
|
|
|
|
return Geolocator.distanceBetween(
|
|
currentLatitude,
|
|
currentLongitude,
|
|
startLatitude,
|
|
startLongitude,
|
|
);
|
|
}
|
|
|
|
// A helper function to safely show dialogs after the build cycle is complete.
|
|
void _showDialogAfterBuild(Widget dialog) {
|
|
// FIX 2: Use addPostFrameCallback to ensure dialogs are shown after the build process.
|
|
// This resolves the "visitChildElements() called during build" error.
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
Get.dialog(
|
|
dialog,
|
|
barrierDismissible: true,
|
|
transitionCurve: Curves.easeOutBack,
|
|
transitionDuration: const Duration(milliseconds: 200),
|
|
);
|
|
});
|
|
}
|
|
|
|
Future<void> getRideAvailable() async {
|
|
try {
|
|
isLoading = true;
|
|
update();
|
|
|
|
LatLngBounds bounds = calculateBounds(
|
|
Get.find<LocationController>().myLocation.latitude,
|
|
Get.find<LocationController>().myLocation.longitude,
|
|
4000);
|
|
|
|
var payload = {
|
|
'southwestLat': bounds.southwest.latitude.toString(),
|
|
'southwestLon': bounds.southwest.longitude.toString(),
|
|
'northeastLat': bounds.northeast.latitude.toString(),
|
|
'northeastLon': bounds.northeast.longitude.toString(),
|
|
};
|
|
|
|
var res =
|
|
await CRUD().get(link: AppLink.getRideWaiting, payload: payload);
|
|
|
|
isLoading = false; // Request is complete, stop loading indicator.
|
|
|
|
if (res != 'failure') {
|
|
final decodedResponse = jsonDecode(res);
|
|
// Check for valid response structure
|
|
if (decodedResponse is Map &&
|
|
decodedResponse.containsKey('message') &&
|
|
decodedResponse['message'] is List) {
|
|
rideAvailableMap = decodedResponse;
|
|
// If the list of rides is empty, show the "No Rides" dialog
|
|
if ((rideAvailableMap['message'] as List).isEmpty) {
|
|
_showDialogAfterBuild(_buildNoRidesDialog());
|
|
}
|
|
} else {
|
|
// If response format is unexpected, treat as no rides and show dialog
|
|
rideAvailableMap = {'message': []};
|
|
_showDialogAfterBuild(_buildNoRidesDialog());
|
|
}
|
|
update(); // Update the UI with new data (or empty list)
|
|
} else {
|
|
// This block now handles network/server errors correctly
|
|
HapticFeedback.lightImpact();
|
|
update(); // Update UI to turn off loader
|
|
// Show a proper error dialog instead of "No Rides"
|
|
_showDialogAfterBuild(
|
|
_buildErrorDialog("Failed to fetch rides. Please try again.".tr));
|
|
}
|
|
} catch (e) {
|
|
isLoading = false;
|
|
update();
|
|
// This catches other exceptions like JSON parsing errors
|
|
_showDialogAfterBuild(
|
|
_buildErrorDialog("An unexpected error occurred.".tr));
|
|
}
|
|
}
|
|
|
|
// Extracted dialogs into builder methods for cleanliness.
|
|
Widget _buildNoRidesDialog() {
|
|
return CupertinoAlertDialog(
|
|
title: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Icon(
|
|
CupertinoIcons.car,
|
|
size: 44,
|
|
color: CupertinoColors.systemGrey,
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
"No Rides Available".tr,
|
|
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600),
|
|
),
|
|
],
|
|
),
|
|
content: Padding(
|
|
padding: const EdgeInsets.only(top: 8),
|
|
child: Text(
|
|
"Please check back later for available rides.".tr,
|
|
style:
|
|
const TextStyle(fontSize: 13, color: CupertinoColors.systemGrey),
|
|
),
|
|
),
|
|
actions: [
|
|
CupertinoDialogAction(
|
|
onPressed: () {
|
|
Get.back(); // Close dialog
|
|
Get.back(); // Go back from AvailableRidesPage
|
|
},
|
|
child: Text('OK'.tr),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildErrorDialog(String error) {
|
|
// You can log the error here for debugging.
|
|
// print("Error fetching rides: $error");
|
|
return CupertinoAlertDialog(
|
|
title: const Icon(
|
|
CupertinoIcons.exclamationmark_triangle_fill,
|
|
color: CupertinoColors.systemRed,
|
|
size: 44,
|
|
),
|
|
content: Text(
|
|
error, // Display the specific error message passed to the function
|
|
style: const TextStyle(fontSize: 14),
|
|
),
|
|
actions: [
|
|
CupertinoDialogAction(
|
|
onPressed: () {
|
|
Get.back(); // Close dialog
|
|
Get.back(); // Go back from AvailableRidesPage
|
|
},
|
|
child: Text('OK'.tr),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
getRideAvailable();
|
|
}
|
|
}
|