Corner-anchored cards carrying a headline, supporting detail and optional
action buttons. Reached through the global notification getter — no
BuildContext required.
notification.success('Done', description: 'Your file was uploaded.');Like message, notifications render into the root navigator's overlay:
MaterialApp(
navigatorKey: UiKit.navigatorKey,
home: const HomePage(),
)| Method | Description |
|---|---|
notification.success(message, ...) |
Success card |
notification.error(message, ...) |
Error card |
notification.warning(message, ...) |
Warning card |
notification.info(message, ...) |
Info card |
notification.open(config) |
Full control via NotificationConfig |
notification.config(...) |
Changes defaults for later calls |
notification.destroy([key]) |
Dismisses everything, or one card by key |
The status openers accept description, duration, placement, actions,
onClose and key, and return a NotificationHandle that dismisses the
card early.
Default duration is 4.5 seconds — longer than a message, since there is more
to read.
Four corners, each an independent stack:
notification.info(
'Saved',
placement: NotificationPlacement.bottomLeft,
);| Value | Behaviour |
|---|---|
topRight |
Default; slides in from the right, stacks downward |
topLeft |
Slides in from the left, stacks downward |
bottomRight |
Slides in from the right, stacks upward |
bottomLeft |
Slides in from the left, stacks upward |
Because the corners are independent, maxCount applies per corner and cards
in different corners never reorder each other.
Cards are 384px wide, capped to the space the viewport leaves between the corner offsets, so they never run off the opposite edge on a narrow screen. Once a corner's cards outgrow the viewport the stack scrolls.
Past stackThreshold cards (3 by default) a corner folds into a deck: the
newest card stays legible and the ones behind it peek out, scaled and faded.
The deck unfolds into the full list on hover, and on touch — where there is no
hover — on a tap. Tapping outside folds it back, as does system back (the
Android button, a back gesture, the browser's back), which is intercepted only
while the deck is held open by a tap.
Collapsing and unfolding is one continuous animation: every card interpolates its own height, offset, scale and opacity, so nothing jumps between two layouts.
notification.config(stack: false); // never fold
notification.config(stackThreshold: 5); // fold past five cardsA card leaves by playing its entry animation in reverse — sliding back towards
the edge it came from while its slot collapses — whether it was dismissed by
the close button, the timeout, destroy() or maxCount.
It can also be swiped away towards that same edge: right for a right-anchored corner, left for a left-anchored one. The swipe commits past 30% of the card's width or on a fling; short of that the card springs back. Dragging the other way only rubber-bands.
late final NotificationHandle close;
close = notification.open(NotificationConfig(
message: const Text('Update available'),
description: const Text('Version 2.0 is ready to install.'),
type: StatusType.info,
duration: Duration.zero,
actions: [
Button(
size: SoftSize.small,
onPressed: () => close(),
child: const Text('Later'),
),
Button(
size: SoftSize.small,
color: ButtonColor.primary,
onPressed: () {
close();
install();
},
child: const Text('Install'),
),
],
));Pair actions with duration: Duration.zero: a card that vanishes mid-decision
is worse than no card. Use late final for the handle so the buttons can
close their own notification.
Reusing a key replaces the card showing under it. destroy(key) dismisses
just that one:
notification.info('Syncing…', duration: Duration.zero, key: 'sync');
notification.success('Synced', key: 'sync'); // replaces it
notification.destroy('sync'); // dismisses itnotification.config(
maxCount: 3,
duration: const Duration(seconds: 8),
placement: NotificationPlacement.bottomRight,
offset: 32, // distance from the screen corner
stack: true, // fold a crowded corner into a deck
stackThreshold: 3, // how many cards before it folds
);| Field | Type | Description |
|---|---|---|
message |
Widget |
Headline (required) |
description |
Widget? |
Supporting detail |
type |
StatusType? |
Status icon; null renders no icon |
duration |
Duration? |
Duration.zero disables auto-dismiss |
placement |
NotificationPlacement? |
Overrides the configured corner |
icon |
Widget? |
Replaces the status icon |
actions |
List<Widget>? |
Buttons along the bottom |
onClose |
VoidCallback? |
Runs after the exit animation |
onTap |
VoidCallback? |
Tap handler for the card body |
closable |
bool |
Shows the close button; defaults to true |
key |
Object? |
Identity for replacement |
token |
NotificationToken? |
Per-instance token overrides |
Omitting type gives a neutral card with no status icon — right for content
that is not success or failure, such as a chat message or a new-item alert.
A notification's cross is a stop in the tab order and answers Space and Enter, with a ring
round it while the keyboard is there. What a screen reader calls it comes from
the kit's own words, in every language it carries — a cross drawn as a glyph
says nothing out loud.
A token on a single notification overrides these for it alone; a
ComponentsConfig(notification: …) on a ConfigProvider does it for every one
raised under it.
NotificationToken overrides this component's own tokens. Every field is an
override; an unset one falls back to the value derived from the global theme.
| Token | Default |
|---|---|
colorBgElevated |
colorBgElevated — the card |
padding |
sizeMD on every side |
borderRadius |
borderRadiusLG |
titleFontSize |
fontSizeLG |
descriptionFontSize |
fontSize |
width |
384 — what the card asks for, before the room it is in has its say |
notification.info('Hello', duration: Duration.zero);
await tester.pump();
await tester.pump(const Duration(milliseconds: 400));
expect(find.text('Hello'), findsOneWidget);
notification.destroy();
await tester.pump();
await tester.pump(const Duration(milliseconds: 400));
await tester.pump();The stack is global, so dismiss everything at the end of each test. Avoid
pumpAndSettle — see testing.