butter_toast 0.1.0 copy "butter_toast: ^0.1.0" to clipboard
butter_toast: ^0.1.0 copied to clipboard

Smooth, buttery toasts for Flutter: pill and Sonner-style card toasts, 7 positions with their own motion, promise toasts, swipe to dismiss, no context needed.

🧈 butter_toast #

Toasts that feel smooth as butter.

Pill and Sonner-style card toasts for Flutter, with spring animations, seven positions that each move their own way, promise toasts, and swipe to dismiss. No BuildContext needed.

pub package likes pub points license: MIT

Card toasts stacking, spreading out and swiping away

πŸ“š Contents #

✨ Why butter_toast? #

fluttertoast shows the native Android toast. Since Android 11 the system cuts that toast to two lines, ignores your position and blocks custom layouts. Most Flutter alternatives fix that by drawing a wide snackbar. butter_toast draws a real toast: small, light, and out of your way.

🍞 Two styles A compact pill like the native toast, or a card with title and description that stacks like Sonner
🎯 7 positions Top and bottom slide from their edge, corners slide in from the side, centre scales in
πŸŒ€ Spring motion Enter with a soft bounce, swipe away in any direction, interrupt any animation smoothly
πŸ‘† Swipe any toast Even one from the middle of a spread-out stack; the rest close the gap
⏳ Promise toasts Saving… β†’ Saved βœ“ in the same toast
πŸ“ Never covers the screen Long text collapses behind Show more, inside a height budget
⌨️ Keyboard and safe area aware Stays clear of the notch, home indicator and keyboard
πŸͺŸ Above everything Shows over dialogs and bottom sheets
πŸ–ΌοΈ Your app icon Show the real launcher icon on toasts, read at run time, no asset needed
🧹 No pile-ups Repeated taps merge into one toast; a tag updates a toast in place
🧡 No context needed Call it from blocs, services or a Dio interceptor
🎨 Themeable App-wide with a ThemeExtension, or per toast with theme:
β™Ώ Accessible Screen readers announce toasts; reduced motion fades instead of sliding
πŸ“¦ Zero dependencies Only the Flutter SDK

🎬 See it move #

Swipe any toast, any direction Corners slide in from the side Promise: loading β†’ success
Toasts in a spread stack swiped away diagonally, up and sideways, starting with the middle one Card toasts sliding in from each corner A loading pill turning into a success pill
Pill Card Long message
Pill toast Card toast with description Long message collapsed behind Show more

πŸš€ Quick start #

1. Add the package

dependencies:
  butter_toast: ^0.1.0

2. Add the toast layer to your app, once

import 'package:butter_toast/butter_toast.dart';

MaterialApp(
  builder: ButterToast.init(), // πŸ‘ˆ that's it
  home: const HomePage(),
);

ButterToast.init() puts the toasts above the Navigator, so they show on every screen, dialog and bottom sheet. Already using builder? Chain them:

builder: (context, child) => ButterToast.init()(context, MyWrapper(child)),

3. Toast from anywhere

ButterToast.show('Copied to clipboard');

No BuildContext, so it works from widgets, blocs, services and interceptors alike.

🍞 Showing toasts: show and the shortcuts #

There's one method that does everything, ButterToast.show. You choose what the toast is about with the type enum:

ButterToast.show('Event created', type: ButterToastType.success);
ButterToast.show("Couldn't send", type: ButterToastType.error);

For everyday code there are shortcuts that fill in the type for you. They take the same parameters as show, so use whichever reads better:

Shortcut Same as Looks like
ButterToast.show('Hi') type: ButterToastType.normal No icon (or your theme icon)
ButterToast.success('Saved') type: ButterToastType.success βœ… green check
ButterToast.error('Failed') type: ButterToastType.error β›” red icon
ButterToast.warning('Low storage') type: ButterToastType.warning ⚠️ amber icon
ButterToast.info('New version') type: ButterToastType.info ℹ️ blue icon
ButterToast.loading('Saving…') type: ButterToastType.loading πŸ”„ spinner, stays until updated or dismissed

