Skip to content

Instantly share code, notes, and snippets.

@IeuanWalker
Last active July 21, 2025 09:48
Show Gist options
  • Select an option

  • Save IeuanWalker/af5cd204af87e9b4bfbea606d38ad252 to your computer and use it in GitHub Desktop.

Select an option

Save IeuanWalker/af5cd204af87e9b4bfbea606d38ad252 to your computer and use it in GitHub Desktop.
.NET MAUI Loader logic
using App.Resources.Languages;
using Mopups.Services;
namespace App.Features.Loader;
public class AppLoader : IAsyncDisposable
{
readonly LoadingPopup _defaultLoadingPopup = new();
readonly string _defaultLoadingText = AppResources.LblLoading;
public static async Task<AppLoader> CreateAsync()
{
AppLoader myClass = new();
await myClass.ShowLoader();
return myClass;
}
public static async Task<AppLoader> CreateAsync(string loadingText)
{
AppLoader myClass = new();
await myClass.ShowLoader(loadingText);
return myClass;
}
public async Task ShowLoader(string? loadingText = null)
{
try
{
_defaultLoadingPopup.UpdateText(loadingText ?? _defaultLoadingText);
await MopupService.Instance.PushAsync(_defaultLoadingPopup);
}
catch(Exception)
{
//! Important - Loading pop up is already open. Hide and reshow the loader.
// This mainly happens on page navigation.
// i.e. Loader is open on the previous page and is not closed in time before the OnAppearing method is triggered on the next page
await HideLoader();
await ShowLoader(loadingText);
}
}
public async Task HideLoader()
{
try
{
await MopupService.Instance.RemovePageAsync(_defaultLoadingPopup);
}
catch(Exception)
{
//! Important - thrown if loading pop up has already been removed
}
}
public void UpdateText(string loadingText)
{
_defaultLoadingPopup.UpdateText(loadingText);
}
bool _disposed; // to detect redundant calls
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore();
GC.SuppressFinalize(this);
}
protected virtual async ValueTask DisposeAsyncCore()
{
if(!_disposed)
{
await HideLoader();
_disposed = true;
}
}
}
async void LoaderExample_Clicked(object sender, EventArgs e)
{
await using AppLoader loader = await AppLoader.CreateAsync();
await Task.Delay(2000);
}
<?xml version="1.0" encoding="utf-8" ?>
<Mopups:PopupPage x:Class="App.Features.Loader.LoadingPopup"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Animation="clr-namespace:Mopups.Animations;assembly=Mopups"
xmlns:Mopups="clr-namespace:Mopups.Pages;assembly=Mopups">
<Mopups:PopupPage.Animation>
<Animation:ScaleAnimation DurationIn="500"
PositionIn="Center"
PositionOut="Right"
ScaleIn="1"
ScaleOut="0" />
</Mopups:PopupPage.Animation>
<Border Margin="20,20"
Padding="20"
BackgroundColor="{DynamicResource BackgroundGradientEndColor}"
HorizontalOptions="Center"
VerticalOptions="Center">
<Border.StrokeShape>
<RoundRectangle CornerRadius="10" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="10">
<ActivityIndicator x:Name="Spinner"
AutomationProperties.ExcludedWithChildren="True"
AutomationProperties.IsInAccessibleTree="False"
BackgroundColor="Transparent"
IsRunning="true"
Color="{DynamicResource AccentColour}" />
<Label x:Name="LoadingLbl"
Margin="0"
FontAttributes="Bold"
FontFamily="InterRegular"
HorizontalTextAlignment="Center"
Loaded="LoadingLbl_Loaded"
TextColor="{DynamicResource TextPrimaryColour}"
VerticalTextAlignment="Center" />
</VerticalStackLayout>
</Border>
</Mopups:PopupPage>
using App.Extensions;
using App.Resources.Languages;
using Mopups.Pages;
namespace App.Features.Loader;
public partial class LoadingPopup : PopupPage
{
public LoadingPopup(string? loadingText = null)
{
InitializeComponent();
BackgroundColor = Color.FromArgb("#80000000");
LoadingLbl.Text = loadingText;
if(loadingText is not null)
{
SemanticProperties.SetDescription(LoadingLbl, loadingText.Equals(AppResources.LblLoading) ? AppResources.LblLoadingAccessibility : loadingText);
}
}
protected override bool OnBackButtonPressed()
{
return true;
}
protected override bool OnBackgroundClicked()
{
return false;
}
public void UpdateText(string loadingText)
{
LoadingLbl.Text = loadingText;
SemanticProperties.SetDescription(LoadingLbl, loadingText.Equals(AppResources.LblLoading) ? AppResources.LblLoadingAccessibility : loadingText);
try
{
LoadingLbl.TrySetSemanticFocusForcefully();
}
catch(Exception)
{
// Intentionally left empty
}
}
void LoadingLbl_Loaded(object sender, EventArgs e)
{
if(sender is Label label)
{
label.TrySetSemanticFocusForcefully();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment