NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #1399 most downloaded on pub.dev
iOS 26-style liquid glass widgets for Flutter. Built on a custom fragment shader for real blur, liquid interactions, specular highlights, and chromatic aberration.
Last release 2 days ago
16 Sep 2026
Ships on a steady schedule
a new release about every 8 days
Nearly every release is documented
notes for 60 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
9 months old
169 releases · first in 2025
One column per month.
Fix: Raised the minimum Flutter constraint to 3.41.0. The filterQuality parameter on FragmentShader.setImageSampler() was actually introduced in Flutt
3.41.0. The filterQuality parameter on FragmentShader.setImageSampler() was actually introduced in Flutter 3.41.0 (commit add442b29c), not 3.24.0 as previously stated. This prevents users on 3.38.x from failing at compile time.meta constraint back to ^1.18.0 since Flutter 3.41.0 guarantees this version is available.Fix: Loosened the meta dependency constraint to ^1.12.0 (instead of ^1.18.0) to avoid pub resolution conflicts for users on older Flutter SDKs where f
meta dependency constraint to ^1.12.0 (instead of ^1.18.0) to avoid pub resolution conflicts for users on older Flutter SDKs where flutter_test is bound to meta 1.17.0.Adds selectedLabelStyle / unselectedLabelStyle (TextStyle?) to GlassTabBar.bottom, GlassTabBar.searchable, and the deprecated GlassBottomBar / GlassSe…
selectedLabelStyle / unselectedLabelStyleAdds selectedLabelStyle / unselectedLabelStyle (TextStyle?) to GlassTabBar.bottom, GlassTabBar.searchable, and the deprecated GlassBottomBar / GlassSearchableBottomBar.
This complements the selectedLabelColor / unselectedLabelColor parameters 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 shared textStyle can 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 / unselectedLabelColor was silently dropped whenever a textStyle was also supplied (the per-state color only fed the fallback style). It now overrides the base color — including a color baked into textStyle — while textStyle-only callers are unaffected.
innerBlur — Apple-style rest-blur behind the selected tabThe innerBlur parameter on GlassTabBar.bottom/.searchable (and the deprecated GlassBottomBar/GlassSearchableBottomBar shims) now renders. It was declared and threaded bar→internal, but never forwarded to the indicator, and AnimatedGlassIndicator had no implementation. This wires it through the tab-bar internals and adds the rest-gated BackdropFilter.
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.indicatorColor).platformViewBackdrop on the public glass widgetsFollows up #94 by exposing the premium-over-PlatformView flag on the remaining public widgets — GlassContainer, GlassButton, GlassIconButton, GlassButtonGroup, GlassMenu, and GlassModalSheet — so apps can render premium glass cleanly over an iOS PlatformView (e.g. a Mapbox MapWidget) for any control, not just the bottom bar.
Each gets an explicit platformViewBackdrop parameter defaulting to false (zero overhead / no behavior change for callers that don't need it), forwarded to the underlying AdaptiveGlass. For GlassModalSheet the flag threads through GlassModalSheetScaffold and the sheet state down to the _SheetLayout's glass. Adds widget tests covering the simple-widget forwards.
GlassTintBlend (a no-op since 0.17.0)GlassTintBlend (added in #107) and the LiquidGlassSettings.tintBlend field have been removed.
Since the 0.17.0 shader rewrite, tintBlend was 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. applyGlassColor still picks luminosity-preserving blending for chromatic tints and flat blending for achromatic tints. Recipes that previously passed tintBlend: flat for 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.
Raised minimum Flutter to >=3.24.0 — this was incorrect. The actual minimum is 3.41.0, corrected in 0.18.5.
Fix: Eliminated stair-step aliasing on AnimatedGlassIndicator / GlassEffect pill edges during press animations. Root cause: FragmentShader.setImageSam
Fix: Eliminated stair-step aliasing on AnimatedGlassIndicator / GlassEffect pill edges during press animations.
Root cause: FragmentShader.setImageSampler() defaults to FilterQuality.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: Pass filterQuality: FilterQuality.medium to every setImageSampler() call in liquid_glass_render_object.dart, glass_effect.dart, and lightweight_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/dFdy evaluate 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 in liquid_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 at sd = 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) — so sd = 0 maps 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 LiquidStretch scaling and jelly overshoot.
Root cause: Impeller's default texture sampler wrap mode is Repeat. A fragment slightly outside uGeometrySize (e.g. during a spring overshoot) produced a geometryUV marginally 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: Clamp geometryUV to [0, 1] before the texture fetch. Clamped-edge pixels have near-zero SDF alpha and are discarded by the existing geometryData.a < 0.01 early-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 its RepaintBoundary bounds, out-of-bounds textureBilinear sample 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 the texture() fetch.
Fix: Eliminated jagged/pixelated stair-step artifacts on AnimatedGlassIndicator pill edges when indicatorPinchStrength > 0.
Root cause: BackdropFilterLayer implicit samplers are bound to FragmentShader as 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 a textureBilinear helper to liquid_glass_render.frag that 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 at pixelRatio: 1.0, so each texel covered a 3×3 block of physical pixels on a 3× Retina display; (2) Impeller's setImageSampler() 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. A textureBilinear GLSL helper replaces all raw texture() calls in interactive_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 for uWhiten, uWhitenGated, and uPinchStrength (#121).
All shader uniforms (slots 0–20) are now written atomically on every paint frame, preventing a stale or zero-initialised FragmentShader snapshot from reaching the Metal draw call. Affected: GlassTabBar.bottom(quality: GlassQuality.premium) with an animating indicator. No API changes.
textureBilinear workaround in liquid_glass_render.frag (4-tap bilinear in GLSL) remains necessary for backdrop sampling because Impeller binds the implicit BackdropFilterLayer sampler as Nearest-Neighbor with no Dart API to override FilterQuality. 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 single texture() call.liquid_glass_render.frag — the if (any(lessThan(geometryUV, ...))) check after clamp(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.render.glsl (computeY, getHeight, calculateLighting, calculateRefraction, renderLiquidGlass, debugNormals) — functions superseded by the inline logic in liquid_glass_render.frag. Reduces compiled shader binary size.Hotfix: Resolved missing coverage in layout engines and segmented controls.
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), a…
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 GlassTabBar becomes the single source of truth for all tab-navigation work.
GlassScaffold now automatically manages the Android system navigation bar padding for bottomBar. If you previously added manual Padding or SafeArea around your bottom bar to prevent it from slipping behind the Android navigation buttons, please remove it to avoid double-padding.
GlassTabBar.bottom() — iOS 26 UITabBar equivalentNamed constructor for bottom navigation bars. Full liquid glass layer, jelly physics pill indicator, MaskingQuality dual-layer icon rendering, and optional dividerSettings.
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),
)
GlassTabBar.searchable() — UITabBar + morphing searchNamed constructor combining bottom navigation with a morphing glass search pill. Identical API to GlassTabBar.bottom() with additional searchBarConfig and controller parameters.
GlassTabBar.searchable(
tabs: [...],
selectedIndex: _selectedIndex,
onTabSelected: (i) => setState(() => _selectedIndex = i),
searchBarConfig: GlassSearchBarConfig(hintText: 'Search...'),
controller: _tabBarController,
)
GlassSegmentedControl — icon support + scrollable modesegments now accepts List<GlassTab> instead of List<String>. Each segment can render:
GlassTab(label: 'Weekly')GlassTab(icon: Icon(Icons.photo))GlassTab(icon: Icon(Icons.photo), label: 'Photos') (icon above label)This matches iOS 26 UISegmentedControl which has supported UIImage segments since early iOS.
GlassTabBar(isScrollable: true)GlassTabBar() inline constructorThe default GlassTabBar() constructor has been removed. GlassTabBar is now exclusively used for structural bottom navigation (GlassTabBar.bottom() and GlassTabBar.searchable()).
Migration:
For all inline tab bars, pill menus, or scrollable tag lists, use GlassSegmentedControl() or GlassSegmentedControl.scrollable(). They provide 100% feature parity with the old inline GlassTabBar.
- 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 uses ScrollableSegmentContent — a dedicated widget that owns scrollable pill physics, gesture handling, and 3-layer rendering. Structurally identical to the old inline GlassTabBar(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),
)
All tab-bar layout logic now lives in dedicated layout files:
| File | Owns |
|---|---|
interactive/shared/scrollable_segment_content.dart |
ScrollableSegmentContent — scrollable pill + gesture engine (used by GlassSegmentedControl.scrollable) |
interactive/shared/segmented_control_internal.dart |
SegmentedControlContent — fixed-width pill + gesture engine (used by GlassSegmentedControl) |
surfaces/shared/tab_bar_bottom_layout.dart |
TabBarBottomLayout — full glass bottom shell |
surfaces/shared/tab_bar_searchable_layout.dart |
TabBarSearchableLayout — search morph shell |
GlassTabBar dispatches to these shells. GlassBottomBar and GlassSearchableBottomBar are now zero-logic shims that delegate to the same shells.
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'),
],
...
)
| Widget | iOS 26 equivalent | Glass tier |
|---|---|---|
GlassSegmentedControl(...) |
UISegmentedControl |
Light tint + glass pill |
GlassSegmentedControl.scrollable(...) |
Scrollable filter tabs | Light tint + glass pill |
GlassTabBar.bottom(...) |
UITabBar |
Full liquid glass |
GlassTabBar.searchable(...) |
UITabBar + search |
Full liquid glass |
GlassTabBar.bottom(), GlassTabBar.searchable(), GlassBottomBar, and GlassSearchableBottomBar expose three additional styling parameters:
selectedLabelColor — tab label colour when selected, independent of selectedIconColorunselectedLabelColor — tab label colour when unselected, independent of unselectedIconColorindicatorBorderRadius — pill indicator corner radius, independent of barBorderRadius (e.g. 100 for 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,
)
No API changes. No breaking changes.
platformViewBackdrop toggle no longer snaps the selected-tab indicator (#112 by @jfhair)Toggling platformViewBackdrop at 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 the LiquidGlassBlendGroup wrapper in AdaptiveLiquidGlassLayer, changing the child's depth in the element tree and forcing Flutter to discard and re-inflate the whole subtree via initState.
Fix: AdaptiveLiquidGlassLayer is now a StatefulWidget that holds a stable GlobalKey. The child is always wrapped in a KeyedSubtree with that key, so the element identity is preserved across the wrapper toggle — the indicator's AnimationControllers survive and the morph continues correctly.
No API changes. No breaking changes.
The indicatorPinchStrength concave lens warp is now unified across all four interactive pill widgets. During a drag the pill edges curve inward (iOS 2
The indicatorPinchStrength concave 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.0 disables it, 1.0 is maximum distortion.
GlassTabBar.indicatorPinchStrength (default 0.4)GlassTabBar.indicatorExpansion (default EdgeInsets.symmetric(horizontal: 12, vertical: 8))GlassSegmentedControl.indicatorPinchStrength (default 0.4)GlassSegmentedControl.indicatorExpansion (default EdgeInsets.symmetric(horizontal: 12, vertical: 8))AnimatedGlassIndicator exported from the public API — enables baseIndicatorSettings.copyWith(...) from app code.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.GlassSegmentedControl refraction — labels are now refracted through the glass pill at GlassQuality.premium (was rendered in wrong z-order).GlassTabBar indicator radius — resting pill now inherits the tab bar's borderRadius (was hardcoded 16 px).AnimatedGlassIndicator settings merge — partial indicatorSettings overrides no longer silently reset chromaticAberration.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×.The example app includes a live Indicator Parity demo (Demos → Indicator Parity) with all four pill widgets side-by-side and real-time sliders for pinchStrength, indicatorExpansion, and chromaticAberration. Use it to tune parameters before writing any code.
LiquidGlassSettings.backerColor (#111 by @jfhair)New optional backerColor on LiquidGlassSettings — 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 clear Glass variant.
LiquidGlassSettings(
glassColor: Color(0x20FFFFFF),
backerColor: Color(0x59000000), // ~35% black — Apple's starting point
)
backerColor (Color?, default null) — the color's alpha is the dimming opacity. null means no backer, so all existing recipes are untouched.shadow) and clipped to the glass shape via ClipRRect, so it composites correctly even over a PlatformView — maps, video — where a shader-side tint cannot reach.Stack between grouped glass and its shared layer would break metaball morphing.lerp fades backerColor smoothly from transparent when one side is null, rather than snapping at the midpoint.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),
No breaking changes. All new parameters are optional with safe defaults.
GlassTintBlend — selectable tint blending path (#107 by @jfhair)New GlassTintBlend enum on LiquidGlassSettings to explicitly control how glassColor blends 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 through AdaptiveGlass elevation rebuilds. Frosted fallback renders flat by construction and ignores the setting.
GlassScrollEdgeEffect.bottomFadeInset (#109 by @jfhair)New optional bottomFadeInset parameter (default 0.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.
No breaking changes. New parameters are all optional with safe defaults — existing code compiles and behaves identically without changes.
GlassMenu / GlassPopover rebuild on keyboard open/closeGlassMenu and GlassPopover were rebuilding on every keyboard open/close event,
even when closed. Caused by MediaQuery.of(context) in didChangeDependencies
subscribing to viewInsets. Fixed by switching to scoped accessors
(disableAnimationsOf, maybeSizeOf, textScalerOf). Regression tests added.
The continuous companion to the contentAwareBrightness discrete 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() gains onLuminanceChanged (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 toward darkFadeColor as content darkens (luminanceDarkBelow / luminanceLightAbove thresholds, 280 ms ease-out). Inert without a scope.GlassScaffold.contentAwareEdgeFade — one flag to enable both bars, composing with contentAwareBrightness.GlassScaffold was wrapping the body in GlassContentAwareContent after 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.GlassModalSheet handle-drag fixes (#106)_handleDragActive notifier is set on pointer-down (before the inner Scrollable can claim slop), disabling inner scroll for the gesture lifetime. Fixes a freeze when dragging the handle over a PlatformView.dragIndicatorColor now actually reaches the drag indicator — it was silently wired into _SheetLayout but never forwarded to _GlassDragIndicator.GlassEffect — defer capture when boundary is mid-paint (#106)toImageSync called during a dirty repaint boundary spammed [GlassEffect] toImageSync failed in debug and dropped the frame's capture. Guarded with debugNeedsPaint check (release-safe, same pattern as GlassScrollEdgeEffect).
No breaking changes. New parameters are all optional with safe defaults — existing code compiles and behaves identically without changes.
Three indicator defaults have been updated across GlassBottomBar and GlassSearchableBottomBar to better match the iOS 26 bottom-bar pill out of the bo
Three indicator defaults have been updated across GlassBottomBar and
GlassSearchableBottomBar to better match the iOS 26 bottom-bar pill
out of the box. No API changes — all parameters remain fully configurable.
indicatorPinchStrength — 1.0 → 0.4The previous default of 1.0 applied the maximum concave lens / pinch effect
during drag. iOS 26's actual pinch is more restrained — 0.4 produces 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),
...
)
AnimatedGlassIndicator chromatic aberration — 0.0 → 0.15The indicator's internal _baseGlassSettings now sets
chromaticAberration: 0.15. Real iOS 26 glass has a faint iridescent
rainbow fringe at the rim. At 0.15 the effect is a whisper — visible
up close, subliminal during normal use.
To disable the aberration pass a full indicatorSettings override:
GlassBottomBar(
indicatorSettings: LiquidGlassSettings(
chromaticAberration: 0.0,
// include other fields you need
),
...
)
GlassBottomBar — indicatorPinchStrength and indicatorExpansionGlassSearchableBottomBar — indicatorPinchStrength and indicatorExpansionAnimatedGlassIndicator — chromaticAberration baselineGlassTabBar and GlassSegmentedControl retain their existing expansion
defaults (EdgeInsets.all(8.0)) as their geometry is different from a
bottom navigation bar.
Glass bars now automatically adapt their icon and label colors to match the content scrolling behind them — light glyphs over dark content, dark glyph
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.
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 a
RepaintBoundary that the scope captures. Controls must be outside this
region (e.g. in Scaffold.bottomNavigationBar with extendBody: true).GlassContentAwareBrightness — per-control consumer that cross-fades
between the light and dark GlassThemeVariant via GlassThemeVariant.lerp.
Supports an external brightnessOverride (for PlatformView escape hatches),
configurable grid dimensions, and per-control flip duration/curve overrides.GlassBottomBar — adaptiveBrightness, brightnessOverride,
onBrightnessChanged. Set adaptiveBrightness: true to opt in.GlassSearchableBottomBar — same three parameters. Both bars
automatically wrap in GlassContentAwareBrightness when enabled.GlassScaffold — contentAwareBrightness. When true, the scaffold
automatically wraps the body in GlassContentAwareContent and the entire
layout in GlassContentAwareScope. One flag, no manual widget wiring.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 from CupertinoTheme given an overridden brightness.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,
...
),
),
)
GlassThemeVariant.== / hashCode missing borderRadius — fixed a
pre-existing bug where two variants differing only in borderRadius compared
equal. This caused stale radius during content-aware cross-fades where
intermediate lerped variants would not trigger rebuilds._sample() error reporting — the bare catch (_) in the sampling
pipeline now reports to FlutterError inside an assert closure. Errors are
still suppressed in release builds but surface in debug mode so programming
mistakes are visible.setState — _GlassContentAwareBrightnessState._setBrightness
no longer calls setState since AnimatedBuilder already listens to the
animation controller and rebuilds on every tick.Fixed a bug in LightweightLiquidGlass where the shader's internal brightness estimation (backdropLuma) was incorrectly reading from the OS-level Media
Fixed a bug in LightweightLiquidGlass where the shader's internal brightness estimation (backdropLuma) was incorrectly reading from the OS-level MediaQuery.platformBrightnessOf(context) rather than the inherited Flutter Theme.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.
No API changes. No breaking changes.
Replaced the 2-stop linear alpha gradient in GlassScrollEdgeEffect with a
multi-stop eased curve that matches the perceptual dissolve of iOS 26.
soft and hard styles — eliminates
the "denser in the centre" banding and the visible seam at the fade boundary.hard style reworked: steeper hold → sharper drop curve instead of just
compressing the soft profile. Height multiplier relaxed from 0.33× to 0.5×.GlassStatusBarStyle.auto, adaptive solid-bar colour).No API changes. No breaking changes.
No new API surface. No breaking changes. Purely visual polish.
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 (default true): 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.
GlassSearchableBottomBar whiten-at-bottom: when a scrollController
is 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.
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.
easeOut curve alongside opacity. Text and icons visually
grow with the expanding glass container.GlassMenu and GlassPopover for 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.GlassMenu scale-with-morph contributed by @F1orian in PR #97.
Internal optimizations targeting the render object paint() hot path and background capture lifecycle. Zero API changes, zero visual changes. All 2,050
Internal optimizations targeting the render object paint() hot path and background
capture lifecycle. Zero API changes, zero visual changes. All 2,050 tests passing.
GlassBottomBar and
GlassSearchableBottomBar now respond with a near-subliminal horizontal
shift when the interactive pill is flicked quickly, matching iOS 26 bottom
bar physics. Velocity-gated (slow drags produce no movement) and
spring-animated back to center on release.ImageFilter in _RenderLightweightGlass — the composed
blur+saturation ImageFilter (and its 20-element ColorFilter.matrix list) was
previously reconstructed on every paint() frame for every visible glass widget.
With 5 glass cards at 60 fps, that's ~300 list allocations/second hitting the GC.
The filter is now cached on the render object and only rebuilt when blur or
saturation changes.ImageFilter in _RenderInteractiveIndicator — same optimization
for the interactive indicator shader path (GlassEffect). The brightness+blur
composed filter is now cached and invalidated only when blurSigma changes.ImageFilter.blur in RenderLiquidGlassLayer — the premium glass
layer's BackdropFilterLayer blur filter was previously recreated on every
paintLiquidGlass() call, even when the blur sigma hadn't changed. During
jelly/morph animations (60 fps), this creates an ImageFilter.blur object per
frame per premium glass layer. Now cached and reused._shapesWithGeometry list — the premium glass paint() method
previously allocated a new list on every frame to collect active geometry
shapes. It now reuses a cleared instance list, eliminating another per-frame
allocation on the hot path._RenderLightweightGlass and
_RenderInteractiveIndicator now cache cos(lightAngle) and -sin(lightAngle)
instead of recomputing them on every _updateShaderUniforms() call. Matches the
caching pattern already used by the premium LiquidGlassRenderObject._cachedLightDir.alwaysNeedsCompositing — both _RenderLightweightGlass and
_RenderInteractiveIndicator previously returned true unconditionally, forcing
the framework to create a compositing layer even in the fallback path (null shader
or zero blur). Now returns true only when a BackdropFilterLayer will actually
be pushed. Reduces layer tree depth on low-end devices.LightweightLiquidGlass runs a Ticker to
capture the background behind each glass widget via toImageSync. Previously,
this ticker ran at 60 fps permanently — even when the background hadn't changed
for minutes (e.g. a static bottom bar over a static page). After 3 consecutive
no-change frames (~50 ms), the ticker now self-suspends. It restarts automatically
when didUpdateWidget detects a configuration change. For a bottom bar with 5 tab
pills, this eliminates 300 unnecessary method calls/second while idle.GlassQualityAdapter._percentile — the Phase 3 runtime percentile
computation previously called _window.toList() + List.from(data)..sort() every
120 frames, creating two heap-allocated lists on the frame timing callback path.
Now reuses a pre-allocated sort buffer (List<int>.filled) that is overwritten
in-place. Eliminates GC pressure from the very code path that monitors for
GC-induced frame drops.GlassSearchableBottomBar now wraps in LiquidStretch, giving it the same
press-scale physics as the normal tab indicator.
Contributed by @g3mf0r in PR #96.GlassSearchableBottomBar — the three morph-
animation AnimationControllers (tab width, search left, search width) each
had their own anonymous addListener(() => setState(() {})) callback. During
a pill morph all three springs tick every frame, producing three independent
setState calls per frame. Now all three share a single named _onSpringTick
callback — Flutter coalesces the dirty mark so only one rebuild fires. Also
fixes a subtle listener leak: anonymous lambdas can't be matched by
removeListener, but named methods can.GlassEffect.preWarm() dummy image — changed from async await picture.toImage(1, 1)
to synchronous picture.toImageSync(1, 1), matching the LightweightLiquidGlass
pattern. Eliminates a 1-frame async initialization delay for a 1×1 transparent
texture. Added picture.dispose() to prevent a minor leak.New opt-in flag that lets glass bars render correctly over native iOS views (Google Maps, Apple Maps, WebView, MapLibre, video players) while keeping
platformViewBackdrop — Premium Glass Over iOS PlatformViewsNew opt-in flag that lets glass bars render correctly over native iOS views
(Google Maps, Apple Maps, WebView, MapLibre, video players) while keeping the
premium indicator animations alive. Previously, developers had to downgrade to
GlassQuality.standard on iOS — now they can stay on premium.
Contributed by @jfhair in PR #94.
platformViewBackdropGlassBottomBar — new platformViewBackdrop parameter. When true,
the bar background renders via live BackdropFilter (the premium shader's
toImageSync cannot capture a UIKitView), while the premium indicator
refracts the bar's own icon layer instead of the un-capturable backdrop.GlassSearchableBottomBar — same parameter, applied to both the tab
indicator and the search pill. The collapsed search button automatically
falls back to GlassQuality.standard over a PlatformView.AdaptiveGlass / AdaptiveGlass.grouped() — new platformViewBackdrop
parameter that forces the BackdropFilter rendering path even at
GlassQuality.premium.AdaptiveLiquidGlassLayer — new platformViewBackdrop parameter that
skips the LiquidGlassBlendGroup wrapper when rendering over a PlatformView.GlassBottomBar(
quality: GlassQuality.premium,
platformViewBackdrop: Platform.isIOS, // ← one line fix
tabs: myTabs,
selectedIndex: _index,
onTabSelected: (i) => setState(() => _index = i),
)
platformViewBackdrop: Platform.isIOS
instead of the old Platform.isIOS ? GlassQuality.standard : GlassQuality.premium
workaround. The bar now stays at GlassQuality.premium on all platforms.Replaced the previous inverse-clip shadow system with GPU SDF shadows that render directly from the merged geometry matte. This is both a performance
Replaced the previous inverse-clip shadow system with GPU SDF shadows that render
directly from the merged geometry matte. This is both a performance improvement
and a visual correctness fix — shadows now correctly follow the liquid metaball
shape during morph animations, which was impossible with the old per-element
ClipPath approach.
RenderLiquidGlassLayer.paintGlass now includes a
Pass 0 that draws each BoxShadow using the geometry matte image. The matte
is drawn with ImageFilter.blur for the shadow spread, tinted via
ColorFilter.mode, and then the interior is punched out with BlendMode.dstOut
so the glass backdrop filter never samples its own shadow. This replaces the
widget-level _InversePillClipper / _InverseOvalClipper ClipPath approach.LiquidGlassSettings.effectiveShadow
is now resolved at the AdaptiveGlass and AdaptiveLiquidGlassLayer level and
passed through LiquidGlass.withOwnLayer → LiquidGlassLayer → _RawShapes →
RenderLiquidGlassLayer. Dark mode and flat-edge shapes (borderRadius: 0)
correctly receive an empty shadow list._InversePillClipper,
_InverseOvalClipper from GlassBottomBar, and the equivalent clippers from
GlassSearchableBottomBar. Removed _wrapWithLightModeShadow from
AdaptiveGlass. This eliminates ~140 lines of widget-level shadow plumbing and
the associated ClipPath compositing cost.LiquidGlassRenderObject now
exposes geometryImage and geometryLocalBounds as @protected getters so
RenderLiquidGlassLayer can access the matte for shadow rendering.GlassMenu overlay renderingAdaptiveLiquidGlassLayer in menu overlay — the morph overlay now uses
AdaptiveLiquidGlassLayer instead of raw LiquidGlassLayer, ensuring correct
quality resolution and backdrop blur when the menu is rendered in premium mode.Brightness.light) to serve
as the definitive showcase for GPU SDF shadows. Moved from the Demos tab to the
Examples tab with updated card gradient and subtitle.useOwnLayer: true to
correctly enter the GPU SDF shadow pipeline. Menu glass settings are
brightness-aware (Colors.white12 dark / Color(0x99FFFFFF) light)._buildDemoBackground() now accepts BuildContext and
returns a light frosted gradient in Light Mode instead of the hardcoded dark
navy gradient that caused unreadable text.snap_rect_to_pixels.dart in liquid_glass_layer.dart.isDark variable in glass_menu_internal.dart.package:flutter/material.dart import from
adaptive_liquid_glass_layer.dart (already provided by cupertino.dart).glass_bottom_bar.dart.GlassScrollEdgeEffect._captureBackground —
RenderRepaintBoundary.toImage() throws synchronously on layer! when called
before the first paint completes (layer not yet composited). The
debugNeedsPaint guard only runs inside assert() and is stripped in release
builds. Fixed by deferring the initial capture to a post-frame callback and
wrapping toImage() in try/catch as a safety net. Falls back to the
solid-colour gradient overlay on failure. (reported by @RuslanTsitser via #93)GlassBottomBar golden test reference image.liquid_glass_widgets is now a fully adaptive iOS 26 UI kit. Every widget — buttons, menus, search bars, sliders, switches, sheets, toasts, chips, form
liquid_glass_widgets is now a fully adaptive iOS 26 UI kit. Every widget
— buttons, menus, search bars, sliders, switches, sheets, toasts, chips, form
fields, and all surface bars — automatically resolves the correct glass color,
rim lighting, shadow, text, and icon values for both Light and Dark
mode. No manual configuration required.
This release closes the last remaining light-mode rendering gaps and ships a reference implementation in the Apple Messages demo that matches the iOS 26 Messages app in both modes. Run the example app to toggle light and dark mode for all demo pages.
LiquidGlassSettings.shadowElevation scales the light-mode drop shadow (0.0 = off, 1.0 = default, 2.0 = double). LiquidGlassSettings.shadow accepts a custom List<BoxShadow> for full control. Both flow through globalSettings (theme-level) and per-widget settings:.GlassShadow constants — centralised shadow values (GlassShadow.elevation, .contact, .defaults, .scaled(double)) exported for custom widget authors.GlassButtonGroup.icons() — introduced a lightweight group constructor for iOS 26 style segmented icon toolbars. Uses a single GlassButton parent to drive cohesive group-level stretch/glow interaction, while children are rendered as zero-overhead stateless tap targets.GlassMenuController — imperative controller for GlassMenu with open(), close(), and isOpen. Drives the menu programmatically instead of (or in addition to) tapping the trigger — useful for gesture-arena-driven menus. (contributed by @F1orian)GlassMenu.showDismissBarrier — when false, suppresses the full-screen tap-to-dismiss barrier so an external gesture owner can keep receiving pointer events while the menu is open. Defaults to true. (contributed by @F1orian)GlassMenu.morphFromZero — when true, the menu body lerps from a zero-size point at the trigger center instead of from the trigger's own dimensions, suppressing the spawn blob (Blob A). For invisible or zero-sized triggers. (contributed by @F1orian)GlassMenuController.setFollowOffset(Offset) — nudges the open menu to track a moving anchor in real-time. The offset is added to the captured trigger position each frame and reset on the next open(). (contributed by @F1orian)CupertinoTheme.brightnessOf(context). Switching between light and dark mode requires zero widget-level changes.CupertinoColors against the active theme brightness.GlassSearchBar and GlassTextField default colors now brightness-aware — default text, icon, and glow colors now resolve dynamically against CupertinoTheme.brightnessOf(context) instead of being hardcoded to white. This ensures correct contrast in both light and dark mode.} else { in lightweight_glass.frag that caused PATH B (standard widgets) to run inside PATH A.GlassSheet sharp corners on macOS by decoupling top and bottom border radius.GlassMenu selection pill alignment and hit-test accuracy when system text scaler is active.GlassChip resolving with invisible white text and icons in light mode.GlassFormField label (Colors.white) and helper text (Color(0x99FFFFFF)) being invisible in light mode — now resolves from CupertinoColors.label / .secondaryLabelGlassPicker value text (Colors.white) and chevron icon (Colors.white70) being invisible in light mode — now resolves from CupertinoColors.label / .secondaryLabel.GlassPasswordField lock and eye toggle icons (Colors.white70) being invisible in light mode — now resolves from CupertinoColors.secondaryLabel.GlassActionSheet forcing dark card background (Colors.black @ 0.65), dividers, and pressed highlights regardless of brightness — now resolves to light frosted glass in light mode.GlassToast forcing dark pill (Colors.black @ 0.7) and white text regardless of brightness — background and text now resolve from brightness for correct contrast in both modes.import 'package:flutter/material.dart' from GlassFormField, GlassPicker, GlassPasswordField, and GlassToast.GlassMenu morph size going negative during spring undershoot on small triggers — currentWidth/currentHeight now clamped to >= 0 to prevent debug BoxConstraints assertion. (contributed by @F1orian)GlassMenu sub-pixel Impeller crash — body container is replaced with SizedBox.shrink() when dimensions fall below 1.0 logical pixel, preventing 0-area matte rasterisation. (contributed by @F1orian)GlassIconButton bypassing theme quality chain — quality now passes null through to GlassButton.custom() so GlassThemeHelpers.resolveQuality can resolve from ancestor layers, theme, then widget default. (contributed by @F1orian)GlassTextFieldGlassTextField no longer applies a hidden internal darkening overlay ("frosted well") on top of the glass surface. Previously, a DecoratedBox with Colors.black.withValues(alpha: 0.12) plus a top-edge gradient was composited inside the glass shape to simulate an iOS 26 recessed input tray. This has been removed entirely.
Why: The frosted well fought against user-specified glassColor, making text fields appear darker/muddier than buttons with identical settings. It also caused visible nested border artifacts when the overlay's BorderRadius.circular() didn't match the glass surface's LiquidRoundedSuperellipse shape — especially with useOwnLayer: true and padding: EdgeInsets.zero.
Design philosophy: GlassTextField is a hero surface (search bars, compose bars, app bar inputs) — not a form field nested inside glass cards. glassColor is now the single source of truth for appearance, consistent with every other glass widget.
Migration: If you relied on the frosted well for visual distinction inside a grouped layout, set a slightly darker glassColor on the text field explicitly. Most users will see cleaner, more predictable text fields with no action required.
GlassCard wrappers around form fields with flat CupertinoColors.systemFill containers. Glass text fields now render as standalone hero surfaces (useOwnLayer: true) inside flat-colored form sections, demonstrating the correct pattern. Glass-in-glass nesting is an anti-pattern.#F2F2F7), brightness-aware white glass tint, and dynamic layer separation to enable shadows. Dark mode retains liquid blending via the shared AdaptiveLiquidGlassLayer. Press interactions use persistPressOnDrag: true and an elevated ambientBaseLight in light mode so the pressed state remains visible while holding and dragging off the button edge.Custom iconColor, titleStyle, and color parameters still take priority over the theme default — zero breaking changes.
Removes Material-leaning widgets and thin wrappers that don't map to real
iOS 26 components. Standardises the public API for v1.0 readiness. This is a
breaking release — users on ^0.14.0 will not auto-upgrade.
| Widget | Migration |
|---|---|
GlassPanel |
Replace with GlassCard(padding: EdgeInsets.all(24)) |
GlassWizard / GlassWizardStep |
No direct replacement — use GlassStepper for sequential steps |
GlassSideBar / GlassSideBarItem |
No direct replacement — a proper UISplitViewController-style sidebar is planned for post-1.0 |
GlassSnackBar |
Replace with GlassToast (identical API — GlassSnackBar was documented as an alias) |
glassSettings → settings renameThe glassSettings parameter on 8 widgets has been renamed to settings for
consistency. The Glass prefix on the widget name already identifies the
settings type — glassSettings was redundant.
Affected widgets: GlassBottomBar, GlassSearchableBottomBar,
GlassSegmentedControl, GlassButtonGroup, GlassMenu, GlassPopover,
GlassToolbar, GlassPicker.
Migration: Find-and-replace glassSettings: → settings: in your code.
GlassSheet.show() API CleanupRemoved dead Material parameters from GlassSheet.show() that have no effect in the liquid glass rendering pipeline:
backgroundColorelevationshapeclipBehaviorconstraintsMigration: Remove these parameters from your GlassSheet.show() calls. Use settings to configure the glass visual appearance, and margin / padding / borderRadius to control the layout and shape.
All hardcoded Colors.white / Colors.black in widget content layers replaced with CupertinoColors adaptive equivalents. Widgets now render correctly in both light and dark mode without requiring a MaterialApp ancestor.
Affected widgets: GlassDialog, GlassActionSheet, all interactive and surface widgets.
GlassPage is now fully safe to use inside a pure CupertinoApp — the Theme.of guard no longer throws when no Material ancestor is present.
GlassSheet sharp corners (All Platforms)Fixed a bug where GlassSheet would render with completely sharp, square corners on all devices (including iOS and Android). The internal SafeArea wrapper was consuming the bottom safe area before the adaptive radius calculation could read it, causing it to incorrectly fallback to 0.0 everywhere. GlassSheet now decouples its top and bottom border radii, defaulting to topBorderRadius: 32.0, and smartly assigning bottomBorderRadius to 32.0 when floating (with margin) or 0.0 when docked.
GlassButtonStyle.prominentA new button style matching iOS 26's .prominentGlass / .glassProminent
configuration — thicker, more opaque glass for primary call-to-action buttons.
GlassButton(
style: GlassButtonStyle.prominent,
icon: Icon(CupertinoIcons.plus),
onTap: () {},
)
GlassGroupedSectionA convenience wrapper that groups GlassListTiles inside a GlassCard,
automatically applying isLast: true to the final tile to suppress its
bottom divider — the most common source of bugs.
GlassGroupedSection(
header: Text('Network'),
children: [
GlassListTile(title: Text('Wi-Fi')),
GlassListTile(title: Text('Bluetooth')),
GlassListTile(title: Text('VPN')), // isLast applied automatically
],
)
GlassPageControliOS UIPageControl equivalent — dot indicators for paged content (carousels,
onboarding, gallery pages). Features animated capsule-shaped active dot with
glass treatment and optional tap-to-navigate.
GlassPageControl(
count: 5,
currentPage: _currentPage,
onPageChanged: (page) => _pageController.animateToPage(page,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
),
)
Added a design philosophy section explaining iOS 26's glass hierarchy:
glass for navigation chrome and controls, opaque for content areas.
Repositioned GlassContainer as an advanced primitive in the widget catalogue.
GlassBackdropScope — deprecated no-op stays until 1.0.0 as previously
committed in the 0.14.0 changelog. Zero cost to keep.GlassMenu auto-scrolling with large system textWhen the user increased system text size, GlassMenu would become scrollable
even without menuHeight being set. The height calculation now accounts for
textScaler so the menu sizes correctly at any accessibility text scale.
GlassMenu selection pill misalignment at large text scaleThe sliding selection highlight and hit-test math used the nominal 44px item
height for positioning, while the actual GlassMenuItem widgets grew taller
via ConstrainedBox when system text was scaled up. This caused the pill to
drift out of alignment and sometimes produce a "double highlight" effect. All
layout math (_getItemOffset, _calculateIndexFromPosition, _isScrollable)
now uses the TextScaler-aware height calculation.
GlassMenuItem / GlassMenuDivider / GlassMenuLabel Light ModeThese widgets previously hardcoded Colors.white for text, icons, and divider
colours, making them invisible on light backgrounds. They now inherit from
CupertinoTheme.of(context):
| Widget | Colour Source |
|---|---|
GlassMenuItem |
theme.textTheme.textStyle.color → CupertinoColors.label |
GlassMenuDivider |
theme.textTheme.tabLabelTextStyle.color at 15 % opacity |
GlassMenuLabel |
theme.textTheme.tabLabelTextStyle.color at 45 % opacity |
Custom iconColor, titleStyle, and color parameters still take priority
over the theme default — zero breaking changes.
CupertinoApp compatibilityRemoved all Theme.of(context) dependencies from the core library, replacing them with CupertinoTheme and defaultTargetPlatform. This ensures glass widgets adapt correctly to dark mode and background colours in pure CupertinoApp structures without falling back to Material defaults.
CupertinoApp migrationAll 16 standalone demos and the main showcase app have been migrated from
MaterialApp to CupertinoApp. This is the correct root widget for an iOS 26
glass library — it provides CupertinoTheme to the entire widget tree, which
glass widgets depend on for colour resolution.
A Theme(data: ThemeData.dark(...)) builder is injected below CupertinoApp
so that any Scaffold widgets in demo pages continue to receive proper Material
theming (background colour, text defaults).
GlassMenu demo controlsAdded text-scale slider (1.0×–3.0×) and light/dark theme toggle to the menu
demo. Also added GlassMenuLabel, GlassMenuDivider, and a destructive
GlassMenuItem to the menu items so their theme colour inheritance can be
visually verified.
Replaced internal Material primitives with Cupertino-native equivalents for better iOS 26 fidelity. No public API changes.
Replaced internal Material primitives with Cupertino-native equivalents for better iOS 26 fidelity. No public API changes.
InkWell → GestureDetector + iOS-style opacity highlight
in GlassListTile and GlassActionSheet.Icons.chevron_right, Icons.add, Icons.remove, Icons.info_outline
→ CupertinoIcons equivalents in GlassListTile and GlassStepper.Colors.green / Colors.red → CupertinoColors.systemGreen
/ CupertinoColors.destructiveRed across GlassSwitch, GlassBadge,
GlassWizard, GlassDialog, GlassMenuItem, and GlassFormField.GlassScaffold with no background: widget was forcing the inner Scaffold to Colors.transparent, rendering black instead of Theme.scaffoldBackgroundColo
GlassScaffold black backgroundGlassScaffold with no background: widget was forcing the inner Scaffold to
Colors.transparent, rendering black instead of Theme.scaffoldBackgroundColor.
Added a backgroundColor: Color? parameter; the scaffold now defaults to the
theme colour when no explicit background is provided.
The dismiss × button in GlassSearchableBottomBar was hardcoded to size: 16,
which felt undersized compared to iOS 26. Default bumped to 24. Both
GlassSearchBar and GlassSearchBarConfig now expose cancelIconSize and
cancelIcon so the icon size and glyph are fully overridable.
GlassPopoverA new overlay widget that presents custom content in a glass container with
the same liquid morph animation as GlassMenu. Use it for tooltips, mini forms,
preview cards, colour pickers, or any content that should appear in a glass
popover — without being limited to a list of menu items.
GlassPopover shares GlassMenu's liquid teardrop expansion, spring physics,
auto-positioning, and screen-edge clamping. The contentBuilder receives a
close callback so popover content can dismiss itself.
GlassPopover(
trigger: GlassIconButton(
icon: Icon(CupertinoIcons.info_circle),
onPressed: null, // handled by GlassPopover
),
contentBuilder: (context, close) => Padding(
padding: EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Profile Details',
style: TextStyle(color: Colors.white, fontSize: 16)),
SizedBox(height: 12),
GlassButton(onTap: close, child: Text('Done')),
],
),
),
)
Key parameters:
contentBuilder — builder for custom popover content, receives a close callbacktrigger / triggerBuilder — the widget that opens the popover on tappopoverWidth / popoverHeight — size control (height defaults to intrinsic)alignment — manual GlassMenuAlignment, or null for auto-detectautoAdjustToScreen — screen-edge clamping (on by default, with 12px padding)barrierDismissible — whether tapping outside closes the popover (default: true)onOpen / onClose — lifecycle callbacksLiquidStretch and GlassGlow supportApps using GlassBackdropScope directly will see a deprecation warning but will compile and run without issues. Remove the widget at your convenience;…
GlassAppBar redesigned as pure layout widgetGlassAppBar has been fundamentally redesigned from a StatefulWidget with its own glass rendering to a lightweight StatelessWidget that serves purely as a layout container. This matches iOS 26's actual navigation bar pattern where the bar itself is transparent and glass effects belong on individual interactive elements.
Removed parameters:
settings — glass surface settings (the bar no longer renders glass)quality — glass quality selectoruseOwnLayer — layer isolation controlscrollController — scroll-driven glass transitionscrollEdgeThreshold — scroll fade thresholdNew parameter:
buttonSettings: LiquidGlassSettings? — provides default glass settings to all descendant GlassButton widgets via DefaultButtonSettings (a new InheritedWidget). Individual buttons can still override with their own settings.Migration:
// Before (0.13.x) — scroll-driven glass bar:
GlassAppBar(
title: Text('Title'),
scrollController: _ctrl,
scrollEdgeThreshold: 50.0,
settings: LiquidGlassSettings(blur: 15),
quality: GlassQuality.premium,
)
// After (0.14.0) — transparent layout bar:
GlassAppBar(
title: Text('Title'),
buttonSettings: LiquidGlassSettings(
glassColor: Color(0x33FFFFFF),
thickness: 20,
),
)
// Use GlassScaffold.header + headerScrollController for fading headers.
Apps that relied on GlassAppBar.scrollController for scroll-driven glass should migrate to GlassScaffold with its header / headerScrollController parameters (see below).
GlassScaffold — one-widget page layoutA brand new scaffold that replaces the manual assembly of GlassPage + Scaffold + GlassScrollEdgeEffect + Stack with a single widget. Handles z-ordering (bars always above body), edge fading, safe-area padding, and glass layer setup automatically.
header + headerScrollController + headerFadeDistanceA fixed header widget positioned below the status bar that fades out as the user scrolls — matching the iOS 26 large-title pattern used by Apple Music ("Listen Now"), Apple Podcasts, and similar apps.
GlassScaffold(
header: Text('Listen Now', style: largeTitle),
headerScrollController: _scrollController,
headerFadeDistance: 60.0,
body: CustomScrollView(
controller: _scrollController,
slivers: [...],
),
)
The header is wrapped in IgnorePointer only when fully faded (opacity == 0), so tappable elements remain interactive at full visibility.
bodyOverlaysOptional list of widgets rendered between the body and the navigation bars in z-order. Designed for floating elements like Apple Music's play-bar pill that need to render above scrollable content but below the bottom bar.
GlassScaffold(
bodyOverlays: [
AnimatedPositioned(
bottom: isMiniMode ? miniBottom : aboveBarBottom,
left: 20, right: 20,
child: PlayBarPill(),
),
],
body: scrollContent,
)
AnnotatedRegion status bar fixGlassScaffold now wraps the internal Scaffold in an AnnotatedRegion<SystemUiOverlayStyle> so that the statusBarStyle setting correctly overrides CupertinoPageRoute's own region. Previously, pages without a GlassAppBar could lose their status bar icon styling when pushed via CupertinoPageRoute.
GlassScaffold now wraps app bar and bottom bar in GlassIsolationScope(isolated: false, defaultQuality: GlassQuality.premium) instead of the previous GlassIsolationScope(isolated: true). This means bar buttons join the page-level glass blend group (shared backdrop capture) rather than creating their own layers — saving GPU cost. Stack paint order already guarantees bars render on top of body content.
ValueKeys on stack childrenAll conditional children (header, app bar, bottom bar) now have explicit ValueKeys so Flutter tracks them by identity rather than position. This prevents unmount/remount cycles (and loss of animation state) when toggling the header on and off.
See
example/lib/demos/nav_bar_patterns_demo.dartfor completeGlassScaffoldusage patterns.
GlassIsolationScope.defaultQualityGlassIsolationScope gains a defaultQuality: GlassQuality? parameter that provides a quality hint for descendants without requiring explicit quality: on each widget. This is separate from isolated — a scope can be de-isolated (for grouping) while still providing a premium quality default.
GlassThemeHelpers.resolveQuality now checks scopeDefault and skips inherited page-level quality when a scope provides a defaultQuality. This fixes a regression where bar buttons inside GlassScaffold would inherit the page's standard quality instead of getting the bar's intended premium.
DefaultButtonSettings InheritedWidgetA new InheritedWidget in glass_app_bar.dart that provides default LiquidGlassSettings to descendant GlassButton widgets. GlassButton now checks DefaultButtonSettings.of(context) as a fallback between explicit settings and inherited layer settings.
AdaptiveGlass interactive auto-promotionAdaptiveGlass now automatically promotes interactive elements (isInteractive: true) to their own compositing layer in premium mode. This gives buttons the prominent independent refraction that matches iOS 26's button design, rather than softly blending them into the page blend group. The own-layer wrapper de-isolates children via GlassIsolationScope(isolated: false) so nested glass (e.g. tab items inside a bottom bar) groups correctly with the button's layer.
GlassBottomBar extra button z-orderFixed a compositing z-order issue where the jelly tab indicator would incorrectly render behind the extra trailing button. The internal layout has been restructured from a Row to a Stack with explicit paint ordering — the extra button is painted first (bottom of z-order), and the tab indicator is painted last (top). This matches the existing pattern in GlassSearchableBottomBar.
GlassSearchableBottomBar pill z-orderFixed the paint order of the search pill and tab pill in GlassSearchableBottomBar. The search pill is now painted first (bottom of stack) and the tab pill last (top), so the glass indicator correctly renders on top when it overlaps the search pill during tab transitions.
mounted guards in gesture handlersAdded if (mounted) guards before every setState() call in TabDragGestureMixin, TabIndicator, and SearchableTabIndicator. Pointer events (onPointerDown, onPointerUp, onPointerCancel) and drag gesture callbacks can fire after the widget has been unmounted during rapid tab switching or page transitions, causing setState() called on disposed widget exceptions in debug mode. These guards are zero-cost safety nets.
Removed three GlassAppBar demo patterns that used the now-deleted scroll-driven glass API:
Added a new "Fade Header (No App Bar)" pattern demonstrating the GlassScaffold.header fade approach (Apple Music / Podcasts style).
GlassScaffoldThe main showcase app (main.dart) now uses GlassScaffold instead of manually composing GlassPage + Scaffold + bottomNavigationBar. Added a fourth tab ("Examples") and redesigned the Widgets tab with a 2-column card grid.
GlassScaffoldAll four Apple demo apps have been migrated from manual Scaffold + Stack + GlassScrollEdgeEffect composition to GlassScaffold:
GlassScaffold handles positioning, z-ordering, and edge fading automatically. 8 lines changed.GlassScaffold.header for the "Listen Now" fade header, bodyOverlays for the floating play pill. Major simplification (294 lines changed).GlassScaffold. Removed unnecessary nested Stack. 57 lines changed.GlassScaffold.bodyOverlays for the mini-player pill. 372 lines changed.All 6 showcase category pages (containers, feedback, input, interactive, overlays, surfaces) have been re-indented for consistency. No functional changes.
Fixed a critical rendering issue where GlassAppBar buttons would lose their glass background (rendering as invisible) when multiple LiquidGlassLayers shared a single root BackdropGroup (via GlassBackdropScope). On Impeller, sharing a BackdropKey across multiple RepaintBoundary layers caused the engine to bind stale or incorrect backdrop textures.
Root cause: The shared BackdropGroup architecture assumed that all glass surfaces could safely share a single GPU backdrop capture. On Impeller's tile-based renderer, this created a race between layers trying to use the same BackdropKey, causing visual corruption (backgrounds vanishing, ghost artifacts during route transitions).
Fix: Each LiquidGlassLayer now creates its own isolated BackdropGroup → RepaintBoundary subtree. This ensures every glass surface captures only its own clipped bounding box — a ~30× reduction in GPU bandwidth compared to the previous full-screen capture, and eliminates cross-layer texture conflicts entirely.
GlassBackdropScopeGlassBackdropScope is now a no-op and can be safely removed from your widget tree. Each glass layer manages its own backdrop isolation automatically. GlassPage continues to work exactly as before — no migration needed for apps using GlassPage.
Apps using GlassBackdropScope directly will see a deprecation warning but will compile and run without issues. Remove the widget at your convenience; it will be deleted in 1.0.0.
GlassInteractionSettings is now the canonical interaction APIGlassInteractionSettings (introduced in 0.13.0) is now the recommended way to configure all interaction physics app-wide. Pass it via GlassThemeData.interaction inside LiquidGlassWidgets.wrap():
runApp(LiquidGlassWidgets.wrap(
child: const MyApp(),
theme: GlassThemeData(
interaction: GlassInteractionSettings(
stretch: 0.2, // subtler drag-following globally
interactionScale: 1.03, // less scale-up on press
resistance: 0.01, // drag damping factor
anchorStretch: true, // iOS 26 rubber-band from anchor
anchorStretchSettings: AnchorStretchSettings(
intensity: 0.8,
squashFactor: 0.15,
),
),
),
));
Set stretch: 0.0 to disable drag-following app-wide while keeping press-scale. Individual widgets can still override any value via their own stretch: / interactionScale: parameters.
> ⚠ Breaking change. Apps that set a textColor and rely on the cursor implicitly matching it will see their cursor colour change to whatever their The…
AnchorStretchSettings — fine-tuned stretch feelA new configuration class for LiquidStretch that controls how widgets deform when pressed and dragged. Widgets now stretch from their anchor point toward the drag direction — matching iOS 26 button physics where the surface rubber-bands from its resting position rather than free-following the finger.
GlassButton(
anchorStretchSettings: AnchorStretchSettings(
intensity: 0.8, // more stretchy
squashFactor: 0.3, // perpendicular compression
translationDamping: 0.15, // center-shift toward finger
bounciness: 0.2, // elastic snap-back overshoot
),
)
All parameters have sensible defaults matching iOS 26 behaviour. Most developers won't need to change them.
GlassButton.ambientBaseLight — surface luminosityA subtle white overlay (default 0.08) applied during press/drag interactions. Simulates iOS 26 surface luminosity — when the directional glow tracks off-edge, the button still maintains a faint lit appearance rather than going completely dark.
GlassButton.persistPressOnDragControls whether the pressed visual state persists when the user's finger drags outside the button bounds. Defaults to true, matching iOS 26 behaviour where buttons stay visually pressed during drag-off and only release on pointer-up. Set to false for the traditional behaviour where leaving the hit-test area cancels the press.
GlassPage.settings — page-level glass configurationGlassPage now accepts an optional settings: LiquidGlassSettings parameter and internally wraps its child in an AdaptiveLiquidGlassLayer. This means all glass widgets inside the page (GlassAppBar, GlassCard, GlassButton, etc.) automatically inherit the page's glass settings — no need to set useOwnLayer: true or pass settings: to each widget individually.
GlassPage(
settings: LiquidGlassSettings(
glassColor: Color.fromRGBO(28, 28, 30, 0.8),
thickness: 30,
blur: 4,
),
child: Scaffold(...),
)
When settings is null, the layer inherits from GlassTheme or uses defaults.
GlassScaffold — comprehensive structural layerA new structural widget that coordinates interactions between GlassAppBar, GlassBottomBar, and the page body. It automatically handles edge-to-edge layout, scroll bleeding, and isolates glass rendering layers for maximum performance. This replaces the need to manually compose AdaptiveLiquidGlassLayer and GlassIsolationScope at the page root.
GlassInteractionSettings ThemeAdded a dedicated theme extension to globally configure interaction physics across all glass widgets — stretch, press scale, drag resistance, anchor stretch, and anchor stretch fine-tuning. No more passing interaction parameters to every widget manually.
GlassThemeData(
interaction: GlassInteractionSettings(
stretch: 0.2, // subtler drag-following globally
interactionScale: 1.03, // less scale-up on press
resistance: 0.01, // drag damping factor
anchorStretch: true, // iOS 26 rubber-band from anchor
anchorStretchSettings: AnchorStretchSettings(
intensity: 0.8,
squashFactor: 0.15,
),
),
)
Set stretch: 0.0 to disable drag-following app-wide while keeping press-scale. Individual widgets can still override any value.
Fixed a rendering artifact where very wide GlassButtons would exhibit severe shape distortion at the extreme edges during horizontal stretch physics.ts.
GlassSearchBarConfig.cursorColor — cursor follows Flutter theme by defaultThanks to @jfhair for PR #71. 🙏
GlassSearchableBottomBar's expanded search field now exposes a cursorColor knob via GlassSearchBarConfig, and the default behaviour aligns with Flutter convention — the cursor follows the standard theme-resolution chain (Theme.of(context).textSelectionTheme.cursorColor → CupertinoTheme.primaryColor on iOS → Theme.of(context).colorScheme.primary) rather than being hard-coupled to textColor.
Apps that want the previous behaviour — cursor matching textColor — can opt in explicitly:
GlassSearchBarConfig(
textColor: Colors.white,
cursorColor: Colors.white, // ← previously implicit
)
⚠ Breaking change. Apps that set a
textColorand rely on the cursor implicitly matching it will see their cursor colour change to whatever theirTheme.of(context).colorScheme.primaryis (typicallyColors.blueif untouched). Two-line migration above.
Premium-quality GlassButton could exhibit jagged rasterization edges during stretch deformation. The Impeller LiquidGlassLayer rasterizes at its native resolution, which doesn't perfectly align with the deformed shape boundary during stretch.
Fixed by wrapping the premium glass surface in a vector ClipPath at the shape boundary. This renders at screen resolution every frame while preserving full refraction, chromatic aberration, and 3D specular — no quality downgrade needed.
render_liquid_glass_geometry.dart now guards against zero and negative dimensions in both render() and renderAsync(). During jelly animations or rapid layout transitions (modal expansion, tab switching), matteBounds can momentarily collapse to zero dimensions — producing an invalid GPU texture request that crashes Mali drivers.
The fix returns a minimal 1×1 fallback cache for zero-dimension frames. Additionally, matte.toImageSync() is wrapped in a try/catch to handle Mali driver failures gracefully — returning a safe fallback instead of crashing the app. The next paint frame rebuilds the geometry with valid dimensions automatically.
The collapsed search button and tab indicator in GlassSearchableBottomBar used LiquidRoundedSuperellipse even when collapsed to a square. A superellipse with borderRadius: 32 on a 50×50 square has subtle flat segments between the arcs — invisible at rest but clearly distorted during stretch deformation.
Fixed by switching to LiquidOval when constraints are square (within 2px tolerance), which renders a mathematically perfect circle that stretches uniformly. During the collapse animation (when the width is still wider than the height), the superellipse is used to avoid a squashed oval appearance.
The three default theme variants have been standardised to match the thin, refractive glass aesthetic of iOS 26:
| Property | Dark | Light | Minimal |
|---|---|---|---|
| thickness | 40 → 10 | 20 → 12 | 30 → 10 |
| blur | 5 → 4 | 6 → 5 | 12 → 8 |
| lightIntensity | 1.5 → 0.7 | 1.2 → 0.85 | unchanged |
| lightAngle | unset → 135° | unset → 135° | unchanged |
| chromaticAberration | unset → 0.01 | 0.3 → 0.02 | unchanged |
⚠ Visual change. Apps using
GlassThemeVariant.dark,.light, or.minimalwithout explicitLiquidGlassSettingsoverrides will see thinner, subtler glass. This is intentional — the previous defaults were heavier than the native iOS 26 aesthetic. If you prefer the heavier look, set explicitthicknessandblurvalues in yourGlassThemeData.
GlassAppBar transparent by defaultGlassAppBar now renders a transparent navigation bar by default — no glass surface, no specular rim. This matches iOS 26's actual navigation bar pattern where the glass effect is on individual buttons, not the bar itself.
Previously, GlassAppBar always wrapped its content in an AdaptiveGlass surface with LiquidGlassSettings(blur: 15). This created a visible glass rectangle behind the title and actions — a Material-style app bar with glass paint, not an iOS 26 navigation bar. A better version of this to come next
To opt in to a glass background (e.g. for scroll-edge transitions), pass explicit settings:
// Before (0.12.x) — glass was always on:
GlassAppBar(title: Text('Title'))
// After (0.13.0) — transparent by default:
GlassAppBar(title: Text('Title')) // transparent, iOS 26 style
// Opt-in glass background:
GlassAppBar(
title: Text('Title'),
settings: LiquidGlassSettings(blur: 15, thickness: 10),
)
Additionally, quality now defaults to null (inherits from ambient scope) instead of GlassQuality.premium.
Standard/minimal quality glass surfaces now render a more refined specular inner-border rim:
_ShapeClip, creating an optically correct glass-edge reflection instead of a center-straddling stroke that bleeds outside the shape boundary.BlendMode.overlay replaces BlendMode.srcOver, so the rim reacts to the background colour underneath rather than appearing as a flat white line. Darker backgrounds produce subtler rims; lighter backgrounds produce brighter ones.borderRadius: 0 (used by GlassAppBar, GlassSideBar, GlassToolbar) no longer render the specular rim. On full-width flat surfaces, the rim looked like a Material divider line rather than an internal glass reflection.Only affects standard and minimal quality. Premium quality uses Impeller's native LiquidGlassLayer which has its own refraction-based edge rendering.
LiquidStretch.resistance defaultThe default resistance value for LiquidStretch has changed from 0.08 to 0.01. This makes all stretch interactions feel more responsive and fluid — closer to the iOS 26 native feel. The previous value was overly dampened.
All widgets using LiquidStretch without explicitly setting resistance (including GlassButton, GlassCard, GlassContainer, GlassMenu) will feel stretchier. To restore the previous behaviour:
LiquidStretch(
resistance: 0.08, // previous default
child: ...,
)
GlassButton tests: persistPressOnDrag true/false behaviour, default values, cancel paths.GlassSearchBarConfig.cursorColor tests: default null, explicit value, independence from textColor.glass_quality_adapter tests for degradeWindowCount: 2.resistance default.GlassAppBar defaults test for transparent-by-default change.GlassButton in a PIN-entry layout.Reverted to v0.12.4 — restored exact line-count and layout logic. The v0.12.6–0.12.7 changes introduced regressions (line breaks at wrong character bo
GlassTextField reverted to v0.12.4 + icon drift fixheight parameter), iconAlignment is respected as before.Thanks @g3mf0r for the detailed testing.
`iconAlignment: .end` no longer drifts under system Large Text. The Center widget wraps only the TextField, not the entire icon Row, so CrossAxisAlign
GlassTextField icon alignment (retained) + line-count regression fixiconAlignment: .end no longer drifts under system Large Text. The Center widget wraps only the TextField, not the entire icon Row, so CrossAxisAlignment.end/.start works correctly against the full container height. (retained from 0.12.6)renderBox.size.width (the v0.12.4 approach). The v0.12.6 RenderEditable width change caused line breaks to fire a couple of characters early. Thanks @g3mf0r for catching this.`iconAlignment: .end` no longer drifts under system Large Text. The Center widget now wraps only the TextField, not the entire icon Row, so CrossAxisA
GlassTextField icon alignment and line-count accuracyiconAlignment: .end no longer drifts under system Large Text. The Center widget now wraps only the TextField, not the entire icon Row, so CrossAxisAlignment.end/.start works correctly against the full container height._measureLineCount walks the render tree to find the actual RenderEditable and uses its layout width (which accounts for the internal _caretMargin ≈ 3 px). Falls back gracefully if the render walk fails.Added onClose: VoidCallback? to GlassMenu. Fires when a close is triggered (barrier tap, trigger re-tap, or item selection), before the animation comp
GlassMenu.onClose callbackAdded onClose: VoidCallback? to GlassMenu. Fires when a close is triggered
(barrier tap, trigger re-tap, or item selection), before the animation completes.
Useful for synchronising external state such as a GlassMorphController.
Thanks to @g3mf0r for the contribution (#67).
The onLineCountChanged callback silently stopped firing after the first measurement when the field was inside a fixed-height container (e.g. SizedBox(
GlassTextField layout and reactivityonLineCountChanged fires correctly under fixed-height constraintsThe onLineCountChanged callback silently stopped firing after the first
measurement when the field was inside a fixed-height container (e.g.
SizedBox(height: 46) or height: 46 on the field itself). The internal
guard used size == _lastTextFieldSize — but a fixed outer height keeps the
RenderBox size constant, so the guard always exited early after the first
call. The fix replaces the size-equality guard with a (text, constrainedWidth)
guard: the callback fires whenever the text content or available wrapping width
changes, regardless of what the outer height is doing.
This also resolves the stale-state reactivity bug where _lines stored in
State stopped updating borderRadius after re-opening the keyboard.
When height is specified (fixed-height mode), the outer padding's vertical
component was applied inside the SizedBox, pushing placeholder text and icons
downward when the user enabled a large system font. The field now strips
vertical padding in fixed-height mode and centres the text row via Align,
matching the behaviour of GlassSearchBar. The padding parameter's
horizontal values are unchanged.
bottom panel for GlassTextField and GlassTextAreaBoth widgets now accept an optional bottom widget that renders below the text
area inside the same glass card. Use it to build the "rich composer" pattern — a
text input on top with an action bar, attachment strip, or formatting toolbar
below, all sharing one glass surface:
GlassTextField(
maxLines: 5,
minHeight: 44,
maxHeight: 160,
bottom: Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
children: [
IconButton(icon: Icon(Icons.attach_file), onPressed: _attach),
const Spacer(),
IconButton(icon: Icon(Icons.send), onPressed: _send),
],
),
),
)
The panel renders inside the glass surface alongside the text area.
Callers can add a Divider between the text area and the panel if a visual
separator is desired. Not available on GlassTextField.search (that
constructor is single-line only; bottom is always null there).
`GlassSlider` thumb — increased refractiveIndex (1.15 → 1.3) and thickness (10 → 13) for a more pronounced glass lens feel. Reduced glassColor alpha (
GlassSlider thumb — increased refractiveIndex (1.15 → 1.3) and thickness (10 → 13) for a more pronounced glass lens feel. Reduced glassColor alpha (0.1 → 0.08), lightIntensity (2.0 → 1.8 premium), baseAlphaMultiplier (0.2 → 0.08 premium), and edgeAlphaMultiplier (0.4 → 0 premium) for a cleaner, more transparent thumb.GlassSwitch thumb — reduced refractiveIndex (1.15 → 1.12) and glassColor alpha (0.1 → 0.08) for subtler refraction.Opacity widget — slider thumb now uses widget-level Opacity (matching GlassSwitch pattern) instead of color alpha for the press-down fade. Critical for Impeller: properly removes the child from the compositing tree so native refraction shows through.GlassSlider jelly — replaced raw _velocity tracking with a SingleSpringController feeding buildJellyTransform. Produces smooth squash/stretch with natural deceleration and elastic bounce-back, matching the tab bar / bottom bar pill feel. maxDistortion raised (0.25 → 0.6), velocityScale lowered (30 → 2) to account for the spring's normalised 0→1 position range.Three community-requested features for GlassTextField (and GlassTextArea):
GlassTextField enhancementsThree community-requested features for GlassTextField (and GlassTextArea):
height, minHeight, and maxHeight give direct control over the field's dimensions — no wrapping SizedBox needed:
// Fixed height — matches GlassSearchBar's 44pt:
GlassTextField(height: 44, placeholder: 'Search')
// Constrained range — grows with content:
GlassTextField(minHeight: 44, maxHeight: 200, maxLines: 10)
height is mutually exclusive with minHeight/maxHeight (assertion enforced).
onLineCountChanged callbackFires whenever the number of rendered lines changes (accounting for text wrapping, not \n characters). Also fires on initial build. Uses the TextField's own RenderBox height — no external TextPainter math, so it works correctly with text scaling and system accessibility settings.
GlassTextField(
maxLines: 6,
onLineCountChanged: (lines) {
setState(() => _borderRadius = lines > 1 ? 8.0 : 20.0);
},
)
iconAlignment parameterControls where prefix/suffix icons sit when the field spans multiple lines:
// Pin send button to bottom — chat composer pattern:
GlassTextField(
maxLines: 6,
iconAlignment: CrossAxisAlignment.end,
suffixIcon: Icon(Icons.send),
)
Accepts CrossAxisAlignment.start (top), .center (default), or .end (bottom). No visible effect on single-line fields.
All three features are forwarded through GlassTextArea.
GlassTextField.search named constructorA new convenience constructor that pre-configures GlassTextField with compact search-bar defaults: height: 44, iconSpacing: 8, padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), borderRadius: 22, and textInputAction: TextInputAction.search. Eliminates the boilerplate previously required to match GlassSearchBar visuals:
GlassTextField.search(
placeholder: 'Search messages…',
prefixIcon: Icon(CupertinoIcons.search, size: 20),
useOwnLayer: true,
)
GlassSearchBar now uses this constructor internally, reducing duplicated decoration code.
onLineCountChanged now fires on programmatic controller changes — Previously the callback only responded to physical keyboard input (TextField.onChanged). Setting controller.text = '...' or calling controller.clear() (e.g. in a chat "Send" handler) did not re-measure the line count. The widget now actively listens to the TextEditingController and re-measures on any text mutation.
Suffix icon spacing now respects iconSpacing — The gap before the suffix icon was hard-coded to 12px while the prefix icon correctly used widget.iconSpacing. Both sides now use the same parameter.
LightweightLiquidGlass and _FrostedFallback previously wrapped their glass surface in ClipPath(ShapeBorderClipper(...)). When the parent was an iOS Pl
LightweightLiquidGlass and _FrostedFallback previously wrapped their glass surface in ClipPath(ShapeBorderClipper(...)). When the parent was an iOS PlatformView (e.g. mapbox_maps_flutter MapWidget, video_player on iOS), the descendant BackdropFilter's rectangular blur output leaked past the rounded clip — visible as a faint square halo around the rounded glass shape, most obvious when light backdrop content scrolled underneath.
Flutter framework PR #177551 (merged Dec 2025, shipped in 3.41.0-0.0.pre+) fixed this at the engine level by forwarding ClipRRect clip data to the iOS PlatformView mutator stack — but only ClipRRect, not ClipPath, even when the path inside is mathematically a rounded rect.
This release routes shapes that resolve to a RoundedRectangleBorder (i.e. LiquidRoundedSuperellipse, LiquidVerticalRoundedSuperellipse) through ClipRRect instead of ClipPath. The engine fix now triggers and the halo disappears for those shapes.
LiquidOval is intentionally NOT routed through ClipRRect — empirically the engine fix doesn't forward ClipRRect with circular(double.infinity) nor a LayoutBuilder-computed finite radius on the LiquidOval path. Callers that need a halo-free circular glass surface over a PlatformView should use LiquidRoundedSuperellipse(borderRadius: size / 2) instead, which renders identically on a square widget and triggers the engine fix.
Closes upstream Flutter #175048 and #115926 for liquid_glass_widgets users.
wrap() now accepts an optional GlassThemeData? theme parameter. When provided, it wraps the child in a GlassTheme — eliminating the need for a separat
LiquidGlassWidgets.wrap() — theme parameterwrap() now accepts an optional GlassThemeData? theme parameter. When provided, it wraps the child in a GlassTheme — eliminating the need for a separate GlassTheme widget in your tree.
LiquidGlassSettings.standardOpacityMultiplierA new multiplier applied to the glass colour alpha when rendering in Standard mode. This allows tuning Standard 2D compositing opacity to achieve more parity with Premium 3D volumetric refraction without needing separate colour values for each mode.
LiquidGlassSettings(
glassColor: Colors.white.withValues(alpha: 0.3),
standardOpacityMultiplier: 0.4, // Standard renders at 0.3 × 0.4 = 0.12 alpha
)
Defaults to 1.0 (no change). Fully interpolated via LiquidGlassSettings.lerp() and wired through copyWith().
GlassPage — full-screen glass scaffoldA new full-screen scaffold widget for glass-based layouts. Handles background imagery, status bar styling, and background sampling in a single widget.
enableBackgroundSampling defaults to true when a background widget is provided, and false otherwise — so the common case just works without extra configuration.
GlassPage(
background: Image.asset('assets/wallpaper.jpg'),
child: Scaffold(...),
)
glass_page.dart now uses a show clause: only GlassPage and GlassStatusBarStyle are exported (internal state classes are no longer public).liquid_glass_scope.dart now uses a show clause: only LiquidGlassScope, GlassBackgroundSource, and GlassRefractionSource are exported.lightweight_glass.frag)The Standard-tier lightweight shader composite logic has been improved for closer visual parity with the Premium Impeller path. Shader rim constants are unchanged from 0.11.0 — AdaptiveGlass normalization now handles Premium → Standard scaling in Dart space instead:
applyGlassColorLW() — a luminosity-preserving glass tint that matches Premium's colour handling for both chromatic (mint, bronze) and achromatic (white, grey) glass colours.ambientStrength × 0.25 + 0.08 creates the glass shadow effect that visually separates glass from non-glass, matching what Premium achieves through blur compositing.mix(bgRgb, white, 0.7) brightens the background at the edge, matching Premium's getHighlightColor.smoothstep edge zone with quadratic falloff — the same proven approach as interactive_indicator.frag but scaled for containers. Zero transcendentals (polynomial smoothstep + multiplies only). Flat interior pixels naturally produce zero offset. Currently active on surface widgets (GlassBottomBar, GlassTabBar, GlassSideBar, GlassToolbar) when a backgroundKey is provided; GlassCard and GlassButton use PATH B and will benefit once AdaptiveGlass gains scope-aware background key passthrough.uGlowIntensity press-feedback now applies in PATH A, closing an architectural gap where switch/slider thumbs inside background-sampled containers had no glow.+vertCoord × 0.04) in both PATH A and PATH B creates a natural 3D anchored depth feel, simulating light entering from above. Cost: one multiply + one add per fragment.glassColor.a = 0 (Premium default), preventing invisible glass in SrcOver compositing.0.15 × directionalInfluence × lightIntensity term adds subtle lit-side variation on top of the constant rim base — matching how Premium's 3D bevel naturally brightens toward the light source.GlassSwitch standard thumb: baseAlphaMultiplier: 0.0, edgeAlphaMultiplier: 0.15 — fully transparent body with subtle edge presence for a cleaner glass look.GlassSlider standard thumb: baseAlphaMultiplier: 0.08, edgeAlphaMultiplier: 0.1 — minimal body opacity with soft edge glow.Removed the arbitrary +0.2 alpha boost on elevated widgets inside AdaptiveGlass. Elevation is now expressed purely through the shader's densityFactor physics, making the opacity response predictable and proportional to user settings.
GlassEffect)Standard-tier interactive indicators (slider thumbs, switch thumbs, segmented control pills) now apply the same normalisation as AdaptiveGlass — thickness × 0.4, lightIntensity × 0.6 — preventing the 2D shader from rendering these elements heavier than their Premium counterparts.
example/assets/mountain_landscape.jpg) — eliminates network dependency and first-frame loading flash.This release introduces the Liquid Morph Engine — a standalone, reusable physics and animation system for iOS 26-style liquid glass morphing. It lives
This release introduces the Liquid Morph Engine — a standalone, reusable physics and animation system for iOS 26-style liquid glass morphing. It lives in lib/engine/ and is fully decoupled from any specific widget.
GlassMenu is the first consumer of the engine. Future widgets (sheets, cards, buttons) will use the same engine to achieve consistent, physics-correct liquid glass transitions throughout the library.
Documentation:
docs/LIQUID_MORPH_ENGINE.md— full guide coveringGlassMorphController,LiquidMorphState,LiquidMorphPhysics, and how to integrate the engine into your own custom widgets.
| Type | Role |
|---|---|
GlassMorphController |
Lifecycle owner — manages the spring, exposes open() / close() |
LiquidMorphState |
Immutable value object — one per frame, contains all render values |
MorphPhase |
Semantic lifecycle enum — tells you where in the animation you are |
MorphSpeed |
Enum — controls spring stiffness without exposing raw physics constants |
LiquidMorphPhysics |
Internal stateless math engine |
Two conceptual "blobs" drive every morphing animation:
The SDF metaball shader creates the teardrop neck between the blobs automatically — there is no explicit neck geometry. LiquidMorphPhysics.compute() determines each blob's position, scale, and blend factor on every frame.
GlassMenu — first engine consumerSee it live: The Apple Messages demo (
cd example && flutter run -t lib/apple_messages/apple_messages_demo.dart) showcases the morphing engine in a real-world context — tap the menu or Edit button at the top of the screen to trigger theGlassMenuwith full teardrop open/close physics.
ζ = 1.0) on all spring controllers prevents oscillation on rapid successive opens.interactionScale, stretch, and stretchResistance integrate into the morphing path via the same spring solver used by LiquidStretch.GlassMenu — safe area / notch clipping on iOS and Android · Menu position and maximum height were computed from MediaQuery.padding, which is consumed by ancestor SafeArea widgets and reports 0 inside a fully-safe tree. Switched to View.of(context).padding (raw hardware insets) so the menu is always clamped correctly regardless of SafeArea nesting depth. Fixes the menu appearing under the Dynamic Island on iPhone 14 Pro and similar devices.
GlassMenu (scrollable) — scrolling now works on large menus · Menus with more items than fit on screen can now be scrolled reliably.
demos/ suiteThe example/ package has been reorganised for a cleaner public-facing demo experience:
New example/lib/demos/ folder containing seven self-contained, copy-pasteable demos:
glass_menu_demo.dart — all 9 GlassMenuAlignment positions, scrollable item listglass_tab_bar_scrollable_demo.dart — scrollable GlassTabBar with dynamic tab addglass_modal_sheet_demo.dart — all sheet states (peek / half / full), Apple Maps peek styleglass_bottom_bar_demo.dart — magic-lens masking with GlassBottomBarbottom_bar_tab_width_demo.dart — tabWidth on both bar variants side-by-sidesearchable_bar_demo.dart — GlassSearchableBottomBar edge casesshape_debug_demo.dart — GlassButton shape visualiserApple Messages demo (example/lib/apple_messages/) — showcases the Liquid Morph Engine in a full real-world context; tap the menu or Edit button at the top to trigger GlassMenu.
example/lib/modal_sheet_showcase/ removed (file moved to demos/glass_modal_sheet_demo.dart).
Experimental scratchpad scripts moved to git-ignored example/lib/playground/.
Thanks to @g3mf0r for PR #55. 🙏
Thanks to @g3mf0r for PR #55. 🙏
GlassMenu — menuAlignment enum · A new GlassMenuAlignment enum (10 values: none, topLeft, topCenter, topRight, centerLeft, center, centerRight, bottomLeft, bottomCenter, bottomRight) lets you pin the menu to a specific edge or corner of its trigger instead of relying solely on auto-detection. The enum is now part of the public API surface exported from glass_menu.dart.
GlassMenu — autoAdjustToScreen with menuPadding · When autoAdjustToScreen: true, the new menuPadding: EdgeInsets? parameter applies additional inset constraints so the menu body never clips against device edges.
GlassMenu — itemBorderRadius · Controls the corner radius of individual menu item cells, independent of the outer menuBorderRadius.
GlassTabBar — multi-tab drag jump · Dragging the indicator across multiple tab widths in a single gesture now snaps to the correct distant tab. The previous implementation only incremented/decremented by ±1 regardless of drag distance, causing the indicator to teleport unexpectedly when the finger crossed more than one tab boundary.
GlassTabBar — glass refraction during indicator drag · Refraction and shadow effects are correctly suppressed during the drag animation and restored on settlement, eliminating a visual glitch where the glass distortion would persist after releasing the indicator.
GlassMenu tests covering GlassMenuAlignment enum values, menuAlignment parameter, autoAdjustToScreen + menuPadding, and itemBorderRadius.GlassTabBar tests covering multi-tab drag jump (left and right) to prevent regression of the PR #55 fix.Thanks to @g3mf0r for PR #54. 🙏
Thanks to @g3mf0r for PR #54. 🙏
GlassTabBar (scrollable) — jelly physics on indicator drag · The scrollable indicator pill now feeds real-time drag velocity into the liquid glass shader, producing the same organic stretch-and-settle effect that fixed-mode tabs already had.LiquidGlassWidgets.wrap — adaptiveQuality: true without adaptiveConfig permanently locks to standard · The default fallback config was created with initialQuality: GlassQuality.standard, which the adapter treats as a skip-Phase-2 signal — immediately jumping to Phase 3 at standard without ever running the warmup benchmark. On capable devices (including the iPhone simulator on Apple Silicon) this prevented the adapter from ever discovering that the device can sustain premium. Fixed by removing the erroneous initialQuality from the fallback; Phase 2 now always runs when no explicit quality is provided.
GlassTabBar (scrollable) — indicator overflows bar on low tab counts · The right drag boundary was computed as viewMax instead of viewMax - indicatorWidth, allowing the pill to slide outside the bar when there were only 2–3 wide tabs. Corrected to viewMax - targetWidth.
GlassTabBar (fixed) — tiny accidental drags switch tabs · Tab switching on drag-end now requires either a displacement greater than 20 % of the tab width or a flick velocity above 400 px/s, preventing unintended switches from small incidental movements.
GlassTabBar (scrollable) — flick gesture ignored in scrollable mode · A horizontal flick with sufficient velocity now overrides the nearest-tab distance calculation and advances the indicator in the flick direction, matching the fixed-mode behaviour.
Thanks to @g3mf0r for PR #52. 🙏
Thanks to @g3mf0r for PR #52. 🙏
GlassTabBar — indicator drag drift on desktop/web · The indicator position was accumulated via delta.dx additions each frame, causing the pill to visually lag behind the pointer on desktop platforms where pointer events arrive at a higher frequency than the frame budget. Fixed by computing position from the absolute global pointer coordinate on every update event, eliminating accumulated drift.
GlassTabBar (scrollable) — tab labels hidden behind indicator pill · The SingleChildScrollView (tab labels) and the background pill were inserted in the wrong stack order — labels were painted first, then the pill on top, obscuring them. Fixed by inserting the pill before the labels so labels always paint above the pill (correct z-order).
GlassTabBar (scrollable) — indicator fly-off past bar edges · The indicator pill had no boundary clamping in scrollable mode, allowing it to animate outside the visible bar area. Drag offset is now clamped to [scrollOffset - 35 %, scrollOffset + screen + 35 %].
DividerSettings — new optional dividerSettings parameter on GlassTabBar. Renders animated vertical dividers between tabs with configurable thickness, indent, endIndent, custom decoration, animation duration/curve, and an isHideAutomatically flag that fades out dividers adjacent to the active tab. Includes a copyWith helper for convenient inline customisation.
Grab-to-drag in scrollable mode — the indicator pill in scrollable mode can now be directly grabbed and dragged to a new tab. Uses a GestureArenaTeam (HorizontalDragGestureRecognizer as captain + TapGestureRecognizer) to correctly win the arena against the SingleChildScrollView when the initial touch is within the active indicator's bounds. The scroll view retains natural scrolling behaviour when touching outside the pill.
indicatorShadow — new optional indicatorShadow: List<BoxShadow>? parameter on GlassTabBar. Applies a drop shadow to the resting (solid-colour) indicator pill, improving contrast in light-mode themes where the pill and track share similar colours. The shadow is automatically suppressed during the liquid glass drag animation so it does not interact with the backdrop blur, and restored when the pill returns to its idle state.
Thanks to @yukinoaruu for PR #51. 🙏
Thanks to @yukinoaruu for PR #51. 🙏
GlassMenu — trigger button dead zone after closing · After closing the menu, the trigger button would ignore taps for the duration of the closing spring animation (~95% of travel), forcing the user to wait several seconds before being able to reopen it. Fixed by separating the visual-hide threshold (0.05) from the input-block threshold (0.80) into two independent booleans: isButtonVisible and isMenuBlocking. The button now becomes tappable again as soon as the animation drops below 80%, and the morphing glass overlay wraps in IgnorePointer(ignoring: value < 0.8) to prevent the contracting container from consuming the tap instead.`GlassBottomBar` — `extraButton` causes bar to float in the middle of the screen · Wrapped the inner Row in a SizedBox(height: barHeight) so the Scaff
GlassBottomBar — extraButton causes bar to float in the middle of the screen · Wrapped the inner Row in a SizedBox(height: barHeight) so the Scaffold.bottomNavigationBar slot always receives an explicit tight height constraint. Without this, the Expanded child introduced by extraButton propagated an unbounded height through LiquidGlassLayer, causing Flutter to render the bar centred on screen instead of pinned to the bottom edge.`LiquidGlassWidgets.respectsAccessibility` — deprecated alias added pointing to respectSystemAccessibility. Will be removed in v1.0.
SearchableBottomBarController — added openSearch(), closeSearch(), and isSearchOpen getter for programmatic search control. Previously the only way to open search was by driving isSearchActive from parent state.GlassTabBar — added maskingQuality parameter (MaskingQuality.high / MaskingQuality.off), matching the existing GlassBottomBar API. Set to off to disable the 8 px jelly-bloom expansion on lower-end devices.GlassSlider — added interactionBehavior, glowColor, and glowRadius for consistent drag-glow customisation across all interactive widgets.GlassSegmentedControl — same interactionBehavior, glowColor, glowRadius params added for API parity with GlassSlider and GlassTextField.LiquidGlassWidgets.respectsAccessibility — deprecated alias added pointing to respectSystemAccessibility. Will be removed in v1.0.GlassTextField — fixed a use-after-dispose crash when focusNode cycled null → external → null. The widget now tracks ownership with an explicit _ownsNode flag and correctly creates a fresh internal node on each transition.GlassTabBar — resolved scrollable-mode visual glitches. The indicator now stays perfectly glued to the active tab during scrolling without drifting, uses native "snappy" spring physics for consistent feel, and implements a three-layer rendering architecture so the solid indicator pill cleanly clips at the rounded viewport corners while the 8px jelly bloom expands freely over the tab bar boundaries.A huge, heartfelt thank-you to @yukinoaruu for PR #49. 🙏
A huge, heartfelt thank-you to @yukinoaruu for PR #49. 🙏
We made a mess of the original 0.10.3 merge of his work — introducing regressions that broke the very things he had so carefully built. He came straight back, fixed every issue, and did it with incredible patience and generosity. This release is entirely his. If you are enjoying GlassMenu, it is because of him.
_cachedWrappedItems mechanism that was accidentally dropped. The previous merge caused the entire wrapped-item list to be recreated on each pointer move, resetting pressed/hover states mid-gesture and tanking performance on menus with many items.ValueNotifier system (_hoveredIndexNotifier, _isDraggingNotifier). Individual menu items now rebuild in isolation instead of triggering a full setState on the entire menu tree, keeping animations at a steady 60 fps.GlassMenuItem painted its own hover fill on top of the sliding pill, producing a faint ghost ring. Selected items now transition to Colors.transparent instantly.onTap or closes the menu. The pill highlight correctly skips disabled items during pointer tracking.onTap firing · Removed a redundant onTap callback in the internal wrapped-item builder that was causing every selection to fire twice.RangeError when item list shrinks while open · didUpdateWidget now clears _hoveredIndex when items.length decreases, preventing an out-of-bounds crash when the pill tried to measure a deleted item.Opacity(0.4) to match the design spec and test expectations.title/child API · GlassMenuLabel now accepts either a title String (rendered as stylised uppercase) or an arbitrary child Widget, enabling diverse non-interactive content beyond simple section headers.height default · Default height set to 30.0 so the selection pill cannot drift when items with non-standard font sizes are mixed in.glowIntensity parameter · Added glowIntensity and wired it through to GlassContainer, completing the full interaction-glow parameter surface.glowOnTapOnly default corrected · Default changed to true to prevent a permanently stuck glow artefact during scroll and drag gestures.allowPositiveXStretch / allowNegativeXStretch / allowPositiveYStretch / allowNegativeYStretch to allowPositiveX / allowNegativeX / allowPositiveY / allowNegativeY to align with the LiquidStretch API surface.RepaintBoundary nodes that were leaving descendant glass layers DETACHED from the compositor scene, and moved GlassGlow inside GlassContainer's clip subtree to prevent glow bleed onto the background.GlassMenu stretch parameter renamesThe four optional stretch-axis override parameters introduced in 0.10.3 have been renamed:
| 0.10.3 name | 0.10.4 name |
|---|---|
allowPositiveXStretch |
allowPositiveX |
allowNegativeXStretch |
allowNegativeX |
allowPositiveYStretch |
allowPositiveY |
allowNegativeYStretch |
allowNegativeY |
All four remain optional with null defaults (auto-inferred from menu position). Only code explicitly passing the old names needs updating.
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →