Native Prompt is a native UI plugin for Unity. It provides alerts, bottom sheets, toasts, loading overlays, and in-app review requests on iOS and Android through one small C# API.
- Requirements
- Installation
- Quick Start
- Unity Editor Preview
- Native Alert
- Native Bottom Sheet
- Native Toast
- Native Loading
- Store Review
- Handles
- Lifecycle Events
- Sample Scene
- Documentation
- License
- Unity 6000.0 or later
- iOS 13 or later
- Android API level 24 or later
The Android prompt UI uses Android SDK dialogs and views. It does not require
Material Components, Compose, or another external UI library. Store Review uses
Google Play In-App Review com.google.android.play:review:2.0.2, resolved by the
package's Android Library.
The package ID is com.ishix.nativeprompt.
To install it with Unity Package Manager:
- Open Window > Package Manager in Unity.
- Select Install package from git URL from the add menu.
- Enter the following URL:
https://github.com/IShix-g/NativePrompt.git?path=/Packages/com.ishix.nativeprompt#v1
You can instead add the package directly to your project's
Packages/manifest.json:
{
"dependencies": {
"com.ishix.nativeprompt": "https://github.com/IShix-g/NativePrompt.git?path=/Packages/com.ishix.nativeprompt#v1"
}
}Start with the included sample to try every API without writing setup code.
- Open Window > Package Manager.
- Select Native Prompt, then open the Samples tab.
- Import Native Prompt Sample.
- Open
Assets/Samples/Native Prompt/{version}/Native Prompt Sample/NativePromptSample.unity. - Enter Play Mode and use the sample controls.
For common application flows, see Recipes.
Alert, Bottom Sheet, Toast, Loading, and Store Review have interactive, iOS-inspired
previews in the Game view while running in the Unity Editor. Store Review uses an
image-free simulation and writes a test log; it does not report whether the platform
would display or accept a review. Preview assets are Editor-only and loaded through
AssetDatabase, so they are not included in player builds.
Use an alert for a confirmation or a short notice. Content is required. When Yes
and No are omitted, Native Prompt displays one close button.
NP.ShowAlert(
new AlertOptions
{
Title = "Delete save?",
Content = "This cannot be undone.",
YesButtonText = "Delete",
NoButtonText = "Keep"
},
result =>
{
if (result == AlertResult.Yes)
{
DeleteSave();
}
})
.AddTo(this);Optional: view the Awaitable version
private async Awaitable DeleteSaveWithConfirmationAsync(
CancellationToken cancellationToken = default)
{
// Link cancellation to this GameObject's lifecycle.
using var linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
destroyCancellationToken);
AlertResult result = await NP.ShowAlertAsync(
new AlertOptions
{
Title = "Delete save?",
Content = "This cannot be undone.",
YesButtonText = "Delete",
NoButtonText = "Keep"
},
linkedCancellation.Token);
if (result == AlertResult.Yes)
{
DeleteSave();
}
}Alerts are shown one at a time in request order. On Android, the Back button and a backdrop tap do not close an alert.
See the Alert API reference for all options, results, queue behavior, and manual dismissal.
Use a bottom sheet to offer one to three actions. Give each action a stable, unique ID and handle that ID in the callback.
NP.ShowBottomSheet(
new BottomSheetOptions
{
Title = "Photo",
Actions = new[]
{
new BottomSheetAction
{
Id = "share",
Text = "Share"
},
new BottomSheetAction
{
Id = "delete",
Text = "Delete",
Style = BottomSheetActionStyle.Destructive
}
}
},
result =>
{
if (!result.IsCancelled)
{
RunPhotoAction(result.ActionId);
}
})
.AddTo(this);Optional: view the Awaitable version
private async Awaitable ShowPhotoActionsAsync(
CancellationToken cancellationToken = default)
{
// Link cancellation to this GameObject's lifecycle.
using var linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
destroyCancellationToken);
BottomSheetResult result = await NP.ShowBottomSheetAsync(
new BottomSheetOptions
{
Title = "Photo",
Actions = new[]
{
new BottomSheetAction
{
Id = "share",
Text = "Share"
},
new BottomSheetAction
{
Id = "delete",
Text = "Delete",
Style = BottomSheetActionStyle.Destructive
}
}
},
linkedCancellation.Token);
if (!result.IsCancelled)
{
RunPhotoAction(result.ActionId);
}
}A cancel button, backdrop tap, or Android Back returns a cancelled result. Disabled actions can remain visible without being selectable.
See the Bottom Sheet API reference for action options, result values, validation, and dismissal behavior.
Use a toast for brief feedback that does not interrupt the current flow.
NP.ShowToast(
new ToastOptions
{
Message = "Saved",
Position = ToastPosition.Bottom
},
reason => Debug.Log($"Toast dismissed: {reason}"))
.AddTo(this);Optional: view the Awaitable version
private async Awaitable ShowSavedToastAsync(
CancellationToken cancellationToken = default)
{
// Link cancellation to this GameObject's lifecycle.
using var linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
destroyCancellationToken);
ToastDismissReason reason = await NP.ShowToastAsync(
new ToastOptions
{
Message = "Saved",
Position = ToastPosition.Bottom
},
linkedCancellation.Token);
Debug.Log($"Toast dismissed: {reason}");
}Toasts dismiss automatically after 2.5 seconds by default. Only one toast is visible at a time; a new toast replaces the previous one. Keep the returned handle when you disable automatic dismissal and need to close the toast yourself.
See the Toast API reference for duration, tap behavior, positions, and dismissal reasons.
Use Loading while an operation such as a purchase or network request is running. Loading does not end automatically, so keep the returned handle and dismiss it on every success, failure, and cancellation path.
private LoadingHandle _loading;
public void BeginPurchase()
{
_loading?.Dismiss();
_loading = NP.ShowLoading(new LoadingOptions
{
BlocksInteraction = true,
ShowsBackground = true,
Position = LoadingPosition.Center,
Message = "Processing..."
}).AddTo(this);
}
public void EndPurchase()
{
_loading?.Dismiss();
_loading = null;
}Input blocking and background visibility are separate options. Visual elements appear after a short delay by default, which avoids flashing the spinner for quick operations. Multiple loading handles may coexist; the newest active request controls the shared loading view.
At corner positions, the message sits toward the inside of the screen and the
spinner stays on the outside edge. At Center, the message appears below the
spinner. Long messages are limited to two lines at corners and four lines at the
center.
See the Loading API reference for appearance options, delayed display, overlapping requests, and lifecycle events.
Request the platform-native rating and review flow:
NP.RequestReview();Native Prompt only sends the request. The platform may choose not to display the dialog, and no display or submission result is returned. No additional project configuration is required.
Call it after a meaningful user interaction. See the Store Review API reference for timing, platform behavior, localization, and device testing.
ShowAlert(), ShowBottomSheet(), ShowToast(), and ShowLoading() each return a
handle for that request. The optional Show*Async() variants for Alert, Bottom
Sheet, and Toast return a Unity Awaitable<T> instead; control their lifetime with
a CancellationToken.
- Call
Dismiss()to close it and deliver the normal dismissal result. - Call
Dispose()to remove Alert, Bottom Sheet, or Toast without a result callback or completion event. - Chain
AddTo(this)to clean up automatically when the owningMonoBehaviouris destroyed. - Use
RequestId,Tag, andGroupIdto identify a request. Tags and groups are metadata; they do not dismiss related prompts automatically.
Both Dismiss() and Dispose() are safe to call more than once. Loading has no
per-request result callback; LoadingEnded reports how its request ended.
AlertHandle alert = NP.ShowAlert(new AlertOptions
{
Content = "Continue?"
}).AddTo(this);
// Close this specific alert later if needed.
alert.Dismiss();See the Handle lifetime reference for ownership,
disposal, metadata, and AddTo behavior.
Use the callback passed to Show*() when only the caller needs the result. Use
static lifecycle events when another part of the application needs to observe all
prompts of a type.
| UI | Displayed | Finished |
|---|---|---|
| Alert | NP.AlertOpened |
NP.AlertCompleted |
| Bottom Sheet | NP.BottomSheetOpened |
NP.BottomSheetCompleted |
| Toast | NP.ToastShown |
NP.ToastDismissed |
| Loading | NP.LoadingStarted |
NP.LoadingEnded |
For application-wide behavior that should change while any Loading request is
active, use NP.LoadingStateChanged and initialize from NP.IsLoading:
private void OnEnable()
{
NP.LoadingStateChanged += OnLoadingStateChanged;
OnLoadingStateChanged(NP.IsLoading);
}
private void OnDisable()
{
NP.LoadingStateChanged -= OnLoadingStateChanged;
}
private void OnLoadingStateChanged(bool isLoading)
{
Debug.Log(isLoading ? "Loading started" : "Loading ended");
}Replace the log with application behavior such as muting audio or pausing selected events. The event runs only when the first Loading starts or the last one ends.
Subscribe and unsubscribe with the listener's lifecycle because NP events are
static:
private void OnEnable()
{
NP.AlertCompleted += OnAlertCompleted;
}
private void OnDisable()
{
NP.AlertCompleted -= OnAlertCompleted;
}
private void OnAlertCompleted(AlertCompletedEventArgs args)
{
Debug.Log($"{args.RequestId}: {args.Result}");
}Callbacks and events run on the Unity main thread. For a completion, the
per-request callback runs before the static event. LoadingStarted reports that a
request was accepted, which may be before its delayed spinner becomes visible.
See the Lifecycle event reference for event arguments, metadata, delivery order, Loading counts, and end reasons.
The imported sample includes Alert, Bottom Sheet, Toast, Loading, and Store Review test controls and displays their latest results. Loading controls cover spinner sizes, representative positions, a message, background/input blocking, and manual dismissal. The Store Review result reports only that the request was called. Follow Quick Start to import and open it.
This project is licensed under the MIT License. See LICENSE.







