Vapolia.UserInteraction 5.0.2

Prefix Reserved
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Vapolia.UserInteraction --version 5.0.2
                    
NuGet\Install-Package Vapolia.UserInteraction -Version 5.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Vapolia.UserInteraction" Version="5.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Vapolia.UserInteraction" Version="5.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Vapolia.UserInteraction" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Vapolia.UserInteraction --version 5.0.2
                    
#r "nuget: Vapolia.UserInteraction, 5.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Vapolia.UserInteraction@5.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Vapolia.UserInteraction&version=5.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Vapolia.UserInteraction&version=5.0.2
                    
Install as a Cake Tool

User Interaction for Maui

Platforms: Android, iOS, MacCatalyst, Windows.

Nuget
NuGet
Nuget

https://user-images.githubusercontent.com/190756/233765252-7db79043-6312-4c6b-b59b-d59db1777b01.mp4

Combined with MauiGestures helpers, you can easily set the menu position on tablets and desktops: <img width="566" height="333" alt="image" src="https://github.com/user-attachments/assets/3360f7ce-b9fe-4b5b-814d-34902c9e6112" />

Usage

This library provides two ways to use user interaction dialogs.
Static methods or dependency injection.

But first you need to add a call to .UseUserInteraction(() => Application.Current!.Windows[0]).
See the setup section below for more details.

1. Static Methods

//confirm
var ok = await UserInteraction.Confirm("Are you sure?");

//display a wait indicator while waiting for a long mandatory operation to complete
var dismiss = new CancellationTokenSource();
try
{
  var userCancelled = UserInteraction.WaitIndicator(dismiss.Token, "Please wait", "Loggin in");
  await Task.Delay(3000, userCancelled); //simulate a long operation
}
finally
{
  dismiss.Cancel();
}

//display an obtrusive loading indicator
var dismiss = new CancellationTokenSource();
try
{
  await UserInteraction.ActivityIndicator(dismiss.Token, apparitionDelay: 0.5, argbColor: (uint)0xFFFFFF);
  await Task.Delay(3000); //simulate a long operation
}
finally
{
  dismiss.Cancel();
}

//Single choice menu with optional cancel and destroy items
var cancel = default(CancellationToken); //you can cancel the dialog programatically.
var menu = await UserInteraction.Menu(cancel, true, "Choose something", "Cancel", null, "item1", "item2", ...); //You can add as many items as your want
//returns:
//0 => always cancel action (even if not displayed, ie the cancel text is null)
//1 => always destroy action (even if not displayed, ie the destroy text is null)
//2+ => item1, item2, ...
if (menu >= 2)
{
}

2. Dependency Injection

// In your view model or service, inject IUserInteraction
public class MyViewModel
{
    private readonly IUserInteraction _userInteraction;

    public MyViewModel(IUserInteraction userInteraction)
    {
        _userInteraction = userInteraction;
    }

    public async Task DoSomething()
    {
        // Use the injected service
        var ok = await _userInteraction.Confirm("Are you sure?");

        if (ok)
        {
            await _userInteraction.Toast("Action completed!");
        }
    }

    public async Task ShowComplexExample()
    {
        // Show a menu with multiple options
        var choice = await _userInteraction.Menu(
            title: "Choose an action",
            description: "What would you like to do?",
            cancelButton: "Cancel",
            destroyButton: "Delete All",
            otherButtons: ["Edit", "Share", "Copy"]);

        if (choice >= 2) // User selected an action
        {
            // Show wait indicator during processing
            using var cts = new CancellationTokenSource();
            var waitIndicator = _userInteraction.WaitIndicator(
                cts.Token,
                "Processing...",
                "Please Wait");

            try
            {
                await Task.Delay(2000); // Simulate work
                await _userInteraction.Toast("Operation completed!", ToastStyle.Notice);
            }
            finally
            {
                cts.Cancel();
            }
        }
    }
}

Documentation

Single choice menu

Menu: standard action sheet with single item choice
UIAlertController on iOS. MaterialAlertDialog on android.

Task<int> Menu(CancellationToken dismiss, bool userCanDismiss, string? title, string description, int defaultActionIndex, string cancelButton, string destroyButton, params string[] otherButtons);
Task<int> Menu(CancellationToken dismiss, bool userCanDismiss, string? title, string cancelButton, string? destroyButton, params string[] otherButtons);
Task<int> Menu(CancellationToken dismiss, bool userCanDismiss, RectangleF? position, string? title, string description, int defaultActionIndex, string cancelButton, string destroyButton, params string[] otherButtons);

cancel and destroy buttons are optional, and are displayed differently on iOS.
destroy is in red, cancel is separated from the other buttons.
This is the best UI practice, don't try to change it.

position helps the menu to display around that rectangle, preferably below/right/above/left in this order. This is useful on tablets and large screens.
You can obtain the position of an interaction using the Gesture nuget:

