Building a Cross-Platform In-App Notifications System with .NET MAUI

🔔 Building a Cross-Platform In-App Notifications System with .NET MAUI

Real-Time UX Without Leaving Your App

Push notifications are great—but they’re out-of-app by nature. Modern apps also need in-app notifications: contextual messages that appear while the user is actively using your application.

With .NET MAUI, you can design a clean, reusable, cross-platform notification system that works consistently across Android, iOS, and Windows—without relying on external services.

In this guide, we’ll build a scalable in-app notifications layer with:

  • 🧩 Decoupled architecture
  • ⚡ Real-time delivery
  • 🎨 Custom UI overlays
  • 🔄 Queueing & prioritization

🧠 Why In-App Notifications?

Unlike push notifications, in-app notifications:

  • Appear instantly inside the UI ⚡
  • Don’t require OS permissions 🔐
  • Are fully customizable 🎨
  • Can be contextual (based on user actions) 👉 Examples:
  • “Item added to cart” 🛒
  • “Connection lost” ⚠️
  • “Saved successfully” ✅

🏗️ Architecture Overview

A robust system should look like this:

ViewModels / Services
        ↓
INotificationService (Abstraction)
        ↓
NotificationManager (Queue + State)
        ↓
UI Layer (Overlay / Toast / Banner)

🧩 Step 1: Define the Contract

public interface INotificationService
{
    void Show(NotificationMessage message);
}

📦 Notification Model

public class NotificationMessage
{
    public string Title { get; set; } = string.Empty;
    public string Message { get; set; } = string.Empty;
    public NotificationType Type { get; set; }
    public int Duration { get; set; } = 3000;
}

public enum NotificationType
{
    Info,
    Success,
    Warning,
    Error
}

⚙️ Step 2: Notification Manager (Queue System)

public class NotificationManager
{
    private readonly Queue<NotificationMessage> _queue = new();
    private bool _isProcessing;

    public event Action<NotificationMessage>? OnNotification;

    public void Enqueue(NotificationMessage message)
    {
        _queue.Enqueue(message);
        ProcessQueue();
    }

    private async void ProcessQueue()
    {
        if (_isProcessing) return;

        _isProcessing = true;

        while (_queue.Count > 0)
        {
            var message = _queue.Dequeue();
            OnNotification?.Invoke(message);

            await Task.Delay(message.Duration);
        }

        _isProcessing = false;
    }
}

🧩 Step 3: Service Implementation

public class NotificationService : INotificationService
{
    private readonly NotificationManager _manager;

    public NotificationService(NotificationManager manager)
    {
        _manager = manager;
    }

    public void Show(NotificationMessage message)
    {
        _manager.Enqueue(message);
    }
}

🎨 Step 4: UI Layer (Overlay Toast)

XAML Layout

<Grid>
    <!-- Main Content -->
    
    <StackLayout
        x:Name="NotificationContainer"
        VerticalOptions="Start"
        Spacing="10"
        Padding="10" />
</Grid>

Code-Behind Binding

public partial class MainPage : ContentPage
{
    private readonly NotificationManager _manager;

    public MainPage(NotificationManager manager)
    {
        InitializeComponent();
        _manager = manager;

        _manager.OnNotification += ShowNotification;
    }

    private void ShowNotification(NotificationMessage message)
    {
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            var view = CreateNotificationView(message);

            NotificationContainer.Children.Add(view);

            await view.FadeTo(1, 250);

            await Task.Delay(message.Duration);

            await view.FadeTo(0, 250);

            NotificationContainer.Children.Remove(view);
        });
    }
}

🎨 Notification View Factory

private View CreateNotificationView(NotificationMessage message)
{
    return new Frame
    {
        BackgroundColor = GetColor(message.Type),
        CornerRadius = 10,
        Padding = 10,
        Content = new Label
        {
            Text = message.Message,
            TextColor = Colors.White
        },
        Opacity = 0
    };
}

⚖️ In-App vs Push Notifications

📊 Comparative Table

Feature In-App 🔔 Push 📡
Works in foreground
Requires permission
Custom UI ⭐⭐⭐⭐⭐ ⭐⭐
Real-time context ⭐⭐⭐⭐⭐ ⭐⭐
Background delivery

🧠 Best Practices

✅ 1. Queue Notifications

Avoid overlapping messages.


✅ 2. Keep Them Short

2–4 seconds is ideal ⏱️


✅ 3. Use Visual Hierarchy

Color-code by type:

  • 🟢 Success
  • 🔴 Error
  • 🟡 Warning

✅ 4. Avoid Blocking UI

Never use modal dialogs for simple feedback.


✅ 5. Combine with Push

Use push to trigger, in-app to display contextually.


🧱 Advanced Patterns (PRO Level)

🔄 Real-Time Integration (SignalR)

hubConnection.On<string>("Notify", message =>
{
    notificationService.Show(new NotificationMessage
    {
        Message = message,
        Type = NotificationType.Info
    });
});

🎯 Priority System

public int Priority { get; set; }

Sort queue before display.


🎛️ Animation Enhancements

  • Slide-in from top
  • Scale bounce
  • Blur background

🔗 Reference Links


🔔 Final Thoughts

A well-designed in-app notification system turns your app from reactive… into communicative. 👉 It guides users
👉 It confirms actions
👉 It prevents confusion


An unhandled error has occurred. Reload 🗙