Create a simplified Uber-like MAUI app with the following features:
Map integration (Google Maps for Android, Apple Maps for iOS)
Automatic driver assignment after 10 seconds
Real-time driver movement simulation (2km from user's location)
No login/signup screens
Basic Uber-like UI
Implementation
1. Create a new MAUI project
2. Install required NuGet packages
dotnet add package Microsoft.Maui.Controls.Maps dotnet add package Plugin.Geolocator
3. Platform-specific setup
Android:
Add Google Maps API key to
AndroidManifest.xml
Add required permissions:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" />
iOS:
Add
NSLocationWhenInUseUsageDescription
toInfo.plist
Add required entitlements for Apple Maps
4. Main Page Implementation
// MainPage.xaml.cs using Microsoft.Maui.Controls.Maps; using Microsoft.Maui.Maps; using Plugin.Geolocator; using System.Timers; public partial class MainPage : ContentPage { private Pin _driverPin; private Map _map; private Location _userLocation; private Location _driverLocation; private Timer _driverMovementTimer; private bool _driverAssigned = false; private Button _bookRideButton; private Grid _rideStatusPanel; private Label _rideStatusLabel; private Label _driverInfoLabel; private Label _etaLabel; public MainPage() { InitializeComponent(); SetupUI(); LoadMap(); } private void SetupUI() { // Uber-like dark theme this.BackgroundColor = Color.FromArgb("#000000"); // Main layout var mainGrid = new Grid { RowDefinitions = { new RowDefinition { Height = GridLength.Star }, new RowDefinition { Height = GridLength.Auto } } }; // Map _map = new Map { IsShowingUser = true, MapType = MapType.Street }; mainGrid.Add(_map, 0, 0); // Bottom panel var bottomPanel = new Grid { BackgroundColor = Color.FromArgb("#FFFFFF"), Padding = new Thickness(20), RowDefinitions = { new RowDefinition { Height = GridLength.Auto }, new RowDefinition { Height = GridLength.Auto } } }; // Where to? panel var searchPanel = new Frame { BackgroundColor = Color.FromArgb("#F5F5F5"), CornerRadius = 8, Padding = new Thickness(15), Content = new Label { Text = "Where to?", TextColor = Color.FromArgb("#9E9E9E"), FontSize = 18 } }; bottomPanel.Add(searchPanel, 0, 0); // Book ride button _bookRideButton = new Button { Text = "BOOK RIDE", BackgroundColor = Color.FromArgb("#000000"), TextColor = Color.FromArgb("#FFFFFF"), FontSize = 18, FontAttributes = FontAttributes.Bold, CornerRadius = 8, HeightRequest = 50, Margin = new Thickness(0, 15, 0, 0) }; _bookRideButton.Clicked += OnBookRideClicked; bottomPanel.Add(_bookRideButton, 0, 1); // Ride status panel (initially hidden) _rideStatusPanel = new Grid { BackgroundColor = Color.FromArgb("#FFFFFF"), Padding = new Thickness(20), IsVisible = false, RowDefinitions = { new RowDefinition { Height = GridLength.Auto }, new RowDefinition { Height = GridLength.Auto }, new RowDefinition { Height = GridLength.Auto } } }; _rideStatusLabel = new Label { Text = "Driver assigned", FontSize = 18, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Center }; _rideStatusPanel.Add(_rideStatusLabel, 0, 0); _driverInfoLabel = new Label { Text = "Driver: John Doe - Toyota Prius (ABC123)", FontSize = 16, Margin = new Thickness(0, 10, 0, 0), HorizontalOptions = LayoutOptions.Center }; _rideStatusPanel.Add(_driverInfoLabel, 0, 1); _etaLabel = new Label { Text = "ETA: 5 minutes", FontSize = 16, Margin = new Thickness(0, 10, 0, 0), HorizontalOptions = LayoutOptions.Center }; _rideStatusPanel.Add(_etaLabel, 0, 2); bottomPanel.Add(_rideStatusPanel, 0, 1); mainGrid.Add(bottomPanel, 0, 1); Content = mainGrid; } private async void LoadMap() { try { var locator = CrossGeolocator.Current; var position = await locator.GetPositionAsync(TimeSpan.FromSeconds(10)); _userLocation = new Location(position.Latitude, position.Longitude); _map.MoveToRegion(MapSpan.FromCenterAndRadius( _userLocation, Distance.FromKilometers(1))); // Add user pin var userPin = new Pin { Label = "You are here", Location = _userLocation, Type = PinType.Generic }; _map.Pins.Add(userPin); } catch (Exception ex) { await DisplayAlert("Error", "Unable to get location: " + ex.Message, "OK"); } } private async void OnBookRideClicked(object sender, EventArgs e) { _bookRideButton.IsEnabled = false; _bookRideButton.Text = "Searching for driver..."; // Simulate driver search (10 seconds) await Task.Delay(10000); AssignDriver(); } private void AssignDriver() { if (_userLocation == null) return; // Create driver location 2km away from user (random direction) var random = new Random(); var angle = random.NextDouble() * 2 * Math.PI; var distanceKm = 2.0; var earthRadiusKm = 6371.0; var lat1 = _userLocation.Latitude * Math.PI / 180.0; var lon1 = _userLocation.Longitude * Math.PI / 180.0; var lat2 = Math.Asin(Math.Sin(lat1) * Math.Cos(distanceKm / earthRadiusKm) + Math.Cos(lat1) * Math.Sin(distanceKm / earthRadiusKm) * Math.Cos(angle)); var lon2 = lon1 + Math.Atan2(Math.Sin(angle) * Math.Sin(distanceKm / earthRadiusKm) * Math.Cos(lat1), Math.Cos(distanceKm / earthRadiusKm) - Math.Sin(lat1) * Math.Sin(lat2)); _driverLocation = new Location(lat2 * 180.0 / Math.PI, lon2 * 180.0 / Math.PI); // Add driver pin _driverPin = new Pin { Label = "Your driver", Location = _driverLocation, Type = PinType.Place }; _map.Pins.Add(_driverPin); // Show both locations on map var latMin = Math.Min(_userLocation.Latitude, _driverLocation.Latitude); var latMax = Math.Max(_userLocation.Latitude, _driverLocation.Latitude); var lonMin = Math.Min(_userLocation.Longitude, _driverLocation.Longitude); var lonMax = Math.Max(_userLocation.Longitude, _driverLocation.Longitude); var center = new Location((latMin + latMax) / 2, (lonMin + lonMax) / 2); var span = new MapSpan(center, latMax - latMin + 0.02, lonMax - lonMin + 0.02); _map.MoveToRegion(span); _driverAssigned = true; _bookRideButton.IsVisible = false; _rideStatusPanel.IsVisible = true; // Start driver movement simulation StartDriverMovement(); } private void StartDriverMovement() { _driverMovementTimer = new Timer(1000); // Update every second _driverMovementTimer.Elapsed += (sender, e) => { MainThread.BeginInvokeOnMainThread(() => { if (!_driverAssigned) return; // Move driver 0.0001 degrees (~11 meters) towards user each second var latDiff = _userLocation.Latitude - _driverLocation.Latitude; var lonDiff = _userLocation.Longitude - _driverLocation.Longitude; var step = 0.0001; var distance = Math.Sqrt(latDiff * latDiff + lonDiff * lonDiff); if (distance < step) step = distance; // Don't overshoot var newLat = _driverLocation.Latitude + (latDiff / distance) * step; var newLon = _driverLocation.Longitude + (lonDiff / distance) * step; _driverLocation = new Location(newLat, newLon); _driverPin.Location = _driverLocation; // Update ETA var remainingDistanceKm = CalculateDistance(_driverLocation, _userLocation); var etaMinutes = (int)(remainingDistanceKm / 0.5 * 60); // Assuming 30 km/h speed _etaLabel.Text = $"ETA: {etaMinutes} minute{(etaMinutes != 1 ? "s" : "")}"; // If driver arrived if (remainingDistanceKm < 0.01) // 10 meters { _driverMovementTimer.Stop(); _rideStatusLabel.Text = "Driver has arrived!"; } }); }; _driverMovementTimer.Start(); } private double CalculateDistance(Location loc1, Location loc2) { // Haversine formula to calculate distance between two points var lat1 = loc1.Latitude * Math.PI / 180.0; var lon1 = loc1.Longitude * Math.PI / 180.0; var lat2 = loc2.Latitude * Math.PI / 180.0; var lon2 = loc2.Longitude * Math.PI / 180.0; var dLat = lat2 - lat1; var dLon = lon2 - lon1; var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2); var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); return 6371 * c; // Distance in km } protected override void OnDisappearing() { base.OnDisappearing(); _driverMovementTimer?.Stop(); } }
5. App.xaml.cs
public partial class App : Application { public App() { InitializeComponent(); MainPage = new NavigationPage(new MainPage()); } }
Features Implemented
Map Integration:
Uses MAUI Maps which automatically uses Google Maps on Android and Apple Maps on iOS
Shows user location and driver location
Driver Assignment:
After clicking "BOOK RIDE", waits 10 seconds then assigns a driver
Driver appears 2km away from user in a random direction
Real-time Movement:
Driver moves towards user at a realistic speed (~30 km/h)
Updates position every second
ETA is calculated and updated in real-time
Uber-like UI:
Dark theme like Uber
Simple booking flow
Ride status panel showing driver info and ETA
Location Services:
Uses Geolocator plugin to get accurate user location
Proper permissions set up for both platforms
Improvements Needed
Map Customization:
Add custom map pins for user and driver
Add route drawing between driver and user
Real-world Simulation:
Use actual road directions instead of straight-line movement
Add traffic simulation
Performance:
Optimize location updates to balance accuracy and battery life
Consider using background services for real-world apps
Error Handling:
Better handling of location permission denials
Network connectivity checks
This implementation provides a solid foundation for an Uber-like app with the core functionality you requested, while keeping it simple with just a few screens and no authentication requirements.
No comments:
Post a Comment