.NET Multi-platform App UI (.NET MAUI) is Microsoft’s evolution of cross-platform development, enabling you to build native applications for Android, iOS, macOS, and Windows using a single shared codebase. With .NET MAUI, developers can create rich, responsive user interfaces leveraging familiar XAML and C# constructs, all while enjoying a simplified project structure and improved tooling in Visual Studio.
The platform builds on the legacy of Xamarin.Forms by addressing its limitations and expanding its reach. While Xamarin.Forms focused primarily on mobile (Android and iOS), .NET MAUI extends support to desktop platforms like macOS and Windows. This unification streamlines development with a single project model and enhances performance, resource management, and platform-specific customization. Essentially, if you’re familiar with Xamarin.Forms, you’ll find that MAUI carries forward its core principles while offering a more integrated and versatile approach to cross-platform development.
In this blog, I’ll demonstrate how to harness .NET MAUI’s capabilities by building a Google Maps app. I’ll walk you through how to set up your environment and how to integrate native services like the Google Maps Android SDK—all within the modern, unified framework that .NET MAUI provides. (For the sake of brevity, we will just be focusing on running on the Android Platform, but these principles can be used for iOS as well.)
Setup
For this post, I am using Visual Studio, which can be downloaded here. Through the guided install process, there is the option to install MAUI. Once you have it installed, go ahead and create a new MAUI project. The folder structure should look similar to this:
For this project, we will be deploying to the Android platform, so go ahead and install an Android emulator if you haven’t done so already. This can be done by going to Tools->Android->Android Device Manager.
Make sure the device RAM is greater than 3GB, just to be cautious, as running Google Maps on Android requires at least 2GB. We will also need to install the Map Control NuGet package, which is Microsoft.Maui.Controls.Maps. Once you have this setup running, we can go ahead and update the code.
Getting Google Maps Android SDK API Key
In order to use the map on Android, we’ll need to get an API Key. To do so, follow the steps that are listed on the Google Developer website.
Showing a Location on the Map
Once you have the API Key, the fun really begins. Let’s go ahead and add the necessary code in the Android project. Open up the AndroidManifest.xml
file as an editable XML file, and add the following inside the application
tags.
<meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_API_KEY" />
YOUR_API_KEY
is the key that you got from Google Developers. Now, let’s go ahead and add a specific location that we will show on the map.
Open up the file MainPage.xaml.cs
replace everything in there with the following code.
using Microsoft.Maui.Maps; using Map = Microsoft.Maui.Controls.Maps.Map; namespace CrossPlatformMap { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); Location location = new Location(42.361145, -71.057083); MapSpan mapSpan = new MapSpan(location, 0.1, 0.1); Map map = new Map(mapSpan); Content = map; } } }
This is the main page of the application, defined in MainPage.xaml.cs
, which starts by initializing the user interface through the InitializeComponent()
method. This is standard in .NET MAUI and ensures all XAML-defined elements are correctly loaded.
The code then defines a specific geographical location using the Location
class, setting the latitude and longitude for downtown Boston. The MapSpan
object is used to create a viewable region around this location, with the parameters defining the zoom level. By creating a new instance of the Map
class and passing the MapSpan
, we instruct the app to display this specific area.
Now, run the app. The emulator will bring up a map centered on downtown Boston.
Adding Interactive Elements to the Map
Our map centers on the correct area (Boston), but let’s take it a step further; let’s make it more interactive. How about we find and display five restaurants near the area, and then create pins that users can click on to display more information.
First create a new JSON file called restaurants.json
and populate it with the following data.
[ { "name": "The Paramount", "description": "Popular American diner with a lively, casual atmosphere.", "website_link": "https://www.paramountboston.com", "latitude": 42.361000, "longitude": -71.057500 }, { "name": "Legal Sea Foods – Long Wharf", "description": "Renowned seafood chain serving fresh, classic New England dishes.", "website_link": "https://www.legalseafoods.com", "latitude": 42.360200, "longitude": -71.050300 }, { "name": "Mamma Maria", "description": "Elegant Italian restaurant offering fine cuisine in a cozy setting.", "website_link": "https://www.mammamaria.com", "latitude": 42.363000, "longitude": -71.053500 }, { "name": "Neptune Oyster", "description": "Acclaimed seafood spot serving fresh oysters and creative dishes.", "website_link": "https://www.neptuneoyster.com", "latitude": 42.363700, "longitude": -71.054700 }, { "name": "Row 34", "description": "Trendy seafood restaurant offering oysters, craft beers, New England flavors.", "website_link": "https://row34.com", "latitude": 42.346100, "longitude": -71.034000 } ]
Add that to the Resources
folder of the MAUI project, which will automatically deploy the file along with the application.
Once that’s done, we will add a new function that loads the following data and returns an array of Restaurant
objects.
Restaurant
Objects:
public class Restaurant { public string name { get; set; } public string description { get; set; } public string website_link { get; set; } public double latitude { get; set; } public double longitude { get; set; } }
Load File:
async Task<List<Restaurant>> LoadRestaurantsMauiAsset() { using var stream = await FileSystem.OpenAppPackageFileAsync("restaurants.json"); using var reader = new StreamReader(stream); var contents = reader.ReadToEnd(); List<Restaurant> restaurants = JsonSerializer.Deserialize<List<Restaurant>>(contents); return restaurants; }
This asynchronous call to open the text file allows a non-blocking approach, which ensures that the UI remains responsive while the file is read and parsed. The JSON is deserialized into a list of Restaurant
objects, each containing details such as the name, description, website link, and coordinates.
Loading and Displaying Nearby Restaurants
Since this is running asynchronously, let’s go ahead and move our map creation code into the OnAppearing
override method. This method will run right before the page is displayed to the user.
protected async override void OnAppearing() { base.OnAppearing(); List<Restaurant> restaurants = await LoadRestaurantsMauiAsset(); Location location = new Location(42.361145, -71.057083); MapSpan mapSpan = new MapSpan(location, 0.1, 0.1); Map map = new Map(mapSpan); Content = map; }
The OnAppearing
method is a key lifecycle event in .NET MAUI that is invoked just before a page is displayed to the user. As part of the ContentPage
class, OnAppearing
is designed to allow you to execute custom code every time the page becomes visible. This makes it an ideal place to initialize or refresh data, update the UI, or start animations.
Now, let’s create a new function that will take the list of restaurants and create the necessary pins.
private List<Pin> CreatePinsForRestaurants(List<Restaurant> restaurants) { var pins = new List<Pin>(); foreach (var restaurant in restaurants) { var pin = new Pin { Label = restaurant.name, Address = restaurant.description, Type = PinType.Place, Location = new Location(restaurant.latitude, restaurant.longitude) }; pins.Add(pin); } return pins; }
Our OnAppearing
function should now look like this:
protected async override void OnAppearing() { base.OnAppearing(); List<Restaurant> restaurants = await LoadRestaurantsMauiAsset(); List<Pin> pins = CreatePinsForRestaurants(restaurants); Location location = new Location(42.361145, -71.057083); MapSpan mapSpan = new MapSpan(location, 0.1, 0.1); Map map = new Map(mapSpan); foreach (var pin in pins) { map.Pins.Add(pin); } Content = map; }
Now, we should see the pins on the map, and when we click on each one, we should see the text we added.
Making Map Pins Clickable
Finally, let’s add a click handler on the info window, such that when it is clicked, it will ask the user if they want to navigate to that restaurant’s website. Right after the pin object is created, add the following.
pin.InfoWindowClicked += async (s, args) => { string pinName = ((Pin)s).Label; bool navigate = await DisplayAlert( pinName, $"Navigate to website?", "Yes", "No"); if (navigate) { await Launcher.OpenAsync(new Uri(restaurant.website_link)); } };
DisplayAlert
is a method in .NET MAUI used to show a simple modal dialog to the user with options like “OK” or “Yes/No”. It pauses user interaction until the dialog is dismissed and can return a boolean value based on the user’s choice. The Launcher
class in .NET MAUI allows you to open external links, such as URLs or phone numbers, in the device’s default browser or app.
Now when we click the info window we will see this popup:
Conclusion
This guide has taken you through the process of creating a dynamic, interactive Google Maps application using .NET MAUI. MAUI is a modern, unified framework that builds on and extends the legacy of Xamarin.Forms. By setting up your environment, integrating the Google Maps SDK, and implementing features such as location pins and clickable info windows, you’ve seen firsthand how .NET MAUI simplifies cross-platform development.
With a single shared codebase, you can now target Android, iOS, macOS, and Windows, unlocking new possibilities for building responsive and rich user interfaces. This project not only illustrates the seamless integration of native services but also provides a solid foundation for expanding your app with additional features and customizations.
As you move forward, consider exploring more advanced functionalities—like real-time location tracking or data-driven map updates—to further harness the power of .NET MAUI. Follow the Keyhole Dev Blog for more coding tutorials spanning MAUI and more. Happy coding!