Liquid Glass Widgets
Bring Apple's iOS 26 Liquid Glass to your Flutter app — a comprehensive glass widget library with real shader-based blur, physics-driven jelly animations, and dynamic lighting. Works on every platform out of the box.
https://github.com/user-attachments/assets/2fe28f46-96ad-459d-b816-e6d6001d90de
Wanderlust — a luxury travel showcase built entirely with liquid_glass_widgets
Features
- Comprehensive glass widget library — containers, interactive controls, inputs, feedback, overlays, and navigation surfaces (see Widget Categories)
- Liquid Morph Engine — a standalone physics system powering iOS 26-style liquid morphing.
GlassMenuis the first consumer; future widgets will use the same engine for consistent liquid transitions. Seedocs/LIQUID_MORPH_ENGINE.md - Real frosted glass — native two-pass Gaussian blur + shader refraction on Impeller; lightweight shader on Skia/Web
- Just works everywhere — iOS, Android, macOS, Web, Windows, Linux; rendering path chosen automatically
- Adaptive quality (experimental) —
GlassAdaptiveScopebenchmarks the device at startup and adjusts quality in real time:minimalon slow hardware,standardon mid-range,premiumon fast devices. Degrades on thermal throttle, recovers when cool - Zero dependencies — no third-party runtime libraries, just the Flutter SDK
- One-line setup —
LiquidGlassWidgets.wrap(child: myApp)handles shader prewarming, accessibility bridging, root backdrop sharing, and global theming; addGlassBackdropScopeper screen to prevent ghost artifacts on navigation (see Backdrop Isolation) - Gyroscope lighting —
GlassMotionScopedrives specular highlights from anyStream<double> - WCAG-compliant by default — Reduce Motion and Reduce Transparency are respected automatically; no setup required
Examples
Wanderlust — Luxury Travel Showcase
A premium app demonstrating liquid_glass_widgets in a real-world production context — full-bleed imagery, parallax scroll, hero transitions, and a concierge chat interface. This is the app shown in the video above.
cd example/showcase && flutter pub get && flutter run
Apple Music Demo — iOS 26 Replica
A recreation of the Apple Music app demonstrating GlassSearchableBottomBar, a floating playback pill, and the full iOS 26 navigation model with smooth morphing transitions.
cd example && flutter pub get && flutter run -t lib/apple_music/apple_music_demo.dart
Apple Messages Demo — iOS 26 Replica
A replica showcasing the Liquid Morph Engine via GlassMenu. Tap the menu or Edit button at the top to see the teardrop open/close physics live.
cd example && flutter pub get && flutter run -t lib/apple_messages/apple_messages_demo.dart
Apple News Demo — iOS 26 Replica
A recreation of the Apple News app demonstrating GlassSearchableBottomBar with its morphing search pill, category chips, hero cards, and rounded article tiles.
cd example && flutter pub get && flutter run -t lib/apple_news/apple_news_demo.dart
Widget Showcase — Full Component Library
A complete catalogue of every glass widget organised by category. Use it to explore components, try live settings, and copy patterns directly into your app.
cd example && flutter pub get && flutter run
Component Demos — Copy-Pasteable Examples
Eight focused, self-contained demos — one widget, one file, runnable standalone:
| Demo | Run command (from example/) |
|---|---|
glass_menu_demo.dart — all 9 menu alignments |
cd example && flutter run -t lib/demos/glass_menu_demo.dart |
glass_tab_bar_scrollable_demo.dart — scrollable tab bar |
cd example && flutter run -t lib/demos/glass_tab_bar_scrollable_demo.dart |
glass_modal_sheet_demo.dart — peek / half / full states |
cd example && flutter run -t lib/demos/glass_modal_sheet_demo.dart |
glass_bottom_bar_demo.dart — magic-lens masking |
cd example && flutter run -t lib/demos/glass_bottom_bar_demo.dart |
bottom_bar_tab_width_demo.dart — tabWidth showcase |
cd example && flutter run -t lib/demos/bottom_bar_tab_width_demo.dart |
searchable_bar_demo.dart — searchable bar edge cases |
cd example && flutter run -t lib/demos/searchable_bar_demo.dart |
shape_debug_demo.dart — GlassButton shapes |
cd example && flutter run -t lib/demos/shape_debug_demo.dart |
quality_comparison_demo.dart — premium & standard quality comparison playground |
cd example && flutter run -t lib/demos/quality_comparison_demo.dart |
Widget Categories
Containers
GlassCard · GlassPanel · GlassContainer · GlassDivider · GlassListTile · GlassStepper · GlassWizard
Interactive
GlassButton · GlassIconButton · GlassChip · GlassSwitch · GlassSlider · GlassSegmentedControl · GlassPullDownButton · GlassButtonGroup · GlassBadge
Input
GlassTextField · GlassTextArea · GlassPasswordField · GlassSearchBar · GlassPicker · GlassFormField
Feedback
GlassProgressIndicator · GlassToast · GlassSnackBar
Overlays
GlassDialog · GlassSheet · GlassModalSheet · showGlassActionSheet · GlassMenu · GlassMenuItem
Surfaces
GlassAppBar · GlassBottomBar · GlassSearchableBottomBar · GlassTabBar · GlassSideBar · GlassToolbar
Installation
dependencies:
liquid_glass_widgets: ^0.12.1
flutter pub get
Quick Start
Two steps — that's the entire setup:
Step 1. Call initialize() in main() to pre-warm shaders.
Step 2. Wrap your app with LiquidGlassWidgets.wrap():
import 'package:flutter/material.dart';
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await LiquidGlassWidgets.initialize();
runApp(LiquidGlassWidgets.wrap(child: const MyApp()));
}
That's it. Then add any glass widget to your tree — no per-widget configuration needed:
Scaffold(
appBar: GlassAppBar(title: const Text('My App')),
body: const Center(child: GlassCard(child: Text('Hello, Glass!'))),
)
Accessibility is on by default. The library automatically reads the device's Reduce Motion and Reduce Transparency settings — no extra setup required. See Accessibility for details.
Optional: quality & theming
For production apps, pass adaptiveQuality and/or theme to wrap() at the same call site:
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
adaptiveQuality: true, // auto-benchmarks device, degrades gracefully
theme: GlassThemeData.simple( // optional app-wide glass defaults
blur: 10,
thickness: 30,
quality: GlassQuality.standard,
),
));
Both parameters are optional — omit them and the library uses sensible defaults.
Theming
Pass a theme: to LiquidGlassWidgets.wrap() to set your app-wide defaults — every glass widget inherits them automatically, no per-widget configuration needed:
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
theme: GlassThemeData(
light: GlassThemeVariant(
settings: GlassThemeSettings(thickness: 30, blur: 6),
quality: GlassQuality.standard,
),
dark: GlassThemeVariant(
settings: GlassThemeSettings(thickness: 40, blur: 8),
quality: GlassQuality.standard,
),
),
));
For a quick single-quality theme, use the GlassThemeData.simple shorthand:
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
theme: GlassThemeData.simple(
blur: 10,
thickness: 30,
quality: GlassQuality.standard,
),
));
GlassThemeSettingsvsLiquidGlassSettings: UseGlassThemeSettingsinsideGlassThemeVariant. It accepts the same parameters but all are nullable — only fields you explicitly set are applied; everything else inherits from each widget's own defaults.LiquidGlassSettingsis the full settings type used on individual widgets.
Three-level override hierarchy (highest wins):
- Widget
settingsparameter — explicit, widget-level override GlassPage(themeOverride: ...)— per-screen override for special pages (onboarding, paywalls)GlassTheme/wrap(theme:...)— app-wide defaults
Access the current theme programmatically:
final variant = GlassThemeData.of(context).variantFor(context);
Per-subtree theming
For advanced use cases where you need different glass styles within a single screen, place a GlassTheme widget anywhere in your tree:
GlassTheme(
data: GlassThemeData.simple(blur: 4, quality: GlassQuality.minimal),
child: MyListSection(), // list cards get minimal quality
)
Glow Colors
GlassGlowColors controls the interaction glow emitted by surfaces like GlassBottomBar and GlassSearchableBottomBar:
GlassThemeVariant(
glowColors: GlassGlowColors(
primary: Colors.blue,
glowBlurRadius: 12,
glowSpreadRadius: 0.2,
glowOpacity: 0.8,
),
)
Platform Support
| Platform | Renderer | Notes |
|---|---|---|
| iOS | Impeller (Metal) | Full shader pipeline, chromatic aberration |
| Android | Impeller (Vulkan) | Full shader pipeline, chromatic aberration |
| macOS | Impeller (Metal) | Full shader pipeline, chromatic aberration |
| Web | CanvasKit | Lightweight fragment shader |
| Windows | Skia | Lightweight fragment shader |
| Linux | Skia | Lightweight fragment shader |
Platform detection is automatic — no configuration required.
Glass Quality Modes
Standard — Default, Recommended
The right choice for 95% of use cases. Works on every platform with iOS 26-accurate glass effects.
GlassContainer(
quality: GlassQuality.standard, // this is the default
child: const Text('Great for scrollable content'),
)
Premium — Impeller Only
Enables the full Impeller shader pipeline with texture capture and chromatic aberration. On Skia/Web, automatically falls back to Standard.
GlassAppBar(
quality: GlassQuality.premium,
title: const Text('Static header'),
)
Use Premium only for static, non-scrolling surfaces (app bars, bottom bars, hero sections). It may not render correctly inside
ListVieworCustomScrollViewon Impeller.
Minimal — Shader-Free
Zero custom fragment shader cost on any device. Uses BackdropFilter blur + a Rec. 709 saturation matrix + a specular rim stroke. Visually equivalent to a high-quality frosted panel.
GlassCard(
quality: GlassQuality.minimal,
child: const Text('No shader overhead'),
)
Two ideal use cases:
- Device fallback — very old Android devices or any device where
ImageFilter.isShaderFilterSupportedisfalse - GPU budget management — use
minimalfor background panels and list cards while keepingstandardorpremiumon the focal element. A screen with 15 glass list cards runningminimalfires zero shader invocations during scroll
Theme shorthand:
GlassThemeVariant.minimalappliesminimalquality globally viaGlassThemeData.
GlassPage
GlassPage is the recommended root widget for any screen that uses glass surfaces. It eliminates several common setup mistakes in one widget:
// Minimum — just wrap your Scaffold, GlassPage handles everything else:
GlassPage(
child: Scaffold(
appBar: GlassAppBar(title: const Text('Home')),
body: MyContent(),
),
)
// With a wallpaper:
GlassPage(
background: Image.asset('assets/wallpaper.jpg', fit: BoxFit.cover),
edgeToEdge: true,
statusBarStyle: GlassStatusBarStyle.auto,
child: Scaffold(
appBar: GlassAppBar(title: const Text('Home')),
body: MyContent(),
),
)
| What it handles | Without GlassPage |
|---|---|
Transparent Scaffold |
Must set scaffoldBackgroundColor: transparent manually |
| Navigation ghosting | Must remember to add GlassBackdropScope to every route |
| Background scope setup | Must wrap in LiquidGlassScope manually |
| Status bar icons | Must call SystemChrome.setSystemUIOverlayStyle and restore it |
| Edge-to-edge mode | Must call SystemChrome.setEnabledSystemUIMode and restore it |
| Per-screen theme | Must wrap subtree in a local GlassTheme manually |
Parameters
| Parameter | Default | Purpose |
|---|---|---|
background |
null |
Optional wallpaper/background widget. When omitted, Scaffold background is left unchanged |
child |
required | Screen content, typically a Scaffold |
enableBackgroundSampling |
true when background is set, false otherwise |
GPU texture capture for real colour absorption. Set false explicitly to opt out |
statusBarStyle |
GlassStatusBarStyle.none |
Status bar icon brightness; auto is recommended for wallpaper screens |
edgeToEdge |
false |
Draw content behind system bars (full immersive) |
themeOverride |
null |
Per-screen GlassThemeData override for special screens |
Tip for
edgeToEdgeon Android: Whentrue, content draws underneath the Android navigation bar. Remember to wrap yourScaffoldbody in aSafeArea(or useextendBody: trueand pad the bottom) so your content isn't hidden behind the system buttons.
Specular Sharpness
Control the tightness of the specular highlight on any glass surface via LiquidGlassSettings.specularSharpness:
GlassCard(
settings: LiquidGlassSettings(
specularSharpness: GlassSpecularSharpness.sharp, // tight, mirror-like
),
child: ...,
)
| Value | Look |
|---|---|
GlassSpecularSharpness.soft |
Wide, diffuse — frosted / matte glass |
GlassSpecularSharpness.medium |
Default — matches iOS 26 |
GlassSpecularSharpness.sharp |
Tight, polished — mirror-like surface |
Each value maps to a fixed power-of-2 exponent. The GPU uses a zero-transcendental multiply chain for each — no pow() overhead.
Performance Tips
LiquidGlassWidgets.initialize()at startup — pre-caches shaders, eliminates the white flash on first renderLiquidGlassWidgets.wrap()inmain.dart— installs root backdrop sharing, accessibility, and global theming; passadaptiveQuality: truefor automatic per-device quality tuning. For multi-screen apps, also addGlassBackdropScopeto each route — see Backdrop Isolation- Standard quality for scrollable content — lists, forms, interactive widgets
- Premium quality for fixed surfaces — app bars, bottom bars, and hero sections
- Minimal quality for shader-dense screens — use
GlassQuality.minimalfor background panels and list cards to fire zero custom shader invocations during scroll, then keepstandardorpremiumonly on the focal element - Accessibility fallbacks are zero-cost — when Reduce Transparency is active, the glass shader is bypassed entirely;
BackdropFilterblur runs in Flutter's own paint layer with no custom shader overhead
Automatic Quality Adaptation (experimental)
📊 Help us tune the thresholds — takes 2 minutes
GlassAdaptiveScopeis@experimentalbecause its Phase 2 timing thresholds are based on limited community data, not yet validated across the full Android device landscape. Current defaults (v0.12.0):
P75 warmup Quality assigned < 20 ms premium(based on 1 report — please share yours)20–28 ms standard(provisional — no real-device data yet)> 28 ms minimalIf you use
adaptiveQuality: true, please post your results to our Threshold Calibration Discussion with the snippet below. Every report directly informs the threshold calibration and gets us closer to removing@experimental. Thank you 🙏// Add to your GlassAdaptiveScopeConfig while testing — remove before shipping: // Option A: zero-wiring (recommended for quick reports) GlassAdaptiveScopeConfig( debugLogDiagnostics: true, // prints to console in debug builds only ) // Option B: custom handler for analytics GlassAdaptiveScopeConfig( onDiagnostic: (d) { // d.reason, d.p75Ms, d.p95Ms, d.framesMeasured, d.phase are all set debugPrint('📊 ${d.from.name} → ${d.to.name} | reason: ${d.reason.name} | P75: ${d.p75Ms?.toStringAsFixed(1)}ms'); }, )
GlassAdaptiveScope (enabled via wrap(adaptiveQuality: true)) automatically
benchmarks the device at startup and adjusts quality in real time:
// Minimal — let the library decide the best quality for the device:
runApp(LiquidGlassWidgets.wrap(child: const MyApp(), adaptiveQuality: true));
// Per-screen — fine-grained control on specific routes:
GlassAdaptiveScope(
initialQuality: GlassQuality.standard, // conservative start
allowStepUp: true,
// Android calibration — raise if your device is incorrectly demoted to standard.
// Post your P75 + device model to the Threshold Calibration Discussion!
// warmupPremiumThresholdMs: 24.0, // default 20.0
// warmupStandardThresholdMs: 32.0, // default 28.0
child: Scaffold(...),
)
Eliminating repeat warmup jank (recommended for production)
On the first launch, GlassAdaptiveScope runs a ~3-second warm-up benchmark
to measure real raster performance. On a Pixel 4a, this benchmark observes slow
frames and steps down to minimal. Without persistence, this happens on every
cold start — the user sees 3 seconds of degraded quality every time they open
the app.
Within a single app process, the library caches the settled quality automatically. If the scope is disposed and remounted (e.g. navigating away and back to the root), Phase 2 is not re-run — no extra code required.
Across cold starts, use onQualityChanged + initialQuality with your
preferred storage mechanism:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Load previously settled quality — avoids warmup jank on repeat launches.
final prefs = await SharedPreferences.getInstance();
final saved = prefs.getString('glass_quality');
final initial = saved != null
? GlassQuality.values.byName(saved) // Dart 2.15+ built-in
: null; // null = run Phase 2 on first launch, then persist
await LiquidGlassWidgets.initialize();
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
adaptiveQuality: true,
adaptiveConfig: GlassAdaptiveScopeConfig(
initialQuality: initial, // restore immediately — no warmup window
allowStepUp: true, // allow recovery after thermal throttle
onQualityChanged: (_, to) => // persist whenever quality settles
prefs.setString('glass_quality', to.name),
),
));
}
On first launch: initial is null → Phase 2 runs → quality settles → persisted.
On every subsequent launch: initial is non-null → Phase 2 skipped → no jank.
GPU Budget Monitoring
GlassPerformanceMonitor watches raster frame durations while GlassQuality.premium surfaces are active. When frames exceed the GPU budget for 60 consecutive frames it emits a single FlutterError with actionable guidance — which widget to change, which quality tier to try, and why.
Zero production overhead — automatically disabled in release builds. Enabled by default in debug/profile via LiquidGlassWidgets.initialize():
// Default — auto-enabled in debug/profile, zero-cost in release
await LiquidGlassWidgets.initialize();
// Opt out entirely
await LiquidGlassWidgets.initialize(enablePerformanceMonitor: false);
// Custom thresholds
GlassPerformanceMonitor.rasterBudget = const Duration(microseconds: 8333); // 120 fps
GlassPerformanceMonitor.sustainedFrameThreshold = 120;
Backdrop Isolation — Preventing Ghost Artifacts
LiquidGlassWidgets.wrap() installs one root BackdropGroup that all glass
surfaces share for GPU backdrop captures. When navigating between screens, the
previous screen's backdrop texture stays bound for 1–2 frames — causing the old
page's content to briefly bleed through glass on the new screen.
Recommended fix: use GlassPage — it handles backdrop isolation automatically.
class MyNewPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// GlassPage creates a fresh GlassBackdropScope on mount automatically.
// No extra widgets needed.
return GlassPage(
child: Scaffold(
appBar: GlassAppBar(title: const Text('New Page')),
body: ...,
bottomNavigationBar: GlassBottomBar(...),
),
);
}
}
Manual alternative — GlassBackdropScope:
If you are not using GlassPage (e.g., you have a custom routing setup), add a
GlassBackdropScope at the root of each route yourself:
class MyLegacyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GlassBackdropScope( // ← forces a fresh capture on mount
child: Scaffold(
appBar: GlassAppBar(title: const Text('New Page')),
body: ...,
),
);
}
}
GlassBackdropScope creates a child BackdropGroup scoped to that screen. The
moment it mounts, it captures a fresh GPU backdrop — no memory of the previous
page's content.
Rule of thumb:
GlassPageis the easiest path — it wrapsGlassBackdropScopefor you. Use rawGlassBackdropScopeonly whenGlassPageis not suitable.
Why adaptiveQuality tabs don't ghost:
When switching tabs within the same screen, all glass surfaces share the same
GlassBackdropScope which is refreshed correctly during animation.
The ghost appears only when crossing a Navigator route boundary or an
AnimatedSwitcher that replaces the whole screen widget.
Custom Refraction for Interactive Indicators
On Skia and Web, interactive widgets like GlassSegmentedControl can display
true liquid glass refraction from a background image.
Recommended: use GlassPage(background:...) — it wires up the refraction source
automatically and is the cleanest integration path:
// GlassPage handles LiquidGlassScope + GlassRefractionSource for you:
GlassPage(
background: Image.asset('assets/wallpaper.jpg', fit: BoxFit.cover),
child: Scaffold(
body: Center(
child: GlassSegmentedControl(
segments: const ['Option A', 'Option B', 'Option C'],
selectedIndex: 0,
onSegmentSelected: (i) {},
quality: GlassQuality.standard,
),
),
),
)
Manual alternative — LiquidGlassScope:
For advanced scenarios (e.g. isolated sections within a screen, non-GlassPage setups),
use LiquidGlassScope directly:
// Shorthand — wallpaper behind your Scaffold:
LiquidGlassScope.stack(
background: Image.asset('assets/wallpaper.jpg', fit: BoxFit.cover),
content: Scaffold(
body: Center(child: GlassSegmentedControl(...)),
),
)
// Manual — granular control over which surface is sampled:
LiquidGlassScope(
child: Stack(
children: [
Positioned.fill(
child: GlassRefractionSource(
child: Image.asset('assets/wallpaper.jpg'),
),
),
Center(child: GlassSegmentedControl(...)),
],
),
)
On Impeller, GlassQuality.premium uses the native scene graph — no
LiquidGlassScope needed.
Migration note (0.7.0):
LiquidGlassBackgroundwas renamed toGlassRefractionSource. The old name still compiles (deprecated typedef) and will be removed in 1.0.0.
| When | Recommendation |
|---|---|
| Skia / Web (recommended) | GlassPage(background:...) — automatic wiring |
| Skia / Web (manual) | LiquidGlassScope.stack with GlassQuality.standard |
| iOS / macOS (Impeller) | GlassQuality.premium — native scene graph |
| Multiple isolated sections | Separate LiquidGlassScope per section |
Gyroscope Lighting
GlassMotionScope drives the specular highlight angle from any Stream<double>, including a device gyroscope via sensors_plus:
GlassMotionScope(
stream: gyroscopeEvents.map((e) => e.y * 0.5),
child: Scaffold(
appBar: GlassAppBar(title: const Text('My App')),
body: ...,
),
)
No new dependencies required — connect any stream source (scroll position, mouse, gyroscope).
Accessibility
Every glass widget in this package respects the user's system accessibility preferences automatically — no setup required.
| System Setting | Effect on glass widgets |
|---|---|
| Reduce Motion (iOS/macOS/Android) | All spring/jelly animations snap instantly to their target |
| Reduce Transparency / High Contrast | Glass shader replaced with a plain frosted BackdropFilter panel — zero GPU shader cost |
No setup needed
Just ship your app. If the user has Reduce Motion on, your widgets snap. If they have Reduce Transparency on, they get a solid frosted fallback. Nothing to configure.
Optional: GlassAccessibilityScope
Place GlassAccessibilityScope in your tree to override system defaults — useful for testing, showcases, or per-subtree customisation:
// In your app (optional — place inside MaterialApp.builder for full coverage)
MaterialApp(
builder: (context, child) => GlassAccessibilityScope(
child: child!, // reads system flags automatically
),
)
// Force a specific state (e.g. demo frosted fallback in a settings screen)
GlassAccessibilityScope(
reduceTransparency: true,
child: GlassSettingsPreview(),
)
GlassAccessibilityScope always wins over the system flag — it's the highest-priority override.
Opting out globally
For experiences where full glass fidelity is intentional (games, creative tools):
// 0.10.0+: child is a required named parameter
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
respectSystemAccessibility: false,
));
This disables only the automatic system-flag bridge. An explicit GlassAccessibilityScope in the widget tree still works regardless.
Priority order (highest wins)
GlassAccessibilityScopein the widget tree — explicit developer override- System
MediaQueryflags — automatic, respects user's OS setting wrap(respectSystemAccessibility: false)— disables (2) globally
Architecture
Rendering pipeline
On Impeller, every GlassQuality.premium surface uses a two-pass pipeline:
- Blur pass —
BackdropFilterLayer(ImageFilter.blur), clipped to the exact widget shape. Shared across all surfaces inside aGlassBackdropScope(injected automatically byLiquidGlassWidgets.wrap()). - Shader pass —
BackdropFilterLayer(ImageFilter.shader)— refraction, edge lighting, glass tint, and chromatic aberration.
On Skia/Web, lightweight_glass.frag runs as a single pass with no backdrop capture.
Liquid Morph Engine
A standalone physics and animation system powering iOS 26-style teardrop morphing. It lives in lib/engine/ and is fully decoupled from any specific widget — GlassMenu is its first consumer.
Key types: GlassMorphController · LiquidMorphState · LiquidMorphPhysics · MorphPhase · MorphSpeed
See docs/LIQUID_MORPH_ENGINE.md for a full integration guide.
Content-Adaptive Glass Strength (0.7.0)
Both render paths automatically adapt glass strength to background brightness:
- Dark backgrounds → richer, more opaque glass (1.2× strength, brighter Fresnel rim)
- Light backgrounds → subtler, more translucent glass (0.8× strength)
On Impeller, backdrop luminance is sampled directly from the refracted texture (zero extra reads).
On Skia/Web, MediaQuery.platformBrightnessOf provides a lightweight proxy.
Testing
# All tests
flutter test
# Exclude golden tests
flutter test --exclude-tags golden
# macOS golden tests (require Impeller)
flutter test --tags golden
Dependencies
Zero third-party runtime dependencies beyond the Flutter SDK.
The glass rendering pipeline builds on the open-source work of whynotmake-it. Their liquid_glass_renderer (MIT) has been vendored and extended with bug fixes, performance improvements, and shader optimisations.
Contributing
Contributions are welcome. For major changes, open an issue first to discuss your proposal.
License
MIT — see the LICENSE file for details.
Credits
Special thanks to the whynotmake-it team for their liquid_glass_renderer (MIT), whose shader pipeline, texture capture, and chromatic aberration work forms the foundation of the rendering engine in this library.
Links
Libraries
- constants/glass_defaults
- Common constants used throughout the liquid_glass_widgets package.
- liquid_glass_setup
- liquid_glass_widgets
- Liquid Glass Implementation according to Apple's Guidelines
- theme/glass_theme
- theme/glass_theme_data
- theme/glass_theme_helpers
- theme/glass_theme_settings
- types/glass_quality
- types/glass_quality_change_reason
- types/glass_specular_sharpness
- utils/accessibility_config
- utils/draggable_indicator_physics
- utils/glass_morph_controller
- utils/glass_performance_monitor
- Glass Performance Monitor
- utils/glass_quality_adapter
- Glass Quality Adapter
- utils/glass_spring
- utils/liquid_morph_physics
- widgets/containers/glass_card
- widgets/containers/glass_container
- widgets/containers/glass_divider
- widgets/containers/glass_list_tile
- widgets/containers/glass_panel
- widgets/containers/glass_stepper
- widgets/containers/glass_wizard
- widgets/feedback/glass_progress_indicator
- widgets/input/glass_form_field
- widgets/input/glass_password_field
- widgets/input/glass_picker
- widgets/input/glass_search_bar
- widgets/input/glass_text_area
- widgets/input/glass_text_field
- widgets/interactive/glass_badge
- widgets/interactive/glass_chip
- widgets/interactive/glass_segmented_control
- widgets/interactive/glass_slider
- widgets/interactive/glass_switch
- widgets/interactive/liquid_glass_scope
- widgets/interactive/shared/segmented_control_internal
- widgets/overlays/glass_action_sheet
- widgets/overlays/glass_dialog
- widgets/overlays/glass_modal_sheet
- widgets/overlays/glass_sheet
- widgets/overlays/glass_toast
- widgets/overlays/shared/glass_sheet_defaults
- widgets/shared/adaptive_glass
- widgets/shared/adaptive_liquid_glass_layer
- widgets/shared/animated_glass_indicator
- widgets/shared/glass_accessibility_scope
- widgets/shared/glass_adaptive_scope
- widgets/shared/glass_backdrop_scope
- widgets/shared/glass_effect
- widgets/shared/glass_motion_scope
- widgets/shared/glass_page
- widgets/shared/inherited_liquid_glass
- widgets/shared/lightweight_liquid_glass
- widgets/surfaces/glass_app_bar
- widgets/surfaces/glass_bottom_bar
- widgets/surfaces/glass_searchable_bottom_bar
- widgets/surfaces/glass_side_bar
- widgets/surfaces/glass_tab_bar
- widgets/surfaces/glass_toolbar
- widgets/surfaces/shared/bar_layout_utils
- widgets/surfaces/shared/bottom_bar_internal
- widgets/surfaces/shared/glass_search_bar_config
- widgets/surfaces/shared/searchable_bottom_bar_controller
- widgets/surfaces/shared/searchable_bottom_bar_internal
- widgets/surfaces/shared/tab_bar_internal
- widgets/surfaces/shared/tab_drag_gesture_mixin