Which one should I use?

  • Use a shortcut when you know the type while writing the code. It's shorter and reads like a sentence:

    ButterToast.success('Profile saved', description: 'Changes are live.');
    
  • Use show with type when the type comes from data, like an API result, so you don't need an if for every case:

    ButterToast.show(
      result.message,
      type: result.ok ? ButterToastType.success : ButterToastType.error,
    );
    

Both return a ButterToastHandle you can use to update or dismiss the toast later.

🧾 All parameters #

Every parameter except the message is optional. Anything you leave out comes from the theme.

ButterToast.show(
  'Payment received',                        // main text (the title on a card)
  description: 'β‚Ή1,200 from Sam',            // quieter second line
  type: ButterToastType.success,             // icon and its colour
  style: ButterToastStyle.card,              // pill or card
  position: ButterToastPosition.topCenter,   // one of 7 positions
  duration: const Duration(seconds: 6),      // Duration.zero = until dismissed
  icon: const Icon(Icons.payments),          // replaces the type icon
  showIcon: true,                            // false hides the icon
  onTap: () => openPayments(),               // called when tapped
  dismissible: true,                         // false turns off swiping
  tag: 'payment',                            // same tag updates this toast
  theme: const ButterToastTheme(...),        // style for this toast only
);
Parameter Type Default What it does
message String required The main text. On a card it's the title.
description String? none A second, quieter line.
type ButterToastType normal Picks the icon and its colour. Only on show; the shortcuts set it for you.
style ButterToastStyle? theme (pill) pill or card.
position ButterToastPosition? theme (bottomCenter) Where it appears. See Positions.
duration Duration? theme (4 s) How long it stays. Duration.zero keeps it until dismissed. Not on loading.
icon Widget? type icon Replaces the icon for this toast.
showIcon bool true false shows no icon at all.
onTap VoidCallback? none Called when the toast is tapped.
dismissible bool true (false for loading) Whether it can be swiped away.
tag String? none A toast with the same tag replaces this one in place.
theme ButterToastTheme? none Theme for this toast only. See Per toast.

πŸ”„ Updating and dismissing a toast #

Every call returns a ButterToastHandle:

final toast = ButterToast.loading('Syncing…');

// later
toast.update(message: 'All synced', type: ButterToastType.success);
// or
toast.dismiss();

toast.isActive; // false once it has left the screen

update morphs the same toast smoothly, and restarts its timer when it turns from loading into anything else. To clear the screen, call ButterToast.dismissAll().

⏳ Promise toasts #

One toast follows your future from start to finish:

final photos = await ButterToast.promise(
  api.uploadPhotos(),
  loading: 'Uploading photos…',
  success: (count) => '$count photos uploaded',
  error: (e) => "Couldn't upload your photos",
);

It returns the future's result. If the future fails, the error toast shows and the error is rethrown, so your own try/catch still works. promise also takes style, position, tag and theme.

🎯 Positions #

ButterToast.show('Hi', position: ButterToastPosition.topRight);
Position Enters Leaves / swipe Stack grows
topCenter ⬇️ from the top ⬆️ up down
bottomCenter (default) ⬆️ from the bottom ⬇️ down up
topLeft, bottomLeft ➑️ from the left edge ⬅️ left away from the edge
topRight, bottomRight ⬅️ from the right edge ➑️ right away from the edge
center πŸ” scales in fades replaces the last one

A toast keeps its position for its whole life, even if the default changes while it's on screen.

πŸƒ Pill or card #

ButterToast.show('Saved', style: ButterToastStyle.pill);
ButterToast.show(
  'Payment received',
  description: 'β‚Ή1,200 from Sam',
  style: ButterToastStyle.card,
);
  • Pill hugs its text, like the platform toast. Great for "Copied" or "Saved".
  • Card shows a title and description at full width. Several cards stack like a deck; tap (or hover on web) to spread them out.

While a stack is spread out, its timers pause so nothing disappears while you read. It closes again when you tap anywhere else, or on its own after 5 seconds without a touch, and then the toasts leave as usual.

πŸ‘† Swipe to dismiss #

Throw a toast in any direction to dismiss it: up, down, sideways or diagonally. A short drag springs back. In a spread-out stack every toast can be swiped on its own, including the ones in the middle. Pass dismissible: false to turn swiping off for a toast.

🧹 Repeated taps and tags #

