sole_toast 0.2.0 copy "sole_toast: ^0.2.0" to clipboard
sole_toast: ^0.2.0 copied to clipboard

A gooey, morphing toast for Flutter — organic pill-to-blob animation, light/dark/glossy modes, semantic types, and iPhone Dynamic Island choreography. Zero dependencies.

Sole Toast — a gooey, morphing toast for Flutter

Sole Toast #

pub package license: MIT platforms dependencies

A gooey, morphing toast for Flutter. A compact pill lands on screen, the body melts out of it with an organic blob morph, and after a few seconds it folds itself back up and slips away — driven by physics-based springs, a landing squish, and an iPhone Dynamic Island choreography.

Zero runtime dependencies. No assets, no icon fonts, no third-party packages — every shape, icon, and animation is painted and simulated with Flutter primitives.

Real, unedited captures from the example app on an iPhone 16 Pro.

Glossy · success Light · info Dark · warning
Glossy success toast Light mode info toast Dark mode warning toast
Error + action button Promise (loading pill) Countdown progress
Error toast with retry action Loading spinner pill Toast with countdown progress bar

The Dynamic Island choreography #

1 — the icon melts out beneath the island 2 — the sheet continues down
Icon docked beside the Dynamic Island Sheet expanded beneath the Dynamic Island

Table of contents #

Platform support #

Sole Toast is pure Dart/Flutter — no native code, no platform channels, no plugins — so it runs on every platform Flutter targets:

Android iOS Web macOS Windows Linux

Platform notes: the Dynamic Island choreography activates automatically only on island iPhones (everywhere else the standard top-center drop is used — or force the island preview with SoleIslandMode.always). Haptics fire where the OS supports them and no-op silently elsewhere. The glossy backdrop blur renders on all platforms with Flutter's default renderer.

Features #

  • Organic pill → blob morph — the background is a parametric path regenerated every frame, so the body genuinely grows out of the pill and text never reflows mid-animation.
  • Semantic typessuccess, error, warning, info. The accent color, stroke-drawn icon, and action-button tint all derive from the type.
  • Three surface modeslight, dark, and glossy: a frosted hybrid (backdrop blur + translucent tint + specular sheen + hairline border) that adapts to the app's brightness.
  • Dynamic Island choreography — on island iPhones the toast docks to the hardware cutout: a gooey chin melts out beneath the island revealing the type icon, then the sheet continues down through the title and content. Everything reveals downward, so it never collides with the status-bar clock, signal, or battery. Auto-detected, can be forced for demos or disabled entirely.
  • Physics everywhere — expand/collapse springs, a landing squish, a header squish, an error shake, icon draw-in strokes, and a single bounce dial (0.05–0.8) that tunes them all. Presets: smooth, bouncy, subtle, snappy.
  • Promise toasts — spinner → success/error morph; in-place update(); action buttons with an optional success-label morph-back.
  • Mobile-first interactions — swipe to dismiss, tap a folding toast to re-open it, long-press to pause the timer.
  • Considerate by default — stacking with a FIFO queue, optional countdown progress bar and timestamps, reduced-motion support, screen-reader announcements, optional haptics.

Installation #

flutter pub add sole_toast

Or add it to pubspec.yaml manually:

dependencies:
  sole_toast: ^0.2.0

Setup #

Mount the toast layer once, at the root of your app:

import 'package:sole_toast/sole_toast.dart';

MaterialApp(
  builder: SoleToast.init(),
  home: const HomePage(),
);

Already using builder (for example EasyLoading.init() or a text-scale override)? Chain it:

MaterialApp(
  builder: SoleToast.init(builder: yourExistingBuilder),
);

Quick start #

SoleToast.success('Saved', description: 'Your changes have been synced.');

SoleToast.error('Payment failed',
    description: 'Your card was declined. Please try again.');

SoleToast.warning('Storage almost full');

SoleToast.info('Share link ready',
    description: 'Your link has been generated.',
    action: SoleToastAction(
      label: 'Copy to clipboard',
      successLabel: 'Copied!',   // morphs back to a pill saying this
      onPressed: copyLink,
    ));

API reference #

Methods #

Method Description
SoleToast.show(title, …) Shows a toast (defaults to info). Returns its id.
SoleToast.success / error / warning / info(title, …) Type-specific shorthands.
SoleToast.promise<T>(future, loading:, success:, error:, …) Spinner toast that morphs when the future settles. Returns the future's value; errors rethrow.
SoleToast.update(id, …) Mutates a visible or queued toast in place (title, description, type, action).
SoleToast.dismiss([id]) Folds up one toast — or every toast when called without an id.
SoleToast.dismissByType(type) Dismisses all visible and queued toasts of one type.
SoleToast.dismissAll() Alias for dismiss().
SoleToast.init({builder}) Returns the TransitionBuilder that mounts the layer.
SoleToast.config Read/assign the global SoleToastConfig.

Per-toast options #

Option Type Description
description String? Body text revealed by the blob morph.
action SoleToastAction? Full-width button in the body. successLabel morphs the toast back to a pill after the tap.
duration Duration? Total on-screen time (overrides config).
mode SoleToastMode? Surface override for this toast only.
showProgress bool? Countdown bar override.
id Object? Custom id (otherwise generated).
onDismiss VoidCallback? Fires once the toast has left the screen.

Configuration #

SoleToast.config = const SoleToastConfig(
  mode: SoleToastMode.glossy,            // light | dark | glossy
  position: SoleToastPosition.topCenter, // topCenter | bottomCenter
  islandMode: SoleIslandMode.auto,       // auto | always | never
  bounce: 0.4,                           // 0.05 subtle … 0.8 jelly
  spring: true,                          // false → smooth ease curves
  displayDuration: Duration(seconds: 4),
  maxVisible: 3,                         // extras wait in a FIFO queue
  gap: 12,                               // px between stacked toasts
  pillHeight: 38,
  showProgress: false,
  showTimestamp: false,
  enableHaptics: true,
  timings: SoleToastTimings.normal,  // or .fast / .scaled(1.5)
);

