Introduction
.NET MAUI (Multi-platform App UI) révolutionne le développement cross-platform en 2026, permettant de cibler Android, iOS, macOS et Windows à partir d'un unique codebase C# et XAML. Contrairement à Xamarin, MAUI simplifie la gestion des handlers natifs et intègre nativement le hot reload. Ce tutoriel intermédiaire vous guide pour créer une app qui fetch des posts via JSONPlaceholder API, utilise MVVM avec CommunityToolkit.Mvvm, et gère la navigation fluide. Pourquoi c'est crucial ? Les apps MAUI offrent des performances natives (jusqu'à 2x plus rapides que Flutter sur certains benchmarks) et un partage de code >90%. À la fin, vous aurez une app bookmarkable, déployable en un clic, avec 1500+ lignes de code prêtes à l'emploi. Idéal pour booster votre carrière .NET mobile.
Prérequis
- Visual Studio 2022 17.10+ avec workload ".NET Multi-platform App UI development" installé (via Visual Studio Installer).
- .NET 8.0 ou supérieur (SDK MAUI inclus).
- Connaissances de base en C#, XAML et async/await.
- Émulateur Android ou simulateur iOS configurés.
- Un compte gratuit JSONPlaceholder pour les tests API.
Créer le projet MAUI
dotnet new maui -n MauiPostsApp --framework net8.0
cd MauiPostsApp
dotnet buildCette commande initialise un projet MAUI vide avec .NET 8, configure les plateformes cibles (Android/iOS/Windows/macOS) et compile pour vérifier l'environnement. Le flag --framework assure la compatibilité 2026. Évitez dotnet new maui-blazor pour ce tut UI natif.
Structure initiale du projet
Le projet généré inclut Platforms/ pour les configs natives, Resources/ pour Styles/Images, MauiProgram.cs pour le DI, et MainPage.xaml comme entrée. Ouvrez dans Visual Studio : F5 lance sur l'émulateur par défaut. Pensez à MAUI comme un 'Blazor pour mobile' : XAML = markup, C# = logique partagée.
Installer les packages NuGet
dotnet add package CommunityToolkit.Mvvm --version 8.3.2
dotnet add package System.Net.Http.Json --version 8.0.0
dotnet restoreCommunityToolkit.Mvvm apporte [ObservableProperty] et [RelayCommand] pour MVVM sans boilerplate. Http.Json simplifie les appels API JSON. Versionnez précisément pour éviter breaking changes en 2026 ; restore rafraîchit les dépendances.
Configurer MauiProgram.cs (DI)
using CommunityToolkit.Mvvm.DependencyInjection;
using MauiPostsApp.Services;
namespace MauiPostsApp;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddTransient<HttpPostsService>();
builder.Services.AddTransient<MainViewModel>();
#if DEBUG
builder.Services.AddTransient<IViewModelLocator, ViewModelLocator>();
#endif
return builder.Build();
}
}On enregistre les services singleton/transient pour MVVM et HTTP. IViewModelLocator (de CommunityToolkit) résout les ViewModels automatiquement. Le #if DEBUG limite aux builds debug ; cela évite les fuites mémoire en prod.
Mise en place du DI et services
Avec ce DI natif (basé Microsoft.Extensions), vos ViewModels sont injectés sans code-behind pollué. Analogie : comme un conteneur Spring en .NET. Prochaine étape : modéliser les données.
Créer le modèle Post
namespace MauiPostsApp.Models;
public class Post
{
public int Id { get; set; }
public int UserId { get; set; }
public string Title { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
}Modèle POCO simple mappé à l'API JSONPlaceholder. Utilisez string.Empty pour init nullable reference types (C# 8+). Pas d'Observable ici : les props seront bindées via ViewModel.
Implémenter le service HTTP
using System.Net.Http.Json;
using MauiPostsApp.Models;
namespace MauiPostsApp.Services;
public class HttpPostsService
{
private readonly HttpClient _httpClient;
public HttpPostsService()
{
_httpClient = new HttpClient();
}
public async Task<List<Post>> GetPostsAsync()
{
try
{
var posts = await _httpClient.GetFromJsonAsync<List<Post>>("https://jsonplaceholder.typicode.com/posts?_limit=10");
return posts ?? new();
}
catch (Exception)
{
return new();
}
}
}HttpClient injecté manuellement (singleton-like). GetFromJsonAsync désérialise directement en List
Gestion des données asynchrones
- Service découplé pour testabilité.
- Permissions : Ajoutez
dans Platforms/Android/AndroidManifest.xml (auto en MAUI 8+).
Créer le ViewModel principal
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MauiPostsApp.Models;
using MauiPostsApp.Services;
namespace MauiPostsApp.ViewModels;
public partial class MainViewModel : ObservableObject
{
private readonly HttpPostsService _postsService;
[ObservableProperty]
private ObservableCollection<Post> posts = new();
[ObservableProperty]
private bool isLoading;
public MainViewModel(HttpPostsService postsService)
{
_postsService = postsService;
}
[RelayCommand]
private async Task LoadPostsAsync()
{
IsLoading = true;
Posts.Clear();
var fetchedPosts = await _postsService.GetPostsAsync();
foreach (var post in fetchedPosts)
{
Posts.Add(post);
}
IsLoading = false;
}
}ObservableObject + [ObservableProperty] génère INotifyPropertyChanged auto. [RelayCommand] gère async sans boilerplate. Injectez le service via ctor pour test unitaire. Clear() + Add pour rafraîchissement UI fluide.
Définir la page XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="MauiPostsApp.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MauiPostsApp.ViewModels"
x:DataType="viewmodels:MainViewModel"
Title="Posts MAUI">
<VerticalStackLayout Padding="20">
<ActivityIndicator IsRunning="{Binding IsLoading}"
IsVisible="{Binding IsLoading}"
Color="Blue" HorizontalOptions="Center" />
<Button Text="Charger les Posts"
Command="{Binding LoadPostsCommand}"
HorizontalOptions="Center"
Margin="10" />
<ListView ItemsSource="{Binding Posts}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text="{Binding Title}"
FontAttributes="Bold"
FontSize="18"
LineBreakMode="TailTruncation" />
<Label Grid.Row="1"
Text="{Binding Body}"
FontSize="14"
LineBreakMode="TailTruncation"
MaxLines="2"
Margin="0,5,0,0" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
</ContentPage>x:DataType active compiled bindings (perf x10). Binding directement sur commandes/propriétés ViewModel. ListView avec DataTemplate pour custom UI ; HasUnevenRows gère hauteurs variables comme RecyclerView Android.
Code-behind de la page
using CommunityToolkit.Mvvm.DependencyInjection;
using MauiPostsApp.ViewModels;
namespace MauiPostsApp;
public partial class MainPage : ContentPage
{
public MainPage(MainViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
}Injectez ViewModel via ctor (DI résout). BindingContext = viewModel lie XAML au VM. Pas de logique ici : pur MVVM. Sur Navigation, ctor s'appelle auto.
Test et déploiement
F5 pour lancer ! Cliquez "Charger les Posts" : liste apparaît avec spinner. Testez sur émulateur Android (x86_64 pour vitesse). Pour prod : dotnet publish -f net8.0-android génère APK.
Bonnes pratiques
- Toujours MVVM : Sépare UI/logique pour 90% code partagé.
- Utilisez Compiled Bindings (x:DataType) pour perf et IntelliSense.
- HttpClientFactory en prod : Remplacez new HttpClient() par builder.Services.AddHttpClient().
- Hot Reload : Éditez XAML/C#, sauve = preview instantané.
- Versionnez NuGet et testez multi-plateformes (DeviceInfo.Platform).
Erreurs courantes à éviter
- Oublier BindingContext : UI statique → vérifiez ctor injection.
- HttpClient sans Dispose : Memory leaks → utilisez IHttpClientFactory.
- Pas de try-catch async : App crash offline → toujours fallback List vide.
- ListView sans virtualization : Lag sur 100+ items → _limit=10 ou CollectionView.
Pour aller plus loin
- Docs officielles : learn.microsoft.com/dotnet/maui.
- Avancé : Shell navigation, SQLite EF Core, BlazorHybrid.
- Formations pro : Découvrez nos formations Learni sur .NET MAUI.
- Repo GitHub exemple : Forkez et contribuez !