queued_toast

Overlay toasts for Flutter that queue per screen position, de-duplicate identical messages, stack up to five at a time, and slide/fade in and out — all without a BuildContext at the call site.

Seven toasts raised at once: five stack up, two wait in the queue

The five toast styles stacked at the centre of the screen One toast at each of the top, center and bottom positions A stack of toasts on a dark-themed screen

Queueing · the five styles · the three positions · dark mode

Features

  • Three positionstop, center and bottom, each with its own independent queue.
  • Adjustable edge offsetoffset sets how far a top or bottom toast sits from its edge, on top of the margin and safe area.
  • Automatic queueing — at most five toasts are visible per position (Toast.maxVisible); the rest wait their turn.
  • De-duplication — a toast whose title and description match one already shown or queued is dropped.
  • Five styleserror, success, info, warning and minimal, each with a default icon and colour.
  • Light/dark themingToastTheme resolves background, text and icon colours from the ambient Theme brightness.
  • Fully overridable per call — colours, icon, radius, elevation, padding, margin, max width, offset, and both durations.
  • Readable by default — high-contrast surfaces, a hairline border, a blurred backdrop, and tuned type sizing, weight, tracking and line height.
  • Interactive when you want it — tap and swipe to dismiss, plus an optional action button. A toast with no callbacks never steals a tap from the screen underneath.
  • Dismissible by handleToast.show returns a ToastHandle for closing or inspecting that one toast.
  • Accessible — every toast is announced to screen readers, and the slide and fade are skipped when the platform asks to reduce motion.
  • No third-party dependencies — nothing but Flutter itself.

Install

dependencies:
  queued_toast: ^1.1.0

Usage

Toasts are rendered into the navigator's overlay, so the package needs your root navigator key once at startup:

import 'package:queued_toast/queued_toast.dart';

final navigatorKey = GlobalKey<NavigatorState>();

void main() {
  Toast.init(navigatorKey: navigatorKey);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) =>
      MaterialApp(navigatorKey: navigatorKey, home: const HomePage());
}

Then show a toast from anywhere — a bloc, a repository, a callback:

Toast.show(
  title: 'Order filled',
  description: '0.5 BTC at 64,120 USDT',
  style: ToastStyle.success,
  position: ToastPosition.top,
);

Toast.show throws if Toast.init has not been called.

Options

Parameter Default Description
description required Body text.
title null Optional bold line above the description.
style ToastStyle.minimal Picks the default background colour and icon.
position ToastPosition.bottom Which queue the toast joins.
duration 2 s How long the toast stays before reversing out. Duration.zero keeps it up until it is dismissed.
animationDuration 300 ms Slide + fade in/out length.
offset 30% of screen height Extra distance from the top or bottom edge, in logical pixels. Ignored at center.
icon style default Replaces the built-in icon entirely.
iconSize 20 Size of the default icon.
bgColor / textColor theme Per-call colour overrides.
borderRadius 14 Corner radius of the card.
elevation 6 Material elevation.
maxWidth 420 Widest the card grows before text wraps.
maxLines null Lines the description may wrap onto before it ellipsizes.
onTap null Called when the card is tapped.
dismissOnTap false Whether a tap also dismisses the card.
dismissOnSwipe false Whether a horizontal drag dismisses the card.
actionLabel / onAction null Trailing button and its callback. The button dismisses the toast after running it.
onDismissed null Called once the card has left the screen, however it went.
padding EdgeInsets.symmetric(horizontal: 16, vertical: 12) Inside the card.
margin EdgeInsets.all(12) Between the card and the screen edge.

The offset is measured on top of margin and the safe area, and only the offset of the oldest toast still on screen at a position is applied — one position is one stack:

Toast.show(
  description: 'Pinned 24px above the bottom edge',
  position: ToastPosition.bottom,
  offset: 24,
);

Dismissing and interaction

Toast.show returns a ToastHandle for the toast it raised:

final handle = Toast.show(
  description: 'Uploading…',
  duration: Duration.zero, // stays until something dismisses it
);

await upload();
handle.dismiss();

Toasts can also be tapped, swiped away, or carry an action button:

Toast.show(
  title: 'Message archived',
  description: 'Moved to All Mail',
  actionLabel: 'Undo',
  onAction: restore,
  dismissOnSwipe: true,
  onDismissed: () => log('gone'),
);

A toast with none of onTap, dismissOnTap, dismissOnSwipe or onAction is wrapped in an IgnorePointer, so it never blocks the screen underneath.

To control the stacks themselves:

Toast.maxVisible = 3;                              // cap per position
Toast.dismissAll(position: ToastPosition.top);     // animate a stack out
Toast.reset();                                     // clear everything at once
Toast.visibleCount(), Toast.queuedCount();         // inspect a stack

Theming

Pass a ToastTheme to Toast.init to restyle every toast at once:

Toast.init(
  navigatorKey: navigatorKey,
  theme: ToastTheme(
    lightBackgroundColors: {ToastStyle.success: Colors.teal.shade700},
    darkBackgroundColors: {ToastStyle.success: Colors.teal.shade900},
    fontFamily: 'Inter',
    titleFontSize: 15,
    titleFontWeight: FontWeight.w600,
    descriptionFontSize: 13.5,
    descriptionOpacity: 0.88,
  ),
);

Typography is tunable beyond size: titleFontWeight, titleLetterSpacing, titleHeight, descriptionFontWeight, descriptionLetterSpacing, descriptionHeight and descriptionOpacity all have defaults tuned for small overlay text, and fontFamily swaps the face for both lines. lightBorderColor and darkBorderColor control the hairline around the card.

ToastTheme.copyWith merges background-colour overrides into the existing maps rather than replacing them, so you can tweak a single style and keep the rest.

Regenerating the screenshots

The images above are rendered headlessly, so they stay in step with the code:

flutter test tool/generate_media.dart --update-goldens && python3 tool/assemble_gif.py

The first command writes the stills into screenshots/ and the GIF's frames into build/gif_frames/; the second encodes those frames into screenshots/queue.gif (needs Pillow). Neither tool/ nor build/ ships with the package.

License

MIT — see LICENSE.

Libraries

queued_toast
Overlay toasts that queue per screen position.