Sole Sheet — premium glass bottom sheets for Flutter

Sole Sheet

pub package license: MIT platforms dependencies

A premium status bottom sheet for Flutter. A floating 3D glass badge pops out above a blurred translucent sheet, the content rises in on staggered beats, the badge floats and breathes forever — and on celebrations it rains confetti. Seven semantic types, three surface modes (including the signature glassy frost), a Future-based dismissal contract and an RTL-aware text engine.

Zero runtime dependencies. No assets, no icon fonts, no third-party packages — the sparkle burst, confetti rain and every surface are painted with Flutter primitives.

Real, unedited captures from the example app on an iPhone 16 Pro — every type in all three surface modes.

Light · success Dark · success Glassy · success
Success sheet on the solid light surface Success sheet on the solid dark surface Success sheet on the frosted glassy surface
Light · error Dark · error Glassy · error
Error sheet on the solid light surface Error sheet on the solid dark surface Error sheet on the frosted glassy surface
Light · warning Dark · warning Glassy · warning
Warning sheet on the solid light surface Warning sheet on the solid dark surface Warning sheet on the frosted glassy surface
Light · info Dark · info Glassy · info
Info sheet on the solid light surface Info sheet on the solid dark surface Info sheet on the frosted glassy surface
Light · confirm Dark · confirm Glassy · confirm
Confirmation sheet with two actions, solid light surface Confirmation sheet with two actions, solid dark surface Confirmation sheet with two actions, frosted glassy surface
Light · celebration Dark · celebration Glassy · celebration
Celebration sheet with confetti and chips, solid light surface Celebration sheet with confetti and chips, solid dark surface Celebration sheet with confetti and chips, frosted glassy surface
Light · custom Dark · custom Glassy · custom
Custom sheet with an emoji badge widget, solid light surface Custom sheet with an emoji badge widget, solid dark surface Custom sheet with an emoji badge widget, frosted glassy surface

Table of contents

Platform support

Sole Sheet 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

Haptics fire where the OS supports them and no-op silently elsewhere. The glassy backdrop blur renders on all platforms with Flutter's default renderer. Requires Flutter 3.27+.

Features

  • Floating 3D glass badge — a circular frosted disc with a radial sheen, accent glow and glowing glyph, overlapping the sheet's top edge. It bubbles in with an elastic pop once the sheet lands, then floats (±8px bob + ~4% breathing) forever. Tap it for a decorative sparkle star-burst in accent-derived hues. Swap the glyph for any widget — avatar, Lottie, emoji.
  • Seven semantic typessuccess, error, warning, info, confirmation, celebration, custom. Each ships an accent, glyph, default title, default action label and button treatment (solid fill or 135° gradient) — all overridable per call.
  • Three surface modes — solid light, solid dark, and the signature glassy frost (backdrop blur over a translucent tint with a hairline border) that adapts to the app's brightness.
  • Staggered choreography — title, body, chips and actions rise in on their own beats (80/150/220/290 ms) with a single-line auto-scaling title.
  • Confetti rain — celebration sheets rain confetti by default (opt-in for any other type); implemented in-package as a ticker-driven painter.
  • Real dismissal semantics — every method returns a Future that resolves on dismissal; confirm returns Future<bool>. Barrier tap, drag-down and the system back gesture all follow isDismissible.
  • RTL-aware text engine**bold** spans, newline normalisation (including literal "\n" payloads), list-aware auto-alignment, and Arabic/Hebrew auto-detection that flips the whole sheet.
  • Respectful motion — one durationScale rescales the entire choreography; MediaQuery.disableAnimations collapses it to near-instant.

Installation

dependencies:
  sole_sheet: ^0.1.0

Setup

Mount the host once — it lets the static API find your Navigator:

MaterialApp(
  builder: SoleSheet.init(),
  // ...
);

Already using builder? Chain it:

MaterialApp(
  builder: SoleSheet.init(builder: (context, child) => MyWrapper(child: child)),
);

Alternatives: set SoleSheet.navigatorKey = myKey;, or pass context: to any call.

Quick start

// Status sheets — the Future resolves when the sheet is dismissed.
await SoleSheet.success('Your **leave request** has been submitted.');
SoleSheet.error('The server could not be reached.', onAction: retry);
SoleSheet.warning('Your session expires in **5 minutes**.');
SoleSheet.info('A new policy takes effect on **August 1st**.');

// Celebration — confetti + milestone chips.
SoleSheet.celebrate(
  'Congratulations on **5 years** with the team!',
  title: 'Work Anniversary 🎉',
  chips: ['MILESTONE', 'TEAM EVENT'],
);

// Confirmation — Future<bool>.
final confirmed = await SoleSheet.confirm(
  'This will **permanently** delete the draft.',
  title: 'Delete Draft?',
  confirmText: 'YES, DELETE',
);

// Fully custom — any accent, any badge content.
SoleSheet.custom(
  'Any widget works in the badge.',
  title: 'Ship It',
  accent: const Color(0xFFA78BFA),
  iconWidget: const Text('🚀', style: TextStyle(fontSize: 44)),
);

API reference

Member Returns Notes
SoleSheet.init({builder}) TransitionBuilder One-time mount in MaterialApp.builder.
SoleSheet.show<T>(message, …) Future<T?> Core presenter — resolves with the dismiss result.
SoleSheet.success(message, …) Future<void> Sugar for SoleSheetType.success.
SoleSheet.error(message, …) Future<void> Sugar; onAction is the retry callback.
SoleSheet.warning(message, …) Future<void> Sugar for SoleSheetType.warning.
SoleSheet.info(message, …) Future<void> Sugar for SoleSheetType.info.
SoleSheet.celebrate(message, …) Future<void> Confetti on by default, optional chips.
SoleSheet.confirm(message, …) Future<bool> Two stacked actions; false on any other dismissal.
SoleSheet.custom<T>(message, …) Future<T?> Any accent / title colour / badge / button styling.
SoleSheet.dismiss([result]) void Closes the top sheet, resolving its future with result.
SoleSheet.dismissAll() void Closes every open sheet.
SoleSheet.isShowing bool Whether any sheet is up.
SoleSheet.config SoleSheetConfig Global defaults (see below).
SoleSheet.navigatorKey GlobalKey? Optional explicit navigator wiring.

Per-call parameters

All optional, available on show / custom (sugar methods expose the common subset):

Parameter What it does
title, titleColor, messageColor Copy + inks (defaults come from the type).
accent Retints badge, glow, sparkle and title in one go.
icon / iconWidget Swap the badge glyph, or put any widget in the badge.
actionText, onAction The built-in action (auto-closes, then runs the callback).
buttons, showDefaultAction Custom action widgets; see Custom buttons.
chips Mono-pill row; the first chip is highlighted.
confetti Force the rain on/off for this sheet.
isDismissible Barrier tap + drag + system back, together.
onDismiss Fires however the sheet closes.
enableHaptics, barrierColor Per-sheet overrides.
mode, theme Surface mode / full theme for this sheet only.
textAlign, textDirection, isRtl Text engine overrides.
buttonColors, buttonTextColor, buttonRadius Built-in action styling.
durationScale, badgeSize, topRadius, blurSigma, horizontalInset, enableSparkle, enableFloat, confettiSettings Per-call visual knobs (see Configuration).
context Present via an explicit context instead of the mounted host.

Per-type defaults (dark/glassy palette)

Type Accent Glyph Default title Default action Button
success #4ADE80 check Action Successful Done solid green
error #FFB4AB exclaim Something Went Wrong Try Again solid salmon
warning #FFB300 exclaim Attention Required Got it amber 135° gradient
info #3291FF info Information Close blue 135° gradient
confirmation #FFB300 question Are You Sure? YES amber gradient + ghost
celebration #3291FF party Congratulations 🎉 Close blue 135° gradient
custom #3291FF sparkles Notice Close blue 135° gradient

The light palette re-inks every accent for contrast on the pale surface (e.g. success #1A8F6B, info #2A74C8).

Configuration

SoleSheet.config = const SoleSheetConfig(
  mode: SoleSheetMode.glassy,   // light / dark / glassy (the default)
  theme: null,                  // full SoleSheetTheme override
  durationScale: 1.0,           // rescales EVERY choreography constant
  badgeSize: 104,
  topRadius: 32,
  blurSigma: 24,
  horizontalInset: 16,
  enableHaptics: true,
  enableSparkle: true,
  enableFloat: true,
  barrierColor: null,           // defaults to the theme scrim
  confetti: SoleConfettiSettings(
    particleCount: 90,
    emissionDuration: Duration(seconds: 4),
    gravity: 1.0,
    colors: null,               // reference palette
  ),
);

Every knob is also overridable per call via show / custom.

Surface modes & theming

SoleSheetMode mirrors sole_toast's light / dark / glossy:

  • light — solid pale surface, dark ink. Fixed.
  • dark — solid near-black surface, light ink. Fixed.
  • glassy (default) — the signature frost: backdrop blur over a translucent tint with a hairline border. Follows Theme.of(context).brightness — dark glass under a dark theme, pale frost under a light one.

Built-in themes: SoleSheetTheme.light, .dark, .glassy, .glassyLight. Start from one and copyWith what you need — sheet fill/border, scrim, ink colours, chip fill + highlight ink, badge sheen intensity, frosted flag and wholesale per-type overrides via typeStyles.

No fonts are bundled — text inherits the ambient style. Inject families (with separate RTL faces) once:

SoleSheet.config = SoleSheetConfig(
  theme: SoleSheetTheme.glassy.copyWith(
    titleStyle: GoogleFonts.hankenGrotesk(),
    bodyStyle: GoogleFonts.hankenGrotesk(),
    rtlTitleStyle: GoogleFonts.cairo(),
    rtlBodyStyle: GoogleFonts.cairo(),
  ),
);

Action buttons render the design system's classic treatment — solid fill or 135° gradient with a soft accent glow, per-type label inks, and a hairline ghost secondary.

Custom badge widget

Anything can live in the glass circle — the disc, glow, entrance pop, float and sparkle still apply:

SoleSheet.success(
  'Profile photo updated.',
  iconWidget: CircleAvatar(backgroundImage: NetworkImage(url)),
);

Custom buttons

The built-in action auto-closes the sheet, then runs onAction. Custom buttons dismiss themselves — call SoleSheet.dismiss() (optionally with a result that resolves the sheet's future):

final choice = await SoleSheet.show<String>(
  'Where should this file go?',
  title: 'Save File',
  showDefaultAction: false,
  buttons: [
    SoleSheetButton(label: 'Documents', onTap: () => SoleSheet.dismiss('docs')),
    SoleSheetButton.secondary(label: 'Cancel', onTap: () => SoleSheet.dismiss()),
  ],
);

SoleSheetButton is the matching action button: primary (solid or 135° gradient with glow) and a .secondary() ghost variant, both with a press haptic + scale.

The text engine

  • **bold** spans render bold and brightened to on-surface.
  • Every newline variant is normalised — CRLF, bare CR, and literal "\n" / "\r\n" strings that arrive un-decoded in API payloads.
  • Auto-alignment: a numbered or bulleted list of 2+ items renders start-aligned at full width; prose stays centred on a ~300px measure; an explicit textAlign wins.
SoleSheet.warning(
  '1. **Name** is required\n2. **Date** must be in the future',
  title: 'Please Fix These',
); // renders as a start-aligned list

RTL support

Arabic/Hebrew content is auto-detected and flips the whole sheet's Directionality, applying the theme's separate RTL text styles. Force it either way with textDirection: or isRtl:.

SoleSheet.success('تمت الموافقة على طلب الإجازة الخاص بك بنجاح.',
    title: 'تمت العملية بنجاح', actionText: 'تم');

Motion, reduced motion & speed

The full choreography: sheet slides up (460 ms) → badge bubbles in with an elastic pop (780 ms after a 360 ms delay) then floats forever → title, body, chips, actions rise in on staggered beats → celebration confetti rains from the top.

  • SoleSheetConfig.durationScale rescales every constant — slide, pop, float, reveals, press feedback, sparkle lifetime and confetti clock.
  • When the platform asks to disable animations (MediaQuery.disableAnimations), the whole presentation collapses to near-instant and decorative motion (float, sparkle, confetti) is skipped.

Example app

Example app controls page

The example app exercises everything interactively:

  • Surface mode segmented control (Light / Dark / Glassy) and an app theme toggle proving the glassy auto-resolve.
  • Switches for haptics, sparkle, badge float, confetti-on-any-type, dismissible, default action, custom buttons (primary + ghost), chips row, a custom 🦄 badge widget and RTL Arabic content.
  • A speed slider (0.1×–2×) driving durationScale.
  • One button per type — confirm awaits its Future<bool> and reports the answer — plus extras: programmatic dismiss, an onDismiss callback demo and a list-style validation message.
cd example
flutter run

License

MIT © Sohail Ahmad

Libraries

sole_sheet
A premium glass notification bottom sheet — floating 3D icon badge, staggered reveals, sparkle bursts, confetti, light & dark glass themes, and an RTL-aware text engine. Zero dependencies.