liquid_glass_widgets 1.10.0
liquid_glass_widgets: ^1.10.0 copied to clipboard
iOS 26/27-style liquid glass widgets for Flutter. Built on a custom fragment shader for real blur, liquid interactions, specular highlights, and chromatic aberration.
1.10.0 #
Features #
-
GlassBarItem.spacer()multi-capsule rendering (#245, PR #390): A spacer now splits a pinned bar's run of shared items into separate capsules, asToolbarSpacer(.fixed)does natively, where it used to assert. Across a transition each capsule follows its items, matched byidand otherwise by position, rather than one capsule morphing into another. A capsule the destination adds beside one buds out of it through a liquid bridge, as on iOS 27, and one it drops merges back in.Thanks to @JakeThomson for the contribution (#390).
-
External slide-to-select on
GlassMenu(discussion #369, PR #384): AddedglideTo(Offset),endGlide(), andcancelGlide()toGlassMenuController. Enables external gesture recognizers (such as a long-press on an outer card) to drive menu item highlighting, selection haptics, and activation without needing direct touch ownership.Thanks to @F1orian for the contribution (#384).
-
Dynamic sheet appearance with
expandedDarkColor(PR #385): Added optionalexpandedDarkColorparameter toGlassModalSheetandGlassModalSheet.showso presented full-height sheets and morph droplets dynamically follow system light/dark appearance switches while open.Thanks to @DFelten for the contribution (#385).
-
Accessibility semantic label overrides (PR #387): Added optional
semanticLabelparameters acrossGlassPicker,GlassMenu,GlassPopover,GlassTextField,GlassChip,GlassPasswordField,GlassSearchBar, andGlassSearchBarConfigto allow custom screen reader announcements.Thanks to @Ahmadre for the contribution (#387).
Bug Fixes #
-
Tinted
GlassBarItemdropped the bar'sbuttonSettings(fixes #372, PR #389): AGlassBarItemwith atintColorbuilt its capsule's settings from scratch, so underLiquidGlassSettings.ios27Lightit lost the outline, rim light and shadow the untinted capsules beside it kept. The tint now replaces only the body colour of the bar's button settings, hoisted and in-route.Thanks to @JakeThomson for the fix (#389).
-
LiquidGlassSettings.ios27Darkover bright backdrops (fixes #373, PR #388): From mid-grey up,ios27Darklifted the backdrop and drew a light rim where the native dark material darkens it and keeps a dark outline, and over a white page it stayed white where native greys to about 184. The newbodyShadeterm pulls a bright backdrop down by its luminance squared, outline included;ios27Darksets it to0.28and raisesrimShadeto1.Thanks to @JakeThomson for the fix (#388).
-
Screen reader, dynamic type, and Reduce Motion pass (PR #387): Exposed missing button semantics and dismiss actions on menu/popover barriers and plain triggers; prevented chip delete buttons from merging into name text; allowed
GlassPickerto grow with large system fonts; and honored Reduce Motion across toasts, page controls, and searchable tabs.Thanks to @Ahmadre for the contribution (#387).
-
Controlled
GlassSwitchstate synchronization (PR #383): Fixed visual state desynchronization whenwidget.valueis unchanged or rejected by the parent. Reconciles the position controller to authoritativewidget.valueafter gestures and settles thickness bloom correctly.Thanks to @sbezhuk for the fix (#383).
-
One screen-reader node per control (issue #381): Every interactive control now surfaces as a single semantics node carrying its label, role, state, tap and focus. Previously
GlassFocusRegionsplit its focus semantics into a second node — unlabelled on switches, icon buttons, tabs, steppers and button groups (an extra TalkBack stop), and a repeat of the visible text on segments, chips and menu rows (read twice by VoiceOver). The drag detectors onGlassSegmentedControlandGlassSwitch, and the inner detector onGlassListTile, no longer add unlabelled scroll/tap nodes either.- An explicit label (
GlassButton.label,GlassSegment.semanticLabel,GlassMenuItem.title, …) now replaces the control's visible text instead of being announced alongside it, asGlassButton.labeldocuments. GlassMenuItem.subtitleis announced as the row's value;GlassListTile.onLongPressis reachable by screen readers.GlassChip's delete button defaults to "Remove ‹label›" instead of being unnamed.canRequestFocus: falseis now honoured; it was silently overridden, so such controls still took keyboard focus.
Thanks to @cosmeao for the detailed report and reproduction (#381).
- An explicit label (
1.9.0 #
Features #
-
Vertical bars on iPhone Duo (#348, PR #365): Under a
GlassNavigationShell, the pinned chrome,GlassTabBar.bottomandGlassToolbarnow move into the 84pt strip iOS 27.1 reserves on the outer display and on the inner display in landscape, laid out to the native strip's measured geometry. The title stays in a row at the top of the content, andGlassBarItem.axisBehaviordecides which items go vertical. Where the strip runs short, the chrome overflows into a ••• menu and the tab bar collapses to its selected tab. The strip is read fromMediaQuery.viewPaddingand published asGlassVerticalBar;GlassNavigationShell.verticalBarBehaviorandverticalBarCompressionmirror UIKit'spreferredVerticalBarBehaviorandverticalBarCompressionBehavior. Nothing changes on any other device.GlassNavigationShell( verticalBarBehavior: GlassVerticalBarBehavior.automatic, verticalBarCompression: GlassVerticalBarCompression.automatic, child: child!, )Thanks to @JakeThomson for the contribution (#365).
-
Plain cross-fade for pinned chrome after committed back-swipe (PR #360): Added
swipeCommitTransitiontoGlassNavigationShell(GlassSwipeCommitTransition.crossFadevsGlassSwipeCommitTransition.effect) to perform a plain cross-fade without capsule swell, scale, or blur after an interactive back-swipe commits.Thanks to @DFelten for the contribution (#360).
-
Search and large titles in iPhone Duo's strip (#366, PR #370):
GlassTabBar.searchablenow joins the strip's capsule with search as its last slot, and opens its field in the row at the top of the content. AGlassLargeTitlemoves into that row at 28pt, drawn by theGlassAppBar.pinnedsharing its controller, and scrolls away with the content; itssearchBarbecomes a magnifier at the bottom of the strip, which opens the field along the bottom of the content and hides the bar, assearchabledoes natively. The row's insets are physical, as natively, so in a right-to-left app the title ends against the strip.Thanks to @JakeThomson for the contribution (#370).
-
Sheets, popovers and Reduce Transparency in iPhone Duo's strip (#367, PR #371): A
GlassModalSheetnow takes the strip's place where it covers it, as natively: on the outer display itsGlassAppBar.pinnedstacks down a strip of its own, and the sheet rises to 8pt from the top with its margins kept. On the inner display it is a 653pt card with a horizontal bar, placed by the newplacement(GlassSheetPlacement, mirroringpresentationPlacement(_:));.trailingdocks it over the strip and moves its bar into it. AGlassMenuorGlassPopoveropened from the strip opens towards the content, centred on its item. Under Reduce Transparency the strip and the title row turn opaque behind a hairline. A pinned bar inside a presented route no longer registers with the shell, since the presentation is its container.Thanks to @JakeThomson for the contribution (#371).
-
GlassAdaptiveScope.frostStep(also onGlassAdaptiveScopeConfig): opt-in step through premium without frost. The first step down from premium only switches the frost off, standing a regular blur in for it, and keeps the premium lens, rim and highlight; the next step goes to standard, and recovery takes the same steps back.GlassAdaptiveScopeDatagainsfrostEnabled. Default off.
Performance #
-
GlassBackdropGroup(#357): On Impeller everyBackdropFiltercopies the whole screen, and premium glass uses two or three per surface. Glass inside a group shares one backdrop read for its blur and frost; refraction stays per surface. Same idea as SwiftUI'sGlassEffectContainer. Glass under an opacity, shader mask, save-layer clip or inside another glass surface stays out on its own. Wrap a screen's bars rather than aNavigator. No effect on web or Skia. -
GlassTabBarandGlassAppBargroup their glass by default: newgroupBackdrop(defaulttrue; setfalseto opt out). Inside your ownGlassBackdropGroupthe bars join it. Glass that overlaps the bar (selected-tab indicator, search pill while the keyboard is up, extra button, bottom accessory) stays out. iPhone Air,ios27Light, opening a sheet: 23.6 → 9.3 ms average GPU time per frame. No visible change. -
Cheaper iOS 27 material on premium: The shader skips frost and light work that has no visible effect, and the frost's ghost takes 6 taps instead of 45 at the floor sigma (
ios27Light,blur: 0, glass cards: 59.7 → 50.0 ms GPU). -
Less work for moving and resting glass: Frost rows are shifted instead of rebuilt, the geometry matte is kept when glass moves as a whole, and the light-mode shadow is baked into an image once the shape holds still.
-
Benchmark:
example/integration_test/perf_glass_test.dartmeasures raster and GPU frame times on a device in profile mode.Thanks to @Ahmadre for the contribution (#357, PR #359), and to @JakeThomson for the review.
Bug Fixes #
-
GlassMenuslide-to-select released between two rows (PR #375): Releasing over the 2px gap between two rows activated nothing and left the menu open. Each row's hit zone now includes half of the gap on either side, so a release there activates the nearer row.Thanks to @F1orian for the fix (#375).
-
GlassMenukeyboard and screen-reader activation (PR #376): On menus that don't scroll, pressing Enter or Space on a focused row, or activating it with VoiceOver or TalkBack, did nothing; only touch worked. These now activate the row, and a touch tap still activates it exactly once.Thanks to @F1orian for the fix (#376).
-
GlassMenupointer events after unmount (fixes #363, PR #364): Ignore events from active menu and continuous-swipe pointers after disposal, preventing unmounted context and disposed notifier errors when a route is removed during a gesture.Thanks to @yairsts for the fix (#364).
-
In-route bar items slot size (PR #362): Size in-route bar items in
GlassPinnedBarChrometo match the pinned cluster's slot geometry even with custom icon sizes.Thanks to @DFelten for the fix (#362).
-
Pinned chrome icons blur on pop to a route with no pinned bar (follow-up to #351): When popping from a route with a pinned capsule to a route with no pinned bar, item icons no longer blur out on the first frame while the glass capsule stays visible. Items now dissolve together with the capsule glass, matching native iOS behaviour.
-
GlassPinnedBarChromeaction items order in RTL (fixes #374): Keep action items in consistent visual order when a modal sheet triggers hand-over under RTL layout. -
iOS 27 presets on the standard path and web (visible change):
ios27Lightandios27Darkcame out flat and nearly clear on standard and on web, since the lightweight shader draws neither the frost nor the rim light. They now get a regular blur in place of the frost and the highlight in place of the rim light. -
LiquidGlassWidgets.wrap()passes the warm-up thresholds on:warmupPremiumThresholdMsandwarmupStandardThresholdMsfromGlassAdaptiveScopeConfignever reached the scope, and the config's==andhashCodeignored them. -
Docs: The adaptive scope degrades after 2 windows (not 3) and caps explicit
quality:parameters too;docs/ARCHITECTURE.mdno longer claims resting premium glass skips live backdrop passes.
1.8.1 #
Bug Fixes #
GlassScrollEdgeEffectstale texture on theme switch (fixes #352): Recaptures the background when brightness orfadeColorchanges; stale in-flight captures are discarded via a generation counter.- Pinned navigation chrome
buttonSettingsand inset on pop (fixes #351):buttonSettingsandhorizontalInsetnow adopt the destination route's values from the first frame of a pop transition, rather than snapping at completion. GlassButton/GlassIconButtonisolated semantics node (fixes #354): The button label, role, and tap action now land on a single isolated semantics node. Previously they merged into an ancestor boundary or split across two nodes.- Glow layout crash during route transitions (fixes #355): Eliminates
StateError: RenderBox was not laid outcrashes that occurred when a glow animation updated in the same frame a route transition wrapper (e.g.SlideTransition) was inserted above the route. - Interactive widget
CurvedAnimationandValueNotifierdisposal (fixes #356): All interactive and overlay widgets now correctly dispose theirCurvedAnimationinstances andValueNotifiers on unmount, preventing listener leaks andleak_trackerfailures in widget tests.
1.8.0 #
Features #
-
iOS 27 material for
GlassQuality.premium: NewLiquidGlassSettingsterms, each measured against a SwiftUIglassEffect(.regular)control on the same screen:frost,frostOpacity,frostClampandfrostWeightfor the cloud that a copy of the content still shows through (blurWeightshapes that copy),rimShadeandrimShadeEndsfor the half-point outline,rimLightfor its highlight lobes, andGlassLensModel.paraxialfor the evenly folded rim band.LiquidGlassSettings.ios27Lightandios27Darkput them together, andGlassThemeSettingscarries each term so a theme can apply them app-wide. All default to off, and existing settings render exactly as before. The frost costs one blur pass, written to alternate pixel rows of the shape, plus a colour pass whenfrostWeightis not 1.Thanks to @JakeThomson for the contribution (#347).
-
GlassMenuItem.closeDelay: New optionalDurationthat defers menu dismissal afteronTapfires. Useful when the item hosts an animatedtrailingwidget (e.g.GlassSwitch) so its animation completes before the menu morphs away. Defaults tonull(synchronous close — backward compatible). -
Apple Mail iOS 26/27 showcase demo: Added an Apple Mail showcase in
example/lib/apple_mail/featuring pinned bar navigation with gel-morph across mailbox, inbox, and detail screens, living liquid morph compose sheet viaGlassMorphTriggerandGlassModalSheet, floating triage bar withGlassButtonGroup, options popover withGlassMenu, and full Cupertino styling. Includes aGlassSwitchrow (withcloseDelay: 650 ms) in the Inbox···menu to toggle between iOS 26 and iOS 27 glass modes, with state persisted globally across the full navigation stack viakMailUseIos27andMailGlassScope. -
Liquid droplet morph return and metaball fusion for
GlassMenuandGlassPopover: Implements centroid flight trajectory on close forGlassMenuandGlassPopover, unpinning the returning shape from the trigger corner to fly directly toward the trigger anchor center. Adds an ease-in droplet contraction curve and earlyanchorScaleramp-in to enable authentic SDF metaball bridging, morphs border radius directly to capsule/droplet shape on close, rapidly flushes menu items over the first 15% of close to maintain a pure liquid droplet in flight, and resolves narrow constraint layout inGlassMenuItem.
Bug Fixes #
- Graceful quality tier routing for standard and minimal overlay close: Standard and minimal tiers in
GlassMenuandGlassPopovercleanly route through simplified fade and scale fallbacks on close rather than attempting premium shader metaball blending. GlassModalSheetsubmerged offset tracking viaSheetStateInfo.submergedBottom: ExposessubmergedBottomonSheetStateInfoviaGlassModalSheetStateProviderso sheet descendants can accurately compensate for the submerged depth ofGlassModalSheetinGlassSheetState.fullwhen positioning keyboard toolbars, floating action bars, or scroll view insets above the software keyboard and safe area floor.- Scoped backdrop pass rect resolution for nested glass and layer coordinators: Differentiates descendants painted inside a glass shape's
BackdropFilterLayer(such asGlassButtoninsideGlassModalSheet) from children painted outside the pass viasuper.paint(such asAnimatedGlassIndicatorinsideAdaptiveLiquidGlassLayer/GlassTabBar.bottom). RetainsbackdropPassClipRectLocalacross frames for isolated descendant repaints (preventing buttons in modal sheets from disappearing when clicked or animated), while ensuring peer children of layer coordinators correctly resolve against root screen coordinates. - Nested
AdaptiveGlasshonorsuseOwnLayer: Ensures nested glass controls explicitly marked withuseOwnLayer: truepreserve their own refractive shader layer and specular highlights rather than collapsing into an ancestor's 2D vibrancy backer. GlassNavPinnedHosttrailing action groups align to the trailing edge: Pairs action groups from the anchored trailing edge whenanchoredAtStartis false instead of raw index 0. When navigating between routes with differing numbers of trailing groups, the rightmost buttons now correctly morph into each other in place on the trailing edge while remaining groups cleanly materialize or dematerialize without unexpected layout shifting.GlassGlowtouch specular coordinates in nested layers: Corrects screen-to-pass coordinate translation in_TouchSpecularBridgeso touch specular highlights track accurately within nested backdrop passes.
1.7.2 #
Bug Fixes #
-
GlassTabBarno longer exposes an unlabelled bar-wide semantics node (fixes #340, PR #343): The drag and tap detector that spans the full bar is now excluded from semantics across bothGlassTabBar.bottomandGlassTabBar.searchable, leaving the labelled per-tab controls as the only announced actions.Thanks to @DeepanshuPal for the fix (#343).
-
GlassTabBardefault indicator follows app brightness (fixes #341, PR #344): The selected pill now resolves its default black/white tint throughGlassTheme.brightnessOf(context)across bothGlassTabBar.bottomandGlassTabBar.searchableinstead of the device appearance, so app-level light and dark overrides remain visible.Thanks to @DeepanshuPal for the fix (#344).
-
iOS Reduce Motion is now respected (fixes #342, PR #345): Accessibility motion handling now combines Flutter's
disableAnimationsflag with iOS's separatereduceMotionplatform feature, including popover, menu, and modal-sheet morph controllers.Thanks to @DeepanshuPal for the fix (#345).
1.7.1 #
Bug Fixes #
-
Prevent infinite
borderRadiusfrom collapsing clipping and shadows (#339): Guards againstdouble.infinityevaluating to0.0in Flutter's corner-scaling formula across clipping layers, drop shadows, and shader uniforms. Centralizes safe radius handling onGlassDefaultsandLiquidShape.Thanks to @iimrudy for the fix (#339).
-
GlassTabBar.searchableRTL support (fixes #338): Fixes miscalculated indicator positioning, drag hit-testing, and tab selection underTextDirection.rtl.
1.7.0 #
Features #
- Continuous swipe-to-select for
GlassMenuandGlassPullDownButton(#331): Adds native iOS-style continuous press-and-slide interaction. Users can press and hold the trigger, slide directly onto an item, and release to select in a single fluid motion without requiring a second tap.- Enabled by default on
GlassPullDownButton(enableContinuousSwipe: true), opt-in onGlassMenu. - Configurable
continuousSwipeSlop(defaults to 10 logical px) to distinguish quick taps from deliberate swipes without latency. - Pointer-ID isolation for multi-touch safety, haptic feedback on item boundary crossings, optional glow tracking, and automatic deactivation on scrollable menus.
- Enabled by default on
Performance #
-
Geometry matte pixel budget while a premium surface animates (#330):
GlassQuality.premiumsurfaces that resize (e.g. aGlassModalSheetopening) now rasterize their geometry matte at a capped 1 MP budget during animation, settling at full resolution once the shape comes to rest. Reduces animation memory peaks from 400–800 MB to ~340–394 MB on iPhone 17 Pro Max at 120 Hz.Thanks to @JakeThomson for the contribution (#330).
Bug Fixes #
-
Async sheet presenters no longer lose their hoisted capsule (#335, fixes #334): A
GlassBarItem.sheetpresenter that does async work before callingGlassModalSheet.show(e.g. measuring content offscreen, awaiting a fetch) could lose the shell's hold, reverting to the pre-#325 symptom.GlassBarItem.sheetno longer requires a synchronous present.Thanks to @JakeThomson for the fix (#335).
-
GlassTabBar.searchable:textColoris no longer overridden byhintStyle's colour (#336): When bothGlassSearchBarConfig.textColorand a colour onhintStylewere set, the hint colour was applied to typed text too, ignoringtextColor. The standard iOS look — muted placeholder, strong typed text — can now be expressed by setting both. Callers using onlyhintStyleare unaffected; font metrics are shared between hint and text in all cases.Thanks to @jfhair for the fix (#336).
-
Rim refraction no longer bleeds past the glass boundary on small surfaces (#337): On small
GlassQuality.premiumpills and icon buttons the rim refraction could reach far enough to sample content above the surface's top edge, producing rainbow-coloured artefacts once chromatic dispersion split it. Displacement is now capped to the surface's own footprint. Larger surfaces are unaffected.Thanks to @hinata-platform for the fix (#337).
-
Premium glass inside a
BackdropFilterancestor no longer renders displaced on Impeller (#333): Glass nested inside any backdrop-reading ancestor was displaced by the ancestor's screen origin due toFlutterFragCoord()being relative to the offscreen pass.LiquidGlassRenderObjectnow resolves uniforms relative to the enclosing Impeller compositor pass. Top-level surfaces are unaffected.Thanks to @brockbrunson for the root-cause analysis and fix (#333).
1.6.2 #
Features #
-
Native interaction glow for
GlassTabBar(#329, fixes #323):interactionGlowRadiusis now nullable (defaulting tonull), bringingGlassTabBarto parity withGlassButton's iOS 26 native glow calibration (1.6 radius, sigma-16 blur, soft sheen). Explicit values still preserve custom palettes and geometry.Thanks to @azizibahram for the contribution (#329, fixes #323).
-
Configurable
GlassSlider.thumbShadowwith softer default (#327): AddsthumbShadowtoGlassSliderfor custom resting thumb shadow lists (pass[]to disable), while softening the default resting shadow opacity from 25% to 15% black for a cleaner look on light backgrounds.Thanks to @leoluobuqi for the contribution (#327).
Bug Fixes #
GlassTabBaroverdrag no longer clips the edge tab icon (#328): Rubber-banding past the first or last tab no longer slides the selection window off the edge icon.JellyClippernow clamps the clip window against the bar boundary so the icon stays visible and the pill presses elastically against the wall.
1.6.1 #
Bug Fixes #
-
Hoisted capsule kept through sheet morph (#325, fixes #324): When a
GlassBarItem.sheetwas tapped, the capsule it morphed out of vanished mid-flight and the route's copy appeared under the sheet barrier in its place. The shell now keeps only the tapped capsule hoisted through the presentation while all other chrome hands back.GlassPinnedBarChromeexposes the presenting item viachrome.presenting.GlassNavBarRegistration.presentSheetis deprecated.Thanks to @JakeThomson for the fix (#325).
-
GlassSliderresting shadow no longer clipped on light backgrounds (#319): The thumb'sBoxShadowwas part of the material child insideGlassEffect, whose content is clipped to the glass shape — hiding the shadow on white backgrounds. The shadow is now painted as a sibling behindGlassEffect, outside the clip, and its alpha is multiplied by the material's own fade so it vanishes cleanly during interaction and returns on release or cancellation.Thanks to @leoluobuqi for the fix (#319).
-
Cupertino back-swipe no longer crashes with a layout assertion (#326): Returning through a Cupertino page transition could throw a layout assertion when glass surfaces were present. The coordinate snapshot is now refreshed during paint, after all ancestors have been laid out, preserving UV freeze behaviour across repeated sheet presentations.
Thanks to @leoluobuqi for the fix (#326).
-
Glow highlight no longer dies when dragging off a bar (#322):
GlassGlowwas forwarding unclamped pointer coordinates toGlassGlowLayer. On captured moves outside the widget the centre walked past the layer's clip and the light faded out mid-gesture, while the selection indicator — which does clamp — kept tracking the same finger. The position is now clamped to the layer bounds so the highlight parks on the nearest edge and keeps travelling with the finger, matching platform behaviour.Thanks to @azizibahram for the fix (#322).
1.6.0 #
Features #
- Added
tintColortoGlassBarItem— floods the entire glass capsule with the colour; foreground flips to white or black automatically. Separate-background items only. Demonstrated in the example app under Nav Patterns → Tinted Bar Items (#312) with an interactive 8-color palette switcher and before/after comparison.
Bug Fixes #
-
Slow drags over glass inside a scroll view no longer freeze (#317):
GeometryTransformTrackingLayerwas callingonTransformChangeddirectly fromaddToScene— inside a frame — where no new frame gets scheduled. The callback now defers to a post-frame callback during compositing.Thanks to @almazfm for the fix (#317).
-
GlassTabBar.bottomno longer drags backwards under RTL (#313): The drag path was mirroring the pointer twice — a press landed on the correct tab but the slide ran backwards.DraggableIndicatorPhysics.getAlignmentFromGlobalPositionnow takes amirrorForRtlflag;TabDragGestureMixinpassesfalse.Thanks to @azizibahram for the fix (#316).
-
GlassTabBartabs expose a semantics tap action (#314): Tab nodes were buttons with a selected state but noSemanticsAction.tap— TalkBack and VoiceOver could read every destination but activate none of them.Thanks to @azizibahram for the fix (#315).
-
Fixed
AdaptiveGlassignoring explicitsettingswhen inside a glass container (useOwnLayer: false). BrokeGlassBodyMode.cleartinting at standard quality. -
Fixed UV freeze jitter with
responsive_framework(#292): UV freezing now requires an explicitLiquidGlassPushBackScopesignal emitted byGlassPageduring a real CupertinoSheet push-back. Static app-level scales fromresponsive_framework,FittedBox, andInteractiveViewerno longer trigger the freeze. Zero user code changes required.
1.5.0 #
Features #
-
Shader-level touch specular (
uTouchPosition): When aGlassGlowtouch is active, the specular highlight on the glass rim now concentrates on the side facing the finger — a physically correct optical glint matching Apple iOS 26 Liquid Glass, driven by two new GLSL uniforms (uTouchPosition vec2,uTouchIntensity float). The specular calculation is isotropic in physical pixels (immune to aspect ratio warping on wide pills and bars), includes contact distance falloff (smoothstep), aligns outward rim normals (anisoN) with touch rays, is Reinhard-compressed, and costs zero at rest (GPU uniform coherence culls the branch whenuTouchIntensity == 0). Wired with zero frame lag via synchronous controller listeners onGlassGlowLayerStateand dual-direction propagation: child_RenderGlassGlowLayerobjects propagate directly to ancestorLiquidGlassRenderObjects, while ancestorGlassGlowLayers feed_TouchSpecularBridgeonRenderLiquidGlassLayerwithout widget rebuilds. -
Vibrancy fill for nested glass (
avoidsRefraction: true): When aGlassEffectorAdaptiveGlasssits inside aGlassContainer(or any surface that setsInheritedLiquidGlass.avoidsRefraction), it now renders a proper_VibrancyFill— a translucent tintedShapeDecorationwith_SpecularRimPainterrim highlights and fullGlassMaterializeScopesupport — instead of silently routing toGlassQuality.minimaland producing a near-invisible widget. NoBackdropFilter, no compositor stall, zero GPU overhead. Matches iOS 26 UIKit behaviour: aUIVibrancyEffectnested inside aUIVisualEffectViewnever issues a second backdrop read.AdaptiveGlass.vibrancy()static factory added as the public entry-point. -
Continuous menu pointer tracking (
GlassMenu.glowOnTapOnlydefault:false): Changed the default value ofglowOnTapOnlyfromtruetofalseinGlassMenu. BecauseGlassMenuoperates within a modal dismiss barrier without nested scroll rows, pointer tracking now continuously moves the specular glow across menu items during touch drag, matching native iOS 26 context menu optics. Menus containing scrollable content can still explicitly setglowOnTapOnly: trueto prevent persistent glow during scrolling. -
Official AI Agent Skill for vibecoding & pair programming (#306): Added a comprehensive agent skill (
skills/liquid-glass-widgets/SKILL.md) and onboarding guide (skills/README.md) that teaches AI assistants (Antigravity, Cursor, Claude Code, GitHub Copilot) the library's architecture, setup lifecycle (LiquidGlassWidgets.initialize()),GlassScaffoldlayout, component substitution dictionary, and anti-hallucination checklists to prevent syntheticBackdropFilterimplementations or obsolete pre-1.0 APIs. Complemented by repository-levelAGENTS.mdandREADME.mdintegration instructions.
Bug Fixes #
- Native press parity for collapsed search-active tab indicator: When search is active on
GlassTabBar.searchableand the tab bar collapses to the left dismiss/reopen button, tapping it now features the native press growth (LiquidStretch.nativePressGrowth), micro-tremor (AnchorStretchSettings.nativeTremor), and surface highlight (PressAmbientLift), exactly matchingGlassButton,SearchPill, andMinimizableTrailingPill(PR #272 parity).
Tests #
- Light-mode golden snapshots for key widgets: Added
goldenTestLight()helper and companionLightGoldenTestGroup/LightGoldenTestScenario/buildWithLightBackground()utilities totest/shared/test_helpers.dart. Newtest/golden/light_mode_widgets_golden_test.dartprovides 5 targeted light-mode goldens —GlassButton,GlassAppBar,GlassTabBar.bottom,AdaptiveGlass/GlassCard, andGlassToolbar— exercising subsystems that the all-dark suite cannot reach:_InverseShapeClipper/_InverseBarClipperdrop-shadow rendering,ambientBaseLightdoubling (0.14vs0.07), and Rec.709 adaptive glass strength (0.8×on high-luminance backgrounds). Goldens generated on macOS (Impeller); excluded from CI by existingdart_test.yamltag filter.
Chores #
-
Engine organization & attribution: Consolidated foundational rendering primitives into
lib/src/engine/with formal attribution (lib/src/engine/ATTRIBUTION.md) crediting original work by Tim Lehmann. Internal refactoring only; no public API changes. -
Shader rename:
liquid_glass_final_render.frag→liquid_glass_render.frag. Drops the redundant_final_prefix, aligning the on-disk filename 1:1 with the Dart constantShaderKeys.liquidGlassRender. All references updated acrosspubspec.yaml,shaders.dart, setup docs, attribution, ROADMAP, and changelogs. -
Touch specular
pow()elimination: Replacedpow(rimTouchDot, 6.0)in the touch-specular block ofliquid_glass_render.fragwith a two-multiply chain(x²)³ = x⁶.pow()compiles asexp2(6·log2(x))on Mali/Adreno/Apple GPU — two transcendental SFU calls. The multiply chain is exact, branchless, and 4–8× faster per fragment on mobile. -
Beer-Lambert meniscus deduplication in
lightweight_glass.frag: Extracted the 5-line hemisphere-lens × light-modulated absorption formula (previously copy-pasted at 3 call sites: PATH A fallback, PATH A normal, and PATH B) into a sharedmeniscusAbsorption()function. Mathematically identical output; reduces maintenance surface and compiled shader binary size.
1.4.4 #
Features #
GlassPinnedBarChrome.horizontalInset— the guide a hoisted bar is drawn on (#307): The chrome was always positioned atGlassNavPinnedMetrics.horizontalPadding, which is right for a bar the package draws at both ends and wrong for one the app draws. Presenting a sheet hands the chrome back to its route, and a bar aligned to its own page gutter stepped sideways at every hand-over. It now takes the inset its bar reports, defaulting to the old one.
Thanks to @JakeThomson for the feature (#308).
GlassPinnedBarChrome.platformViewBackdrop— pinned chrome over a platform view (#310): The shell drew a hoisted capsule with its ownGlassButtonon the shader path, whose captured backdrop excludes a platform view — so over a map the capsule had nothing to refract and rendered clear with a rim, whateverbuttonSettingssaid, while the bar's own capsule blurred through a liveBackdropFilter. The registration carries the flag now and the host forwards it to the capsule and menu it draws, resolved to the route being entered so a pop back over the view is on the backdrop from its first frame.
Thanks to @JakeThomson for the feature (#311).
Bug Fixes #
GlassSheetremoves redundant innerSafeArea(#309):GlassSheet's inner column wrapped its content inSafeArea(bottom: true)without disablingtop: true, causing unconsumed top status-bar padding from the window to leak into the sheet and push the top drag handle 50+ dp down. In addition, the inner wrapper forced bottom safe-area insetting even whenGlassSheet.show(useSafeArea: false)was requested. The redundant innerSafeAreahas been removed while preserving outeruseSafeAreabehavior.
Thanks to @yohom for the fix (#309).
1.4.3 #
Features #
-
GlassBarItem.sheet— a bar item whose sheet morphs out of the capsule (#304): Nothing reached the capsule a pinned bar draws, so an app wanting the liquid morph had to hoist its cluster as oneGlassBarItemBackground.ownitem and give up the cross-route morph. The new item hands its tap aGlassMorphAnchorforGlassModalSheet.show(morphFrom:)— the route's own capsule, which is the one still on screen once the sheet has handed the chrome back, and the only one a sheet can cover.GlassAppBar.pinned( actions: [ GlassBarItem.sheet( icon: const Icon(CupertinoIcons.add), onPresent: (anchor) => GlassModalSheet.show<void>( context: context, morphFrom: anchor, builder: (context) => const AddSheet(), ), ), ], )
Thanks to @JakeThomson for the feature (#304).
Bug Fixes #
-
Pinned chrome morphs over a sprung route's full duration (#302): The shell drove its choreography from the route's animation value, which is non-linear for spring-based transitions (zoom), so the morph completed in the first few frames while the page was still flying. A route that answers
createSimulationnow plays chrome on the shell's own controller overtransitionDuration; duration-driven routes are unchanged. -
Two custom items that draw their own glass no longer overlap mid-morph (#302): A matched pair of
GlassBarItemBackground.ownitems sharing the same id cross-faded like plain content, stacking both surfaces mid-transition and causing a visible brightness artifact. The outgoing surface now dissolves before the incoming one materializes.
Thanks to @JakeThomson for the fix (#302).
GlassTabBar.searchabletext colour now follows the app's ThemeMode, not the device OS brightness (#305):GlassSearchBarConfig.textColordefaulted toCupertinoColors.label.resolveFrom(context), which readsMediaQuery.platformBrightnessrather than the app'sThemeData. On a device in OS dark mode with the app forced to light mode this produced invisible white-on-white text. Resolution now routes throughGlassTheme.brightnessOf, the package's single brightness authority, consistent with how icon colours are resolved in the same widget.
1.4.2 #
Bug Fixes #
-
Custom bar items that draw their own glass no longer flash across a push or pop (#296): The pinned chrome fades and blurs item content by painting it under opacity and image-filter layers, and a glass surface painted under either has no backdrop to sample — a
GlassBarItem.customcarrying its ownGlassButton.customcapsule rendered as its opaque backer for the whole transition and snapped to glass on the last frame.GlassBarItemBackground.ownmarks such an item, and the cluster dissolves it through the surface's own visibility instead. -
Pinned clusters now dissolve across a push or pop instead of popping in and out:
GlassMenuwraps its trigger in a restingGlassMaterializeScopeso the trigger can fade under an open menu, and every pinned cluster sits inside that wrapper — so the materialize the shell runs around a cluster only one route has never reached the glass. The outgoing shell stayed solid until it was dropped, the incoming one appeared solid, and for a few frames both were drawn. The menu's scope now composes with an enclosing one.
Thanks to @JakeThomson for the fix (#297).
- Dismissible modal sheets retain their frame below the lowest enabled detent (#298): Sheets without a small detent no longer jump to peek width, margins, or corners during dismissal. Large-only sheets slide away with their full frame; medium+large sheets preserve the medium frame below that detent.
Thanks to @hank205 for the fix (#299).
- Menu & Popover dismissal during page transitions and declarative navigation (#274):
- Zero Transition Overlap: Replaced
OverlayChildLocation.rootOverlaywithOverlayChildLocation.nearestOverlayin bothGlassMenuandGlassPopover. In addition, trigger position is now calculated locally relative tonearestOverlay(renderBox.localToGlobal(Offset.zero, ancestor: overlayBox)), preventing menu positioning drift in nested layouts while ensuring the menu lives in the same overlay stack as its route. As a result, incoming pushed routes render in front of closing menus instead of allowing the menu and its glass blur to float above the incoming page. - Declarative Navigation Safety (
go_router/Navigator.pages): Immediate route dismissal calls that fire during Flutter's persistent callbacks phase (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) are now deferred to post-frame callbacks viaSchedulerBinding.instance.addPostFrameCallback. This completely eliminates theSchedulerBinding.instance.schedulerPhase != SchedulerPhase.persistentCallbacksassertion crash when updating pages in declarative routers. - Standalone Navigation Demo: Added
GlassMenuNavigationDemoPage(example/lib/demos/glass_menu_navigation_demo.dart) providing an interactive demonstration of clean overlay dismissal across imperative, declarative (go_router), and nested tab navigator setups with slow-motion transition controls.
- Zero Transition Overlap: Replaced
Calibration #
- Corrected luminance weights to ITU-R Rec.709 across all rendering paths: All
shader and Dart-side luminance computations now use
0.2126 R / 0.7152 G / 0.0722 B(the standard for sRGB and Display P3) instead of the legacy BT.601 SDTV coefficients that were previously in use. The correction is most visible on blue- and cyan-heavy backgrounds where BT.601 measurably overestimated perceived brightness.
1.4.1 #
Bug Fixes #
GlassNavigationShellno longer marks its chrome dirty mid-build (#293): The shell listens to every registered route's animations and re-resolved the pinned chrome synchronously on each tick. A page-basedNavigatorapplies its pages insidedidUpdateWidget— the build phase — and a route with no transition completes its animation right there, so the tick landed assetState() or markNeedsBuild() called during buildon the chrome'sListenableBuilderfor every such push and pop, and the chrome missed that frame. Ticks now defer past build the way status changes already did.
Thanks to @JakeThomson for the fix (#294).
-
GlassScaffolddark mode stuck white inMaterialApp(#289):GlassScaffoldnow scopes its internalCupertinoThemeandGlassStatusBarStyle.autoicon style throughGlassTheme.brightnessOf. This correctly honoursThemeMode.dark/.lighteven when the device OS brightness differs from the app theme. -
Menu and popover dismissal across nested Navigators (#274):
GlassMenuandGlassPopovernow listen to transitions across all ancestorModalRoutes up to the root. When navigation occurs on an enclosing shell or rootNavigator, the overlay dismisses immediately instead of lingering over the incoming destination page.
Thanks to @Vincen-dev for the reproduction and test (#274).
1.4.0 #
Features #
-
GlassBodyMode(adaptivevsclear) — exact design token color fidelity (#269): IntroducedGlassBodyModeenum andbodyModeproperty onLiquidGlassSettings(defaulting toGlassBodyMode.adaptive), achieving 1:1 parity with Apple iOS 26 Liquid GlassGlass.regularvsGlass.clear.GlassBodyMode.adaptive(default): Employs iOS 26 dynamic luminosity normalization, ambient tint modulation, and brightness compensation based on underlying backdrop luminance.GlassBodyMode.clear: Bypasses luminosity normalization, white-point lift, and content-adaptive modulation. Directly composites the designer's exact hex color and alpha fromglassColorover the refracted scene while preserving all 3D optical properties (specular rim reflections, Fresnel edge glow, meniscus edge absorption, and surface refraction).- Perfectly resolves color fidelity when
blur: 0is combined with custom tinted glass surfaces. - Fully integrated across all shader tiers (Impeller
liquid_glass_render.frag, standardlightweight_glass.frag, and Skia_FrostedFallback).
-
Decoupled track background quality in
GlassTabBar: AddedbackgroundQuality: GlassQuality?acrossGlassTabBar.bottom,GlassTabBar.inline,GlassTabBar.searchable, andGlassTabBar.minimizable.- Directly matches UIKit's
UITabBarAppearance.backgroundEffectdecoupling from the active selection capsule. - Allows tab bar containers to render with lightweight frosted glass (e.g.,
backgroundQuality: GlassQuality.minimalor.standard) while the moving selection pill retains full liquid refraction (quality: GlassQuality.premium). - Defaults to
null, which seamlessly inherits fromqualitywithout any visual regression.
- Directly matches UIKit's
-
GlassTabBarTrailingButton.menuandGlassTabBarExtraButton.menu— native pull-down menus from tab bars (#275): Both the trailing pill onGlassTabBar.minimizableand the extra action button onGlassTabBar.bottomandGlassTabBar.searchablenow support an optionalGlassMenupull-down, using the same.menunamed-constructor pattern established byGlassButtonGroupItem.menuandGlassBarItem.menu.// Minimizable trailing pill → opens a menu on tap GlassTabBarTrailingButton.menu( icon: const Icon(CupertinoIcons.ellipsis_circle), label: 'More', menuItems: [ GlassMenuItem(label: 'Edit', onTap: _edit), GlassMenuItem(label: 'Share', onTap: _share), ], ) // Bottom-bar extra button → opens a menu on tap GlassTabBarExtraButton.menu( icon: const Icon(CupertinoIcons.plus), label: 'New', menuItems: [ GlassMenuItem(label: 'New Note', onTap: _newNote), GlassMenuDivider(), GlassMenuItem(label: 'Import', onTap: _import), ], )- Liquid spring origin: The menu expansion spring originates from the button's actual render coordinates — identical behaviour to
GlassBarItem.menuandUIBarButtonItem(image:menu:)in SwiftUI. - Auto-upward expansion:
autoAdjustToScreen: trueis always applied, so menus anchored inside a bottom bar always expand upward, matching the iOS 26 Liquid GlassUIMenubehaviour on toolbar items. enabledtoggle: Settingenabled: falseon either constructor dims the button and suppresses the menu — consistent with the existing tap-callback variant.- Accessibility: Both triggers are wrapped in a
Semanticsnode using the requiredlabelso screen readers announce the control correctly. - API surface:
GlassTabBarTrailingButtongains:menuItems,menuAlignment,menuWidth,label,enabled.GlassTabBarExtraButtongains:menuItems,menuAlignment,menuWidth. Thelabelandenabledfields were already present.- The
isMenugetter on both classes returns true when the menu variant is active.
- No scope creep: Direct pass-through of arbitrary widget trees to the trigger position is explicitly deferred; use
GlassMenu.triggerBuilderdirectly for fully custom triggers.
Thanks to @JoetineY for the feature request (#275).
- Liquid spring origin: The menu expansion spring originates from the button's actual render coordinates — identical behaviour to
-
GlassModalSheetdrag indicator geometry (#288):dragIndicatorHeight(default 4) anddragIndicatorTopPadding(default 8) joindragIndicatorWidth, so a sheet can match a host's own pill. Apple's own apps vary the pill's thickness, width and inset from sheet to sheet, and sit it higher where a control row follows (Maps), so there is no one right value to bake in; the defaults are unchanged.Thanks to @jfhair for the feature (#288).
Bug Fixes #
- Color configuration is uncertain when blur=0 (#269): When developers configured custom tint colors with
blur: 0, the surface previously suffered from unexpected luminance and saturation drift because the shader applied adaptive ambient and light calculations intended for blurred glass. Developers can now setbodyMode: GlassBodyMode.clearonLiquidGlassSettingsto bypass adaptive tinting and composite the exact designer hex color while maintaining specular highlights, Fresnel sheen, and 3D meniscus edge refraction.
Thanks to @JoetineY for the bug report (#269).
- Minimized bar keeps the selected tab's icon colour (#279): On
GlassTabBar.minimizable, the minimized pill drew the selected tab's icon inunselectedIconColor, so a customselectedIconColordropped out on minimize and returned on expand. The pill now usesselectedIconColor, as the native bar does — the tab is still selected, only the bar has shrunk.GlassTabBar.searchableis unchanged: its collapsed pill shows the tab search was opened from, which is no longer the selected one.
Thanks to @JakeThomson for the fix (#280).
GlassScrollEdgeStyle.blurstays on its own route (#278): The progressive blur is a backdrop filter, and left unclipped a backdrop filter frosts everything beneath it up to the nearest ancestor clip — under a Cupertino pop that included the route being revealed, which showed the outgoing screen's top and bottom bands until the transition settled. The shader path lost itsClipRectwhen the region moved to paint time in 0.30.1; it is back, so aProgressiveBlurnow frosts nothing outside its own rectangle wherever it sits.
Thanks to @JakeThomson for the fix and on-device integration tests (#281).
GlassAppBartitle alignment (#282): Left-aligned titles (centerTitle: false) no longer add an unintended 8 px start gap when no leading widget is present, correctly aligning with content padding.
Thanks to @Vincen-dev for the bug report and reproduction test (#282).
- Menu and popover dismiss instantly on route navigation (#274): When an item or action navigates to another page,
GlassMenuandGlassPopoverdismiss immediately instead of playing the close spring across the destination route transition.
Thanks to @Vincen-dev for the bug report (#274).
GlassModalSheetfinishes its travel when a drag hands over to the content (#284): An upward content drag hands the pointer to the scroll view once the sheet is within 5% of its top detent, and that pointer's release is a scroll, not a drag — so the sheet parked up to 5% short of the top with noonStateChanged, the host still believed it sat at the lower detent, and the next collapse skipped its scroll-to-top. The sheet now snaps the remainder at the moment of handover: it arrives, reportsfulland gives its haptic as the content starts to scroll, not when the finger eventually lifts.
Thanks to @jfhair for the fix (#284).
GlassModalSheetController.progressandvalueare current insideprogressListenable(#285): Both reported the position the sheet had last built at, and the listener fires before that build — a frame behind, so the final tick of any drag never showed where the sheet stopped. They now refresh on every controller tick, before listeners run.
Thanks to @jfhair for the fix (#285).
GlassModalSheetdrags after a background launch (#290): The sheet read the window size once, in its first post-frame callback, and re-read it only on a change from a non-zero size. An app the system launches in the background for a push builds against a 0×0 window, so a sheet created then kept the zero for life: when the app came to the foreground, its first drag divided by that zero and parked the sheet at infinity — nothing painted, noonStateChanged, and every latersnapToStatestarting from infinity — until the process was killed. The size is now taken from the view whenever the cache still holds that zero and refreshed on every window-size change, and a drag on a window with no size is ignored.
Thanks to @jfhair for the fix (#290).
1.3.0 #
Bug Fixes #
-
GlassButtonpresses like a native button (behaviour change) (#267): Measured at 120 fps, the press now grows by ~17 pt on a snappy spring (interactionScale: null), drags stretch no more than ~5 %, and the surface combines ambient lift (ambientBaseLight: 0.3) with a subtle shape-clipped specular sheen (glowRadius: null, resolving to a wide 1.6 radius with soft sigma-16 blur). The highlight is clipped strictly toshapeviaShapeBorderClipper, staying bounded within the button geometry and sweeping across grouped buttons without creating a pointy hotspot.LiquidStretchdeclares its scale throughLiquidGlassSelfScaleScopeso the release undershoot no longer freezes refraction.Migration: Pass
glowRadius: 0.0to disable the directional sheen (pure ambient lift). PassinteractionScale: 1.05,ambientBaseLight: 0.08, or useGlassInteractionSettingstheme-wide to customize. -
Search circle and trailing pill press like native buttons (#272, #276): The collapsed pills now use
GlassButton's ~17 pt growth, tremor stretch, and ambient lift, staying round through inflation. The expanded search field follows a lengthwise drag (≈7 pt saturation) and gives a couple of points of vertical stretch, measured against Photos.pressScaleandambientBaseLightare both nullable — null means the native behaviour, a number is a fixed override.
Thanks to @JakeThomson for the native press implementation, LiquidOval corner fix, and axis-constrained expanded field stretch (#276).
- Transform tracking crash on route pop fixed (#268):
GeometryTransformTrackingLayer.addToScenenow checksrenderObject.attachedand wrapsgetTransformTo(null)in a try-catch, preventing theStateErrorthat occurred while render objects were detaching during page transitions.
Thanks to @kdbhalala for the fix (#268).
Performance #
- O(N) Quickselect for frame timing percentiles (#268): The Phase 3 P95 hysteresis check now uses in-place Quickselect (Hoare partition, midpoint pivot) instead of a full sort, eliminating per-evaluation heap allocations on the frame callback path.
Thanks to @kdbhalala for the optimisation (#268).
- Zero-allocation glow paint path (
_RenderGlassGlowLayer): Eliminates per-framePathallocations during gesture spring animations._RenderGlassGlowLayernow follows the Flutter engine'sRenderCustomClippattern: clip paths are cached keyed onsize,shouldReclipprevents re-clipping on value-equal rebuilds, andcanvas.translatereplaces.shift(). Drops native path heap churn from up to 240 allocations/s on 120 Hz ProMotion displays to zero steady-state allocations during drags.
Internal #
THIRD_PARTY_NOTICESadded (#273): The pub.dev archive now includes full MIT copyright notices for the vendoredliquid_glass_rendererand adaptedmotorspring utilities. README updated to link to the notices file.
1.2.3 #
Bug Fixes #
- Inline bottom accessory reaches the trailing edge without a trailing button (#264): On
GlassTabBar.minimizable, a minimized bar with a bottom accessory and no trailing button left a dead gap on the trailing edge — the accessory geometry reserved space for the trailing pill regardless of whether one was present. The accessory now extends flush to the horizontal padding when no trailing button is configured, and still clears the pill when one is.
Thanks to @eomgerm for the fix (#265).
Internal #
MinimizableTrailingPill— dedicated stateless pill for the trailing slot: The trailing-button pill was previously synthesised by re-usingGlassSearchBarConfigwith a dummy search config, creating a double-source-of-truth between_buildMinimizableand the layout engine. It is now a dedicatedMinimizableTrailingPillwidget sharing the sameLayoutBuilder/oval-detection morph pattern asSearchPill— identical pill physics, single authoritative gate. A debug-mode assert now fires ifsearchConfigandtrailingButtonare both non-null; these are mutually exclusive rendering slots.
1.2.2 #
New Features #
- Scrollable segmented control — selection stability and picker behaviors (#263): The pill no longer slides in from the track edge on mount or blinks out on list changes (
VelocitySpringBuilder.teleportEpoch).GlassSegment.idkeeps surviving cells mounted across list changes. New opt-in picker behaviors:SegmentSelectionAlignment.center,SegmentDragBehavior.scroll,regridDuration(anchored re-grid morph, defaultDuration.zero), and an externally injectablescrollController.
Thanks to @jfhair for the full implementation, tests, and example demo (#263).
1.2.1 #
Bug Fixes #
-
Quality recovery is reachable again (#261): Glass quality demoted by a transient cost spike (a map, large images, a native view) now recovers after a sustained run of under-budget frames. Previously, frames landing in the neutral band between the upgrade and downgrade thresholds reset the recovery counter, making recovery essentially unreachable in practice.
-
Tab indicator settles back when host declines selection (#255): Dragging or tapping to a new tab when the host declines the selection (action tab, navigation guard, failed route push) previously left the indicator parked on the wrong tab. It now springs back to the host's current tab on the next frame across all gesture paths.
-
Sheet ↔ content scroll handover (#258): A multi-detent
GlassModalSheetnow grows and then scrolls its content on one unbroken drag, and reverses the same way. Content on aScrollControllerof its own is now observed too — no call-site changes required. -
GlassNavigationShellpinning no longer gated on blur quality (#262): Pinning is chrome geometry, not a shader effect — it costs no render pass of its own. Theminimalquality gate that previously switched pinning off has been removed.GlassQualityAdaptersteps down tominimalautomatically under frame pressure, so pinning was silently disabling itself in debug builds and on low-end devices that need it most.
Thanks to @Nixxx19 for the quality recovery, tab indicator, and pinning gate fixes (#255, #261, #262).
Thanks to @JakeThomson for the sheet ↔ content scroll handover (#258).
1.2.0 #
New Features #
-
Native gel morph for pinned clusters: A cluster present on both routes now reshapes the way the iOS 26 bar does instead of gliding between widths. Its width and item positions ride the package's bouncy spring profile for the full length of the route transition, settling in the same breath the page lands; the spring's overshoot is deliberately not walked in width — it is expressed as a whole-shell squeeze instead, layered on a gel pulse that inflates the shell early — height, radius and glyphs together, as real geometry through the glass renderer. Glyphs no longer plainly cross-fade: an outgoing glyph smears away under heavy blur while the shell reshapes beneath it, and an incoming one arrives soft and sharpens last. A pop plays the same forward choreography toward the other cluster rather than the push in reverse. The morph stays a pure function of the route clock; timing and blur constants live together in
GlassNavPinnedMetrics, and the choreography is locked down by a frame-by-frame trace test. (Clusters only one route has materialize instead — see below.) -
Header Actions Morph demo: New pattern in the example app's Navigation Patterns page — a repository-style drill-down where each destination carries a different trailing cluster (contract to one, identifier-matched hold, widen to three), so every capsule morph can be exercised and scrubbed in isolation.
-
Progressive Blur Scroll Edge Style (
GlassScrollEdgeStyle.blur): Introduces a hardware-accelerated GPU progressive Gaussian frost option viaProgressiveBlur, applying anImageFilter.shaderpass with ease-in quadratic falloff (falloff: 1.2) directly over live scrolling content. Default remainsGlassScrollEdgeStyle.soft(the diffused gradient fade matching iOS 26's.scrollEdgeEffectStyle(.soft)). Developers can opt into.blurfor richer frosting over custom dynamic gradients, video backdrops, or media grids. -
Configurable
maxSigma& Signed Fade Extents: ExposesmaxSigma(default 18.0) forGlassScrollEdgeStyle.blur, alongside signedtopEdgeFadeExtentandbottomEdgeFadeExtent(default 20.0) onGlassScaffoldandGlassScrollEdgeEffectfor granular transition zone control (including negative extents for tight floating-bar insets). -
Materialize entrance & exit transitions (
GlassMaterialize): Glass items now appear and disappear the way iOS 26 does, mirroring SwiftUI'sglassEffectTransition(.materialize): the glass fades up as it settles inward from slightly oversized, and its content sharpens only after the shape has resolved — reversed on the way out, where the content blurs away first and leaves the shell briefly empty before it dissolves.GlassMaterializeis the implicitvisible:-driven form (theAnimatedOpacityidiom);GlassMaterializeTransitionis the explicitAnimation-driven form (theFadeTransitionidiom), and doubles as anAnimatedSwitcher.transitionBuilderviaGlassMaterializeTransition.switcherBuilder. Works on any glass surface at any quality tier: rather than fading a layer — which pops, because a backdrop pass renders fully or not at all — the effect drives the glass shader's own visibility uniforms, where the refraction warp lerps to identity and the render pass drops out entirely at zero. -
Pinned navigation chrome materializes (behaviour change): A pinned back button or actions capsule that only one of the two routes has no longer switches on or off at the transition midpoint — it materializes or dematerializes over a window straddling it. The phase is a pure function of route progress, so a pop plays the windows in reverse with the exit still leading the entrance in both directions. An interactive back-swipe does not scrub them: the page and title track the finger, but the chrome holds still until the gesture commits and then plays its transition over the travel that remains, so a swipe you abandon never half-dissolves anything. A capsule present on both routes is unaffected: it still morphs in place with one persistent glass shell. Opt out with
GlassNavigationShell.effectTransition: GlassEffectTransition.identity, which restores the 1.1.0 behaviour exactly; Reduce Motion selects it automatically, and the standalone widgets likewise fall back to a plain cross-dissolve with no scale or blur. -
Materialize Playground Demo: Added
example/lib/demos/materialize_demo.dart— a single glass button, a multi-item capsule, and anAnimatedSwitcherswapping glass chips, each toggleable, over a busy backdrop with a live Reduce Motion switch. -
GlassPinnedBarChrome— pin a bar that isn't aGlassAppBar: The registration handshake behindGlassAppBar.pinnedis now public, so an app whose bars are its own widgets — a MaterialAppBarwith a bespoke backdrop, a collapsing large-title sliver — can join aGlassNavigationShellwithout reimplementing it. Items are declared once as data; the builder receiveschrome.leadingandchrome.actions, which hold the real glass buttons until the shell has both accepted the registration and had a frame to render its copy, and same-sized unpainted placeholders after — so the bar never builds a second copy and nothing shifts at the hand-over.GlassAppBar.pinnednow builds its own slots the same way, so there is one implementation rather than two. Anenabledflag keeps a nested navigator's roots out of the shell, which ranks routes within a singleNavigator. New Custom Bar Pinning pattern in the nav-patterns demo pins a plain MaterialAppBar. -
Pinned leading items (
GlassAppBar.pinned(leading:)): The pinned bar's leading slot is no longer limited to its own automatic back button, so a screen with a Cancel, a close button or a profile photo can pin its chrome instead of falling back whole.leadingtakes the sameGlassBarItemvocabulary asactionsand inherits the whole morph — width interpolation,idmatching, icon cross-fade, taps swallowed mid-transition — because the cluster render object now anchors to whichever edge it is pinned to rather than always the trailing one. A non-emptyleadingreplaces the back button, matchingUINavigationItem.leftBarButtonItemsand Flutter's ownAppBar.leading;leadingItemsSupplementBackButton: trueshows both, mirroringUINavigationItem.leftItemsSupplementBackButton. A lone back button renders exactly as before. -
GlassBarItemBackground— per-item glass: Collapses the two booleans iOS 26 added toUIBarButtonIteminto their three distinct results:shared(the default —sharesBackground, items form one capsule),separate(sharesBackground: NO— its own shell, which at a lone icon's size is the circular button iOS 26 draws for a single bar item) andnone(hidesSharedBackground— no glass at all, for content that carries its own shape, such as a profile photo). A cluster now renders one shell per group rather than always one capsule. -
Leading Items demo: The nav-patterns showcase gains a pattern walking the four leading configurations — a bare avatar, the implied back button, a leading that replaces it, and one beside it — plus the lone circular shell growing into a two-item capsule.
-
Scroll Edge Playground Demo: Added a comprehensive interactive showcase in the example app (
example/lib/demos/scroll_edge_style_demo.dart) featuring live style switching (soft,hard,blur), real-time extent/sigma sliders, top/bottom toggles, and floatingGlassAppBar&GlassTabBar.bottomintegration with solid content cards. -
Bottom accessory follows the bar (behaviour change) (#226): on
GlassTabBar.minimizable, abottomAccessorywith no explicitbottomAccessoryPlacementnow resolves toGlassTabBarAccessoryPlacement.inlinewhile the bar is minimized, matching how iOS 26 animates atabViewBottomAccessorydown into the minimized bar. Previously it stayedexpandedunlessinlinewas passed explicitly.This changes what
GlassTabBarAccessoryPlacementScope.of(context)returns for affected callers — an accessory that switches on it will render its compact variant on scroll where it previously did not, with no code change on your side — and shrinkspreferredSizebybottomAccessorySpacing + bottomAccessoryHeightwhile minimized. Affects only bars that have an accessory, pass no explicit placement, and reach the minimized state. PassGlassTabBarAccessoryPlacement.expandedto keep the previous behaviour.GlassTabBar.searchableis deliberately unchanged. Auto-collapsing on search was removed in 0.x because it hid the mini-player behind the search capsule, and that decision stands — a search field expanding is not the bar minimizing.
API #
-
PlatformViewGlassMode.passthrough— glass over platform views (#247): New enum value onLiquidGlassSettings. Instead of resolving to black where the backdrop capture held nothing (over a map, camera, video, or WebView), coverage follows what was actually sampled so the live view shows through. Default isPlatformViewGlassMode.fallbackColor— no behaviour change for existing callers.GlassTabBargains a matchingpassthroughOverPlatformViewflag that lifts selected-tab content above the glass to avoid the doubled-label the refracted icon layer would otherwise cause. -
GlassChip.platformViewBackdrop(#250):GlassButtonalready exposed this parameter;GlassChipnow surfaces and forwards it, closing the gap for chips rendered over platform views. Default isfalse, no change for existing callers. -
GlassNavPinnedMetricsis now exported from the package barrel: The geometry the pinned shell redraws hoisted chrome at — 44pt back circle, 46pt action slots, 44pt toolbar band, 8pt edge inset. A bar that is not aGlassAppBarhad no supported way to reach it and had to hardcode the numbers, which drift the first time the package retunes them. Theshowclause exposes the metrics only;GlassNavPinnedHostand the cluster render objects stay internal. Documented under Glass Navigation Transition.
Bug Fixes #
GlassNavPinnedMetrics.crossFadeStart/crossFadeEndchanged meaning: These are now fractions of the morph window (morphStart..morphEnd) rather than raw route progress;glyphSharpenStart/glyphSharpenEndfollow the same convention. Read them throughGlassNavPinnedMetrics.morphProgressAtto align custom chrome.- Matched icons no longer spuriously cross-fade: Icons were compared by reference rather than value, so two identical
Icon(Icons.x)literals on different routes cross-faded with themselves on every push. Icons are now compared by value (glyph, size, colour, key). GlassTabBarMinimizeControllerdrivable without aScrollController:handleSampleis no longer@visibleForTesting, and a newhandleNotification(ScrollNotification)method drives the bar directly from aNotificationListener. LeaveGlassTabBar.minimizable'sscrollControllerunset when using this path.blur: 0no longer downgrades any tier to_FrostedFallback(#253): Ablur: 0surface now keeps its rim, Fresnel, and specular — the shader's own guard already handles the zero-blur pass cleanly. Visual change at standard: surfaces that previously resolved to_FrostedFallbacknow render the lightweight shader. UseGlassQuality.minimalexplicitly if frost fallback was intentional.- Presented routes now cover the pinned chrome (#259): A dialog, action sheet,
GlassModalSheet, orfullscreenDialogpreviously left the pinned glass back button and actions capsule painted above the presentation at full brightness. The shell now hands the chrome back to the presented route so it dims with everything else.
Thanks to @JakeThomson for the materialize transitions, pinned navigation chrome, leading API and per-item glass backgrounds, scroll-to-minimize controller improvements, bottom accessory inline behaviour, metrics export, and the presented-route chrome fix (#240, #238, #236, #239, #233, #234, #259).
Thanks to @marco242424 for the native gel morph (#243).
Thanks to @Nixxx19 for the platform-view glass passthrough mode, GlassChip backdrop parameter, and the blur tier-downgrade fix (#247, #250, #253).
1.1.0 #
New Features #
- GlassNavigationTransition — pinned navigation-bar chrome: New
GlassNavigationShellhosts the glass back button and trailing actions capsule above theNavigator, reproducing the iOS 26 navigation bar: page content and title slide during push/pop (including the interactive back-swipe, scrubbed proportionally) while the glass chrome stays pinned and morphs in place. Screens opt in with theGlassAppBar.pinnedconstructor, declaring actions as data (GlassBarItem.icon/GlassBarItem.custom); items sharing anidare treated as the same item across routes, mirroringUIBarButtonItem.identifier. Custom widgets are measured at intrinsic width, exactly as UIKit measures acustomView. Works with any Pages-API router (go_router, auto_route, beamer) — the shell only readsModalRouteanimations. Without a shell, or where the effect cannot render, the same data renders in-route as today's glass capsule. GlassBarItem.menu— pull-down menus in a pinned bar: TheUIBarButtonItem.menuanalogue, and the iOS 26 overflow button. The whole capsule morphs into the pull-down, matchingGlassButtonGroupItem.menu. Menus cannot be opened mid-transition, and one already open is dismissed when navigation starts — the pinned capsule outlives the route that owns it, so nothing else would. Falls back toGlassButtonGroupItem.menuin-route wherever pinning does.- GlassAppBar single-line titles: The inline title no longer wraps to a second line when wide actions squeeze it; it truncates with an ellipsis, matching iOS.
GlassModalSheetswipe-dismissals morph back from the release point (#223): Dragging a morphed sheet away used to skip the morph and slide the sheet off. Below the lowest detent the sheet now shrinks about the grabbed point as it follows the finger — the interactive zoom-dismissal measured off iOS 26 — and the release hands that exact frame to the closing morph. Sideways, the card chases the finger through a tracking spring, pinning at the screen edge. A release short of the dismiss threshold springs back. Sheets shown withoutmorphFromkeep their plain slide-away unchanged.- Scroll-to-minimize for
GlassTabBar.minimizable(#228): A newGlassTabBarMinimizeControllerdrives the minimize from scrolling — the Flutter equivalent of SwiftUI's.tabBarMinimizeBehavior(_:).GlassBarMinimizeBehaviormirrors Apple's enum case for case. The trigger is accumulated distance, not a per-frame delta — fixing the 1.0.0 ProMotion 120 Hz reliability issue.
Bug Fixes #
- Spring reversals no longer stall (#228):
SearchableBottomBarController.makeSpringnow carries in-flight velocity through retargets; reversing mid-morph no longer reads as a stall followed by a restart. Also benefitsGlassTabBar.searchable. - Shared
ScrollControllercrash fixed (#228): The searchable and minimizable placements readingScrollController.positionnow safely guard when multiple scroll views share one controller during transitions. - Premium glass shape no longer stranded during swipe-dismiss (#229):
RenderLiquidGlassLayerfroze shader UVs under any uniform scale-down, which was correct for the CupertinoSheet push-back but wrong for a swipedGlassModalSheetwhose backdrop holds still. A new internalLiquidGlassSelfScaleScopelets the sheet declare which arrangement it is; the push-back path is unchanged.
Improvements #
GlassNavActionSlot.crossFades— documented widget-equality behaviour: Cross-fade detection uses reference equality;consticon widgets share an instance across routes and never trigger a spurious cross-fade. Documented in the API dartdocs and the navigation guide.- Multiple
GlassBarItem.menuin one cluster — debug warning: Using more than one menu item in a single cluster silently treated the extras as plain icons. A debug-modedebugPrintnow surfaces this so developers see the constraint immediately rather than wondering why a second menu does not open. GlassModalSheetfling velocity carried into the closing morph (#227): A fast swipe no longer stalls at the release point — the closing droplet now starts at the speed the fling was running at rather than from rest.
Documentation #
GlassModalSheet.peekSizedocs: Clarified thatpeekSizeaccepts both absolute pixels (> 1.0) and a screen-height fraction (≤ 1.0), consistent withhalfSizeandfullSize.- New guide:
docs/GLASS_NAVIGATION_TRANSITION.md— setup (including.router), matching rules, a behaviour table, a direction section, and known limitations. ROADMAP.md—GlassNavigationTransitionchecked off; the "pinning becomes the default" trajectory and parity work recorded.
Thanks to @JakeThomson for the navigation transition, modal sheet, tab bar scroll-to-minimize, and premium renderer fix improvements (#221, #223, #227, #228, #230).
1.0.0 #
Major Milestone: General Availability #
This release marks the 1.0.0 General Availability of liquid_glass_widgets, delivering a stable, unified API surface, zero third-party dependencies, and production-grade iOS 26-style liquid glass with hardware-adaptive quality tiers (Metal, Vulkan, Skia, and shader-free fallbacks) across all supported Flutter platforms.
New Features #
GlassTabBar.minimizable()— SwiftUItabBarMinimizeBehaviorparity (#217): A new named constructor that collapses the tab bar to a single selected-tab circle without requiring a search bar.minimizedreplacesisSearchActive,onMinimizedTabTaphandles the restore tap,minimizedBarHeightshrinks both pills while minimized, and an optionalGlassTabBarTrailingButtonfills the trailing slot — exactly modellingTab(role: .search)priority-visibility behaviour. Scroll-driven minimize is caller-controlled, matching the SwiftUI.onScrollDownpattern.GlassSearchBarConfig.showPill(#217): A new boolean onGlassSearchBarConfig(defaulttrue) that removes the search pill entirely and returns its width to the tab pill. Hiding is done by unmounting the pill and spring-scaling it at its slot —Opacityand zero-width leave a glass remnant fused to the tab pill on the shared blend layer; only absence can hide a grouped glass surface.
Thanks to @JakeThomson for the contribution (#217).
GlassModalSheetmorphs from a trigger button (#219):show()gainsmorphFrom— theGlassMorphAnchorfrom a newGlassMorphTriggerwrapper — presenting the sheet with the iOS 26 liquid morph instead of the slide-up: the trigger empties, a glass droplet detaches and inflates as it travels, and lands as the sheet. Makes the sheet the second consumer of the Liquid Morph Engine afterGlassMenu.morphSpeedtunes the spring.GlassMorphTrigger/GlassMorphAnchor(#219): The wrapper owns the trigger's key and opacity so the trigger can empty itself for the morph and take the closing bounce on its own ticker.morphFromRectremains for triggers that can't be wrapped, blooming from a point. The morph degrades gracefully to the slide presentation on Skia/web,GlassQuality.minimal, andplatformViewBackdrop.
Thanks to @JakeThomson for the contribution (#219).
Breaking Changes & API Unification #
- Unified Navigation Surface (
GlassTabBar):- Consolidated bottom navigation into
GlassTabBarvia named constructors:GlassTabBar.bottom(),GlassTabBar.searchable(), andGlassTabBar.inline(). - Removed legacy transitional shims
GlassBottomBarandGlassSearchableBottomBar. - Tab items are now canonically represented by
GlassTabacross all tab bars. - Tab bar collapse types
GlassTabBarCollapseConfigandGlassTabBarCollapseDirectionhave been removed. The collapse-to-extra-button pattern is not an iOS 26 design primitive and was unreliable on 120 Hz ProMotion displays. UseGlassTabBar.minimizableinstead — it directly mirrors SwiftUI'stabBarMinimizeBehavior(.onScrollDown)with spring physics.
- Consolidated bottom navigation into
- Modal Sheet Simplification:
- Removed deprecated
enablePeekparameter fromGlassModalSheet,GlassModalSheet.show(), andGlassModalSheetScaffold. Sizing and peeking behavior is now governed cleanly and declaratively bydetentsandmode.
- Removed deprecated
- Initialization & Setup Cleanup:
- Removed deprecated
respectsAccessibilityfromLiquidGlassWidgets.initialize(). System accessibility preferences (Reduce Motion, Reduce Transparency) are now automatically detected and respected out of the box. - Removed deprecated
warmUpImpellerPipelinefromLiquidGlassWidgets.initialize(). Shader bytecode preloading is handled asynchronously and safely during app bootstrap.
- Removed deprecated
- Shader Quality & Scope Purge:
- Removed deprecated
usesBackdropFiltergetter fromGlassQuality. - Removed deprecated
LiquidGlassScope.stack,GlassRefractionSource, andLiquidGlassBackgroundin favor ofGlassPageandGlassBackgroundSource. - Removed deprecated
GlassBackdropScopestub.
- Removed deprecated
Documentation & Tooling #
- Added comprehensive Migration Guide detailing step-by-step code upgrades for 0.x projects.
- Updated documentation and all example demo screens for 1.0.0 APIs.
For pre-1.0 history see CHANGELOG_ARCHIVE.md or the GitHub Releases page.