Isaac.

mobile

MAUI Cross-Platform Apps

Build cross-platform applications for Android, iOS, Windows, and macOS.

By Emem IsaacDecember 7, 20233 min read
#maui#dotnet#cross-platform#mobile#xamarin
Share:

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

  1. Use MVVM: Separate logic from UI
  2. Responsive layouts: Handle different screen sizes
  3. Async/await: Keep UI responsive
  4. Platform abstractions: Hide platform differences
  5. 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.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours