Sole Sheet
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.
Gallery
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 |
![]() |
![]() |
![]() |
| Light · error | Dark · error | Glassy · error |
![]() |
![]() |
![]() |
| Light · warning | Dark · warning | Glassy · warning |
![]() |
![]() |
![]() |
| Light · info | Dark · info | Glassy · info |
![]() |
![]() |
![]() |
| Light · confirm | Dark · confirm | Glassy · confirm |
![]() |
![]() |
![]() |
| Light · celebration | Dark · celebration | Glassy · celebration |
![]() |
![]() |
![]() |
| Light · custom | Dark · custom | Glassy · custom |
![]() |
![]() |
![]() |
Table of contents
- Gallery
- Platform support
- Features
- Installation
- Setup
- Quick start
- API reference
- Configuration
- Surface modes & theming
- Custom badge widget
- Custom buttons
- The text engine
- RTL support
- Motion, reduced motion & speed
- Example app
- License
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 types —
success,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, soliddark, and the signatureglassyfrost (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
Futurethat resolves on dismissal;confirmreturnsFuture<bool>. Barrier tap, drag-down and the system back gesture all followisDismissible. - 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
durationScalerescales the entire choreography;MediaQuery.disableAnimationscollapses 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. FollowsTheme.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
textAlignwins.
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.durationScalerescales 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
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 —
confirmawaits itsFuture<bool>and reports the answer — plus extras: programmaticdismiss, anonDismisscallback 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.




