Showing a toast identical to one already on screen restarts that toast instead of adding a copy, so a button tapped five times shows one toast.

For toasts whose text changes, give them a tag. A new toast with the same tag updates the existing one in place:

ButterToast.loading('Saving…', tag: 'save');
// later, or on the next tap:
ButterToast.success('Saved', tag: 'save'); // same toast, now a success

πŸ–ΌοΈ Icons and the app icon #

By default, typed toasts show their own icon and plain show toasts show none. You can change that at three levels:

I want to… Do this
Change the icon of one toast icon: const Icon(Icons.wifi_off)
Hide the icon on one toast showIcon: false
Give plain toasts an icon everywhere ButterToastTheme(icon: ...)
Use one icon on every toast ButterToastTheme(icon: ..., typeIcons: false)

ButterToastAppIcon shows your app's launcher icon, the way Android shows it on native toasts. It's read from the platform at run time, so it always matches the icon on the home screen, with no asset to add or keep in sync:

// App-wide
ButterToastTheme(
  icon: ButterToastAppIcon(), // plain toasts show the app icon
  typeIcons: false,           // success, error, … show it too
)

// Or just on one toast
ButterToast.success('Order placed', icon: const ButterToastAppIcon());

Three toasts showing the app's launcher icon instead of type icons

Platform Icon used
Android The launcher icon, shown round like the launcher does
iOS The app icon from Info.plist, with rounded corners
Web The icon from your web app manifest, with rounded corners

Any widget works as an icon, such as Image.asset('assets/logo.png').

How the app icon works on the web

A web app has no launcher to ask, so butter_toast uses the same icon the browser uses when someone installs your app:

  1. flutter build web copies your web folder into build/web, so web/manifest.json and web/icons/… are served next to your app.
  2. ButterToastAppIcon finds the manifest through the <link rel="manifest"> tag in web/index.html and reads its icons list.
  3. It picks a regular icon (not a maskable one, which is made to be cropped), the smallest that's at least 96 px, and loads it like any image on your site.
  4. If there's no manifest, it has no icons, or the icon fails to load, it uses icons/Icon-192.png. If that's missing too, the toast shows no icon.

The lookup happens once, and the result is reused. Since it follows your manifest, changing the icons there (by hand or with flutter_launcher_icons) changes the toast icon too, even if you move them to another folder. Paths are relative to the manifest, so apps served from a sub-folder work as well.

🎨 Theming: app-wide and per toast #

App-wide #

butter_toast reads a ThemeExtension, so light and dark themes just work:

MaterialApp(
  theme: ThemeData(
    extensions: const [
      ButterToastTheme(
        style: ButterToastStyle.card,
        position: ButterToastPosition.topCenter,
        duration: Duration(seconds: 3),
        bottomOffset: 80,        // above a NavigationBar
        maxHeightFraction: 0.4,  // height budget
        maxLines: 3,             // lines before "Show more"
        cardBorderRadius: 16,
        successColor: Color(0xFF16A34A),
      ),
    ],
  ),
  darkTheme: ThemeData.dark().copyWith(
    extensions: const [ButterToastTheme(style: ButterToastStyle.card)],
  ),
  builder: ButterToast.init(),
);

Not using ThemeData extensions? Pass it to init instead: ButterToast.init(theme: const ButterToastTheme(...)).

Per toast #

Want a different look on one screen or for one kind of message, like a dark checkout toast with your app icon? Pass theme: to that call. Set only the fields you want to change; everything else still comes from the app theme.

const checkoutToast = ButterToastTheme(
  style: ButterToastStyle.card,
  cardColor: Color(0xFF0F172A),
  cardForegroundColor: Colors.white,
  cardBorderColor: Color(0xFF1E293B),
  icon: ButterToastAppIcon(),
  typeIcons: false,
);

ButterToast.success('Payment done', theme: checkoutToast);
ButterToast.error('Card declined', theme: checkoutToast);

Keep the theme in a const and reuse it on every toast of that screen. Only the toasts you pass it to change; other toasts keep the app look.

A per-toast theme applies its colours, text style, shadows, radius, icons, maxLines, style, position and duration. Settings that shape the whole stack at a position (margin, maxWidth, gap, bottomOffset, visibleCount, maxToasts, maxHeightFraction) stay app-wide.

