MAUI Cross-Platform Apps
Build cross-platform applications for Android, iOS, Windows, and macOS.
By EMEPublished: February 20, 2025
mauidotnetcross-platformmobilexamarin
A Simple Analogy
MAUI is like building one house that works in different countries. One codebase adjusts to platform requirements automatically (iOS layout vs Android, etc.).
Why MAUI?
- One codebase: Build for iOS, Android, Windows, macOS
- C# everywhere: Use same language across platforms
- Xaml: Declarative UI like WPF
- Native performance: Direct API access per platform
- Hot reload: Instant feedback during development
Project Setup
# Create MAUI app
dotnet new maui -n MyApp
cd MyApp
# Run on platform
dotnet build -t run -f net8.0-android
dotnet build -t run -f net8.0-ios
dotnet build -t run -f net8.0-windows
XAML UI Definition
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MainPage"
Title="Order List">
<VerticalStackLayout Padding="20" Spacing="10">
<Label Text="My Orders" FontSize="24" FontAttributes="Bold" />
<SearchBar x:Name="SearchBar" Placeholder="Search orders..." />
<CollectionView ItemsSource="{Binding Orders}" SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" Spacing="5">
<Label Text="{Binding Id, StringFormat='Order {0}'}" FontAttributes="Bold" />
<Label Text="{Binding Total, StringFormat='${0:F2}'}" FontSize="14" />
<Label Text="{Binding CreatedAt, StringFormat='{0:MMM d, yyyy}'}"
FontSize="12" TextColor="Gray" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Create Order" Command="{Binding CreateOrderCommand}" />
</VerticalStackLayout>
</ContentPage>
ViewModel and Binding
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new OrderViewModel();
}
}
public partial class OrderViewModel : BaseViewModel
{
private readonly IOrderService _orderService;
[ObservableProperty]
ObservableCollection<OrderDto> orders;
public OrderViewModel()
{
_orderService = ServiceHelper.GetService<IOrderService>();
}
[RelayCommand]
public async Task LoadOrders()
{
if (IsBusy) return;
try
{
IsBusy = true;
var orders = await _orderService.GetAllAsync();
Orders = new ObservableCollection<OrderDto>(orders);
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("Error", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
[RelayCommand]
public async Task CreateOrder()
{
await Shell.Current.GoToAsync("create-order");
}
}
Platform-Specific Code
// Conditional compilation
#if ANDROID
var platformSpecific = new Android.SpecificFeature();
#elif IOS
var platformSpecific = new iOS.SpecificFeature();
#elif WINDOWS
var platformSpecific = new Windows.SpecificFeature();
#endif
// Or using if statements with IsAndroid, IsIOS, etc.
if (DeviceInfo.Platform == DevicePlatform.Android)
{
// Android-specific code
}
else if (DeviceInfo.Platform == DevicePlatform.iOS)
{
// iOS-specific code
}
Navigation
// AppShell.xaml
<Shell FlyoutBehavior="Flyout">
<TabBar>
<Tab Title="Orders" Icon="list_icon">
<ShellContent Title="All Orders" ContentTemplate="{DataTemplate local:OrdersPage}" Route="orders" />
</Tab>
<Tab Title="Create" Icon="plus_icon">
<ShellContent ContentTemplate="{DataTemplate local:CreateOrderPage}" Route="create-order" />
</Tab>
<Tab Title="Settings" Icon="settings_icon">
<ShellContent ContentTemplate="{DataTemplate local:SettingsPage}" Route="settings" />
</Tab>
</TabBar>
</Shell>
// Navigate programmatically
await Shell.Current.GoToAsync($"order-detail/{orderId}");
Dependency Injection
// MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
return builder
.UseMauiApp<App>()
.ConfigureServices(services =>
{
services.AddSingleton<IOrderService, OrderService>();
services.AddSingleton<OrderViewModel>();
services.AddSingleton<MainPage>();
})
.Build();
}
}
Best Practices
- Use MVVM: Separate logic from UI
- Responsive layouts: Handle different screen sizes
- Async/await: Keep UI responsive
- Platform abstractions: Hide platform differences
- Test on devices: Emulator ≠ real device
Related Concepts
- Xamarin.Forms (predecessor)
- React Native
- Flutter
- Blazor for web
Summary
MAUI enables building native cross-platform applications with C# and XAML. Share business logic across platforms while maintaining native look and feel.