// Or start from a preset:
SoleToast.config = SoleToastConfig.preset(SoleToastPreset.bouncy);

Timing & pace #

The default choreography is relaxed (~1.5 s until an expanded toast is fully readable). For instant action feedback, switch the pace:

// Readable content in ~350–400 ms, same gooey style:
SoleToast.config =
    SoleToast.config.copyWith(timings: SoleToastTimings.fast);

// Uniformly scale every phase (2.0 = twice as fast):
SoleToast.config =
    SoleToast.config.copyWith(timings: SoleToastTimings.scaled(2.0));

// Or tune each phase individually:
SoleToast.config = SoleToast.config.copyWith(
  timings: const SoleToastTimings(
    enterMs: 150, expandDelayMs: 50, morphMs: 500,
    bodyFadeMs: 200, collapseMs: 600, lingerMs: 500,
  ),
);

Surface modes #

Mode Surface Best for
light Solid white, soft shadows Light apps, the classic look
dark Solid near-black Dark apps
glossy Backdrop blur + translucent tint + sheen + hairline border; follows the app's brightness A frosted-glass look over any content

The Dynamic Island capsule always renders pure black — it must blend with the hardware cutout — regardless of mode.

Dynamic Island #

On iPhones with a Dynamic Island (detected automatically in portrait via the 59 pt top view padding), toasts dock to the cutout instead of dropping from the top edge:

  1. A black capsule hugs the island and a gooey chin melts out beneath the cutout, revealing the type icon centered inside it. Nothing ever grows sideways — the system clock, signal, and battery flank the island, so the choreography reveals strictly downward and can never collide with them on any device.
  2. The sheet continues down with the gooey morph, revealing the title
  3. …then the description and action, expanded-Live-Activity style. On dismiss it folds back up into the island.

While a toast is docked, showing another one morphs the capsule in place rather than stacking — the island is a single-slot surface.

// Preview the choreography anywhere (Android, simulator, tablets):
SoleToast.config =
    SoleToast.config.copyWith(islandMode: SoleIslandMode.always);

// Opt out entirely:
SoleToast.config =
    SoleToast.config.copyWith(islandMode: SoleIslandMode.never);

Interactions & accessibility #

  • Swipe a toast sideways to dismiss it (follows your finger, fades out).
  • Tap a toast while it is folding up to re-open it and restart its timer.
  • Long-press to hold a toast on screen; release to resume.
  • Reduced motion — when the platform requests it, springs and squishes are replaced with near-instant transitions.
  • Screen readers — every toast is announced (assertively for errors and warnings), and toasts are live-region semantic nodes.
  • Haptics — a light impact on show, a medium impact with the error shake, and a selection click on action taps (disable with enableHaptics: false).

The example app #

Sole Toast example app

The example is a full playground: switch between the three surface modes, force the Dynamic Island preview on any device, toggle the description body and countdown progress bar, drag the bounce slider from subtle to jelly, and fire every variant — including the action button with a success-label morph-back, a promise toast, and an in-place update.

cd example
flutter create .   # one-time: generates ios/ and android/
flutter run

On iPhones with a real Dynamic Island the choreography activates automatically — a welcome toast plays on launch.


How it works #

The blob is not a rounded rectangle being resized. Each frame, a parametric path function receives the pill width, the body width, the total height and a morph progress t, and emits a single closed path: at t = 0 a pure capsule; as t → 1 the body swells out beneath the pill, joined by an organic quadratic junction curve. Content is laid out at its final size from the start, measured offstage, and revealed by an animated clip — which is why text never reflows while the blob is moving.

Springs are real physics simulations (SpringSimulation), not tweened curves: the expand, collapse, squish, resize and drag-return all share one bounce parameter mapped to stiffness/damping/mass, so the whole toast feels like one material. The package is a few small files — a path builder, a painter, one stateful card, and a tiny queue manager — with no runtime dependencies to audit or version-match.

The geometry is even exposed as [soleBlobPath] for your own creative use — this piece was drawn with it, on-device:

Art rendered with soleBlobPath by the library's own engine

FAQ #

Why the builder: setup instead of a context parameter? The layer mounts once above the whole app, so SoleToast.success(…) works from anywhere — services, controllers, timers, after navigation — without threading a BuildContext through your code.

Does it work with any fonts / themes? Toast text uses your ambient Directionality and inherits nothing else by design — the type styles are self-contained so toasts look identical across apps. Colors adapt via the mode (and brightness for glossy).

What about tablets, web, desktop? Everything works; the Dynamic Island stage simply stays inactive (or force it with SoleIslandMode.always for fun).

Credits #

Sole Toast's morph geometry and animation feel are inspired by — and started as a Flutter port of — the excellent goey-toast React library by anl331. The engine here is a from-scratch rebuild on Flutter primitives (CustomPainter, SpringSimulation, a manual overlay) with a mobile-first interaction model and the Dynamic Island choreography added on top. If you build for the web, go check the original — it's beautiful.

Author #

Built and maintained by Sohail Ahmadsohailahmad.com · GitHub @sole-sohail

License #

MIT © 2026 Sohail Ahmad

1
likes
160
points
124
downloads

Documentation

API reference

Publisher

verified publishersohailahmad.com

Weekly Downloads

A gooey, morphing toast for Flutter — organic pill-to-blob animation, light/dark/glossy modes, semantic types, and iPhone Dynamic Island choreography. Zero dependencies.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on sole_toast