Which setting wins? #

From strongest to weakest:

  1. A parameter on the call, such as position: or icon:
  2. The call's theme:
  3. ButterToast.init(theme: ...)
  4. The ButterToastTheme in your ThemeData.extensions
  5. The built-in defaults
All theme options
Option Default What it does
style pill Default look
position bottomCenter Default position
duration 4 s How long toasts stay (Duration.zero = until dismissed)
maxHeightFraction 0.4 Share of the safe height toasts may use
maxLines 3 Lines before "Show more"
maxWidth 420 Widest a toast gets (tablet, web)
bottomOffset 0 Extra space above the bottom edge
margin 16 Distance from the safe area
gap 8 Space between spread-out cards
visibleCount 3 Cards peeking out of a stack
maxToasts 5 Most toasts per position
cardColor, cardForegroundColor, cardBorderColor, cardBorderRadius from ColorScheme Card look
pillColor, pillForegroundColor dark pill / light pill Pill look
successColor, errorColor, warningColor, infoColor green, red, amber, blue Icon colours
textStyle bodyMedium Base text style
shadows soft shadow Shadows under toasts
icon none Icon for plain toasts, e.g. ButterToastAppIcon()
typeIcons true false shows icon on every toast instead of type icons

πŸͺŸ Dialogs and bottom sheets #

Toasts live above the Navigator, so they always show on top of dialogs and bottom sheets, and aren't dimmed by their barrier.

Over a dialog Over a bottom sheet
A toast shown above an open dialog, not dimmed by its barrier A top toast and a bottom toast shown above an open bottom sheet
  • Dialog: the toast appears at its usual position, above the dialog.
  • Bottom sheet: a bottom toast floats over the sheet's lower edge. To keep the sheet clear, show it at the top:
ButterToast.success('Link copied', position: ButterToastPosition.topCenter);

πŸ“ Long text, keyboard and safe area #

A toast never takes over the screen:

  • All toasts at one position share a height budget: 40% of the safe height by default.
  • Text that doesn't fit ends with Show more. Tapping it shows the full text, scrolling inside the toast if needed, and pauses the timer.
  • When the keyboard opens, toasts move above it and the budget shrinks with the space left. If there's no room at all, bottom toasts move to the top.
  • The notch, status bar, home indicator and landscape cutouts are always avoided. Use bottomOffset to float above your bottom navigation bar.

🧩 Fully custom toasts #

Draw anything; butter_toast still animates, stacks, times, positions and swipes it:

ButterToast.custom(
  builder: (context, toast) => MyBrandedToast(
    onClose: toast.dismiss,
  ),
  position: ButterToastPosition.topCenter,
);

πŸ” Coming from fluttertoast? #

fluttertoast butter_toast
Fluttertoast.showToast(msg: 'Hi') ButterToast.show('Hi')
gravity: ToastGravity.TOP position: ButterToastPosition.topCenter
toastLength: Toast.LENGTH_SHORT duration: Duration(seconds: 2)
toastLength: Toast.LENGTH_LONG duration: Duration(milliseconds: 3500)
Fluttertoast.cancel() ButterToast.dismissAll()

πŸ“± Platforms #

Android iOS Web
βœ… βœ… βœ…

Drawn in Flutter, so it looks the same everywhere. A small native part only reads the app icon for ButterToastAppIcon. Tested on a Galaxy A36 (Android 17) and on the web (Chromium); iOS hasn't been tested on a device yet.

🀝 Contributing #

Bugs, ideas and pull requests are welcome in the issue tracker. If butter_toast made your app a little smoother, a πŸ‘ on pub.dev helps others find it.

πŸ“„ License #

MIT Β© Parth Bhensdadiya

0
likes
160
points
--
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Smooth, buttery toasts for Flutter: pill and Sonner-style card toasts, 7 positions with their own motion, promise toasts, swipe to dismiss, no context needed.

Repository (GitHub)
View/report issues

Topics

#toast #notification #snackbar #animation #ui

License

MIT (license)

Dependencies

flutter, flutter_web_plugins

More

Packages that depend on butter_toast

Packages that implement butter_toast