sheen 0.6.2
sheen: ^0.6.2 copied to clipboard
A glass design system for Flutter: frosted glass, spring motion, one-accent theming and 80+ widgets, with booking widgets for travel apps.
sheen #
Frosted glass for Flutter: a design system with one-accent theming, spring motion and 80+ widgets. It is built on
package:flutter/widgets.dart, so it works under MaterialApp, CupertinoApp or a plain WidgetsApp.
Live demo · API docs · Example · Changelog
Why sheen #
- Glass that is cheap to draw. Every glass surface under a
SheenScopeshares one copy of the backdrop (BackdropGroup), so a screen full of glass blurs once, not once per button. No shaders, no platform views. - Accessible by default. Reduce Motion turns springs into fades; high contrast and
reduceTransparencyturn glass opaque; text grows to 200 % without overflowing; every widget mirrors in right-to-left languages; icon-only buttons require a label. - One accent recolours everything. Prominent glass, primary buttons, selection, switches, sliders and links follow
SheenThemeData.light(accent: ...); in the dark theme, accent text is lifted until it reads on the dark ground. - No Material required. Widgets build on the widgets layer and bring their own theme, strings and routes (sheets,
menus, alerts). Text inputs use Flutter's
TextFieldand work under any app. - More than glass. Pages with large titles, sheets with detents, a date-range picker, one-time-code and password fields, toasts, skeletons, empty states, a steps line, rolling digits, and a travel library with hotel cards, prices and map pins, and a music library with a player, a mini player, song rows and synced lyrics.
Install #
flutter pub add sheen
Quick start #
Put a SheenScope above your screens, then build with Sheen* widgets and read the theme with context.sheen:
import 'package:flutter/material.dart';
import 'package:sheen/sheen.dart';
void main() => runApp(
MaterialApp(
builder: (context, child) => SheenScope(child: child!),
home: const HomePage(),
),
);
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) => SheenPage(
title: 'Hello',
children: [
SheenGlass(
padding: const EdgeInsets.all(16),
child: Text('Frosted glass', style: context.sheen.type.headline),
),
const SizedBox(height: 16),
SheenPrimaryButton(label: 'Continue', expand: true, onPressed: () {}),
],
);
}
SheenScope follows the system's light or dark setting and provides the theme, the fallback words widgets use
(Cancel, Done, Search...), Reduce Motion, the shared backdrop and the status bar style.
Theming #
SheenScope(
theme: SheenThemeData.light(accent: const Color(0xFF087F5B)),
darkTheme: SheenThemeData.dark(accent: const Color(0xFF20C997)),
strings: const SheenStrings(cancel: 'Annuler', done: 'OK', search: 'Rechercher'),
reduceTransparency: settings.reduceTransparency,
child: child,
)
- Change single colours with
copyWith:SheenThemeData.light().copyWith(colors: SheenColors.light().copyWith(background: ...)). - Use your font:
theme.copyWith(type: SheenType.material.withFamily('Inter')). - Give part of a screen another look with a nested
SheenTheme. - Flutter has no system "Reduce Transparency" flag yet, so pass your app's setting to
reduceTransparency; high contrast mode turns glass opaque by itself.
Widgets #
| Category | Widgets |
|---|---|
| Foundation | SheenScope, SheenTheme, SheenThemeData, SheenColors, SheenType, SheenStrings, SheenTone, SheenIcon + SheenIcons (82 glyphs), spacing, radii and layout widths |
| Glass | SheenGlass (regular, clear, prominent), SheenLens |
| Motion | SheenPressable, SheenEntrance, SheenPhotoHero, SheenRollingDigits, SheenMotion springs |
| Buttons | SheenPrimaryButton, SheenSecondaryButton, SheenFloatingButton, SheenTextLink, SheenIconButton, SheenButtonGroup |
| Navigation | SheenTabBar (with press-and-slide), SheenTopTabBar, SheenToolbar, SheenSearchBar, SheenActionBar, SheenStepsLine |
| Inputs | SheenTextField, SheenPasswordField, SheenSearchField, SheenCodeField, SheenSwitch, SheenSlider, SheenStepper |
| Selection | SheenChip, SheenChipRow, SheenSegmentedControl, SheenDateRangePicker, SheenDateRangeCalendar, SheenRangeHistogram, SheenChoiceList, showSheenChoiceSheet, SheenPullDownLink, showSheenMenu |
| Sheets | showSheenSheet, showSheenCustomSheet, SheenSheetBody, showSheenDateSheet, showSheenDocumentSheet, SheenFormSheet |
| Feedback | showSheenAlert, SheenToast, SheenActionBanner, SheenStatusPill, SheenStatusNotice, SheenStatusScreen, SheenEmptyState, SheenSkeleton, SheenProgressBar, SheenProgressRing, SheenSpinner, SheenProgressCapsule, SheenCountdownPill, SheenBadge, SheenSuccessCheck |
| Content | SheenCard, SheenListGroup, SheenListRow, SheenAccordion, SheenKeyValueCard, SheenCardSwiper, SheenPhotoPager, SheenAvatar, SheenStarRating, SheenScoreBar |
| Layout | SheenPage, SheenSectionLabel, SheenSectionNote, SheenScrollEdge, SheenEdgeFade |
| Travel | SheenHotelCard, SheenPremiumCard, SheenCityTile, SheenPriceBlock, SheenCancellationLine, SheenScoreBadge, SheenDealBadge, SheenWasPrice, SheenTripCard, SheenItinerary, SheenPricePin, renderSheenPricePin (map marker bitmaps) |
| Music | SheenArtwork, SheenArtworkGlow, SheenMiniPlayer, SheenSeekBar, SheenTransportControls, SheenTrackRow, SheenArtworkTile, SheenShelf, SheenLyricsView, SheenReorderList, SheenBandSlider, SheenPlayingBars, SheenMusicScope |
Every widget has a page in the gallery; most have a code example in their API docs.
Travel library #
Booking widgets live in a second library so apps that do not need them never see them:
import 'package:sheen/travel.dart';
A stay on a deal gets a badge on its photo, the price before the deal struck through and a line about when it ends. Pass the struck price only when it is the same stay without the deal (a like-for-like saving):
SheenHotelCard(
name: 'Palace Downtown',
total: 'AED 1,186.01',
wasTotal: 'AED 1,210.21', // struck through, under the total
wasLabel: 'was AED 1,210.21', // what a screen reader says
deal: '−15%', // the badge on the photo
dealNote: 'Ends Sun 18 Oct',
photoCount: 3,
photoBuilder: (context, i) => Image.network(photos[i], fit: BoxFit.cover),
noPhotoLabel: 'No photo',
saveLabel: 'Save',
onTap: openHotel,
)
Prices, dates and labels are always yours, formatted and translated; the widgets only lay them out.
Music library #
Player widgets live in a third library:
import 'package:sheen/music.dart';
SheenMiniPlayer above the tab bar, SheenSeekBar and SheenTransportControls over SheenArtworkGlow (the cover
blurred behind the glass), SheenTrackRow with a save button that fills as the song is stored, SheenShelf of covers,
SheenLyricsView, SheenReorderList for a queue and SheenBandSlider for an equaliser. For long audio (audiobooks,
podcasts): SheenTransportControls with "back 15 / forward 30" SheenSkipButtons, chapter marks on SheenSeekBar,
SheenSpeedPicker, SheenChapterList, SheenResumeCard, SheenTagRow and book covers (SheenArtwork(book: true)).
See doc/music.md.
Platforms #
Pure Dart and Flutter, with no platform code: iOS, Android, web, macOS, Windows and Linux (checked on the iOS
simulator and in widget tests). The blur is a BackdropFilter, so it costs more on the web (the
live demo shows it honestly) and over platform views such as Google Maps on
Android, where a glass bar over the map makes Flutter compose the map view; keep glass over maps to bars and pins.
FAQ #
Do I need MaterialApp? No. Any app works; with a plain WidgetsApp, add navigatorObservers: [HeroController()]
if you use SheenPhotoHero.
Can I mix sheen with Material or Cupertino widgets? Yes. sheen reads only its own theme.
How do I translate the built-in words? Pass SheenStrings to SheenScope. Every widget that shows text also
takes it as a parameter.
Which Flutter version? 3.41 or newer.
Contributing #
Issues and pull requests are welcome. Run flutter analyze and flutter test before sending a change. After a visual
change, run flutter test --update-goldens test/goldens and look at the images: goldens/ci/ (text drawn as blocks,
checked in CI) and, on macOS, goldens/macos/ (real text; antialiasing differs between macOS versions, so CI does not
compare them). Icons are SVG files in tool/icons/; dart run tool/gen_icons.dart
regenerates SheenIcons. After changing public API docs, run dart doc --output tool/out/api and then
dart run tool/gen_llms.dart to refresh llms.txt, the one-page summary for AI coding assistants.
Authors #
sheen is made and owned by:
- Danil Martsinkovskii (@marzipane)
- Roman Chernogorov (@aristosya)
See AUTHORS.
License #
MIT, © the sheen authors. See LICENSE.
