.NET MAUI Blazor Hybrid Development
Expert guidance for building cross-platform apps with .NET MAUI and Blazor.
Implementation Workflow 1. Analysis Phase (Required)
Before implementation, determine:
App type: Pure MAUI Blazor or MAUI + Blazor Web App (shared UI)
Platform targets: Android, iOS, Windows, macOS
Native features needed: Sensors, camera, file system, notifications
State management: Component state, MVVM, or hybrid approach
Navigation pattern: Blazor-only, Shell, or mixed navigation
2. Architecture Decision
Scenario Recommended Approach
Mobile-first with some web maui-blazor template
Shared UI across mobile + web maui-blazor-web template with RCL
Complex native integration MVVM with platform services
Simple data-driven UI Component state with DI services
3. Project Setup
// MauiProgram.cs - Essential setup
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp
builder.Services.AddMauiBlazorWebView();
if DEBUG
builder.Services.AddBlazorWebViewDeveloperTools();
endif
// Register services
builder.Services.AddSingleton<IDeviceService, DeviceService>();
builder.Services.AddScoped<IDataService, DataService>();
return builder.Build();
}
- BlazorWebView Configuration
Core Patterns
Dependency Injection
Lifetime Use Case
AddSingleton
// Platform implementation registered in MauiProgram.cs
builder.Services.AddSingleton
Component Lifecycle @code { [Parameter] public string Id { get; set; } = "";
// Called once when component initializes
protected override async Task OnInitializedAsync()
{
await LoadDataAsync();
}
// Called when parameters change (including first render)
protected override void OnParametersSet()
{
// React to parameter changes
}
// Called after each render
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// JS interop safe here
}
}
}
Platform Feature Access // Check platform and access native features @inject IDeviceService DeviceService
@if (DeviceInfo.Current.Platform == DevicePlatform.Android)
{
@code {
private async Task AccessCameraAsync()
{
var status = await Permissions.CheckStatusAsync
if (status == PermissionStatus.Granted)
{
// Use camera
}
}
}
State Updates from External Events @implements IDisposable @inject IDataService DataService
@message
@code { private string message = "";
protected override void OnInitialized()
{
DataService.OnDataChanged += HandleDataChanged;
}
private async void HandleDataChanged(object? sender, EventArgs e)
{
message = "Data updated!";
await InvokeAsync(StateHasChanged); // Required for external events
}
public void Dispose()
{
DataService.OnDataChanged -= HandleDataChanged;
}
}
Reference Documentation
For detailed patterns and examples, see:
Project Structure: Solution templates, RCL setup, multi-targeting
Blazor Components: Lifecycle, RenderFragment, EventCallback, data binding
Platform Integration: Device APIs, permissions, platform-specific code
Navigation & Routing: Blazor routing, Shell, deep linking
State Management: MVVM, DI patterns, CommunityToolkit.Mvvm
Quick Reference
Common Service Registration
// Services
builder.Services.AddSingleton
// ViewModels (if using MVVM)
builder.Services.AddTransient
// Pages with DI
builder.Services.AddTransient
Platform Checks // Runtime platform check if (DeviceInfo.Current.Platform == DevicePlatform.iOS) { } if (DeviceInfo.Current.Platform == DevicePlatform.Android) { } if (DeviceInfo.Current.Platform == DevicePlatform.WinUI) { } if (DeviceInfo.Current.Platform == DevicePlatform.macOS) { }
// Compile-time platform check
if ANDROID
// Android-specific code
elif IOS
// iOS-specific code
elif WINDOWS
// Windows-specific code
endif
Navigation Patterns // Blazor navigation (within BlazorWebView) @inject NavigationManager Navigation Navigation.NavigateTo("/details/123");
// MAUI navigation (to other pages) await Navigation.PushAsync(new SettingsPage()); await Shell.Current.GoToAsync("//settings");