liquid_glass_widgets
iOS 26-style liquid glass widgets for Flutter. Built on a custom fragment shader for real blur, liquid interactions, specular highlights, and chromatic aberration.
0.30.2
50K downloads/mo
#1464 most downloaded on pub.dev
sdegenaar/liquid_glass_widgets
What this package is like to depend on
Last release 3 days ago
21 Aug 2026
Ships on a steady schedule
a new release about every 8 days
Most releases are documented
notes for 86 of 123 stable releases
1 version withdrawn
withdrawn after publishing
8 months old
154 releases · first in 2025
154 releases in the last 12 months
see the full history below
Release timeline
154 releases · Dec 2025 to Aug 2026Releases
latest 60 of 154-
0.30.221 Aug 2026Release notes
Open source →Bug Fixes
- GlassPullDownButton / GlassMenu crash in minimal quality (#214): Fixed a crash when quality falls back to
GlassQuality.minimal.LiquidGlassBlendGroupis now skipped when noLiquidGlassLayeris present in the tree. Same fix applied toGlassPopover.
- GlassPullDownButton / GlassMenu crash in minimal quality (#214): Fixed a crash when quality falls back to
-
0.30.119 Aug 2026Release notes
Open source →Bug Fixes
- Dark mode collapsed tab pill icon (#208): Fixed an issue where the collapsed tab pill icon in
GlassTabBar.searchablerendered opaque black in dark mode instead of white whenunselectedIconColorwas unset. - ProgressiveBlur region origin (#210, credit: @jfhair): Fixed
ProgressiveBlurrendering black or losing its gradient when not positioned at the top-left of the backdrop layer (modal sheets, inset containers, scroll edges). The shader region origin is now resolved at paint time, keeping the gradient correct through drags and animated transitions. - GlassScrollEdgeEffect stale background on route resume (#212): Fixed stale background texture and ghosting shadows when returning from routes where the theme or background changed. Background capture is now deferred until the route resumes, and in-flight capture requests are coalesced.
- Dark mode collapsed tab pill icon (#208): Fixed an issue where the collapsed tab pill icon in
-
0.30.019 Aug 2026Release notes
Open source →Bug Fixes
- Windows Impeller startup hang (#204):
LiquidGlassWidgets.initialize()no longer submits blocking GPU draw calls to the raster thread prior torunApp(). On Flutter 3.47+ Windows Impeller (ANGLE /OpenGLESSDF), runtime GLSL driver compilation previously locked the raster thread during OS surface initialization, preventing the native window from presenting. The app window now displays immediately on Frame 1 on all Windows and desktop configurations. - Android GLES ANR (#187 follow-up): Removed the synchronous pre-
runAppoffscreen warm-up draw. All Android devices launch with zero splash-screen delay, and GLES devices are safely protected from runtime driver compile lockups.
Architecture & Performance
- Non-blocking shader preloading with full Vulkan/Metal parity:
LiquidGlassWidgets.initialize()now preloads shader bytecode asynchronously into memory via fast I/O on Android (Vulkan and GLES), iOS, and macOS, eliminating first-frame placeholder flashes while ensuring zero GPU raster stalls beforerunApp(). - Zero GPU work before
runApp(): Removed alltoImageSynccalls frompreWarm(). Internal 1×1 sampler dummy textures are now allocated lazily on first paint in the widget tree. GlassWarmUpModeconfiguration: AddedwarmUpMode(GlassWarmUpMode.auto,.always,.never) toLiquidGlassWidgets.initialize(). Default.autopreloads all shaders for Android, iOS, and macOS, while skipping unused premium shaders on statically-capped desktop/web backends. DeprecatedwarmUpImpellerPipeline.- Windows & Linux adaptive defaults:
GlassAdaptiveScopestatic probe defaults Windows and Linux toGlassQuality.standard(lightweight_glass.fragwith real iOS 26 squircle geometry, dual specular highlights, meniscus absorption, and blur) for guaranteed 60/120fps performance without driver compile delays. - GLES shape compile optimization:
shaders/sdf.glslnow caps shape evaluation at 8 shapes on OpenGL ES / ANGLE backends via#ifdef LGR_OPENGLES_CAP_SHAPES(shaders/gles_compat.glsl), reducing the inlined AST size for runtime JIT drivers while leaving Metal (iOS/macOS) and Vulkan (Android) on the full 16-shape unrolled AOT path with zero changes.
- Windows Impeller startup hang (#204):
-
0.29.818 Aug 2026Release notes
Open source →Maintenance & Upstream Compatibility
- Pure Flutter SDK dependencies: Removed all third-party and external dependencies across runtime and dev environments (
equatable,flutter_shaders,logging,meta,alchemist, andmocktail). Golden tests now use Flutter's nativematchesGoldenFile. Package depends purely onflutter: sdk: flutter. - Optimized value equality: Replaced
Equatablewith nativeoperator ==andObject.hashAllacross shapes and settings, eliminating heap allocations during hot animation loops. - Internalized shader loading & uniform binding: Shaders load directly via
dart:ui.FragmentProgramwith cached isolate pipelines and zero-overhead uniform setters.
- Pure Flutter SDK dependencies: Removed all third-party and external dependencies across runtime and dev environments (
-
0.29.717 Aug 2026Release notes
Open source →Performance
- Progressive blur: 50% fewer texture reads. Gaussian tap count halved with identical ±3σ coverage; GPU memory bandwidth for the blur pass is halved with no perceptible quality change.
- Gaussian weight loop:
exp()eliminated. Per-tap exponential replaced with a two-scalar IIR recurrence — saves 24 GPU instructions per blur pass with mathematically identical output. - PlatformView fallback: skipped when unused. The background composite is now gated on a uniform flag; no GPU cost when there is no PlatformView beneath the glass.
Visual
edgeAbsorptionparameter added (LiquidGlassSettings,GlassThemeSettings, default0.0): Physical rim darkening — the glass absorbs more light at the thickest edge. Default0.0matches iOS 26's crisp luminous glass. Increase (0.10–0.20) for physical-depth or iOS 27-style recipes.- Hemisphere lens profile: Replaced polynomial falloff with a physical circular arc across all shaders — interiors stay crystal clear while absorption steepens at the bevel.
- Light-modulated absorption: Absorption is scaled by light direction for realistic 3D rim separation without washing out specular highlights.
- Cross-platform
fresnelStrengthparity:fresnelStrengthnow controls grazing-angle rim highlights identically across all rendering engines (Skia, Web, Windows, Android, and Impeller). - Edge-concentrated chromatic aberration: Prismatic dispersion is now strictly zero in flat glass interiors, concentrated only at the rim — consistent across all rendering paths.
Example App & Tooling
- Meniscus & Blur Lab (
MeniscusAndBlurDemoPage): Interactive calibration workbench for live testing ofedgeAbsorption,fresnelStrength, thickness, blur, and progressive blur performance.
Bug Fixes
GlassTabBarExtraButtonloses backdrop blur in minimal quality (#203): AddedisStationaryflag toGlassButton(defaultfalse). SettingisStationary: trueonGlassTabBarExtraButtonretains itsBackdropFilterblur inGlassQuality.minimal.
-
0.29.615 Aug 2026Release notes
Open source →Bug Fixes
- Impeller GLES sampling UV double-flip on Flutter 3.46+ (#202): Flutter 3.46 absorbed the OpenGL ES render-to-texture Y-axis inversion inside the engine backend, but six shader sites still compensated for the old convention — actively mirroring every backdrop sample on 3.46+. A new
shaders/gles_compat.glslheader gates the flip onIMPELLER_OPENGLES_UNFLIPPED_DEPRECATED, the migration macro introduced by the Flutter engine team for exactly this transition. The fix is correct across all Flutter versions from the existing>=3.41.0minimum; thepubspec.yamlconstraint does not move. A new source-level regression guard intest/shaders/gles_flip_guard_test.dartprevents the pattern from re-appearing.
Thanks to @TIANLI0 for the contribution (#202).
- Impeller GLES sampling UV double-flip on Flutter 3.46+ (#202): Flutter 3.46 absorbed the OpenGL ES render-to-texture Y-axis inversion inside the engine backend, but six shader sites still compensated for the old convention — actively mirroring every backdrop sample on 3.46+. A new
-
0.29.512 Aug 2026Release notes
Open source →Bug Fixes
GlassAppBartitle not centred whenleadingis set (#198): Toolbar layout rewritten using aCustomMultiChildLayoutdelegate, matching the approach used by Flutter's ownCupertinoNavigationBar. The title is now centred on the full bar width regardless of leading/trailing widget sizes, and is constrained to never overlap either button group.centerTitle: falsecorrectly left-aligns the title after the leading widget in both LTR and RTL locales.
-
0.29.410 Aug 2026Release notes
Open source →Bug Fixes
GlassModalSheetstale_currentState(#197): A drag ending at its origin state left_currentStateholding the predicted mid-drag target._currentStateis now reconciled on every snap unconditionally, preventing the sheet from appearing stuck after a short drag.GlassModalSheetoverscroll axis guard (#197):_onScrollNotificationnow ignores notifications whosemetrics.axisis notAxis.vertical, preventing horizontal descendant lists (carousels, date strips) from hijacking the sheet gesture.GlassModalSheetone-shot axis lock (#197): Gesture axis is decided once on the first movement past the threshold and held until the touch lifts, preventing a sideways swipe from later grabbing the sheet mid-gesture.
Thanks to @jfhair for the contribution (#197).
-
0.29.309 Aug 2026Release notes
Open source →Bug Fixes
- Premium glass lens detaches during
CupertinoSheetdrag (#192): The refracted lens drifted away from its pill while an interactiveCupertinoSheetdrag scaled the background. Fixed by snapshotting the layer's unscaled screen-space coordinates on every paint frame and freezing them the moment a uniform ancestor scale-down is detected, keeping UV mapping locked to the captured texture for the duration of the drag. GlassAppBartitle typography (#194): The title widget is now wrapped inDefaultTextStyleusingCupertinoTheme'snavTitleTextStyle, matching nativeCupertinoNavigationBarbehaviour. A plainTextwidget now automatically picks up correct Cupertino typography (weight, size, ellipsis) without manual styling. Also addsSemantics(header: true)for VoiceOver/TalkBack navigation.
- Premium glass lens detaches during
-
0.29.204 Aug 2026Release notes
Open source →Universal DPR (Device Pixel Ratio) Normalization
The Liquid Glass rendering engine now achieves 1:1 mathematical parity across all display densities (e.g., macOS 2.0x, iOS 3.0x, and various Android fractional densities like 2.75x or 3.5x). Previously, running shaders in physical pixels caused inconsistent refraction scaling on high-density screens.
- Geometry Curvature:
effectiveThicknessis scaled by DPR, guaranteeing identical refraction depth across devices. - Surface Normals: SDF tap spacing is scaled by DPR, ensuring edge highlights and Fresnel rims maintain exact proportional widths.
- Lighting Clamps: The physical thickness floor is scaled by DPR, preventing lighting anomalies across different hardware.
Bug Fixes
- Indicator Pill: Removed chromatic aberration (
0.15→0.0) from the default animated pill to eliminate the rainbow rim artifact, while preserving true lens distortion. - Pinch Shader: Fixed a mathematical bug (L6 norm with an 8th-root extraction) in the squircle distance field. Replaced with an exact L4 norm, producing naturally soft, Apple-like corners during drag animations and saving one GPU instruction per fragment.
- Brightness Cascade (#124): Hardened the brightness resolution cascade by evaluating
brightnessResolverbeforeCupertinoTheme.of. This acts as a defensive backstop for older Flutter versions or edge cases where theMaterialBasedCupertinoThemeDatabridge fails to propagateThemeModecorrectly. - Accessibility / Semantics (#189): Restored VoiceOver/TalkBack tap-to-dismiss behavior. The
GlassModalSheetdrag indicator now exposes aSemantics.onTapaction that correctly triggers sheet dismissal, matching Material's handle behavior. - Customization (#190):
GlassModalSheet'sdragIndicatorColoris now honored. It was previously accepted by the API but dropped internally in favor of hard-coded defaults. - Android Quality (Best Foot Forward):
GlassAdaptiveScopenow seeds atmaxQuality(premium) on Android from the very first frame. Previously, Android cold-started atstandardand promoted to premium only after the 3-second Phase 2 benchmark. The ANR safety net (shader pre-compilation inLiquidGlassWidgets.initialize()) makes this safe; Phase 2 continues to demote genuinely slow/budget devices.
- Geometry Curvature:
-
0.29.103 Aug 2026Release notes
Open source →Fixes
GlassSegmentedControl— duplicate unlabeled semantics node (#188) Each segment emitted two tappable nodes (one unlabeled), breaking VoiceOver/TalkBack. Fixed by addingexcludeFromSemantics: trueto the internalGestureDetector; semantics are fully handled byGlassFocusRegion.GlassTabBarshadow lost in Dark OS + Light app (#124) Shadow disappeared when the device was in Dark Mode butThemeMode.lightwas set. Introduced a zero-material IoC bridge: passbrightnessResolver: Theme.maybeBrightnessOftoLiquidGlassWidgets.wrap()so the package correctly honoursThemeModewithout importingflutter/material.dart.Migration — MaterialApp users must add this one line to fix #124:
runApp(LiquidGlassWidgets.wrap( child: const MyApp(), brightnessResolver: Theme.maybeBrightnessOf, ));CupertinoAppusers: no change required.Dead
flutter/material.dartimport removed fromtab_bar_bottom_internal.dartLeftover from pre-0.26.0, never cleaned up.lib/is now 100% zero-material forcupertino_uicompatibility.
Eliminates production ANRs on Android devices running Impeller GLES (devices without Vulkan support, including many MediaTek and budget Qualcomm Snapdragon SoCs).
Root cause
On Android GLES,
glCompileShader+glLinkProgramexecutes synchronously on the Flutter raster thread at first use (100–800 ms on mid-range hardware). When this coincides withFlutterJNI.nativeSurfaceChangedduring surface setup, Android's watchdog declares an ANR. The previous warm-up implementation instantiated aLiquidGlassLayerwidget outside the widget tree — an unmounted widget is never rasterized, so no GPU work occurred. The warm-up was a no-op.Fix
-
True GPU warm-up (
liquid_glass_setup.dart):_warmUpImpellerPipeline()now draws both premium glass shaders to a 1×1 off-screen surface usingPicture.toImage()and awaits rasterization. This forces GLES pipeline compilation on the raster thread whileinitialize()is still running — beforerunApp— so compilation completes behind the native splash screen and cannot race with surface setup. -
Android-only execution: The warm-up is guarded by
defaultTargetPlatform == TargetPlatform.android. iOS and macOS use precompiled Metal shaders and skip this step entirely, preserving their zero startup overhead. -
Reuses cached programs: The warm-up now calls
MultiShaderBuilder.cachedProgram()to retrieve theFragmentProgramobjects already loaded byprecacheShaders()in step 1 ofinitialize(). No duplicate GPU objects are created. -
warmUpImpellerPipelineparameter:LiquidGlassWidgets.initialize()accepts a newwarmUpImpellerPipeline: boolparameter (defaulttrue). On non-Android platforms the parameter is a no-op. Passfalseonly if you are managing Android shader warm-up yourself. -
Conservative Android quality seeding (
glass_adaptive_scope.dart): Fixed a code-comment mismatch in_GlassAdaptiveScopeState.initState. The file header documented seeding atGlassQuality.standard; the code seeded atmaxQuality(premium). On Android,initStatenow correctly seeds atGlassQuality.standardso Phase 2 benchmarks the device from a stable baseline. iOS and macOS continue to seed atmaxQualityfor an immediate premium experience.
No action required
Existing call sites (
await LiquidGlassWidgets.initialize()) are unchanged and benefit from the fix automatically. TheadaptiveQuality: truepath also benefits from the correctedinitStateseeding on Android.Documentation
- README Platform Support table now distinguishes Android Vulkan from Android GLES and links to a new Android GLES mitigation section.
-
-
0.29.003 Aug 2026Release notes
Open source →🎵 iOS 26
tabViewBottomAccessorySupportAdded
bottomAccessory/bottomAccessoryHeight/bottomAccessoryEnabled/bottomAccessorySpacing/bottomAccessoryPlacementto bothGlassTabBar.bottomandGlassTabBar.searchable— mirroring Apple'stabViewBottomAccessorymodifier.- Expanded mode — accessory floats directly above the nav bar pill with a configurable spacing gap (default
6.0px, calibrated to match Apple's native spacing). - Inline mode (
searchableonly) — setbottomAccessoryPlacement: GlassTabBarAccessoryPlacement.inlineto have the accessory slide horizontally into the gap between the collapsed tab indicator and search capsule, with a simultaneous width squish, matching the iOS 26.inlineplacement. - Two independent animation timelines —
accessoryTdrives the inline↔expanded morph (height, left, right) whilesearchTtracks the tab-pill→search-capsule height change so the accessory follows the bar downward during the search activation, maintaining a consistent visual overlap gap throughout. - Safe defaults —
bottomAccessoryPlacementdefaults to.expanded. The accessory never collapses inline automatically; developers must explicitly opt into.inlineplacement, mirroring the iOS 26 model where placement intent is declared at the call site. - Pixel-accurate scaffold insets —
preferredSizeis always in sync with the layout engine soGlassScaffold's edge fade reserves the exact correct amount of space in both expanded and inline states. GlassTabBarAccessoryPlacementenum —expandedandinlinevalues, readable inside the accessory widget itself viaGlassTabBarAccessoryPlacementScope.of(context)to adapt the accessory's own layout between the full row and compact strip.- Apple Music and Apple Podcasts demos fully showcase the feature — including the expanded/inline transition and the search-active behaviour.
Upgrading from
bodyOverlaysPreviously, the recommended pattern for a floating mini-player was to place it in
GlassScaffold.bodyOverlaysand manually manage its position usingAnimatedPositionedwith scroll-offset math. That approach still works andbodyOverlaysremains available for other use cases (e.g. floating action overlays, toast banners).For a bottom accessory that is architecturally part of the tab bar — which is exactly what iOS 26
tabViewBottomAccessorymodels — thebottomAccessoryAPI is the correct replacement:// Before (bodyOverlays workaround) GlassScaffold( bodyOverlays: [ AnimatedPositioned( bottom: _isMiniMode ? barH : barH + accessoryH + spacing, left: 0, right: 0, child: MiniPlayer(), ), ], ) // After (iOS 26-aligned) GlassTabBar.searchable( bottomAccessory: MiniPlayer(), bottomAccessoryHeight: 50.0, bottomAccessoryPlacement: _isMiniMode && !_isSearching ? GlassTabBarAccessoryPlacement.inline : GlassTabBarAccessoryPlacement.expanded, )The new API removes all manual position arithmetic, keeps the
GlassScaffoldedge fade pixel-accurate, and persists the accessory automatically across tab switches.
- Expanded mode — accessory floats directly above the nav bar pill with a configurable spacing gap (default
-
0.28.102 Aug 2026Release notes
Open source →📚 Internal Refactor — API Documentation
- 100% Dartdoc coverage — every public member is documented.
public_member_api_docsis now permanently enabled inanalysis_options.yaml; future undocumented public API additions will faildart analyze. - Internal layout engines moved to
lib/src/— 9 internal implementation files relocated per Dart convention sodart docand pub.dev omit them from the generated API reference. No public API changes. 2517 tests passing.
- 100% Dartdoc coverage — every public member is documented.
-
0.28.002 Aug 2026Release notes
Open source →🌐 Full RTL (Right-to-Left) Support
Completed the Right-to-Left (RTL) layout audit. All directional padding and alignment primitives now use
EdgeInsetsDirectional/AlignmentDirectionalso widgets mirror correctly in RTL locales (Arabic, Hebrew, Persian, etc.) without any API changes for existing callers.Widgets Updated
GlassGroupedSection— header and footer labels now useEdgeInsetsDirectional.only(start: 16, end: 16). Under RTL the text aligns to the correct physical edge.GlassDivider— horizontal dividerindent/endIndentnow useEdgeInsetsDirectional.only(start: indent, end: endIndent). The leading indent is always on the logical leading side regardless of text direction. Vertical dividertop/bottomare unchanged (not directional).GlassAppBar— non-centered title now usesAlignmentDirectional.centerStart+EdgeInsetsDirectional.only(start: 8)so the title anchors to the leading edge in both LTR and RTL.GlassSearchBar— cancel button gap usesEdgeInsetsDirectional.only(start: 10)so the gap appears between the search field and the cancel button in both directions.GlassDialog— horizontal two-button layout gap usesEdgeInsetsDirectional.only(start: 8)so buttons remain correctly spaced in RTL.GlassLargeTitle— two improvements:paddingandsearchBarPaddingdefaults changed fromEdgeInsets.*toEdgeInsetsDirectional.*— the field types were alreadyEdgeInsetsGeometry, so this is a zero-breaking-change improvement. Callers who pass custom asymmetric directional padding (e.g.EdgeInsetsDirectional.only(start: 32)) now get correct physical mirroring in RTL.Transform.scalealignment changed fromAlignment.bottomLefttoAlignmentDirectional.bottomStart— under RTL, the rubber-band overscroll stretch now scales from the correct logical leading edge rather than always pinning to the physical left.
GlassProgressIndicator.linear— custom canvas drawing now explicitly respectsDirectionality. In RTL locales, both the determinate fill and indeterminate moving bar correctly animate from the physical right (logical start) to the left.GlassSlider— drag logic and active track drawing now invert cleanly under RTL. Dragging left increases the value, and the track anchors to the physical right.
Bug Fixes & Accessibility
GlassTabBar.bottom— Resolved an edge-case visual bug in RTL mode where the animated glass pill would jump to the mirror-image tab when selecting an end tab. Fixed by enforcingAlignment(x, y)physical coordinates instead ofAlignmentDirectionalwithin the physics engine bounds.GlassSegmentedControl&GlassTabBar— Unselected segments now correctly emit a "not selected" accessibility state (resolves #184). Thanks to @Xodus-CO for the detailed report!
-
0.27.001 Aug 2026Release notes
Open source →♿ Accessibility & Keyboard Navigation
Every interactive widget now supports full keyboard traversal, Space/Enter activation, and VoiceOver/TalkBack semantics.
Focus & Keyboard
GlassFocusRegion— new shared widget that is the single source of truth for all focus behaviour. Two modes:- Interactive — wraps
FocusableActionDetector, registersActivateIntent(Space/Enter), lifts hover/focus state to parent viaValueNotifier, and paints the iOS 26-style outset focus ring. - Observe (
.observe()) — for widgets that own their ownFocusNode(e.g.GlassTextField). Listens passively and paints the ring; no duplicate traversal or intent handling.
- Interactive — wraps
- iOS 26 focus ring — 3 px outset, 2 px wide, rounded to the widget's shape. Implemented as
GlassFocusRingPainter(pureCustomPainter; zero cost for touch users viaValueListenableBuilder). GlassStepperkeyboard fix — Space/Enter triggers a single-shot activation; the pointer-only long-press repeat timer is no longer erroneously started by keyboard input.- All 12 interactive widget families wired:
GlassButton,GlassSwitch,GlassSlider,GlassSegmentedControl,GlassListTile,GlassMenuItem,GlassActionSheet,GlassButtonGroup,GlassTextField,GlassSearchBar,GlassStepper,GlassChip.
Semantics
- Semantic roles (
isButton,isSlider,isSelected,toggled,value) set on every widget matching Material and Cupertino SDK conventions. - Destructive and disabled states propagate correctly to the semantic tree.
GlassBadge—semanticLabelandsemanticCountoverrides — resolves two user-reported limitations:semanticLabel(optionalString?) fully replaces the VoiceOver/TalkBack announcement; enables non-notification domains ("5 downloads", "Online", etc.).semanticCount(optionalint?) overrides only the spoken number while leaving the visual cap (99+) unchanged; allows "2500 notifications" to be announced when the badge visually shows "99+".- Both constructors (
GlassBadgeandGlassBadge.dot) supportsemanticLabel;semanticCountis only applicable to count badges. - Fully backwards-compatible — existing callers with no overrides get identical defaults.
GlassPageControl—semanticLabelparam — the capsule now announces'Page N of M'to VoiceOver/TalkBack by default (1-indexed). AsemanticLabeloverride lets callers substitute domain-specific wording (e.g.'Slide 3 of 5'). Non-interactive controls (noonPageChanged) omit the tap hint automatically.GlassPasswordField— toggle button semantics — the show/hide password suffix icon is now wrapped inSemantics(label: 'Show password' / 'Hide password', button: true). Previously theGestureDetectorwas invisible to screen readers; VoiceOver now correctly announces the button and its toggled state.GlassProgressIndicator—semanticLabelparam — the hardcoded'Progress'label is now an overridable default. Callers can passsemanticLabel: 'Download progress'(or any domain string) to both.circular()and.linear()constructors. Backwards-compatible.GlassPullDownButton—semanticLabelparam — icon-only pull-down buttons previously announced an empty string. A newsemanticLabelparam (e.g.'More options') is used as theGlassButton.labelin the icon-only code path. Visible-label variants are unaffected.
Layout & Architecture
GlassInteractionStateMixin— internalStatemixin (modelled onSingleTickerProviderStateMixin) providingisPressed,isFocused,isHoveredValueNotifiers and theirListenable.mergecombinations. Replaces ~76 lines of identical boilerplate acrossGlassListTile,GlassMenuItem,_ActionSheetButton, and_GlassGroupItemWidget. No public API change.- All container-item widgets migrated to
ValueNotifier+ListenableBuilder— only theAnimatedContainerhighlight layer rebuilds on interaction; the surrounding subtree is stable.
-
0.26.131 Jul 2026Release notes
Open source →🐛 Bug Fixes
GlassAdaptiveScopequality oscillation afterreset()— whenallowStepUp: false, areset()-triggered warm-up could silently promote quality back up (e.g.standard → premium). Warm-up now respects the flag and can only confirm or lower the current quality, never raise it. Thanks to @jingluoguo for the contribution (#180).
-
0.26.030 Jul 2026Release notes
Open source →- Remove GlassScaffold.floatingActionButton (breaking, pre-v1 cleanup)
- Zero material.dart imports in lib/ — full Cupertino decoupling
- Apple replica demos material-free (music, podcasts, messages, news, lockscreen)
- Replace Material Slider with CupertinoSlider in demo pages
- Fix navigation double-tap freeze in showcase
- dart format pass — 293 files stable
Release notes
Open source →💥 Breaking Changes
-
GlassScaffold.floatingActionButtonremoved — iOS does not use Floating Action Buttons.floatingActionButtonis removed entirely; this package is pre-v1 and that is the window to get the API right.Migration:
// Before GlassScaffold( floatingActionButton: FloatingActionButton(onPressed: _add, child: Icon(Icons.add)), body: ..., ) // After — glass-treated, iOS-idiomatic GlassScaffold( bodyOverlays: [ Positioned( bottom: 24, right: 24, child: GlassButton( onTap: _add, child: const Icon(CupertinoIcons.add, color: CupertinoColors.white), ), ), ], body: ..., )bodyOverlaysis above the body and below the bars.GlassButtonapplies the correct liquid glass treatment.
♻️ Refactoring — Material Decoupling (
material.dart36 → 0)GlassScaffoldnow usesCupertinoPageScaffoldinternally.GlassPageno longer injects a MaterialThemeshim. AllColors.*constants replaced withCupertinoColorsor explicit hex literals. No visual changes.glass_brightness.dartwas completely rewritten to drop its dependency onTheme.maybeBrightnessOf. It now readsCupertinoTheme.of(context).brightness, which natively inherits from the MaterialThemeModewhen used inside aMaterialApp(thanks to Flutter's automaticMaterialBasedCupertinoThemeDatainjection).With this final swap,
liquid_glass_widgetsnow has zero imports ofpackage:flutter/material.dartin itslib/directory. It is fully decoupled and ready for thecupertino_uipackage split. The example's Apple replica demos (apple_music,apple_podcasts,apple_messages,apple_news,apple_lockscreen) run with zero Material imports as well, proving the library works purely within a Cupertino-only context.🐛 Bug Fixes
LightweightLiquidGlassincorrect brightness inGlassThemeoverride contexts — was callingTheme.of(context).brightnessinstead of the canonicalGlassTheme.brightnessOf(context). Could produce wrong brightness whenGlassThemeset a different mode than the ambient Material theme. Fixed.
-
0.25.129 Jul 2026Release notes
Open source →🐛 Bug Fixes
GlassMenumispositioned inside nestedNavigators —OverlayPortalnow targets the rootOverlay(viaOverlayChildLocation.rootOverlay), matching the screen-global coordinates captured bylocalToGlobal. Previously the menu drifted by the offset of the nearest nestedOverlayfrom the screen edge (e.g. a side-rail in aStatefulShellRoutelayout). Thanks to @sinanhaci for the contribution (#179).
-
0.25.028 Jul 2026Release notes
Open source →✨ New Features
-
Optional sheet detents —
GlassModalSheet(and.show()) gained adetentsset (Set<GlassSheetDetent>, mirroring UIKit's sheet detents) plus adismissibleflag, to compose which stops a sheet offers:{GlassSheetDetent.medium}→ a half-only glass sheet that never morphs to the opaque full state (its content still scrolls at the half detent).{GlassSheetDetent.large}→ a full-only opaque sheet that opens straight to full.{GlassSheetDetent.medium, GlassSheetDetent.large}→ the default two-stop sheet.{GlassSheetDetent.small, ...}→ adds the maps-style peek floor underneath, so one mechanism now describes every resting stop.dismissible: false→ the sheet rubber-bands at its lowest detent instead of swiping away (the Apple Pay / Sign in with Apple pattern).
The set must be non-empty (asserted). Style the small detent with the existing
peek*params (peekSettings,peekWidth, …), which stay top-level: aSetwhose members carried per-instance payload would need equality that ignores that payload, ordetents.contains(small)breaks and two differently-configured smalls could sit in one set.
⚠️ Deprecations
GlassModalSheet.enablePeek→ useGlassSheetDetent.smallindetents. Peek is now a detent like medium and large.enablePeekis still honoured and takes precedence over the set when set explicitly, so existing code keeps working unchanged; it will be removed in a future release.GlassSheetMode.persistentkeeps its peek floor with or without the detent — a persistent sheet is defined by resting rather than dismissing.
🐛 Bug Fixes
GlassModalSheetControllerreattachment — the controller no longer detaches when itsGlassModalSheetis swapped under a stable controller (e.g. aValueKeychange): the replacement'sinitStateruns before the outgoing widget'sdispose, so the detach is now guarded to only clear the attachment it still owns. Previously this left the controller inert and the sheet un-openable.- Half-only content scrolling — a sheet's inner scroll view now enables at
the sheet's topmost detent rather than hardcoding the
fullstate, so a half-only sheet scrolls its content correctly.
Thanks to @jfhair for the contribution (#178).
-
-
0.24.428 Jul 2026Release notes
Open source →🐛 Bug Fixes
GlassBottomBarcollapse trajectory fixed — whenGlassBottomBarCollapseConfigwithdirection: towardsExtraButtonwas set, the collapsed tab pill stopped short of the extra button, leaving a visible gap between the two circles. The pill now travels to the exact horizontal centre of the extra button on both left and right placements, and the extra button correctly overlays the pill at the end of the animation. Thanks to @jingluoguo for the contribution (#176).
✨ Improvements
-
GlassBottomBarCollapseConfigdefaultanimationDurationbumped from 220 ms → 280 ms — the collapse animation carries semantic meaning (the tab pill merges into the action button) and the extra 60 ms is enough for the eye to track the trajectory without feeling sluggish. Override it any time via the config:collapseConfig: GlassBottomBarCollapseConfig( animationDuration: Duration(milliseconds: 220), // snappier ),
-
0.24.327 Jul 2026Release notes
Open source →🐛 Bug Fixes
- Transparent scaffold during navigation fixed —
GlassScaffoldwas unconditionally setting the innerScaffold.backgroundColortoColors.transparent, even when nobackgroundwidget was provided. This caused the previous route to bleed through the incoming screen duringMaterialPageRouteslide transitions, making the new page appear transparent. Fixed by only forcing the scaffold transparent whenGlassScaffoldactually has a background widget orbackgroundColorto render — matching the existingGlassPagebehaviour. Screens without an explicit background now inherit the theme's opaquescaffoldBackgroundColor, producing correct, opaque transitions (issue #177).
- Transparent scaffold during navigation fixed —
-
0.24.227 Jul 2026Release notes
Open source →🐛 Bug Fixes
- Android cold-launch crash fixed — On Android, Flutter's warm-up frame can produce zero-width or unbounded (
Infinity) layout constraints before the window fully resolves.RenderLiquidGlassGeometry._buildGeometryPicturewould attempt to scale andceil()those non-finite bounds, throwingUnsupportedError: Infinity or NaN toInt. Fixed by guarding against empty or non-finite bounds and returning a safe emptyPicturefor the one affected frame. A matching guard was added inLiquidGlassRenderObject.paintto prevent the same non-finite values propagating intotoImageSync. - Compositing bits race condition fixed (Vulkan/Impeller) — When
LightweightLiquidGlassorLiquidGlassRenderObjectchanged settings that affectalwaysNeedsCompositing, the compositing bit was not synchronised, causing incorrect layer decisions on the first paint under Vulkan. Fixed by callingmarkNeedsCompositingBitsUpdate()whenever the compositing contract changes.
- Android cold-launch crash fixed — On Android, Flutter's warm-up frame can produce zero-width or unbounded (
-
0.24.126 Jul 2026Release notes
Open source →🐛 Bug Fixes
GlassButton.customborder invisible in release builds with--obfuscate—GlassEffectandLightweightLiquidGlasswere usingdynamicproperty access andruntimeType.toString()heuristics to extract the shape's corner radius for the shader. Both techniques are silently broken by the Dart AOT obfuscator: property names are mangled and class names become single-character tokens. The fallback silently resolved to0.0, producing a perfectly square (borderless) shape in release builds. Fixed by adding a typedeffectiveRadiusabstract getter toLiquidShapeimplemented by every concrete shape class — a single virtual dispatch that the compiler can optimise and the obfuscator cannot break.
-
0.24.026 Jul 2026Release notes
Open source →⚠️ Breaking Changes
-
GlassAppBar.preferredSizeconstructor parameter removed. Replace withtoolbarHeight: double(default44.0).// Before GlassAppBar(preferredSize: Size.fromHeight(52)) // After GlassAppBar(toolbarHeight: 52)
🐛 Bug Fixes
GlassScaffolddark-mode gradient flash fixed. WhenbackgroundColoris set and the device is in dark mode, the edge-fade gradient aroundGlassTabBar/GlassBottomBarcould briefly flash dark. The scaffold now always renders with a transparent Material background (preventing theme bleed), and passes the explicitbackgroundColorto the edge-effect fallback gradient.GlassBottomBardefault radius inconsistency fixed. Was32.0; nowGlassDefaults.capsuleRadius— consistent withGlassTabBar.bottom.- Indicator radius inconsistent across widgets. All interactive widgets (
GlassSegmentedControl,GlassTabBar,GlassBottomBar) now share a unified radius calculation that is guaranteed correct during jelly-bloom expansion.
✨ New Features
-
3-tier indicator radius system — all interactive bar/control widgets now follow iOS 26 defaults out of the box:
- Tier 1 (default): bar and indicator are both a perfect capsule — no configuration needed.
- Tier 2: set
barBorderRadiusto a custom value and the indicator automatically tracks atbarBorderRadius − padding(concentric nested arcs). - Tier 3: set
indicatorBorderRadiusexplicitly to override everything.
-
GlassDefaults.capsuleRadius— named constant (9999.0) for the capsule sentinel:GlassBottomBar(barBorderRadius: GlassDefaults.capsuleRadius) -
GlassAppBar.bottom— Accepts anyPreferredSizeWidget(typically aTabBar) rendered below the navigation bar title. The scaffold automatically reserves the combined height — no manual sizing needed.GlassAppBar( title: const Text('Browse'), bottom: TabBar(tabs: [...]), )
-
-
0.23.024 Jul 2026Release notes
Open source →⚠️ Breaking Changes
-
AnimatedGlassIndicator.useSuperellipseremoved. This parameter has been deleted from the constructor. Any call site passinguseSuperellipse: trueoruseSuperellipse: falsewill fail to compile.Migration: simply remove the parameter. The indicator is always a rounded rectangle (capsule) now, which is mathematically correct. Squircle geometry is unstable for dynamic stretching elements.
// Before (0.22.1) AnimatedGlassIndicator( useSuperellipse: false, ... ) // After (0.23.0) — just remove the parameter AnimatedGlassIndicator( ... )
✨ New Features
-
LiquidGlassSettings.fresnelStrength— New parameter (range0.0–1.0, default1.0) that scales the natural Fresnel edge luminosity on the Premium rendering path. At1.0the glass behaves as physically lit glass with a rim highlight at grazing angles (existing default). At0.0the rim is completely suppressed, producing a pure blur-overlay appearance that matches iOS 26 system UI glass (Messages buttons, notification banners, lock screen controls). Intermediate values interpolate smoothly. Fully backwards compatible — omitting the parameter preserves all existing rendering. Also exposed onGlassThemeSettingsso it can be set app-wide viaGlassTheme. -
GlassMenuItem.enablePressScale— Newboolparameter (defaulttrue) that controls the 0.98× scale-down animation on press. Set tofalseon fill-rate-limited devices to eliminate the per-frame GPU cost of animating aTransform.scaleover the glass layer. Fully backwards compatible. -
GlassExtraButtonPlacement— AddedGlassExtraButtonPlacement.leftandrightfor non-searchable bottom bars soGlassTabBarExtraButtoncan be placed on either side. Defaults torightto preserve existing behavior. Thanks to @jingluoguo (#169).
🐛 Bug Fixes
- Fixed hard clip at the top of
useOwnLayer: truebuttons during press-scale animation on Impeller. - GlassSlider — Fixed an issue where discrete slider snapping would round the absolute value and shift the snapped range when using a non-zero minimum. Thanks to @huanglizhuo (#168).
♻️ Refactoring — Pure Geometry & iOS 26 Shape Parity
This release fundamentally solves the long-standing geometry tension between Flutter's path rendering and our GPU shaders. We completely rewrote the squircle math to use pure analytical curves, fixed stretching bugs in tab indicators, and simplified the API.
1. Pure Analytic Lamé Squircles
- Shader Rewrite (
sdf.glsl): We completely removed the old piecewise 45-degree seam approach and the hackyblendsafety valve.sdfSquirclenow uses a pure, analytic Lamé curve (|x|^n + |y|^n = 1). Squircles now perfectly match Apple's continuous curve geometry with zero flattening on the edges. - Graceful Degradation (The Ghost-Glow Fix): When a squircle is given a radius that physically cannot fit (e.g.
r = 18on a36pxtall button), the shader now dynamically recalculates the exponentnbased on the clamped available space. As space runs out,nsmoothly degrades to2.0, collapsing into a perfect circle. This mathematically guarantees that the shader's interaction glow always aligns perfectly flush against Flutter's clipping path, eliminating the dark corner gaps.
2. Perfect iOS 26 Pills (Capsules)
Apple never uses squircles for pill shapes (like "Edit" buttons or Tab Indicators). They use pure circular-arc capsules. We audited the library to align with this:
GlassChip& Demo Buttons: ReplacedLiquidRoundedSuperellipsewithLiquidRoundedRectanglefor all pill-shaped elements. They now explicitly usesdfRRectAsym, ensuring perfect circular ends.GlassMenu&GlassPopovermorph blobs: ReplacedLiquidOvalwithLiquidRoundedRectangle. The rounded rect SDF is mathematically stable at all aspect ratios during dynamic morphs.
3. AnimatedGlassIndicator API Cleanup
The glass tab indicator previously suffered from a "stretching bug" where it turned squarish during drag expansion because its finite
borderRadiuswas outgrown by its expanding height.- Removed
useSuperellipse(see Breaking Changes above): This parameter was mathematically incorrect for dynamic stretching indicators. - Optional
borderRadius(Default9999.0):borderRadiusis no longer required. It defaults to9999.0, which offloads the math entirely to the shader'smin(r, shortest)clamp. This guarantees a perfect capsule at any drag size. - Segmented Controls:
GlassSegmentedControlexplicitly passesborderRadius: containerRadius - 3, ensuring it retains its correct inset rounded-rectangle geometry rather than defaulting to a capsule.
📚 Documentation
shape_debug_demo.dart— corrected the Standard-mode description banner from the inaccurate"_SquircleClipper + lightweight shader (CPU L4/L2 path)"to the accurate"ShapeBorderClipper + lightweight blur shader (shape-blind)". There is no_SquircleClipperclass; the lightweight shader is shape-type-blind by design.
-
-
0.22.117 Jul 2026Release notes
Open source →🐛 Bug Fixes
- Tab Bar Semantics — Fixed multiple accessibility issues in
GlassTabBarandGlassBottomBar:GlassTab.semanticLabelis now properly propagated, allowing icon-only tabs to be correctly announced instead of defaulting to'Tab'.- Fixed an issue where tab labels were announced twice by wrapping the internal
Textwidget inExcludeSemantics. - Eliminated duplicate semantic nodes for the active tab by hiding the visual clipping indicator from the accessibility tree, ensuring exactly one node per tab. Thanks to @simiwe (#159).
♻️ Refactoring
- Internal Tab Models — Deprecated
GlassBottomBarTabis no longer used by the internal layout engines. They now natively acceptGlassTab, removing the need for mapping closures andSizedBox.shrink()sentinels.
- Tab Bar Semantics — Fixed multiple accessibility issues in
-
0.22.017 Jul 2026Release notes
Open source →✨ New Features
-
ProgressiveBlur— a graduated backdrop blur that is strongest at one edge and dissolves to sharp at the opposite edge (the iOS 26 / Signal header look). Self-contained — noLiquidGlassLayerancestor required.Positioned( top: 0, left: 0, right: 0, height: 96, child: ProgressiveBlur(maxSigma: 20), )maxSigma— blur sigma at the strong edge (0⇒ passthrough).direction— which edge is strongest (topToBottom/bottomToTop/leftToRight/rightToLeft).falloff— gradient gamma (default1.2).
Pre-warmed by
LiquidGlassWidgets.initialize()at no extra startup cost;ProgressiveBlur.preload()is available for standalone use. Degrades to a uniformBackdropFilteron Skia / web. Seedocs/PROGRESSIVE_BLUR.md. Thanks to @Ahmadre (#162).
⚡ Performance
-
GlassPopoverblur ramp — the backdrop blur now eases in over the opening morph instead of rendering at full strength from frame one. Raster avg halved (10.1 ms → 5.8 ms), worst-case halved, missed-budget frames 15 → 6 on the reference device. Seedocs/POPOVER_BLUR_RAMP.md.Two new backwards-compatible params:
blurRampDuration(defaultDuration(milliseconds: 260)) — set toDuration.zeroto restore the previous always-full-blur behaviour.blurRampCurve(defaultCurves.easeOut).
Automatically disabled when "reduce motion" is active. Thanks to @Ahmadre (#161).
🐛 Bug Fixes
-
GlassPopoverdrifted off its trigger in nested overlays — the morph portal now targetsOverlayChildLocation.rootOverlayto match the root-relative coordinates it is placed at. Top-level usage is unaffected. Thanks to @Ahmadre (#163). -
Intrinsic-height
GlassPopoveroverflowed on live content growth — the popover now re-measures viaSizeChangedLayoutNotifierwhen content grows while open, instead of clamping to the height frozen at open time. Fixed-popoverHeightpopovers are unchanged. Thanks to @Ahmadre (#163).
-
-
0.21.615 Jul 2026Release notes
Open source →🐛 Bug Fixes
GlassTabBar.bottom/GlassBottomBardistorted tap hit regions with >2 tabs (#157) — Tapping a tab incorrectly routed throughDraggableIndicatorPhysics.getAlignmentFromGlobalPosition, which applies an indicator-center remap (±½ tab-widthpadding) designed for continuous drag tracking. For discrete taps this shifted the hit boundaries from the correct25/50/75 %to~31.25/50/68.75 %on a 4-tab bar, making the right ~25 % of tab 2 select tab 3 instead. A newtabIndexFromGlobalPositionhelper bypasses the drag remap and computes the tab index directly from the raw position fraction. The same incorrect remap was present in therecoverIfGestureStuckpath (PlatformView gesture recovery) and is fixed there too. The drag path (onHorizontalDragUpdate/onHorizontalDragStart) is unaffected. Thanks to @bayraktarmdkaraca for the precise root-cause analysis!
-
0.21.514 Jul 2026Release notes
Open source →🐛 Bug Fixes
GlassTabBarloses shadow when OS is Dark but app isThemeMode.light— fixed an issue where glass components incorrectly resolved toBrightness.darkwhen the device OS was in Dark Mode but theMaterialAppwas explicitly set toThemeMode.light. The brightness cascade inresolveGlassBrightnesswas checking theCupertinoThemebefore the MaterialThemeMode; inside aMaterialAppthe Cupertino theme is implicitly derived from the OS, causing it to return the wrong brightness. The cascade now correctly prioritisesThemeModeinMaterialAppcontexts. Thanks to @minhtritc97 for the detailed report!
-
0.21.413 Jul 2026Release notes
Open source →✨ New Features
- Vertical
GlassSegmentedControl— fixed controls now acceptdirection: Axis.verticaland an optionalsegmentExtent. Layout, fractional indicator positioning, jelly expansion, drag velocity, snapping, and gesture recognition all follow the vertical axis. The horizontal default and scrollable constructor remain unchanged. Thanks to @F1orian!
🐛 Bug Fixes
- Impeller shadow corruption fixed —
shadowElevationonLiquidGlassandGlassButtonrendered as solid black circles on Windows, Web, and certain Android emulators due to two known Flutter engine bugs (saveLayertexture corruption on Vulkan andPath.combineclipping failures). The package now selectively bypasses the GPU shadow cutout on affected platforms and uses a safeevenOddwinding rule for the fallback, producing perfect shadows without engine issues.
🧪 Example
- Added an icon-only vertical segmented control to the Interactive page for tap, drag, indicator, and accessibility testing on a physical device.
- Vertical
-
0.21.307 Jul 2026Release notes
Open source →🐛 Bug Fixes
- SVG and custom icons restored —
SizedBox-wrapped icons (e.g.SvgPicture) were silently stripped from the render tree since0.20.0. TheSizedBox.shrink()sentinel detection now checkswidth,height, andchildfields so a caller-suppliedSizedBoxwrapping a real icon is always rendered correctly. - Searchable bar pill stays active while dragging — the glass indicator on
GlassTabBar.searchablecollapsed back to its resting state when the finger passed over the currently selected tab mid-drag. The thickness spring now includes thetabIsDraggingguard, matching the behaviour already present inGlassTabBar.bottom. JellyClipperImpeller radius guard — the clip radius is now clamped to strictly less than half the indicator's shortest side, preventing a malformedRRectpath that caused content to vanish under Impeller's Metal renderer in certain animation frames.
🧹 Example
- Indicator Parity demo calibrated — default refraction set to
1.15(GlassDefaults.refractiveIndex) to match all Apple demos; bothGlassTabBar.inlinevariants now wire live tuner sliders for expansion and pinch strength.
- SVG and custom icons restored —
-
0.21.207 Jul 2026Release notes
Open source →🐛 Bug Fixes
- Extra button stretch disabled over platform views —
GlassTabBarExtraButtonnow correctly disables its stretch effect whenplatformViewBackdrop: true, matching the behavior ofGlassTabBar.bottomand fixing the jittery spring animation. GlassMenuitem scroll wiggle fixed — menu items with wrapped text (e.g.maxLines: 2) could drift sub-pixel vertically during slide-to-select dragging becauseClampingScrollPhysicsallowed fractional scroll offsets even when no overflow was intended. Non-scrollable menus now useNeverScrollableScrollPhysics, locking the content completely in place during drag.GlassPopoverfirst-frame height fixed — the popover was briefly rendered at full-screen height before its content height was known, producing a visible flash and forcing users to wrap content in aSingleChildScrollViewas a workaround. An invisibleOffstagemeasurement pass now runs on Frame 1, letting Flutter's layout engine calculate the exact intrinsic content height before the morph animation starts. The animation launches on Frame 2 with perfect geometry — no mid-flight height correction, no flash, no heuristics.
⚡ Performance
- Removed unnecessary compositing layer in
GlassBottomBar— theRepaintBoundarywrapping the icon layer in_buildHighQualityModeis now only mounted whenplatformViewBackdrop: true, where it is required for the Platform View capture path (bug #99). In the common case (platformViewBackdrop: false) the boundary was creating a GPU offscreen texture every frame with no caching benefit, since theJellyClipperchanges on every animation frame. GlassPopoveridle trigger optimised — when the popover is fully closed, the trigger widget now skips unnecessaryTransform,Opacity, andIgnorePointercompositing layers. This removes redundant GPU work in the common idle state.
🧹 Code Quality
- Null-safe trigger child access — replaced a
child!force-unwrap inGlassPopoverInternalwith a null-safechild ?? const SizedBox.shrink()fallback, preventing a hard crash ifchildis ever omitted in a future refactor.
🧪 Example
- Refraction slider added to Indicator Parity demo tuner — a "Refraction (n)" slider (range 1.0–2.0, default 1.59 matching
GlassTabBar.bottom's internal default) for tuning therefractiveIndexon the Premium (Impeller) glass indicator. Standard indicators do not perform background capture so the parameter has no visual effect there.
- Extra button stretch disabled over platform views —
-
0.21.105 Jul 2026Release notes
Open source →🐛 Bug Fixes — Standard indicator parity
- Two-pills misalignment fixed — at
GlassQuality.standard, the background rim and glass lens now share the same shape geometry (ShapeDecoration) and ride the same jellyTransform, eliminating the visible separation mid-morph. Regression reported againstGlassTabBar.inline/ segmented controls. - Indicator collapse on drag fixed — the glass indicator no longer morphs back to the resting pill when dragging over the selected tab. The thickness gate now includes
tabIsDraggingso the indicator stays fully active for the entire gesture duration. - Standard rim thickness normalised — the indicator rim on
GlassQuality.standardis now proportionally mapped fromindicatorSettings.thickness(same value as Premium) rather than scaling from the raw glass-depth value, which produced a ~2.8 px border. The result is a fine hairline that gracefully matches the Premium look on Standard-quality devices.
- Two-pills misalignment fixed — at
-
0.21.004 Jul 2026Release notes
Open source →✨ New Features
GlassModalSheetdrag progress — controller now exposes aprogressgetter andprogressListenablereporting the live 0–1 drag position between half↔full snap points, so hosts can drive coordinated UI in real time. (#148, @jfhair)GlassTabBar.inlinespring control — the.inlinefactory now acceptsspringDescription, matching the other factory constructors. Previously inline tab bars were locked to the shared default spring. (#149, @jfhair)LiquidGlassSettings.ambientRim— tunable full-perimeter rim on the moving indicator pill. Defaults match Apple Music's segmented control (brighter in light mode, off in dark). (#150, @jfhair)AnimatedGlassIndicatorshadow —shadowElevation/shadowinindicatorSettingsnow correctly paints a drop shadow on the glass jelly. Previously these values were silently ignored. (#151, @jfhair)
🐛 Bug Fixes — PlatformView gesture stability
- Tab bar freeze fixed: Intermittent freeze where the tab indicator stopped
responding after interacting over an iOS
PlatformView(e.g. a map or WebView). The iOS gesture arena can silently drop terminal callbacks, leaving the recognizer wedged. Fixed with proactive cleanup onPointerDown, post-frame recovery, and a gesture ID guard to prevent rapid-tap state corruption. - Hybrid gesture mode on
platformViewBackdrop: true: When the tab bar floats over aPlatformView, the visual indicator now animates to its new position instantly on touch-down (matching native iOS responsiveness), while the actual tab content swap is deferred safely to touch-up. This prevents the iOS UIKit view system from dropping the touch stream mid-gesture due to a mid-frame unmount of thePlatformView. No impact on any screen whereplatformViewBackdropisfalse— those continue to swap instantly on down. - Stretch disabled on
platformViewBackdrop: true: Flutter'sBackdropFiltermust re-acquire the native pixel buffer every time its bounding box changes. Stretch animations resize the glass container, causing a one-frame flicker over aPlatformView. Stretch is now skipped on all bar elements whenplatformViewBackdropis set; press-scale (interactionScale) is unaffected because it is a GPU-level transform that leaves the backdrop bounds stable. No impact on any other screen or platform.
-
0.20.102 Jul 2026Release notes
Open source →🐛 Bug Fix —
GlassButton.customlayout expansionFixes:
GlassButton.customunexpectedly expanding to fill bounded parent constraints (e.g., insideAppBaractions) (#146).GlassButton.customheightnow correctly defaults tonull(matching documented behavior).- The button now properly shrink-wraps to its content when explicit width/height are not provided.
- Migration: No breaking changes. If you were relying on the undocumented
56pxdefault height, explicitly setheight: 56.
-
0.20.002 Jul 2026Release notes
Open source →💥 Breaking —
GlassListTiledivider refactorGlassListTileno longer draws its own divider. TheisLast,showDivider, anddividerIndentparameters have been removed.Rationale: A list tile should be a clean, position-agnostic item. Divider rendering is a layout concern that belongs to the parent container.
Migration
Inside
GlassGroupedSection— no changes needed.GlassGroupedSectionnow automatically injectsGlassDividers between tiles with smart leading-indent detection (56px if the preceding tile has a leading widget, 16px otherwise). The last tile never gets a trailing divider.Standalone column layouts — compose
GlassDividerexplicitly:// Before: Column(children: [ GlassListTile(title: Text('A')), // showDivider: true (default) GlassListTile(title: Text('B'), isLast: true), // suppresses divider ]) // After (standard Flutter composition pattern): Column(children: [ GlassListTile(title: Text('A')), GlassDivider(indent: 16), GlassListTile(title: Text('B')), ])This aligns with Flutter's own
ListTile+Dividercomposition model.
✨ New —
GlassTabBar.inlineA compact glass tab bar for pinned content-filter sections — sits fixed between a page header and its scrollable list, not inside the scroll view itself.
Performance note:
GlassQuality.premiumre-runs the full shader pipeline whenever the backdrop changes. Placing the bar inside a scroll view invalidates the backdrop on every scroll frame. Pin it outside the scrollable region or drop toGlassQuality.standardif embedding inside a list.GlassTabBar.inline( tabs: const [ GlassTab(label: 'For You'), GlassTab(label: 'Following'), GlassTab(label: 'New'), ], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), )Key differences from
GlassTabBar.bottom:- Zero padding — sits flush in its parent container.
- Compact height (40px) with a full stadium shape (
barBorderRadius: 100, clamped toheight/2). - Indicator pill automatically matches the track corner radius (
indicatorBorderRadiusdefaults tobarBorderRadius). - Indicator expansion
h:12, v:10— same horizontal weight asGlassSegmentedControlandGlassTabBar.bottom, +2px vertical to compensate for the shorter bar height giving the glass pill proportional visual mass. indicatorPinchStrength: 0.4— aligned with all other pill controls.- Magnification disabled (
1.0x) — text labels never grow on selection. - Text-only tabs render with correct vertical centering (no icon slot consuming space).
extraButtonnot supported (structural navigation feature only).
All standard physics parameters (
indicatorPinchStrength,indicatorExpansion,indicatorSettings,quality) are still fully configurable.See the updated Indicator Parity demo (
indicator_parity_demo.dart) whereGlassTabBar.inlinenow appears alongside the other five pill widgets — including a text-only variant (40px) and an icon + text variant (52px) — with live tuning sliders.
-
0.19.702 Jul 2026Release notes
Open source →🐛 Bug Fixes
-
GlassTabBar.bottom/GlassBottomBar— fixes RTL support (#143, @naeemeltaief). The pill and tap/drag hit-testing now correctly align with the visually reversed tab order under RTL. The bottom layout normalises the tab data, selected index and callback for RTL, and pins only the two inner tabRows to LTR — leavingindicatorExpansion,tabPaddingand all text to resolve against the ambient direction as expected. Consumers no longer need to forceDirectionality.ltrand reverse tabs by hand. +2 tests. -
AnimatedGlassIndicator— restores the resting selection pill inside aGlassContainer(#144, @jfhair). The background pill was permanently hidden whenever an ancestor setavoidsRefraction, which is a steady-state layout flag onGlassContainer— not a transient capture signal. The guard was incorrect and is removed. The pill now renders correctly in all contexts. No API change. +1 test.
-
-
0.19.601 Jul 2026Release notes
Open source →✨ New —
GlassLargeTitle+GlassLargeTitleControllerFirst-class iOS 26 large-title + search-bar collapse. Replaces manual
ScrollController+setStatewiring with a single controller and two widgets.Two-phase collapse
- Phase 1 — large title fades out (
Curves.easeIn, rubber-band overscroll stretch). - Phase 2 — optional inline search bar collapses under the nav bar immediately after.
New API
GlassLargeTitle— sliver widget. Drop it as the first sliver in anyCustomScrollView.searchBar: Widget?— Phase 2 collapse (e.g.GlassSearchBar).trailing,fontSize,fontWeight,letterSpacing,padding,searchBarPadding,color.
GlassLargeTitleController— owns theScrollController, exposescollapseProgressandsearchBarCollapseProgress. Self-calibrates to Dynamic Type viareportMeasuredHeight/reportSearchBarHeight.GlassAppBar.largeTitleController— new optional param. Bar title cross-fades in as the large title collapses. Non-breaking.
final _ctrl = GlassLargeTitleController(); // dispose in State.dispose() // Phase 1 only GlassLargeTitle(text: 'Chats', controller: _ctrl) // Phase 1 + 2 GlassLargeTitle( text: 'Messages', controller: _ctrl, searchBar: GlassSearchBar(placeholder: 'Search', onChanged: (_) {}), )Demo
- Pattern 2 updated to new API. Pattern 7 (Large Title + Search Bar) added.
- Apple Messages demo migrated from manual
AnimatedOpacitytoGlassLargeTitle.
+10 tests. 2,291 total, all passing.
⚡ Performance —
GlassBottomBarIndicator (Impeller)GlassBottomBarindicator — eliminates the liveBackdropFilterLayeron Impeller premium, replacing it with a deterministictoImageSynccapture path. Fixes the opaque-white indicator rendering on physical iOS (#99) and improves drag performance by removing a redundant compositor pass.
🔧
GlassGroupedSection— Header/Footer styling- Header and footer text is now styled automatically via
DefaultTextStyle(CupertinoColors.secondaryLabel, 13pt) — callers no longer need to style them manually. - Margin is now applied via
Paddingwrapping the full section rather than on the innerGlassCard, fixing card-edge clipping when a header or footer is present. - Import changed to
cupertino.dartfor correctCupertinoColorsresolution.
📝 Dart doc improvements
GlassContainer,GlassCard,GlassGroupedSection,GlassSegmentedControl— added ⚠️ Anti-Pattern sections documenting the glass-in-glass restriction: placing interactive glass controls inside a container degrades refraction and clips jelly animations.
- Phase 1 — large title fades out (
-
0.19.530 Jun 2026Release notes
Open source →-
LiquidGlassSettings— addsplatformViewFallbackColor(#138, @jfhair). SplitsbackerColor's dual role:backerColorremains the aesthetic backer pad; the new field controls theuBackgroundFallbackshader uniform (PlatformView fill). Fully backwards-compatible — defaults tonull, falling back tobackerColor. -
GlassModalSheet— removes the interiorBoxShadowthat bled through the glass as a vignette (#137, @jfhair). Elevation now flows viaLiquidGlassSettings.shadowElevation. PassshadowElevation: 0to disable entirely.
-
-
0.19.429 Jun 2026Release notes
Open source →✨ Enhancements —
GlassButtonGroupItem.menu&GlassPullDownButtonimprovementsGlassButtonGroupItem.menu— whole-pill liquid glass morphAdds a new
GlassButtonGroupItem.menunamed constructor that turns any item in aGlassButtonGroup.iconspill into a pull-down menu trigger.When tapped, the entire pill morphs into the menu — the full
GlassButton.customshell becomes theGlassMenutrigger, so the whole group shape liquefies and expands into the menu card. This matches the iOS 26GlassEffectContainermorph pattern where the source shape, not just the tapped slot, participates in the transition.GlassButtonGroup.icons( items: [ GlassButtonGroupItem(icon: Icon(CupertinoIcons.chart_bar), onTap: () {}), GlassButtonGroupItem(icon: Icon(CupertinoIcons.clock), onTap: () {}), GlassButtonGroupItem.menu( icon: Icon(CupertinoIcons.ellipsis), menuItems: [ GlassMenuItem(title: 'Add to Watchlist', icon: Icon(CupertinoIcons.star), onTap: () {}), GlassMenuItem(title: 'Share', icon: Icon(CupertinoIcons.share), onTap: () {}), GlassMenuDivider(), GlassMenuItem(title: 'Remove', icon: Icon(CupertinoIcons.trash), isDestructive: true, onTap: () {}), ], menuAlignment: GlassMenuAlignment.topRight, // optional menuWidth: 200, // optional, default 200 ), ], )Notes:
- Only the first
GlassButtonGroupItem.menuin the list is used as the menu trigger; any subsequent menu items are treated as plain tap items. - Accepts both
GlassMenuItemandGlassMenuDivider, matchingGlassMenu.itemsdirectly. - Non-menu siblings in the group continue to fire their own
onTapindependently. - Works with both
Axis.horizontalandAxis.verticalgroups.
Alternatively — group + standalone
GlassPullDownButtonFor cases where the menu trigger is a separate, visually distinct action from the group (e.g. a trailing overflow button next to a row of chart controls), compose a
GlassButtonGroupalongside a standaloneGlassPullDownButton. The pull-down button morphs independently and fully, with no pill residue:Row( children: [ GlassButtonGroup.icons( items: [ GlassButtonGroupItem(icon: Icon(CupertinoIcons.chart_bar), onTap: () {}), GlassButtonGroupItem(icon: Icon(CupertinoIcons.clock), onTap: () {}), ], ), SizedBox(width: 8), GlassPullDownButton( icon: Icon(CupertinoIcons.ellipsis), menuAlignment: GlassMenuAlignment.topRight, items: [ GlassMenuItem(title: 'Add to Watchlist', icon: Icon(CupertinoIcons.star), onTap: () {}), GlassMenuItem(title: 'Share', icon: Icon(CupertinoIcons.share), onTap: () {}), GlassMenuDivider(), GlassMenuItem(title: 'Remove', icon: Icon(CupertinoIcons.trash), isDestructive: true, onTap: () {}), ], ), ], ).menuitem (shared pill)group + standalone All actions in one pill ✅ ❌ Entire pill morphs ✅ — Menu button morphs independently — ✅ Best for Overflow within a related set Separate trailing action GlassPullDownButtonimprovementsitemswidened toList<Widget>— now acceptsGlassMenuDivideralongsideGlassMenuItem. Source-compatible: existingList<GlassMenuItem>code compiles and behaves identically. TheonSelectedcallback is applied only toGlassMenuIteminstances.menuAlignmentexposed — newGlassMenuAlignment?parameter forwarded to the underlyingGlassMenu. Defaults tonull(auto-detect from screen position) — fully backward-compatible.
🐛 Bug Fixes — PlatformView Frost Halo & GlassButton Dispose-Race (#134, #135)
Both fixes contributed by @jfhair.
LiquidOvalrectangular blur halo over a PlatformView (#134)Problem: Any glass surface with a
LiquidOvalshape (the default forGlassButton,GlassIconButton, the collapsed search/dismiss pill, andGlassBottomBarExtraButton) rendered a rectangular blur halo whenplatformViewBackdrop: truerouted it through the_FrostedFallbackBackdropFilterpath. The halo matched the widget's bounding box rather than its circular outline.Root cause: Flutter engine PR #177551 (3.41+) forwards
ClipRRectclip data to the iOS PlatformView mutator stack, allowing a descendantBackdropFilterto be bounded correctly over a hybrid-composed view. The same forwarding does NOT apply toClipPath— andLiquidOval(unlikeLiquidRoundedSuperellipse) was routing throughClipPath, leaving theBackdropFilterunclipped.Fix:
_ShapeClipnow accepts aplatformViewBackdropflag. When set, any shape whose border radius can be expressed as aBorderRadius(includingLiquidOval→circular(9999),LiquidRoundedRectangle, and their vertical variants) is routed throughClipRRectinstead ofClipPath. The clip is then forwarded to the PlatformView mutator stack and the frost is bounded cleanly to the shape. Off a PlatformView the originalClipPathis used (true ellipse). The flag is threaded through all_ShapeClipcall sites in_FrostedFallback— the blur body, the content clip, and the specular rim — as well as the backer dimming pad inAdaptiveGlass._wrapWithBacker.Completes the partial fix shipped in 0.19.3 and fully resolves the rectangular-blur regression first reported in #79.
GlassButtoncrash when disposed mid-press (#135)Problem: Tapping a
GlassButtonthat is removed by the very tap that activates it (e.g. a collapsed bar restore button that expands the bar and disposes itself) could throw:AnimationController.reverse() called after AnimationController.dispose() (assert _ticker != null)A queued
pointerUporpointerCancelwas still dispatched to the now-disposedRenderPointerListener, and the press handler called_saturationController.reverse()afterdispose().Fix: All six tap/pointer press handlers in
_GlassButtonStatenow guard on!mountedbefore touching_saturationController. A disposedStatealways hasmounted == false, so the handler bails safely without touching the controller.Surfaces frequently in apps that morph or collapse a bar on the tap of a glass control over a PlatformView (the pattern introduced in #127/#79).
- Only the first
-
0.19.329 Jun 2026Release notes
Open source →🐛 Bug Fixes — Search Pill Colors & PlatformView Compositing
- Fixed an issue where the
SearchPillicon colors (search, mic) would incorrectly render as black when using dark glass (glassColor: Colors.black26) over an iOS PlatformView. The root cause was that the color resolution used the OS system brightness (light) instead of the glass brightness (dark), causingCupertinoDynamicColor.labelto always resolve to its light-mode black variant. The widget now resolves colors throughGlassTheme.brightnessOf()— the package's single brightness authority — and explicitly passes the resolved color viaIconThemeDatato guarantee white glyphs on dark glass regardless of system brightness. - Mitigated a Flutter engine clipping bug over PlatformViews by ensuring
LiquidRoundedSuperellipseandLiquidOvalcorrectly clip their bounds using an outerClipRRectwhen rendering over an iOS PlatformView, preventing blur bleed outside the circular button shape. - Updated the
google_maps_demoto correctly demonstrate how to configureselectedIconColor,unselectedIconColor, andsearchIconColorfor dark glass bottom bars to ensure all tab elements remain visible over the map layer.
- Fixed an issue where the
-
0.19.229 Jun 2026Release notes
Open source →🐛 Bug Fixes — PlatformView Gesture & Rendering
This release resolves a pair of related issues that caused the glass bottom bar to freeze and render incorrectly when floating over an iOS PlatformView (e.g. a Mapbox map). All three fixes were contributed by @jfhair via detailed PRs that included root-cause analysis, regression tests, and working reproductions. Many thanks for the exceptional quality of this contribution.
Gesture freeze over an iOS PlatformView (#127)
Problem:
GlassTabBar.searchable(andGlassBottomBar) would freeze when the draggable indicator was dragged or tapped while the bar floated over an iOS PlatformView. The freeze was permanent until the widget was rebuilt or disposed.Root cause: iOS reconstructs the platform-view clip chain whenever a clip layer is added or removed mid-gesture. The engine responds by cancelling the active touch, which left the bar's
GestureDetectorrecognizer wedged — it never received a terminal callback (onDragEnd/onDragCancel) and stopped responding to input. The most frequent trigger was the indicator'sinnerBlurfrost layer unmounting at drag-start (a clip-add/remove cycle).Fix — two-part:
- Primary fix (PR #127): The indicator's frost
ClipRRect + BackdropFilterlayer now stays mounted across the full drag lifecycle by tracking a persistentbool _frostMountedflag inAnimatedGlassIndicator. This eliminates the clip-add/remove cycle that was triggering the iOS clip-chain reconstruction. - Backstop fix (PR #127):
TabBarDragGestureMixingains agestureEpochcounter, a_gestureActiveliveness flag, and a rawListeneron theGestureDetectorsubtree. If the raw pointer-up or pointer-cancel arrives while_gestureActiveis stilltrue(i.e. the terminal callback was silently dropped by the platform-view gesture arena),recoverIfGestureStuckis called: it selects a fallback tab, bumpsgestureEpoch(forcing theGestureDetectorto be torn down and recreated viaValueKey), and clears the stuck state. Covers both the tap-without-cancel and drag-start-without-end freeze signatures.
PlatformView backdrop routing (#128)
Problem: Setting
platformViewBackdrop: trueon a bar had no visible effect for the glass indicator or bar body — the premium/standard Impeller shader paths read a captured backdrop that excludes hybrid-composed PlatformViews, so the glass rendered inert (opaque black or clear) over a map.Fix:
AdaptiveGlassnow routes any surface withplatformViewBackdrop: trueto the frosted fallback (_FrostedFallback) regardless of the requested quality tier. The frosted fallback uses a liveBackdropFilter, which correctly blurs hybrid-composed PlatformViews._FrostedFallbackalso overrides theisInteractiveblur-omission that would otherwise skip the blur for interactive indicator surfaces — over a PlatformView the live blur must always run.Premium glass goes black over a PlatformView —
backerColorfallback (#129)Problem: At
GlassQuality.premiumover a PlatformView, the Impeller shader sampled a captured backdrop that contained only transparent black where the PlatformView sat. With no real background pixels to refract, the glass lens rendered black.Fix: A new
uBackgroundFallback(vec4) uniform was added toliquid_glass_final_render.frag. The shader composites the fallback colour over the captured backdrop using a standardoverblend weighted by the backdrop's own alpha — where the backdrop is real (alpha ≈ 1) it is left untouched; where it is transparent black (alpha ≈ 0, i.e. over a PlatformView) the fallback fills in.backerColorfromLiquidGlassSettingsis bound to this uniform at render time, giving the premium lens a solid colour to refract instead of transparent black.Extra button
platformViewBackdropthreading (follow-up, this release)GlassTabBarExtraButtonpreviously ignored the bar'splatformViewBackdropflag — the internalBottomBarExtraBtnwidget was not forwarding it to the underlyingGlassButton, so the extra button continued to use the inert shader path even when the rest of the bar correctly used the frosted fallback.platformViewBackdropis now threaded throughBottomBarExtraBtnand both call sites (TabBarBottomLayout,TabBarSearchableLayout) toGlassButton.
- Primary fix (PR #127): The indicator's frost
-
0.19.125 Jun 2026Release notes
Open source →🛡️ Stability Improvements
Addresses production crash and ANR reports seen with v0.19.0 on Flutter 3.44.2 (tracked in flutter/flutter#187140). These are exposure-window mitigations — the root cause is a Flutter engine issue and requires an engine-level fix.
Changes
GlassEffect&LightweightLiquidGlass— lifecycle-aware Ticker suspension Both state classes now implementWidgetsBindingObserverand halt background-capture Tickers duringAppLifecycleState.inactive,paused, andhidden. Captures restart one frame afterresumed. This reduces GPU texture activity during surface transitions (rotation, split-screen, keyboard resize) which is the window where engine-level crashes and ANRs are most likely to occur.GlassEffect&LightweightLiquidGlass—_isDisposedguard A_isDisposedflag prevents Ticker callbacks and async.then()continuations from accessing GPU resources afterdispose()has completed, guarding against post-frame-callback / dispose races during rapid navigation.LiquidGlassWidgets.initialize()— faster startup The Impeller pipeline warm-up is no longerawaited insideinitialize(). It now runs after the first frame viaaddPostFrameCallback, removing a~16msdelay from the startup critical path. Shader disk-loads are still awaited as before.Debug log cleanup Removed stale
debugPrintsuccess messages fromGlassEffectandLightweightLiquidGlassshader pre-warm paths (✓ Shader precached,✓ Created unique shader instance). These fired on every debug startup for every app using the package. Failures still surface via the existing[LightweightGlass] Pre-warm failed:error log. The[LiquidGlass]startup bracket (Initializing.../Initialization complete.) and thePerformanceMonitor startedlog are retained as actionable developer information. -
0.19.025 Jun 2026Release notes
Open source →💥 Breaking: Pre-v1.0 Public API Cleanup
A pre-release naming audit to establish consistent, idiomatic conventions before v1.0 locks the API.
Renames
Old New Reason GlassBottomBarExtraButtonGlassTabBarExtraButtonTracks parent rename GlassBottomBar→GlassTabBarGlassGroupItemGlassButtonGroupItemMirrors Flutter's DropdownMenuItempatternSheetStateGlassSheetStatePrevents collision with Material 3 sheet infrastructure SheetModeGlassSheetModeSame — too generic as a bare name FillTransitionGlassFillTransitionToo generic as a bare name ExtraButtonPositionGlassExtraButtonPositionAmbiguous without prefix Migration: A
@Deprecatedtypedef forGlassBottomBarExtraButtonis provided. All other old names will produce compile errors — migration is mechanical find-and-replace.GlassSegment — new concrete class
GlassSegmentwas previously atypedefalias forGlassTab. It is now a proper class with a focused API forGlassSegmentedControl:// GlassSegment — for GlassSegmentedControl only GlassSegment({ Widget? icon, String? label, String? tooltip, String? semanticLabel, bool enabled = true }) // GlassTab — for GlassTabBar.bottom() and GlassTabBar.searchable() GlassTab({ Widget? icon, Widget? activeIcon, String? label, Color? glowColor, double? thickness })Fields like
activeIcon,glowColor, andthicknessare navigation-specific and only exist onGlassTab.GlassSegmentaddstooltipandenabled(with built-in disabled rendering at 38% opacity).Barrel hygiene
Internal types (
SheetSnapshot,SheetGeometry,GesturePhase,GestureArena,FrozenState) are no longer accessible from the package barrel. These were implementation details that leaked throughpartfile exports.⚡ Performance
- Shader:
interactive_indicator.frag— replacedpow()calls with multiply chains; collapsed duplicate rim pass; zero transcendental functions in highlight path. - Shader:
liquid_glass_final_render.frag—⁶√xcomputed via sqrt cascade (3 SFU vs 2 transcendentals);sceneSDFsamples reduced from 5 → 4. - Dart:
resolveAdaptiveRadiusscoped toMediaQuery.viewPaddingOf+MediaQuery.sizeOf— glass widgets no longer rebuild on keyboard or unrelatedMediaQueryDatachanges. - Dart: Searchable tab bar and
GlassSegmentedControlspring animations now useListenableBuilderscoped to the indicator subtree. Verified on-device: zeroState.build()calls during 120Hz spring animation.
🐛 Fix
LiquidGlassWidgets.initialize()now pre-warms all four shaders —liquid_glass_geometry_blended.fragandliquid_glass_final_render.fragwere previously lazy-loaded, causing first-frame jank.GlassSegment.enabled = falsenow blocks tap/tapDown — disabled segments rendered at 38% opacity but still firedonSegmentSelectedin both fixed-width and scrollable modes. Tap andonTapDownhandlers now early-return when the target segment is disabled.
- Shader:
-
0.18.624 Jun 2026Release notes
Open source →🐛 Fix: Glass widgets now honour app
ThemeMode, not OS dark modeResolves a class of UI inconsistency where glass widgets incorrectly read the device/OS brightness instead of the app's brightness. The most visible symptom was
GlassBottomTabBarshadows disappearing when the device was in Dark Mode but the app was pinned to Light Mode viaMaterialApp(themeMode: ThemeMode.light).Root cause
Every glass widget that needed to decide between light/dark colours or shadow visibility called either
CupertinoTheme.of(context).brightnessorMediaQuery.platformBrightnessOf(context)directly. Both of these APIs fall back to the OS/device setting and are blind toMaterialApp.themeMode.Fix: Centralised brightness cascade
A new
GlassTheme.brightnessOf(context)authority now governs all brightness decisions in the library. It resolves via a four-level cascade:GlassThemeData.brightness— new field; an explicit developer override pinned in theGlassThemewidget tree (highest priority).CupertinoThemeData.brightness— explicit Cupertino pin (non-null only; intentional opt-in).Theme.maybeBrightnessOf(context)— MaterialThemeMode.light/.dark/.system, honouringMaterialApp.themeMode.MediaQuery.platformBrightnessOf(context)— OS/device setting (safe fallback).
Changes
- New:
lib/utils/glass_brightness.dart—resolveGlassBrightness(context)utility (package-private). - New field:
GlassThemeData.brightness— explicit brightness override for fine-grained glass-subtree control. Accepted by both the default andGlassThemeData.simple()constructors. - New method:
GlassTheme.brightnessOf(context)— the single, mandatory brightness authority for the entire library. - Fixed: Shadow suppression in
GlassBottomTabBar,GlassSearchableBottomBar,AdaptiveLiquidGlassLayer, andAdaptiveGlass(_FrostedFallback). - Fixed: Shader
backdropLumaproxy inGlassEffect(controls glass strength in the GPU path). - Fixed: Light/dark colour selection in 20+ widget files across
interactive/,containers/,input/,overlays/, andsurfaces/layers.
Tests
Three new test files cover every level of the cascade:
test/utils/glass_brightness_test.dart— unit tests forresolveGlassBrightness.test/theme/glass_theme_brightness_test.dart—GlassTheme.brightnessOfintegration tests including the canonical regression scenario.test/theme/glass_theme_data_brightness_test.dart—GlassThemeData.brightnessoverride field,copyWith, equality, and backward-compat tests.
-
0.18.524 Jun 2026Release notes
Open source →🔧 Corrected minimum SDK constraint — Flutter ≥ 3.41.0
- Fix: Raised the minimum Flutter constraint to
3.41.0. ThefilterQualityparameter onFragmentShader.setImageSampler()was actually introduced in Flutter 3.41.0 (commitadd442b29c), not 3.24.0 as previously stated. This prevents users on 3.38.x from failing at compile time. - Reverted: Raised the internal
metaconstraint back to^1.18.0since Flutter 3.41.0 guarantees this version is available.
- Fix: Raised the minimum Flutter constraint to
-
0.18.423 Jun 2026Release notes
Open source →- Fix: Loosened the
metadependency constraint to^1.12.0(instead of^1.18.0) to avoid pub resolution conflicts for users on older Flutter SDKs whereflutter_testis bound tometa 1.17.0.
- Fix: Loosened the
-
0.18.323 Jun 2026Release notes
Open source →✨ Per-state label text style on bottom bars —
selectedLabelStyle/unselectedLabelStyleAdds
selectedLabelStyle/unselectedLabelStyle(TextStyle?) toGlassTabBar.bottom,GlassTabBar.searchable, and the deprecatedGlassBottomBar/GlassSearchableBottomBar.This complements the
selectedLabelColor/unselectedLabelColorparameters by letting callers set the selected/unselected label's font family, weight, and letter-spacing per state — needed to match Apple's tab bar, where the selected label is heavier than a single sharedtextStylecan express.The per-state style is merged over the base label style, so it overrides only the fields it sets and keeps the resolved per-state label color unless the style provides its own. Both default to
null→ existing behavior unchanged.Also fixes a related precedence bug: an explicit
selectedLabelColor/unselectedLabelColorwas silently dropped whenever atextStylewas also supplied (the per-state color only fed the fallback style). It now overrides the base color — including a color baked intotextStyle— whiletextStyle-only callers are unaffected.✨
innerBlur— Apple-style rest-blur behind the selected tabThe
innerBlurparameter onGlassTabBar.bottom/.searchable(and the deprecatedGlassBottomBar/GlassSearchableBottomBarshims) now renders. It was declared and threaded bar→internal, but never forwarded to the indicator, andAnimatedGlassIndicatorhad no implementation. This wires it through the tab-bar internals and adds the rest-gatedBackdropFilter.It paints a backdrop blur behind the resting selected pill — the iOS 26 "frost at rest" look — with the sigma scaled by the pill's resting opacity, so the frost is full when settled and fades out as it morphs into the liquid-glass lens during a drag/tap (motion stays crisp).
0.0(default) disables it — no behavior change for existing callers.- Only the background-painting indicator is affected (reads through a translucent
indicatorColor).
✨
platformViewBackdropon the public glass widgetsFollows up #94 by exposing the premium-over-PlatformView flag on the remaining public widgets —
GlassContainer,GlassButton,GlassIconButton,GlassButtonGroup,GlassMenu, andGlassModalSheet— so apps can render premium glass cleanly over an iOS PlatformView (e.g. a MapboxMapWidget) for any control, not just the bottom bar.Each gets an explicit
platformViewBackdropparameter defaulting tofalse(zero overhead / no behavior change for callers that don't need it), forwarded to the underlyingAdaptiveGlass. ForGlassModalSheetthe flag threads throughGlassModalSheetScaffoldand the sheet state down to the_SheetLayout's glass. Adds widget tests covering the simple-widget forwards.🧹 Removed —
GlassTintBlend(a no-op since 0.17.0)GlassTintBlend(added in #107) and theLiquidGlassSettings.tintBlendfield have been removed.Since the 0.17.0 shader rewrite,
tintBlendwas never wired into the renderer — it was packed into no uniform, so setting it had no effect: every surface used the automatic chroma-gated blend regardless of the value. We only caught this during real-device tuning, after all the related PRs had already landed.The automatic behavior is unchanged.
applyGlassColorstill picks luminosity-preserving blending for chromatic tints and flat blending for achromatic tints. Recipes that previously passedtintBlend: flatfor achromatic (white / grey / near-black) tints render identically, because the chroma gate already resolves those to the flat path.Breaking, but inert: code that passed
LiquidGlassSettings(tintBlend: …)must drop the argument. No rendered output changes.🔧 SDK constraints bump (corrected in 0.18.5)
Raised minimum Flutter to
>=3.24.0— this was incorrect. The actual minimum is3.41.0, corrected in0.18.5.
-
0.18.223 Jun 2026Release notes
Open source →Rendering Quality
-
Fix: Eliminated stair-step aliasing on
AnimatedGlassIndicator/GlassEffectpill edges during press animations. Root cause:FragmentShader.setImageSampler()defaults toFilterQuality.none(Nearest-Neighbor). The 4 % press-scale animation was block-replicating geometry texels into 2×2 stair-step patterns visible as jagged fringe on the pill rim. Resolution: PassfilterQuality: FilterQuality.mediumto everysetImageSampler()call inliquid_glass_render_object.dart,glass_effect.dart, andlightweight_liquid_glass.dart. Zero GPU cost — hardware bilinear filtering happens in a single texel-unit clock cycle. -
Fix: Eliminated 2×2 blocky normal artifacts on glass pill edges (all platforms, most visible on iOS/macOS Metal). Root cause: Metal's
dFdx/dFdyevaluate in 2×2 pixel quads, so all four neighbours share one gradient vector. At the high-contrast white rim of the pill this produces a coarse stair-step normal map that manifests as jaggy rainbow banding regardless of scale. Resolution: Replaced hardware derivatives with per-pixel central finite differences inliquid_glass_geometry_blended.frag(dx = sceneSDF(p + 0.5) − sceneSDF(p − 0.5)). Costs 4 extra SDF evaluations per geometry pixel — negligible on a cached, one-shot geometry pass. -
Fix: Restored full rim brightness after the anti-aliasing band was widened. Root cause: The previous asymmetric
smoothstep(-smoothing, 0.0, sd)placed the mathematical pill boundary (sd = 0) at the dark end of the alpha ramp (alpha = 0). The rim-lighting peak lives exactly atsd = 0, so it was multiplied by zero and rendered invisible. Resolution: Centred the smoothstep around the boundary —smoothstep(smoothing * 0.5, -smoothing * 0.5, sd)— sosd = 0maps to alpha = 0.5. This is the canonical SDF anti-aliasing formulation and restores maximum rim brightness with no other visual side-effect. -
Fix: Eliminated backdrop texture wrap-around artifacts during
LiquidStretchscaling and jelly overshoot. Root cause: Impeller's default texture sampler wrap mode isRepeat. A fragment slightly outsideuGeometrySize(e.g. during a spring overshoot) produced ageometryUVmarginally above 1.0; the sampler wrapped it to near-0.0, sampling the opposite edge of the SDF and producing inverted normals and extreme chromatic aliasing. Resolution: ClampgeometryUVto[0, 1]before the texture fetch. Clamped-edge pixels have near-zero SDF alpha and are discarded by the existinggeometryData.a < 0.01early-out — no separate bounds-check branch required. -
Fix: Eliminated chromatic wrap artifacts during jelly overshoot in
interactive_indicator.frag. Root cause: When the indicator pill overshoots itsRepaintBoundarybounds, out-of-boundstextureBilinearsample points could trigger the same Repeat-mode wrap in the background texture. Resolution: Explicitly clamp all four bilinear sample points to[0, physSize − 1]before thetexture()fetch. -
Fix: Eliminated jagged/pixelated stair-step artifacts on
AnimatedGlassIndicatorpill edges whenindicatorPinchStrength > 0. Root cause:BackdropFilterLayerimplicit samplers are bound toFragmentShaderas Nearest-Neighbor with no Dart API to override it (Flutter Issue #139887). Continuous sub-pixel UV shifts from the lens pinch and chromatic aberration were snapping to integer texels, producing blocky rainbow fringes on high-contrast backgrounds. Resolution: Added atextureBilinearhelper toliquid_glass_final_render.fragthat performs a standard 4-texel bilinear interpolation in GLSL, restoring perfectly smooth sub-pixel background sampling. The geometry texture (uGeometryTexture) is intentionally excluded — its pixel-aligned SDF data must not be softened. -
Fix: Eliminated pixelation on the interactive indicator pill (
GlassSegmentedControl,GlassEffect). Two compounding causes: (1) the background texture was previously captured atpixelRatio: 1.0, so each texel covered a 3×3 block of physical pixels on a 3× Retina display; (2) Impeller'ssetImageSampler()binding defaults to Nearest-Neighbor, snapping continuous UV offsets from edge refraction to these large texels. Resolution: Background capture now uses the device's full DPR. AtextureBilinearGLSL helper replaces all rawtexture()calls ininteractive_indicator.frag. ~250 KB additional GPU texture memory; sub-0.1 ms additional GPU time per frame — negligible on any modern device. -
Fix: Resolved intermittent Metal API Validation abort on iOS (
GlassQuality.premium) — missing buffer bindings foruWhiten,uWhitenGated, anduPinchStrength(#121). All shader uniforms (slots 0–20) are now written atomically on every paint frame, preventing a stale or zero-initialisedFragmentShadersnapshot from reaching the Metal draw call. Affected:GlassTabBar.bottom(quality: GlassQuality.premium)with an animating indicator. No API changes.
Notes
- Note (Flutter engine limitation): The
textureBilinearworkaround inliquid_glass_final_render.frag(4-tap bilinear in GLSL) remains necessary for backdrop sampling because Impeller binds the implicitBackdropFilterLayersampler as Nearest-Neighbor with no Dart API to overrideFilterQuality. A Flutter engine feature request to expose sampler filter quality for backdrop layers has been filed at Flutter Issue #188365. Once resolved, the GLSL workaround can be replaced with a singletexture()call.
Chore
- Chore: Removed unreachable early-out branch in
liquid_glass_final_render.frag— theif (any(lessThan(geometryUV, ...)))check afterclamp(geometryUV, 0.0, 1.0)could never fire. Replaced with a single consolidated comment explaining how the clamp and the downstream alpha check together handle both the Impeller Repeat-mode and clipExpansion cases. - Chore: Removed dead code from
render.glsl(computeY,getHeight,calculateLighting,calculateRefraction,renderLiquidGlass,debugNormals) — functions superseded by the inline logic inliquid_glass_final_render.frag. Reduces compiled shader binary size.
-
-
0.18.122 Jun 2026Release notes
Open source →- Hotfix: Resolved missing coverage in layout engines and segmented controls.
- Hotfix: Fixed package analysis warnings due to unused local variables and unnecessary imports in test files.
-
0.18.022 Jun 2026Release notes
Open source →🏗️ Unified Navigation API — iOS 26 Alignment
This release consolidates the widget API to map 1:1 with Apple's iOS 26 control vocabulary. Two v1-era widgets are deprecated (see Migration below), and
GlassTabBarbecomes the single source of truth for all tab-navigation work.
⚠️ Breaking Change: Android Bottom Bar Padding
GlassScaffoldnow automatically manages the Android system navigation bar padding forbottomBar. If you previously added manualPaddingorSafeAreaaround your bottom bar to prevent it from slipping behind the Android navigation buttons, please remove it to avoid double-padding.
New:
GlassTabBar.bottom()— iOS 26 UITabBar equivalentNamed constructor for bottom navigation bars. Full liquid glass layer, jelly physics pill indicator,
MaskingQualitydual-layer icon rendering, and optionaldividerSettings.GlassTabBar.bottom( tabs: [ GlassTab(icon: Icon(Icons.home), label: 'Home'), GlassTab(icon: Icon(Icons.search), label: 'Search'), GlassTab(icon: Icon(Icons.person), label: 'Profile'), ], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), )
New:
GlassTabBar.searchable()— UITabBar + morphing searchNamed constructor combining bottom navigation with a morphing glass search pill. Identical API to
GlassTabBar.bottom()with additionalsearchBarConfigandcontrollerparameters.GlassTabBar.searchable( tabs: [...], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), searchBarConfig: GlassSearchBarConfig(hintText: 'Search...'), controller: _tabBarController, )
New:
GlassSegmentedControl— icon support + scrollable modeIcon + label support (fixed mode)
segmentsnow acceptsList<GlassTab>instead ofList<String>. Each segment can render:- Label only —
GlassTab(label: 'Weekly') - Icon only —
GlassTab(icon: Icon(Icons.photo)) - Icon + label —
GlassTab(icon: Icon(Icons.photo), label: 'Photos')(icon above label)
This matches iOS 26 UISegmentedControl which has supported
UIImagesegments since early iOS.Scrollable mode — 100% parity with original
GlassTabBar(isScrollable: true)
🚨 Removed:
GlassTabBar()inline constructorThe default
GlassTabBar()constructor has been removed.GlassTabBaris now exclusively used for structural bottom navigation (GlassTabBar.bottom()andGlassTabBar.searchable()).Migration: For all inline tab bars, pill menus, or scrollable tag lists, use
GlassSegmentedControl()orGlassSegmentedControl.scrollable(). They provide 100% feature parity with the old inlineGlassTabBar.- GlassTabBar( - tabs: const [ - GlassTab(label: 'A'), - GlassTab(label: 'B'), - ], - selectedIndex: _selectedIndex, - onTabSelected: (i) => setState(() => _selectedIndex = i), - ) + GlassSegmentedControl( + segments: const [ + GlassSegment(label: 'A'), + GlassSegment(label: 'B'), + ], + selectedIndex: _selectedIndex, + onSegmentSelected: (i) => setState(() => _selectedIndex = i), + )New
GlassSegmentedControl.scrollable()named constructor for category filter tabs (6+ items). Internally usesScrollableSegmentContent— a dedicated widget that owns scrollable pill physics, gesture handling, and 3-layer rendering. Structurally identical to the old inlineGlassTabBar(isScrollable: true), now correctly located in the interactive widget family.// Fixed (UISegmentedControl — equal width, 2–6 items) GlassSegmentedControl( segments: const [ GlassSegment(label: 'All'), GlassSegment(icon: Icon(Icons.photo), label: 'Photos'), GlassSegment(label: 'Videos'), ], selectedIndex: _selectedIndex, onSegmentSelected: (i) => setState(() => _selectedIndex = i), ) // Scrollable (category filter tabs — natural width, 7+ items) GlassSegmentedControl.scrollable( segments: List.generate(12, (i) => GlassSegment(label: 'Category ${i + 1}')), selectedIndex: _selectedIndex, onSegmentSelected: (i) => setState(() => _selectedIndex = i), )
Architecture: Dependency inversion
All tab-bar layout logic now lives in dedicated layout files:
File Owns interactive/shared/scrollable_segment_content.dartScrollableSegmentContent— scrollable pill + gesture engine (used byGlassSegmentedControl.scrollable)interactive/shared/segmented_control_internal.dartSegmentedControlContent— fixed-width pill + gesture engine (used byGlassSegmentedControl)surfaces/shared/tab_bar_bottom_layout.dartTabBarBottomLayout— full glass bottom shellsurfaces/shared/tab_bar_searchable_layout.dartTabBarSearchableLayout— search morph shellGlassTabBardispatches to these shells.GlassBottomBarandGlassSearchableBottomBarare now zero-logic shims that delegate to the same shells.
Deprecated — removal in v1.0
GlassBottomBar→GlassTabBar.bottom()// BEFORE GlassBottomBar( tabs: [GlassBottomBarTab(icon: Icon(Icons.home), label: 'Home')], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), ) // AFTER GlassTabBar.bottom( tabs: [GlassTab(icon: Icon(Icons.home), label: 'Home')], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), )GlassSearchableBottomBar→GlassTabBar.searchable()// BEFORE GlassSearchableBottomBar(tabs: [...], ...) // AFTER GlassTabBar.searchable(tabs: [...], ...)GlassTabBar()default constructor →GlassSegmentedControl// BEFORE GlassTabBar( tabs: [GlassTab(label: 'A'), GlassTab(label: 'B')], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), ) // AFTER GlassSegmentedControl( segments: [GlassTab(label: 'A'), GlassTab(label: 'B')], selectedIndex: _selectedIndex, onSegmentSelected: (i) => setState(() => _selectedIndex = i), )GlassSegmentedControl.segments: List<String>→List<GlassTab>// BEFORE GlassSegmentedControl(segments: ['Daily', 'Weekly', 'Monthly'], ...) // AFTER GlassSegmentedControl( segments: [ GlassTab(label: 'Daily'), GlassTab(label: 'Weekly'), GlassTab(label: 'Monthly'), ], ... )
iOS 26 control vocabulary — full mapping
Widget iOS 26 equivalent Glass tier GlassSegmentedControl(...)UISegmentedControlLight tint + glass pill GlassSegmentedControl.scrollable(...)Scrollable filter tabs Light tint + glass pill GlassTabBar.bottom(...)UITabBarFull liquid glass GlassTabBar.searchable(...)UITabBar+ searchFull liquid glass
New: Configurable label colors and indicator border radius for bottom bars
GlassTabBar.bottom(),GlassTabBar.searchable(),GlassBottomBar, andGlassSearchableBottomBarexpose three additional styling parameters:selectedLabelColor— tab label colour when selected, independent ofselectedIconColorunselectedLabelColor— tab label colour when unselected, independent ofunselectedIconColorindicatorBorderRadius— pill indicator corner radius, independent ofbarBorderRadius(e.g.100for a fully round pill on a subtly curved bar)
All three are optional; omitting them preserves existing behaviour exactly.
GlassTabBar.bottom( tabs: [...], selectedIndex: _selectedIndex, onTabSelected: (i) => setState(() => _selectedIndex = i), selectedLabelColor: Colors.blue, unselectedLabelColor: Colors.grey, indicatorBorderRadius: 100, )
- Label only —
-
0.17.120 Jun 2026Release notes
Open source →🐛 Fix —
platformViewBackdroptoggle no longer snaps the selected-tab indicator (#112 by @jfhair)Toggling
platformViewBackdropat runtime (e.g. switching between a map tab and a Flutter-content tab) caused the selected-indicator pill to snap to the new tab instead of sliding. The spring animation controllers inside the indicator subtree were being re-seeded at their already-settled value because the toggle added or removed theLiquidGlassBlendGroupwrapper inAdaptiveLiquidGlassLayer, changing the child's depth in the element tree and forcing Flutter to discard and re-inflate the whole subtree viainitState.Fix:
AdaptiveLiquidGlassLayeris now aStatefulWidgetthat holds a stableGlobalKey. The child is always wrapped in aKeyedSubtreewith that key, so the element identity is preserved across the wrapper toggle — the indicator'sAnimationControllers survive and the morph continues correctly.No API changes. No breaking changes.
-
0.17.019 Jun 2026Release notes
Open source →🔬 iOS 26 Concave Lens Pinch — All Four Pill Widgets
The
indicatorPinchStrengthconcave lens warp is now unified across all four interactive pill widgets. During a drag the pill edges curve inward (iOS 26 "through a lens" effect). Fully tunable —0.0disables it,1.0is maximum distortion.New parameters
GlassTabBar.indicatorPinchStrength(default0.4)GlassTabBar.indicatorExpansion(defaultEdgeInsets.symmetric(horizontal: 12, vertical: 8))GlassSegmentedControl.indicatorPinchStrength(default0.4)GlassSegmentedControl.indicatorExpansion(defaultEdgeInsets.symmetric(horizontal: 12, vertical: 8))AnimatedGlassIndicatorexported from the public API — enablesbaseIndicatorSettings.copyWith(...)from app code.
Changed defaults (
AnimatedGlassIndicator.baseIndicatorSettings)glassColor:alpha: 0.15→alpha: 0.0— glass pill no longer applies a white tint overlay by default.chromaticAberration:GlassDefaults.chromaticAberration→0.15— the iridescent rim fringe is now explicitly set for iOS 26 parity.
Bug fixes
GlassSegmentedControlrefraction — labels are now refracted through the glass pill atGlassQuality.premium(was rendered in wrong z-order).GlassTabBarindicator radius — resting pill now inherits the tab bar'sborderRadius(was hardcoded16 px).AnimatedGlassIndicatorsettings merge — partialindicatorSettingsoverrides no longer silently resetchromaticAberration.- Pinch lens jitter at rest — icon and label content no longer shimmers through the lens when the pill settles. Root cause: the jelly spring's micro-oscillations (±10 % of
thickness) were directly amplified into the UV warp. Fixed by applying a quadratic ease-out to the pinch multiplier (1 − (1 − fade)²), compressing the near-settled oscillation range ≈10×.
Try it — Indicator Parity demo
The example app includes a live Indicator Parity demo (
Demos → Indicator Parity) with all four pill widgets side-by-side and real-time sliders forpinchStrength,indicatorExpansion, andchromaticAberration. Use it to tune parameters before writing any code.🌑 Apple Dimming Layer —
LiquidGlassSettings.backerColor(#111 by @jfhair)New optional
backerColoronLiquidGlassSettings— a shape-matched color pad composited behind the glass, giving a control's content contrast over rich or colorful backdrops (video, maps, photography) where the glass tint alone can't. This is Apple's "dimming layer" guidance from the Human Interface Guidelines (Materials section) and the pattern behind SwiftUI's clearGlassvariant.LiquidGlassSettings( glassColor: Color(0x20FFFFFF), backerColor: Color(0x59000000), // ~35% black — Apple's starting point )backerColor(Color?, defaultnull) — the color's alpha is the dimming opacity.nullmeans no backer, so all existing recipes are untouched.- Rendered at the widget level (like
shadow) and clipped to the glass shape viaClipRRect, so it composites correctly even over aPlatformView— maps, video — where a shader-side tint cannot reach. - Applies in both light and dark mode, and for flat-edge shapes (a bar over a map is a primary use case).
- Skipped on the grouped path (like shadow) — inserting a
Stackbetween grouped glass and its shared layer would break metaball morphing. lerpfadesbackerColorsmoothly from transparent when one side isnull, rather than snapping at the midpoint.
Migration
All four widgets share the same tuning API:
indicatorPinchStrength: 0.4, indicatorExpansion: EdgeInsets.symmetric(horizontal: 12, vertical: 8), indicatorSettings: AnimatedGlassIndicator.baseIndicatorSettings .copyWith(chromaticAberration: 0.15),
-
0.16.318 Jun 2026Release notes
Open source →✨
GlassTintBlend— selectable tint blending path (#107 by @jfhair)New
GlassTintBlendenum onLiquidGlassSettingsto explicitly control howglassColorblends with the refracted backdrop, instead of relying entirely on the chroma heuristic.GlassTintBlend.auto— the default. Existing chroma gate, byte-for-byte unchanged behavior.GlassTintBlend.luminosity— always preserve backdrop luminosity. For near-neutral tints that need to keep the glassy look rather than flattening to a film.GlassTintBlend.flat— always impose the tint's brightness. For dimming layers, backing scrims, or deliberate frost-film surfaces.
Fully threaded through
LiquidGlassSettings(copyWith,lerp,props), both the Premium and Standard shader paths, and preserved throughAdaptiveGlasselevation rebuilds. Frosted fallback renders flat by construction and ignores the setting.✨
GlassScrollEdgeEffect.bottomFadeInset(#109 by @jfhair)New optional
bottomFadeInsetparameter (default0.0) that lifts the bottom fade off the widget's true bottom edge by the specified logical pixels. Fixes cases where the scroll viewport extends below the visible area — such as a bottom sheet whose content box overflows past the screen bottom — causing the bottom fade to anchor off-screen and never appear.No breaking changes. All new parameters are optional with safe defaults.
-
0.16.217 Jun 2026Release notes
Open source →🐛 Bug Fix —
GlassMenu/GlassPopoverrebuild on keyboard open/closeGlassMenuandGlassPopoverwere rebuilding on every keyboard open/close event, even when closed. Caused byMediaQuery.of(context)indidChangeDependenciessubscribing toviewInsets. Fixed by switching to scoped accessors (disableAnimationsOf,maybeSizeOf,textScalerOf). Regression tests added.✨ Content-luminance scroll-edge scrim (#106 by @jfhair)
The continuous companion to the
contentAwareBrightnessdiscrete lever. Scroll-edge fades now track content luminance and dissolve toward a dark color as dark content scrolls under the bars — matching the native App Store early-darkening behaviour.GlassContentAwareScope.register()gainsonLuminanceChanged(brightness callback is now optional). Per-rect mean luminance is delivered from the existing single capture; deliveries are gated on >0.005 movement.GlassScrollEdgeEffect.contentAwareFade— each edge band registers with the scope and lerps towarddarkFadeColoras content darkens (luminanceDarkBelow/luminanceLightAbovethresholds, 280 ms ease-out). Inert without a scope.GlassScaffold.contentAwareEdgeFade— one flag to enable both bars, composing withcontentAwareBrightness.- Latent wrap-order fix (0.16.0):
GlassScaffoldwas wrapping the body inGlassContentAwareContentafter the edge fade, so the fade overlays were inside the sampled region. With the adaptive scrim this is a feedback loop. Wrap order corrected + regression test added.
🐛
GlassModalSheethandle-drag fixes (#106)- Handle drag no longer fights the inner scroll.
_handleDragActivenotifier is set on pointer-down (before the innerScrollablecan claim slop), disabling inner scroll for the gesture lifetime. Fixes a freeze when dragging the handle over aPlatformView. dragIndicatorColornow actually reaches the drag indicator — it was silently wired into_SheetLayoutbut never forwarded to_GlassDragIndicator.
🐛
GlassEffect— defer capture when boundary is mid-paint (#106)toImageSynccalled during a dirty repaint boundary spammed[GlassEffect] toImageSync failedin debug and dropped the frame's capture. Guarded withdebugNeedsPaintcheck (release-safe, same pattern asGlassScrollEdgeEffect).No breaking changes. New parameters are all optional with safe defaults — existing code compiles and behaves identically without changes.
-
0.16.115 Jun 2026Release notes
Open source →🍎 iOS 26 Indicator Defaults — Parity Calibration
Three indicator defaults have been updated across
GlassBottomBarandGlassSearchableBottomBarto better match the iOS 26 bottom-bar pill out of the box. No API changes — all parameters remain fully configurable.Changed defaults
indicatorPinchStrength—1.0→0.4The previous default of
1.0applied the maximum concave lens / pinch effect during drag. iOS 26's actual pinch is more restrained —0.4produces the characteristic "through a lens" look without over-distorting the edges.To restore the previous behaviour:
GlassBottomBar( indicatorPinchStrength: 1.0, ... )indicatorExpansion—EdgeInsets.all(8)→EdgeInsets.symmetric(horizontal: 12, vertical: 8)The indicator pill in iOS 26 bottom bars is slightly wider than it is tall — a subtle "landing pad" shape that reads as a rounded rectangle rather than a near-circle. The new default matches this proportion.
To restore the previous behaviour:
GlassBottomBar( indicatorExpansion: const EdgeInsets.all(8.0), ... )AnimatedGlassIndicatorchromatic aberration —0.0→0.15The indicator's internal
_baseGlassSettingsnow setschromaticAberration: 0.15. Real iOS 26 glass has a faint iridescent rainbow fringe at the rim. At0.15the effect is a whisper — visible up close, subliminal during normal use.To disable the aberration pass a full
indicatorSettingsoverride:GlassBottomBar( indicatorSettings: LiquidGlassSettings( chromaticAberration: 0.0, // include other fields you need ), ... )Affected widgets
GlassBottomBar—indicatorPinchStrengthandindicatorExpansionGlassSearchableBottomBar—indicatorPinchStrengthandindicatorExpansion- All widgets using
AnimatedGlassIndicator—chromaticAberrationbaseline
GlassTabBarandGlassSegmentedControlretain their existing expansion defaults (EdgeInsets.all(8.0)) as their geometry is different from a bottom navigation bar.
-
0.16.012 Jun 2026Release notes
Open source →🎨 Content-Aware Light/Dark Adaptation
Glass bars now automatically adapt their icon and label colors to match the content scrolling behind them — light glyphs over dark content, dark glyphs over light content — with a smooth cross-fade transition. This matches the iOS 26 behaviour where navigation chrome remains legible regardless of what is visible underneath.
Core engine contributed by @jfhair in PR #103.
New widgets
GlassContentAwareScope— wraps a screen and owns the sampling engine. Captures the content boundary at scroll rate (~5 fps), divides each registered control's rectangle into voting cells, and delivers per-control brightness verdicts via WCAG contrast ratios and dual-threshold hysteresis.GlassContentAwareContent— marks the sampled content region. Installs aRepaintBoundarythat the scope captures. Controls must be outside this region (e.g. inScaffold.bottomNavigationBarwithextendBody: true).GlassContentAwareBrightness— per-control consumer that cross-fades between the light and darkGlassThemeVariantviaGlassThemeVariant.lerp. Supports an externalbrightnessOverride(for PlatformView escape hatches), configurable grid dimensions, and per-control flip duration/curve overrides.
New parameters on existing widgets
GlassBottomBar—adaptiveBrightness,brightnessOverride,onBrightnessChanged. SetadaptiveBrightness: trueto opt in.GlassSearchableBottomBar— same three parameters. Both bars automatically wrap inGlassContentAwareBrightnesswhen enabled.GlassScaffold—contentAwareBrightness. Whentrue, the scaffold automatically wraps the body inGlassContentAwareContentand the entire layout inGlassContentAwareScope. One flag, no manual widget wiring.
New API surface
GlassThemeVariant.lerp(a, b, t)— interpolates settings, glow colors, quality, and border radius between two theme variants. Used internally by the cross-fade but available to consumers building custom transitions.GlassThemeSettings.lerp(a, b, t)— interpolates all 9 glass setting fields (thickness, blur, glassColor, lightAngle, lightIntensity, etc.).resolveBarLabelColor(context, brightness)— shared utility for bars to resolve label color fromCupertinoThemegiven an overridden brightness.
Usage
The recommended path — one flag on
GlassScaffold, one on the bar:GlassScaffold( contentAwareBrightness: true, bottomBar: GlassBottomBar( adaptiveBrightness: true, onBrightnessChanged: (b) => /* flip your own icon colors */, tabs: [...], selectedIndex: _index, onTabSelected: (i) => setState(() => _index = i), ), body: CustomScrollView(...), )For custom layouts without
GlassScaffold, use the standalone widgets directly:GlassContentAwareScope( child: Scaffold( extendBody: true, body: GlassContentAwareContent( child: ListView(...), ), bottomNavigationBar: GlassBottomBar( adaptiveBrightness: true, ... ), ), )Bug fix
GlassThemeVariant.==/hashCodemissingborderRadius— fixed a pre-existing bug where two variants differing only inborderRadiuscompared equal. This caused stale radius during content-aware cross-fades where intermediate lerped variants would not trigger rebuilds.
Polish
_sample()error reporting — the barecatch (_)in the sampling pipeline now reports toFlutterErrorinside an assert closure. Errors are still suppressed in release builds but surface in debug mode so programming mistakes are visible.- Removed redundant
setState—_GlassContentAwareBrightnessState._setBrightnessno longer callssetStatesinceAnimatedBuilderalready listens to the animation controller and rebuilds on every tick.
Example app
- Content-Aware Brightness demo (new) — dedicated showcase with alternating light and dark content bands that force visible bar flips during scrolling. Available in the Examples tab.
-
0.15.712 Jun 2026Release notes
Open source →🌙 Adaptive Brightness Fix
Fixed a bug in
LightweightLiquidGlasswhere the shader's internal brightness estimation (backdropLuma) was incorrectly reading from the OS-levelMediaQuery.platformBrightnessOf(context)rather than the inherited FlutterTheme.of(context).brightness.This ensures that glass surfaces now correctly switch to Light Mode parameters (such as the legibility veil) when the app itself overrides the theme to Light Mode, even if the user's physical device remains in Dark Mode.
-
0.15.611 Jun 2026Release notes
Open source →🌫️ Scroll Edge Fade — Perceptual Gradient Curve
Replaced the 2-stop linear alpha gradient in
GlassScrollEdgeEffectwith a multi-stop eased curve that matches the perceptual dissolve of iOS 26.- 5-stop gradient profiles for both
softandhardstyles — eliminates the "denser in the centre" banding and the visible seam at the fade boundary. hardstyle reworked: steeper hold → sharper drop curve instead of just compressing the soft profile. Height multiplier relaxed from 0.33× to 0.5×.- Example app: Nav Patterns demo now fully brightness-aware (adaptive text
colours,
GlassStatusBarStyle.auto, adaptive solid-bar colour).
No API changes. No breaking changes.
- 5-stop gradient profiles for both
-
0.15.511 Jun 2026Release notes
Open source →✨ Whiten Strength — Light-Mode Legibility Veil
Opt-in whitening ("legibility veil") lifts glass toward white for legibility over busy light backgrounds — modelling iOS 26's light-mode glass.
-
LiquidGlassSettings.whitenStrength(0.0–1.0, default 0.0): lifts the finished glass toward white as the last step of the render. A single control-wide value with no spatial seams or halo artifacts. -
LiquidGlassSettings.whitenGated(defaulttrue): when gated, the lift scales by per-pixel luminance so bright content lifts to white while dark content (text, icons) stays crisp. Ungated applies the lift uniformly — useful for dark-mode frost effects. -
Consistent across all three quality tiers from one knob: Premium (fragment shader), Standard (tint lerp), and Minimal (frosted fallback) all render the same whitenStrength value consistently.
-
GlassSearchableBottomBarwhiten-at-bottom: when ascrollControlleris provided, the bar animates its whitening toward full white as the page nears the scroll bottom — the iOS light-mode behaviour where content crowding under a bar gets the strongest legibility lift. -
Example app: Buttons & Shadows demo now includes a real-time whiten slider with side-by-side comparison cards and scroll-to-bottom boost preview.
Contributed by @jfhair in PR #100.
🎬 Scale-with-Morph — Cohesive Overlay Content Reveal
Menu items and popover content now scale in alongside the liquid morph animation instead of popping in at the tail. The glass container and its content feel like a single continuous motion.
- Items enter the tree at 30% morph progress (down from 94%) and scale from
0.5× to 1.0× via an
easeOutcurve alongside opacity. Text and icons visually grow with the expanding glass container. - Applied to both
GlassMenuandGlassPopoverfor consistent overlay behaviour across the widget family. Content scales in on open and scales back out on close — the morph animation is symmetrical in both directions. - No new API surface. No breaking changes. Purely visual polish.
GlassMenuscale-with-morph contributed by @F1orian in PR #97.
-