new Command<PointEventArgs>(async args =>
{
  var model = (MyItemModel)args!.BindingContext;
  var position = args.GetAbsoluteBoundsF();
  var choice = await UserInteraction.Menu(default, true, position, "title", cancelButton: "cancel", otherButtons: [ "New", "Open" ]);
...

Wait indicators with or without progress

//Displays a wait indicator (title + body + indeterminate progress bar)
//Returns a controller for the wait indicator
IWaitIndicator WaitIndicator(CancellationToken dismiss, string message = null, string title=null, int? displayAfterSeconds = null, bool userCanDismiss = true);

//Display an activity indicator which blocks user interaction.
Task ActivityIndicator(CancellationToken dismiss, double? apparitionDelay = null, uint? argbColor = null);

Confirmation prompts and alerts

A Toast is an unobtrusive temporary tooltip-like text used to confirm that an action was done succesfully or failed. An Input is an alert popup with one text field. You can choose the keyboard type to limit to numbers for example.
Confirm: native dialog with 2 buttons (mostly used for ok/cancel)
ConfirmThreeButtons: native dialog with 3 choices. Not Task friendly.

Task<bool> Confirm(string message, string title = null, string okButton = "OK", string cancelButton = "Cancel", CancellationToken? dismiss = null);

void ConfirmThreeButtons(string message, Action<ConfirmThreeButtonsResponse> answer, string title = null, string positive = "Yes", string negative = "No",
    string neutral = "Maybe");

Task Alert(string message, string title = "", string okButton = "OK");

Task Toast(string text, ToastStyle style = ToastStyle.Notice, ToastDuration duration = ToastDuration.Normal, ToastPosition position = ToastPosition.Bottom, int positionOffset = 20, CancellationToken? dismiss = null);

Task<string?> Input(string message, string defaultValue = null, string placeholder = null, string title = null, string okButton = "OK", string cancelButton = "Cancel", FieldType fieldType = FieldType.Default, int maxLength = 0, bool selectContent = true);

If selectContent is true (default), the text is automatically selected, so when the user starts typing it is replaced.

Setup

Add the UserInteraction service to your MAUI app in MauiProgram.cs:

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseUserInteraction(() => Application.Current!.Windows[0]); // Register UserInteraction

        return builder.Build();
    }
}
Android Specific Setup

On Android only, add colorSurface to your application's style:

  <style name="MainTheme" parent="MainTheme.Base">

    
    <item name="colorSurface">#333333</item>

  </style>

Also make sure your MainTheme.Base has a parent which is material components:

 <style name="MainTheme.Base" parent="Theme.MaterialComponents.Light.DarkActionBar"> ...

Theme

iOS

set a default color for all activity indicators:

global::Vapolia.UserInteraction.UserInteraction.DefaultColor = 0xAARRGGBB;
Android

This lib uses a standard Android.Material.Dialog that is themed by the material theme as described in the Google documentation.

Also check this stackoverflow answer for a sample.

About

License: MIT

Enterprise support available, contact Vapolia through the live chat.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-android35.0 is compatible.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-ios18.0 is compatible.  net9.0-maccatalyst was computed.  net9.0-maccatalyst18.0 is compatible.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net9.0-windows10.0.19041 is compatible.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.1.0-ci17233381403 202 8/26/2025
5.1.0-ci17232684902 186 8/26/2025
5.0.2 208 8/27/2025
5.0.1 163 8/17/2025
5.0.0 134 8/17/2025
4.0.2 2,661 5/15/2024
4.0.1 234 4/25/2024
4.0.1-ci8787922139 133 4/22/2024
4.0.1-ci8763325192 129 4/20/2024
4.0.0 162 4/19/2024
4.0.0-ci8279237259 215 3/14/2024
4.0.0-ci8231969674 157 3/11/2024
4.0.0-ci8020973172 231 2/23/2024
4.0.0-ci8018262155 208 2/23/2024
4.0.0-ci5906976627 674 8/18/2023
4.0.0-ci5891860424 761 8/17/2023
4.0.0-ci4763543008 1,006 4/21/2023
3.0.2 1,323 7/21/2022
3.0.1 1,217 4/26/2022
3.0.1-pre4 826 2/6/2022
3.0.1-pre3 684 2/5/2022
3.0.1-pre2 686 2/5/2022
3.0.1-pre1 662 2/5/2022
3.0.0 942 1/10/2022
3.0.0-rc5 657 12/28/2021
3.0.0-rc4 630 12/27/2021
3.0.0-rc3 679 12/27/2021
3.0.0-rc2 643 12/26/2021
2.0.5 1,055 7/13/2021
2.0.4 900 6/10/2021
2.0.3 992 5/6/2021
2.0.2 1,043 2/24/2021
2.0.2-pre1 770 2/23/2021
2.0.1 961 1/18/2021
2.0.0-pre2 885 10/8/2020
2.0.0-pre1 825 10/7/2020
1.2.0-ci4762961590 526 4/21/2023

5.1.0 breaking change: namespace is now Vapolia.UserInteractions to prevent conflicts between the namespace and the UserInteraction class  
         5.0.1 replace system.drawing.rectangle by maui.graphics.rectf
         5.0.0 removed xamarin
         4.0.0 unified version with old Xamarin
         1.0.1 replaced ios WaitIndicator from AlertView to UIAlertAction
         --- version is now 1.0.0
         4.0.1: replace android toast by a snackbar, and make its background color customizable.
         4.0.0: supports MAUI. Moved location fetcher to a standalone nuget.
         3.0.1: supports android 12.0. No more interface (like xamarin.essentials)
         2.0.5: update nugets for Android
         2.0.1: now supports decimal input types
         2.0.0: now framework independant!