Previous MAUI-with-HttpClient MAUI-HttpClient-Error-Handling Next

.NET MAUI REST API UI with HttpClient (CRUD + Search)

let’s wire up a sample UI page in .NET MAUI that consumes a REST API with HttpClient, supports CRUD operations, and includes a search feature. This will give you a complete working example.

📁 Project Structure (Networking Example)

MyMauiApp/
│
├── Models/
│   └── Post.cs
│
├── Services/
│   └── ApiService.cs
│
├── ViewModels/
│   └── PostViewModel.cs
│
├── Views/
│   └── PostPage.xaml
│   └── PostPage.xaml.cs
│
├── MauiProgram.cs
└── App.xaml / App.xaml.cs

🧩 Code Walkthrough

1. Models/Post.cs

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
}

2. Services/ApiService.cs

using System.Net.Http.Json;

public class ApiService
{
    private readonly HttpClient _httpClient;

    public ApiService(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _httpClient.BaseAddress = new Uri("https://jsonplaceholder.typicode.com/");
    }

    // GET
    public async Task<List<Post>> GetPostsAsync() =>
        await _httpClient.GetFromJsonAsync<List<Post>>("posts");

    // SEARCH
    public async Task<List<Post>> SearchPostsAsync(string keyword)
    {
        var posts = await GetPostsAsync();
        return posts.Where(p => p.Title.Contains(keyword, StringComparison.OrdinalIgnoreCase)).ToList();
    }

    // POST
    public async Task<Post> CreatePostAsync(Post newPost)
    {
        var response = await _httpClient.PostAsJsonAsync("posts", newPost);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<Post>();
    }

    // PUT
    public async Task<Post> UpdatePostAsync(Post updatedPost)
    {
        var response = await _httpClient.PutAsJsonAsync($"posts/{updatedPost.Id}", updatedPost);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<Post>();
    }

    // DELETE
    public async Task DeletePostAsync(int id)
    {
        var response = await _httpClient.DeleteAsync($"posts/{id}");
        response.EnsureSuccessStatusCode();
    }
}

3. ViewModels/PostViewModel.cs

using System.Collections.ObjectModel;

public class PostViewModel : BindableObject
{
    private readonly ApiService _api;

    public ObservableCollection<Post> Posts { get; } = new();

    public PostViewModel(ApiService api)
    {
        _api = api;
        LoadPosts();
    }

    private async void LoadPosts()
    {
        var posts = await _api.GetPostsAsync();
        Posts.Clear();
        foreach (var post in posts)
            Posts.Add(post);
    }

    public async Task AddPost(string title, string body)
    {
        var newPost = new Post { Title = title, Body = body };
        await _api.CreatePostAsync(newPost);
        LoadPosts();
    }

    public async Task UpdatePost(Post post)
    {
        await _api.UpdatePostAsync(post);
        LoadPosts();
    }

    public async Task DeletePost(Post post)
    {
        await _api.DeletePostAsync(post.Id);
        LoadPosts();
    }

    public async Task SearchPosts(string keyword)
    {
        var results = await _api.SearchPostsAsync(keyword);
        Posts.Clear();
        foreach (var post in results)
            Posts.Add(post);
    }
}

4. Views/PostPage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             x:Class="MyMauiApp.Views.PostPage"
             Title="Posts">

    <VerticalStackLayout Padding="20">
        <!-- Add new post -->
        <Entry x:Name="TitleEntry" Placeholder="Title" />
        <Entry x:Name="BodyEntry" Placeholder="Body" />
        <Button Text="Add Post" Clicked="OnAddClicked" />

        <!-- Search -->
        <Entry x:Name="SearchEntry" Placeholder="Search posts..." />
        <Button Text="Search" Clicked="OnSearchClicked" />

        <!-- List of posts -->
        <CollectionView ItemsSource="{Binding Posts}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <StackLayout Padding="5">
                        <Label Text="{Binding Title}" FontAttributes="Bold" />
                        <Label Text="{Binding Body}" />
                        <Button Text="Delete" CommandParameter="{Binding .}" />
                    </StackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

5. Views/PostPage.xaml.cs

public partial class PostPage : ContentPage
{
    private readonly PostViewModel _vm;

    public PostPage(PostViewModel vm)
    {
        InitializeComponent();
        BindingContext = _vm = vm;
    }

    private async void OnAddClicked(object sender, EventArgs e)
    {
        if (!string.IsNullOrWhiteSpace(TitleEntry.Text) && !string.IsNullOrWhiteSpace(BodyEntry.Text))
        {
            await _vm.AddPost(TitleEntry.Text, BodyEntry.Text);
            TitleEntry.Text = string.Empty;
            BodyEntry.Text = string.Empty;
        }
    }

    private async void OnSearchClicked(object sender, EventArgs e)
    {
        if (!string.IsNullOrWhiteSpace(SearchEntry.Text))
            await _vm.SearchPosts(SearchEntry.Text);
        else
            await _vm.SearchPosts(string.Empty); // reload all
    }
}

6. MauiProgram.cs

builder.Services.AddHttpClient<ApiService>();
builder.Services.AddTransient<PostViewModel>();
builder.Services.AddTransient<PostPage>();

✅ Features Covered

  • Create: Add new posts.
  • Read: Fetch posts from API.
  • Update: Modify posts.
  • Delete: Remove posts.
  • Search: Filter posts by keyword.

This gives you a complete REST API consumer in MAUI with HttpClient, CRUD, and search.

Back to Index
Previous MAUI-with-HttpClient MAUI-HttpClient-Error-Handling Next
*