Alert
iOS 26's alert on glass: a title, an optional message and capsule actions, shown over a dim with showGlassDialog. It materializes in, blur first and tint last.
GlassAlert · GlassAlertAction · showGlassDialog() · kGlassAlertRadius · kGlassAlertWidth · kGlassModalDim · Source
GlassAlert is iOS 26's alert: a 320 px glass panel with 34 px corners, a title, an optional message and capsule
action buttons. You show it with showGlassDialog, which pushes it on the navigator, centred over a 20% black dim, and
returns a Future with the value the alert was closed with.
The alert materializes in: blur first, tint last, with the text fading in on top. It lifts itself above page glass and glass bars, so you don't need a GlassAbove for it.
When to use
- Short confirmations and destructive-action prompts: "Delete this photo?", "Discard changes?".
- A message the user must acknowledge before going on.
- Not for forms, pickers or anything long. Use a sheet.
- Not for a list of actions on an item. Use a menu.
Usage
final bool? delete = await showGlassDialog<bool>(
context: context,
builder: (BuildContext context) => GlassAlert(
title: const Text('Delete "Lisbon.jpg"?'),
message: const Text('This cannot be undone.'),
actions: <GlassAlertAction>[
GlassAlertAction(label: 'Cancel', onPressed: () => Navigator.pop(context, false)),
GlassAlertAction(label: 'Delete', isDestructive: true, onPressed: () => Navigator.pop(context, true)),
],
),
);
Note
Actions don't close the alert by themselves. Call Navigator.pop(context, value) in each onPressed. The value
you pop with is what showGlassDialog returns.
Behaviour
- Exactly two actions sit side by side. One, or three and more, are stacked.
isDefaultdraws an action's label bold;isDestructivedraws it in iOS red.onPressed: nulldisables it.- The title is 17 px semibold, the message 15 px at 60% of the label colour. Both start-aligned by default; pass
textAlign: TextAlign.centerto yourTexts for the centred iOS look. - The panel is 320 px wide, or the screen width less 16 px on each side when that is narrower.
- By default a tap on the dim does nothing, as in iOS. Set
barrierDismissible: trueto let it close the alert; theFuturethen completes withnull.
Setup
Warning
The GlassHost must be above the navigator, so put it in MaterialApp(builder: ...). The
alert is built in the navigator's overlay; with the host inside a page instead, it finds no host and says so in debug.
Accessibility
- The dim is announced with
barrierLabel("Dismiss" by default) when it can close the alert. - Keep action labels short verbs ("Delete", "Keep"), not "Yes" and "No".
Code
import 'package:flutter/material.dart';
import 'package:g1455/g1455.dart';
/// A row that asks before deleting its file.
/// Assumes a GlassHost above the navigator, e.g. in MaterialApp.builder.
class FileTile extends StatefulWidget {
const FileTile({required this.name, required this.onDelete, super.key});
final String name;
final VoidCallback onDelete;
@override
State<FileTile> createState() => _FileTileState();
}
class _FileTileState extends State<FileTile> {
Future<void> _confirm() async {
final bool? delete = await showGlassDialog<bool>(
context: context,
builder: (BuildContext context) => GlassAlert(
title: Text('Delete "${widget.name}"?', textAlign: TextAlign.center),
message: const Text('It will be removed from all your devices.', textAlign: TextAlign.center),
actions: <GlassAlertAction>[
// Actions don't close the alert: pop with the answer.
GlassAlertAction(label: 'Cancel', onPressed: () => Navigator.pop(context, false)),
GlassAlertAction(label: 'Delete', isDestructive: true, onPressed: () => Navigator.pop(context, true)),
],
),
);
if (delete ?? false) {
widget.onDelete();
}
}
@override
Widget build(BuildContext context) => ListTile(
leading: const Icon(Icons.image_outlined),
title: Text(widget.name),
trailing: GlassButton(
onPressed: _confirm,
semanticLabel: 'Delete ${widget.name}',
padding: EdgeInsets.zero,
child: const Icon(Icons.delete_outline),
),
);
}API
showGlassDialog<T>
| Parameter | Type | Default | Description |
|---|---|---|---|
context |
BuildContext |
required | Where to find the navigator. |
builder |
WidgetBuilder |
required | Builds the dialog, usually a GlassAlert. Centred in a SafeArea. |
barrierDismissible |
bool |
false |
Whether a tap on the dim closes the dialog (with null). |
barrierLabel |
String |
'Dismiss' |
What a screen reader says for the dim. |
transitionDuration |
Duration |
Duration(milliseconds: 250) |
How long the alert takes to materialize. |
| returns | Future<T?> |
The value passed to Navigator.pop, or null. |
GlassAlert
| Parameter | Type | Default | Description |
|---|---|---|---|
title |
Widget |
required | The title, 17 px semibold. |
message |
Widget? |
null |
The message under it, 15 px at 60% of the label colour. |
actions |
List<GlassAlertAction> |
[] |
The buttons. Two sit side by side; other counts are stacked. |
finish |
GlassFinish? |
null |
The glass. Null takes the theme's. |
GlassAlertAction
| Parameter | Type | Default | Description |
|---|---|---|---|
label |
String |
required | The button's text. |
onPressed |
VoidCallback? |
null |
The action. Null disables it. It does not close the alert. |
isDefault |
bool |
false |
Draws the label bold, as the preferred action. |
isDestructive |
bool |
false |
Draws the label in iOS red. |
Constants
| Name | Value | Description |
|---|---|---|
kGlassAlertWidth |
320 |
The alert's width. |
kGlassAlertRadius |
34 |
The alert's corner radius. |
kGlassModalDim |
Color.fromRGBO(0, 0, 0, 0.20) |
The dim behind an alert and a sheet. |