--- name: maui description: "Build, review, or migrate .NET MAUI applications across Android, iOS, macOS, and Windows with correct cross-platform UI, platform integration, and native packaging assumptions. USE FOR: working on cross-platform mobile or desktop UI in .NET MAUI; integrating device capabilities, navigation, or platform-specific code; migrating Xamarin.Forms or aligning. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made." compatibility: "Requires .NET MAUI workload (.NET 8+)." --- # .NET MAUI ## Trigger On - working on cross-platform mobile or desktop UI in .NET MAUI - integrating device capabilities, navigation, or platform-specific code - migrating Xamarin.Forms or aligning a shared codebase across targets - implementing MVVM patterns in mobile apps ## Documentation - [.NET MAUI Overview](https://learn.microsoft.com/en-us/dotnet/maui/what-is-maui) - [Enterprise Patterns](https://learn.microsoft.com/en-us/dotnet/architecture/maui/) - [MVVM Pattern](https://learn.microsoft.com/en-us/dotnet/architecture/maui/mvvm) - [Controls Reference](https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/) - [Platform Integration](https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/) ### References - [patterns.md](references/patterns.md) - Shell navigation, platform-specific code, messaging, lifecycle, data binding, and CollectionView patterns - [anti-patterns.md](references/anti-patterns.md) - Common MAUI mistakes and how to avoid them ## Platform Targets | Platform | Build Host | Notes | |----------|------------|-------| | Android | Windows/Mac | Emulator or device | | iOS | Mac only | Requires Xcode | | macOS | Mac only | Catalyst | | Windows | Windows | WinUI 3 | ## Workflow 1. **Confirm target platforms** — behavior differs across Android, iOS, Mac, Windows 2. **Separate shared UI and platform code** — use handlers and DI 3. **Follow MVVM pattern** — keep views dumb, logic in ViewModels 4. **Handle lifecycle and permissions** — platform contracts need testing 5. **Test on real devices** — emulators don't catch everything ## Current Upstream Notes - `.NET MAUI` `10.0.100` is a broad quality release for the 10.0 line. It fixes Android WebView gestures inside `SwipeView`, RenderThread crashes and synthetic `about:blank` history; iOS WebView file/reload behavior; Shell regressions; XAML source-generation AOT paths; adaptive-trigger leaks; shared/custom `Platforms` mappings; status-bar contrast, window metrics, and SafeArea behavior. - After upgrading MAUI packages, smoke-test grouped and virtualized `CollectionView` flows, Shell/modal/back navigation, tabs, keyboard and SafeArea interactions, maps, WebView/HybridWebView lifecycle, memory retention, and accessibility narration on every shipped target. - The August 2026 `.NET MAUI` Learn overview for `net-maui-10.0` still frames the platform around a shared single-project app, native API access, handlers, and optional Blazor Hybrid UI. Verify each target platform rather than treating shared code as identical runtime behavior. ## Project Structure ``` MyApp/ ├── MyApp/ # Shared code │ ├── App.xaml # Application entry │ ├── MauiProgram.cs # DI and configuration │ ├── Views/ # XAML pages │ ├── ViewModels/ # MVVM ViewModels │ ├── Models/ # Domain models │ ├── Services/ # Business logic │ └── Platforms/ # Platform-specific code │ ├── Android/ │ ├── iOS/ │ ├── MacCatalyst/ │ └── Windows/ └── MyApp.Tests/ ``` ## MVVM Pattern ### ViewModel with MVVM Toolkit ```csharp public partial class ProductsViewModel(IProductService productService) : ObservableObject { [ObservableProperty] private ObservableCollection _products = []; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(LoadProductsCommand))] private bool _isLoading; [RelayCommand(CanExecute = nameof(CanLoadProducts))] private async Task LoadProductsAsync() { IsLoading = true; try { var items = await productService.GetAllAsync(); Products = new ObservableCollection(items); } finally { IsLoading = false; } } private bool CanLoadProducts() => !IsLoading; } ``` ### View Binding ```xml ``` ## Dependency Injection ```csharp public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); // Services builder.Services.AddSingleton(); builder.Services.AddSingleton(); // ViewModels builder.Services.AddTransient(); builder.Services.AddTransient(); // Pages builder.Services.AddTransient(); builder.Services.AddTransient(); return builder.Build(); } } ``` ## Navigation ### Shell Navigation ```csharp // Register routes Routing.RegisterRoute(nameof(ProductDetailPage), typeof(ProductDetailPage)); // Navigate with parameters await Shell.Current.GoToAsync($"{nameof(ProductDetailPage)}?id={product.Id}"); // Receive parameters [QueryProperty(nameof(ProductId), "id")] public partial class ProductDetailViewModel : ObservableObject { [ObservableProperty] private string _productId; partial void OnProductIdChanged(string value) { LoadProduct(value); } } ``` ### Navigation Service ```csharp public interface INavigationService { Task NavigateToAsync(object? parameter = null); Task GoBackAsync(); } public class NavigationService : INavigationService { public async Task NavigateToAsync(object? parameter = null) { var route = typeof(TViewModel).Name.Replace("ViewModel", "Page"); var query = parameter is null ? "" : $"?id={parameter}"; await Shell.Current.GoToAsync($"{route}{query}"); } public Task GoBackAsync() => Shell.Current.GoToAsync(".."); } ``` ## Platform-Specific Code ### Using Partial Classes ```csharp // Services/DeviceService.cs (shared) public partial class DeviceService { public partial string GetDeviceId(); } // Platforms/Android/DeviceService.cs public partial class DeviceService { public partial string GetDeviceId() { return Android.Provider.Settings.Secure.GetString( Android.App.Application.Context.ContentResolver, Android.Provider.Settings.Secure.AndroidId); } } // Platforms/iOS/DeviceService.cs public partial class DeviceService { public partial string GetDeviceId() { return UIKit.UIDevice.CurrentDevice.IdentifierForVendor?.ToString() ?? ""; } } ``` ### Conditional Compilation ```csharp public string GetPlatformInfo() { #if ANDROID return $"Android {Android.OS.Build.VERSION.Release}"; #elif IOS return $"iOS {UIKit.UIDevice.CurrentDevice.SystemVersion}"; #elif MACCATALYST return "macOS Catalyst"; #elif WINDOWS return "Windows"; #else return "Unknown"; #endif } ``` ## Anti-Patterns to Avoid | Anti-Pattern | Why It's Bad | Better Approach | |--------------|--------------|-----------------| | God ViewModel | Unmaintainable | Split into focused ViewModels | | Logic in code-behind | Hard to test | Use MVVM and commands | | Platform code everywhere | Defeats cross-platform | Use handlers/DI | | Direct service calls in Views | Tight coupling | Use ViewModel | | Ignoring lifecycle | Crashes, leaks | Handle lifecycle events | ## Performance Best Practices 1. **Use compiled bindings:** ```xml ``` 2. **Virtualize long lists:** ```xml ``` 3. **Optimize images:** ```csharp var image = ImageSource.FromFile("image.png"); // Use appropriate resolution for platform ``` 4. **Avoid synchronous work on UI thread:** ```csharp // Bad var data = service.GetData(); // Blocks UI // Good var data = await service.GetDataAsync(); ``` ## Testing ```csharp [Fact] public async Task LoadProducts_UpdatesCollection() { var mockService = new Mock(); mockService.Setup(s => s.GetAllAsync()) .ReturnsAsync(new[] { new Product { Name = "Test" } }); var viewModel = new ProductsViewModel(mockService.Object); await viewModel.LoadProductsCommand.ExecuteAsync(null); Assert.Single(viewModel.Products); Assert.Equal("Test", viewModel.Products[0].Name); } ``` ## Deliver - shared MAUI code with explicit platform seams - MVVM pattern with testable ViewModels - navigation and lifecycle behavior that fits each target - a realistic build and deployment path for the chosen platforms ## Validate - cross-platform reuse is real, not superficial - platform-specific behavior is isolated and testable - MVVM pattern is followed consistently - build assumptions for Mac/iOS and Windows are explicit - performance is acceptable on target devices