liquid_glass_easy
A Flutter package that provides real-time liquid glass lens effects with distortion, magnification, refraction, and blur for stunning UI visuals.
4.1.1
10K downloads/mo
#2912 most downloaded on pub.dev
AhmeedGamil/liquid_glass_easy
What this package is like to depend on
Last release 3 days ago
20 Aug 2026
Ships unpredictably
gaps range from 8 days to 5 months
Nearly every release is documented
notes for 17 of 17 stable releases
Nothing withdrawn
no release was ever pulled
9 months old
17 releases · first in 2025
17 releases in the last 12 months
see the full history below
Release timeline
17 releases · Nov 2025 to Aug 2026Releases
latest 17-
4.1.120 Aug 2026Release notes
Open source →- Fixed — on Impeller, the content seen through a lens wobbled while the lens moved or squashed. A lens on the Impeller path samples the live backdrop through backdrop passes, and the engine places each pass inside its clip's bounds rounded to whole physical pixels, every frame. A moving pill's box lands on a different fraction of a pixel every frame — and a stretch/squash moves each edge independently — so the rounding re-framed the refracted content frame to frame, while a still lens was rock solid. The shader pass no longer clips to the outline: it clips to a rect padded past the lens box and snapped outward to whole physical pixels, so its bounds only ever step by exact texels. The outline itself is still drawn by the shader from the fractional geometry, so a squash animates as smoothly as it always did, and the blur keeps its exact outline clip (blur fills whatever clip it is given). Nothing changes on the Skia path. One visible consequence: the glass's edge now keeps the outer half of its anti-aliasing ramp that the outline clip used to trim — about half a logical pixel softer.
- Fixed — the slider's rest pill showed a hair of glass around it on Impeller.
LiquidGlassMotionPilldrew itscover(the slider's white rest pill) as the lens's child, clipped to the lens box — so once the glass's edge reached past the outline (above), a thin ring of refracted content peeked out around the rest pill. The cover is now its own layer above the lens, sized to the glass's visible extent — the deformed box plus that half-pixel reach on the Impeller backdrop path, nothing extra on Skia — and clipped to the matching outline with the live stretch, so at rest it hides the glass completely and still deforms as one body with it. Only hosts that pass acoverare affected; in the package that is the slider.
-
4.1.020 Aug 2026Release notes
Open source →- New —
LiquidGlassFab: the floating action button, in glass. A circular button built from aniconor anychild, plusLiquidGlassFab.extended(...)for the icon-and-label pill, carrying the Material fields you would reach for —heroTag,tooltip,foregroundColor,iconSize,padding— and one Material has no need of:visibility, which animates the glass away instead of dropping the widget out of the tree. The look ships tuned asLiquidGlassFab.defaultStyle(a faint white tint, a small blur, gentle refraction) with its contact shadow ridingappearance.shadow, like every other lens's; astyleyou pass is merged over that default, so one facet changes without retyping the rest, and an appearance carrying no shadow drops the shadow. - New — glass dialogs:
showLiquidGlassDialog,LiquidGlassAlertDialog,LiquidGlassDialog. The presenter pushes throughshowGeneralDialogand carries the ambient themes across withInheritedTheme.capture;LiquidGlassAlertDialoglays the familiar icon / title / content / actions rows onto a glass card (every padding, text style and overflow knob of Material's exposed), andLiquidGlassDialogis the bare card for anything else. Both shareLiquidGlassDialog.defaultStyleand merge over it. The entrance is scale only — deliberately no fade: anOpacitylayer isolates a lens's backdrop, so a fading dialog loses the page behind it mid-flight (no frost, dark buttons) and snaps in when the layer drops. The barrier fades on its own and the glass condenses over it, live the whole way. - New — the scaffold has a
floatingActionButtonslot, and it clears the bar it can see. The button sits in the lens layer with the other slots, so its glass refracts the body on both backends.floatingActionButtonAlignmentdefaults toAlignmentDirectional.bottomEnd— bottom-right in LTR, bottom-left in RTL — and takes any alignment, physical or fractional. When the button is aligned toward the bottom the scaffold measures its ownLiquidGlassTabBarand lifts the button clear of it; a bar it cannot measure from there falls back to a fixed clearance, andfloatingActionButtonClearanceoverrides either. With the default alignment it shares the corner withbottomNavigationBarAction, so use both only when a bar sits between them. - New —
LiquidGlassScaffold.dialog: a glass panel that never leaves the view.showLiquidGlassDialogpushes a route; this is the other answer — an ordinary widget in the scaffold's lens layer, so it refracts the livebodyon every backend and aLiquidGlassBlendercan merge it with the other slots' glass. What it trades away is the navigator: no route, noFutureto await, so the page owns the open state and clears the slot to close (onDialogDismissedfires on a barrier tap or a system back gesture). The layer keeps painting through the exit and only then lets the panel go, and the scrim fades on its own —dialogBarrierColor,dialogBarrierDismissibleanddialogTransitionDurationtune the rest. - Fixed — a dialog's glass came out flat on the Skia capture path. A dialog builds in the navigator's overlay, not inside the
LiquidGlassViewthat captured the page, so its lens had nothing to sample and fell back to frost. The view now plants a portal around its whole subtree that rides along withInheritedTheme.capture— the same tripThemealready makes — and unpacks a real scope inside the route, so a dialog opened from a context inside the view refracts the page behind it. The portal is not a scope itself, so a lens down in the captured background still finds nothing, as it must: it would be refracting a picture of itself. Impeller never cared — it was sampling the live backdrop all along. - Fixed — a lens under an animated ancestor transform drew its glass for the untransformed rect (Impeller). The Impeller shader paints in screen space from uniforms — top-left via
localToGlobal, size from layout — while the clip is a widget that transforms with the tree, so aScaleTransitionover a lens (the dialog entrance) scaled the clip but not the shader's geometry: the border, the outermost band of what the shader draws, fell outside the smaller clip and was cut until the scale reached1.0. The lens now reads its full global transform (the tracking layer already watched it) and, when an ancestor carries real scale or rotation, hands the shader a lens→shader affine map (u_xformRow/u_xformOff, main shader only): the fragment maps through the inverse so ALL geometry — outline, corners, band, rim — evaluates in lens space, and the refracted position maps forward again so the backdrop is sampled in screen space. Scale, rotation, even mirroring now land the glass exactly on the widget clip at every frame. Translation-only lenses — every lens that merely sits somewhere — keep the legacy screen-space uniforms bit-identical, and the Skia path (already local-space by construction) passes identity. One property to know: the border's light direction now rides the lens, so a rotated lens rotates its glint with it.
- New —
-
4.0.016 Aug 2026Release notes
Open source →- Breaking — the navigation components take their real names:
LiquidGlassTabBarandLiquidGlassSwitch. The bottom nav bar is what iOS calls a tab bar, and the package now calls it that:LiquidGlassBottomNavBar→LiquidGlassTabBar(including.withImpeller), with the style groups cascading —LiquidGlassNavItemStyle→LiquidGlassTabItemStyle,LiquidGlassNavPillStyle→LiquidGlassTabPillStyle,LiquidGlassNavMagnifierPillStyle→LiquidGlassTabMagnifierPillStyle,LiquidGlassBottomNavBarLayout→LiquidGlassTabBarLayout. The old floating 2–5-tab capsule that held theLiquidGlassTabBarname is deleted (with its never-exported animated variant): the bar covers its use cases and places anywhere viaalignment/margin. The toggle takes the name the ecosystem uses —Switchin Flutter,UISwitchin UIKit:LiquidGlassToggle→LiquidGlassSwitch(withLiquidGlassSwitchLayout). Migration is a find-and-replace; no parameter changed meaning, andLiquidGlassTabBarItem/LiquidGlassTabBarActionalready carried the tab-bar name. Entries below use the new names throughout. - Breaking — the
.customconstructors are gone; custom content moves to the main constructors..customexisted only to relax onerequiredparameter without breaking the API; with this cycle already breaking, the parameter is simply relaxed at the source instead.LiquidGlassButton.label,LiquidGlassTabBarAction.icon,LiquidGlassTabBarItem.icon(and the same on the internal dock/app-icon/control-tile) are now optional, each guarded by a debug assert that the component was given something to show — a label, an icon, a builder, or achild. Migration is mechanical: delete the.customsuffix (LiquidGlassButton.custom(child: …)→LiquidGlassButton(child: …),LiquidGlassTabBarItem.custom(iconBuilder: …)→LiquidGlassTabBarItem(iconBuilder: …)); every parameter keeps its name and meaning. The never-renderedkLiquidGlassCustomGlyphplaceholder dies with them. The item deliberately keeps builders rather than gaining achild— the reveal tiers render each tab twice per frame, which is precisely what a builder can do and one widget cannot. - Breaking — the jelly family is removed; the spring integrator survives. Nothing in the package ran on it anymore: the slider thumb, the switch and the tab bar's moving pill all deform through the acceleration model (
LiquidGlassLensMotionSpec), and the flex touch response has its own springs. Gone: theLiquidGlassJellywidget,LiquidGlassJellyConfig/LiquidGlassJellyStyle, the drag-drivenLiquidGlassJellySpringsimulation, and the internal (never exported)LiquidGlassPillJellydescriptor and jelly resolver. The one thing all of them shared — the 240 Hz sub-stepped underdamped spring step — is exactly what stays:liquidGlassSpringStep, now inliquid_glass_spring.dart, still public and still the integrator every component steps its springs through. - Changed —
LiquidGlassSliderandLiquidGlassSwitchship the tuned look instead of asking for it. Both controls looked unfinished out of the box: the thumb wore a milky tinted glass with a real1.5 pxblur, and it cast nothing, so it read as painted onto the track rather than lying in it. Every showcase therefore restated the same style by hand. That style is now the default, exposed asLiquidGlassSlider.defaultStyleandLiquidGlassSwitch.defaultStyleso one facet can be changed without retyping the rest (defaultStyle.copyWith(refraction: …)). It is a clear pill — refraction and a soft rim, no tint — with the blur down to0.5, since at thumb size a real blur turns the magnified content to mush while the refraction reads sharper without one. Both also default to a tucked-in contact shadow (LiquidGlassShadow(inset: 3), the glass overhanging its own shadow, since at this size a full-width halo reads as a glow rather than contact) — and it rides in the style, ondefaultStyle.appearance.shadow, rather than as a parameter beside it: a contact shadow is part of how a surface looks, so it travels with the rest of the look wherever the style goes. Retune it withdefaultStyle.copyWith(appearance: defaultStyle.appearance.copyWith(shadow: …)), or hand over an appearance carrying none to drop it. Neither control takes ashadow:parameter. Two numbers move with them: the slider'sinactiveColorgoes from white at 23 % to black at 8 %, which is the value that reads on a light page — over a dark one, pass a translucent white as before — andLiquidGlassSwitchLayout.pinchedHeightgoes from20to22, a shade less pinch in the slice behind the glass. Anything passing its ownstyle/inactiveColor/layoutis unaffected. - New —
widthandheightonLiquidGlassSliderandLiquidGlassSwitch. Both sizes were reachable only through the layout descriptor (layout: LiquidGlassSliderLayout(width: 320)), which is the first thing anyone wants to set and the last place they look for it — the components size themselves from their own layout, so wrapping one in aSizedBoxdoes nothing and there was no visible way in. Both controls now takewidthandheightdirectly, as a shorthand for the two same-named layout fields; they win over the layout when both are given, andnull(the default) leaves it in charge, so nothing existing renders differently. The layout keeps everything else — the thumb pair, the track thickness, the icons, the pinch — and stays the way to state the whole geometry at once. Two boundaries worth knowing, both documented on the parameters: on the slider,heightis vertical room (the lifted thumb overhangs the track and grows further as it squashes, and all of it has to fit inside the glass capture) rather than thumb size, and leaving itnullderives the safe value; on the switch, the two size the track, not the thumb, so a switch that should grow in proportion wantsLiquidGlassSwitchLayout().scaled(…)instead. - New — a contact shadow that belongs to the material:
LiquidGlassAppearance.shadow. The appearance group now carries an optionalLiquidGlassShadowdescriptor, honored byLiquidGlassLens: the lens wraps itself in the ring it describes, corner defaulting to the lens shape's own radius, visibility composing with the lens'svisibility. Because every component builds its surface from an appearance, the shadow rides along wherever a style goes. The wrap sits inside the flex deformation's box, so on a lens withtouch:the ring is not frozen on the rest silhouette — press and it swells, leans and springs back with the body, exactly as the glass does, at no extra cost on an untouched lens.LiquidGlassShadowstays a standalone component, unchanged, for wrapping anything by hand. Two boundaries: a shadow is a descriptor, not an animation, so the moving pill's material lerp carries the nearer end's shadow rather than blending two rings; and appearance shadows are not supported inside aLiquidGlassBlender— a merged metaball silhouette has no single ring to cast. The tab bar authors both of its shadows here too — the bar capsule's on itsstyle's appearance (see the capsule entry below), the moving pill's on its glass style'sappearance.shadow; neither is a flat parameter, and the bar hoists the pill's descriptor into its own tracked ring (scaled with the outline, faded with the rim), which a static wrap could not do. - Changed — the glass pill ships its tuned look, and its shadow lives in the appearance.
LiquidGlassTabPillStyle's defaults are now the flagship look instead of a placeholder: the pill is pure refraction — no tint of its own, since over glass a fill only flattens the capsule — through a thin-rimmed continuous capsule (LiquidGlassShape(borderWidth: 0.5)), withdistortion0.06 → 0.04,distortionWidth10 → 12,growHeight12 → 9, the rest fillcolora barely-there grey (0x2EAEAEB2, was0x26FFFFFF), and a default contact shadow — a soft ring spreading past the rim (LiquidGlassShadow(blur: 9, opacity: 0.3), no inset tuck) riding the default glass appearance. The pill's shadow is authored where every lens's is: on the glass style'sappearance.shadow— there is noshadow:parameter beside the style; aglassStylewhose appearance carries its own shadow replaces the default, and one carrying none drops it. The bar hoists the descriptor into the pill's own tracked ring rather than letting the inner lens wrap a frozen one, so the ring still rides the squash and fades with the rim. And the tier itself is no longer an opt-in:modenow defaults toLiquidGlassPillMode.both— the glass-refracting pill on every renderer, which the sleep-at-rest work makes an honest default (a settled bar costs no shader pass, no clip and no capture on either backend; Skia pays only during the travel itself).LiquidGlassPillMode.impellerOnlyremains the degrade-gracefully choice for weak Skia/Web targets, andnonethe fully flat tier. - Changed — the switch's thumb is a
LiquidGlassLens, and its contact shadow is an appearance shadow. The thumb was the last mainline glass on the classic position-driven engine — abuildLiquidGlassMorphPillconfig in the switch's own view. It is now a layout-drivenLiquidGlassLensin that view's child slot, at the same per-frame box the pixel-snap work already agrees on with the hole and the rest pill, resolving its material through the same logic the config used — extracted asresolveLiquidGlassMorphPillStyle, whichbuildLiquidGlassMorphPillitself now calls, so the two can never drift. Nothing about the feel changes: the morph springs, the pinch, the hole, the gesture, the rest pill riding inside the lens are all untouched. The thumb's shadow moves from a hand-placed sibling in the view's child slot to the lens's ownappearance.shadow, still faded with the morph so only the glass pill ever casts one, still painting behind the glass and above the captured track. With this, every component's mainline glass goes throughLiquidGlassLens; the classic engine remains only inside the experimental copies. - Changed — the tab bar's capsule is a
LiquidGlassLenson every tier, and the bar shadow is an appearance shadow. The glass-pill tier's capsule was the last piece of the bar on the classic position-driven engine — aLiquidGlassconfig in the inner view'schildren:pipeline. It is now a layout-drivenLiquidGlassLensin that view's child slot (Skia refracts the view's captured background, Impeller the live backdrop, exactly as before), positioned off the same_barLeft/_effBottomMarginthe icon shell, the gesture overlay and the moving pill already read — one source of truth, so the four can never disagree by a pixel. With the capsule a real lens, the bar's shadow stops being a hand-placed sibling widget and ridesappearance.shadowon both tiers — authored on the barstyle's appearance, with no flat parameter beside it: the lens wraps itself in the ring, behind the glass, inside the outer view's capture — so the moving pill still refracts the bar's own shadow. One visible nuance: the ring now paints under the capsule's glass rather than over it (the plain tier always did it this way; the tiers now agree). Impeller's lenses are stackedBackdropFilters, and each samples only what is painted beneath it — so the bar now mounts a second, invisible magnifier pill inside the inner stack, above the bar capsule but below the icon shell: the same silhouette, lift, travel, squash and shed as the glass pill, mounted and retired in the same frame, but fully transparent, unblurred, undistorted, rimless and shadowless. Its backdrop is the page and the capsule alone, so its one job — magnification, ramping in with the lift and out through the handover on the glass material's own curve — pushes the bar back under the pill without shrinking the icons drawn above it. The glass pill on top then refracts the receded bar and the crisp icons alike, its own magnification held at1so the two never compound. Configured byLiquidGlassTabPillStyle.magnifierPill, aLiquidGlassTabMagnifierPillStylewithenabled(a feature switch, not an animated visibility — mounted, it already appears and retires with the glass pill) andmagnification(default0.87). The Skia capture path cannot chain lenses and is untouched — there the single pill keeps its configured magnification exactly as before. A settled bar still mounts neither pill. - New — the tab under the glass can be bigger than the rest, and "under glass" is its own state. A tab used to be selected or not; it is now one of three: unselected, selected (the committed tab — its color, weight and
selectedIconart, whether or not a pill is on it), and under glass — the moving glass pill is physically over it right now, lifted on it, dragged across it, or sweeping past it mid-travel.LiquidGlassTabItemStyle.underGlassIconSizeandunderGlassLabelFontSizesize the icon and label for that third state only, and the enlargement is the glass's effect, so it lives and dies with the glass rather than with the selection: it rides under the pill for the whole travel, and on landing it is not cut off at the swap to the static rest pill — the sizes lerp on the same shed signal that retires the rim and the refraction, so the icon glides back down to the sharediconSizethrough the handover and the swap happens on two identical pictures. A flat pill is not glass: the static rest highlight, the non-glass tiers' pills and a hidden pill (show: false) never enlarge anything — a selected tab under them shows its selection through color and weight alone. Both sizes default tonull(no change), so existing bars render unchanged, and custom items see the same state —LiquidGlassGlyphandLiquidGlassLabelnow carry anunderGlassflag alongsideselected. One property of the reveal to know: mid-travel the layer inside the pill draws at the under-glass size while the layer outside stays at the shared size, so the clip edge joins two sizes — a modest delta reads as the glass magnifying the icon, a large one reads as a seam. - New — custom tab labels (
LiquidGlassTabBarItem.labelBuilder). The label was locked to a plainTextbuilt from the item'slabelstring, so a custom font, rich text or a badge row could not be used at all — andLiquidGlassTabBarItem.customcould restyle its icon but never its label. An item can now draw its own:labelBuilderis handed aLiquidGlassLabelcarrying the already resolved color, font size and weight for the layer being drawn (bundled aslabel.textStyle, socopyWithkeeps the stock look and changes only what you need) plus the item'slabeltext and the layer's selected state. LikeiconBuilder, it runs once per rendered layer, so custom labels get the moving pill's reveal instead of sitting frozen while it sweeps past; return the same-sized widget for both states or the reveal clip will show a seam. Honored by every tier through one shared build helper, andlabelmay be omitted entirely for builder-only content. - Fixed — a travelling pill deformed one way on the launch and the opposite way on the landing. The acceleration model is signed on the force, so which way the pill deformed depended on which way it was going: a tap to the right stretched it wide as it launched and squashed it narrow as it braked, and a tap to the left did both in reverse. Every journey therefore contradicted itself halfway through, and the half still on screen when you looked at the tab you had chosen was the one you never chose. The travel now keeps the force's magnitude — the launch peak, the lull at constant speed, the braking peak, all unchanged — and takes its sign from the direction of travel instead, so the deformation the pill lands on is the one it carried the whole way: a tap to the left stretches it wide, a tap to the right holds it narrow and tall. The landing itself is untouched, to the digit; only the launch changed, and it changed to agree. A finger is deliberately left on the raw force, where pushing right stretches and pushing left squashes — a drag is something you are doing to the pill, and it should answer the hand. Tapping a tab on the far side mid-flight crosses between the two keys rather than switching, so a reversal passes through undeformed instead of flipping the pill inside out in one frame.
- Changed — the glass pill now holds its raised size for the whole travel instead of bulging through it. Tapping a tab used to grow the pill along a sine envelope: biggest at the halfway mark, back to its resting size exactly as it arrived, so the size peaked where nothing was happening and was already gone by the time you looked at the tab you had chosen. The lift is now a held state. It goes up the instant a tab is tapped, stays up for the entire journey, and is only released once the pill has landed — so the pill arrives at full size and deflates where it stands. A press-and-hold is the same state held open by a finger: letting go starts the snap, not the settle, and the pill comes down when it gets there rather than the moment it is released (the old
_settleGrowshrink is gone, and the two gestures now run the same path). Three springs carry it, all under or at critical damping: one per axis for the size, at damping ratios that differ just enough that the width overshoots a shade further and settles a shade later than the height — so each end of the lift overshoots, rebounds, and dips a little under the resting size on the way back — and a third, much stiffer and critically damped, for the material, so the glass is fully on long before the size stops moving. The acceleration squash/stretch is untouched and multiplies on top, which puts the braking squash and the deflation in the same moment.handoverStartis now that one landing gate — the lift release and the glass shed both hang off it — and its default moves from0.72to0.92, the last of the travel where the spring is creeping the final pixels in. - Changed — the glass pill now turns into the resting pill instead of being swapped for it. From the landing (
handoverStart) the lens sheds its glass while everything else keeps running: the rim and its contact shadow go first, then the refraction band narrows to nothing behind them. The deflation and the acceleration squash are untouched throughout — the pill is still shrinking and still deforming while it stops looking like glass. Only once there is nothing left in it to see is the lens dropped and the plain pill put in its place, and by then the lens is drawing a flat fill at rest size, which is exactly what the plain pill draws. A settled bar still costs no shader pass, no clip and no outer capture. The shed is a target rather than a switch, so tapping another tab midway turns the glass back around (glassReturnTau) instead of finishing and starting over. - Fixed — the pill stopped deforming halfway through every travel. The acceleration squash was cut at the 50% mark and the returning half ran on the size envelope alone, which threw away the braking squash — the landing — that the model exists to produce. The model also lived inside the glass widget, so it could not have outlived it. It now lives on the bar: the deformation belongs to the selection pill as a thing rather than to whichever widget is drawing it, it runs unbroken for the whole travel, and the bar can see when it has drained and only then retire the lens.
- Fixed — the moving glass pill popped as it handed over to the resting pill. The travel envelope already returned the pill to its rest material exactly as the swap fired, but that rest material was not glass-free:
LiquidGlassTabPillStyle.effectiveRestnever set a refraction, so it inheritedLiquidGlassStyle's full-strength default (distortion0.1over a 30 px band) — stronger than most lifted pills, which inverted the interpolation across the whole travel and left the pill still bending the bar at the instant a non-refracting painted pill replaced it. The rest end is now explicitly inert, the moving pill also retires the rim (borderWidth) at that end since it comes from the host's authored corner shape and cannot interpolate itself away, and aLiquidGlassRefractionTypenamed only on the lifted style now fades by width instead of surviving at full strength through the rest state. The lifted look is unchanged. - Changed — the bottom nav bar's glass pill now deforms from acceleration, not from a jelly spring. The pill's drawn position is sampled every frame in pixels, differentiated twice, and the averaged acceleration scales it oppositely on the two axes (
scaleX = 1 + d,scaleY = 1 − d): it stretches wide and flat as it launches off a tab, squashes narrow and tall as it brakes into the next, and sits undeformed at constant speed. Force, not speed — and there is no lean term, so the pill deforms about its centre and travels on the spring alone. Because the model reads the position the pill is actually drawn at, a drag-release snap feeds it like any other motion and its braking IS the landing squash, with no special-casing. The pill is now a lens widget in the outer view'schild:slot, so its outline stretches as one body (elliptical end caps) instead of the capsule being re-rounded at each new size; one consequence is that the pill paints below the outer lenses rather than above them. It is also mounted only while something moves it — at rest the bar drops to a plain fill with no shader pass, no clip and no outer capture. - Breaking —
LiquidGlassTabPillStyle.jellyis replaced bymotion. The field now takes aLiquidGlassLensMotionSpec(sampleWindow,sensitivity,maxDeformation,responseTime); the default caps the deformation at ±12 %, since the pill travels inside the bar capsule. Code passingjelly:to the nav pill style no longer compiles. (The rest of the jelly family follows it out this same cycle — see the jelly-family entry above.) - New — contact shadows on the nav bar. A shadow authored on the bar
style'sappearance.shadowdraws around the bar capsule; it lands inside the moving pill's capture, so the pill refracts the bar's own shadow. One on the pill's glass style'sappearance.shadowdraws around the moving pill — wrapped outside its lens, so the arc pooling below it is not clipped at the outline, and tracking the live outline stretch so the ring stays on the rim while the pill squashes. The capsule shadow is honored by every tier — the glass-pill bar draws it into the inner stack (inside the pill's capture), the plain bar wraps the capsule lens with it — and followsvisibility.
- Breaking — the navigation components take their real names:
-
3.5.002 Aug 2026Release notes
Open source →The four "What's New in 3.x" sections gave a reader release history where
they wanted an orientation. They are replaced by one Building Blocks table
-- Glass, Touch, Jelly, Blend, View, Components -- and the parts of them
that were the ONLY documentation for touch, the blend, the render paths and
the per-component Skia requirements survive as permanent sections instead
of version-stamped ones.CHANGELOG picks up the 3.5.0 entry, including the "Undefined name
'internal'" web build failure reported on Flutter 3.29, and pubspec goes
to 3.5.0.showcases/liquid_glass_flex.gif is the recording the README points at.
Release notes
Open source →- New —
LiquidGlassTouch/LiquidGlassFlex: a surface that deforms under a finger. Glass could move and it could re-tint, but it could not answer a press with its own geometry. The newtouch:parameter — onLiquidGlassLens,LiquidGlassButton,LiquidGlassAppBarandLiquidGlassTabBarAction— takes aLiquidGlassTouchcarrying aLiquidGlassFlex, and the surface becomes a soft body: press and it swells under the finger, drag and it elongates along the pull, pinches in the cross axis, leans after the finger, then springs back with a wobble on release. The lens itself never moves — its footprint in layout is unchanged, so neighbouring widgets never shift as it wobbles. Unlike a scale transform, which can only grow symmetrically around one anchor, the left/right/top/bottom edges spring independently toward their own targets, so the half nearest your finger deforms more than the far half.nullis the default and adds nothing to the tree: no gesture listener, no ticker, no cost on an untouched lens. The bars (LiquidGlassTabBar,LiquidGlassBottomNavBar) deliberately do not take it — a navigation bar that deforms under a stray drag reads as broken rather than as soft — and neither doLiquidGlassSlider/LiquidGlassToggle, whose touch response is already the jelly. - Nine direct knobs, five behind
LiquidGlassFlexAdvanced:stretch(peak elongation in logical px),squeeze(the along-axis gain is taken back out of the cross axis — elongate horizontally and the lens genuinely gets thinner),lean(how far the body slides after the finger),grip(0= every edge shares the deformation wherever you touched,1= the edges nearest your finger take all of it),compressInward(driving the grabbed edge into the body squashes it instead of inflating it whichever way it is shoved),holdScale(signed swell for as long as the finger is down),tapScale(a one-shot pop fired when a tap completes, so a fast click still reads as a click),maxPull(thetanhsaturation point) andlockAxis(pins the cross axis for shapes whose proportions carry meaning — a nav capsule, a slider track). Content follow, the optical boosts and the three spring constants live inadvanced..subtle(),.uniform()and.pronounced()are tuned presets. - The content rides the glass.
childFollowblends the child's transform toward the lens's own map, and at1the two are literally the same expression — so the content cannot drift off the surface however hard the lens is pulled. The child is laid out at rest size and then transformed, so text never re-wraps and rows never re-space mid-gesture. - A press deepens the optics rather than zooming the backdrop.
refractionBoostscales the refraction itself (through a configuredLiquidGlassRefractionType's own strength when one is set, so a type is not bypassed). The backdrop zoom is a separate knob,magnificationBoost, off by default — magnification is not a refraction depth, and raising it reads as the picture behind the glass sliding rather than the glass bending. - Correct outlines under deformation. The shader evaluates the shape at rest size in a domain divided by the deformation scale, and the clips are stretched to match — so a stretched circle becomes an ellipse instead of a stadium with flat runs, and the clip never crosses the glass and shaves the rim at a cap apex.
- Works inside
LiquidGlassBlender. A deformed member resizes its own box, the blender derives every member rect from the render tree, and the merged metaball silhouette picks the deformation up on its own. The one thing that does not survive the merge isrefractionBoost: the blender refracts the whole surface through a single shared style, so a press on one blob cannot deepen its own optics without deepening every other blob's. All geometry —stretch,squeeze,lean,grip,holdScale,tapScale— pluschildFollowbehave normally. - One finger owns a lens. The driver holds a single grab point and a single accumulated pull, and gestures arrive through a translucent
Listener(never aGestureDetector, so a press can never join the gesture arena and steal taps from the content inside the lens or fight a scrollable beneath it). Pointer events are therefore filtered by identity: the first finger down claims the lens and keeps it until it lifts, a second touch cannot reset the grab point mid-gesture, and a non-owner lifting does not end the press. - Fix —
flutter build webfails withUndefined name 'internal'(reported on Flutter 3.29 / Dart 3.7.2, dart2js):liquid_glass_view.dartandliquid_glass_blender.dartannotate members with@internalbut did not importpackage:meta/meta.dart.package:flutter/foundation.dartonly re-exportsinternalon newer SDKs, so on 3.29 the name resolved to nothing and the compile aborted — for every consumer of the package, whether or not they used the annotated APIs, since the whole library is compiled either way. Both files now import meta directly, carrying an// ignore: unnecessary_importbecause newer SDKs correctly flag the import as redundant.metawas already a declared dependency; nothing else changes. - Example: new glass list page (a single lens holding a four-row list, with every flex value on sliders) and a standalone flex tuner that emits the tuned spec as copy-pasteable Dart.
- New —
-
3.4.026 Jul 2026Release notes
Open source →The widget tests for the custom glyph slots are a local development
aid, not part of the shipped package. Untracked and ignored — the
files stay on disk soflutter testkeeps working.Release notes
Open source →- New — SVG / PNG / any-widget icons in the nav and tab bars (
LiquidGlassTabBarItem.iconBuilder): tab glyphs were locked toIconData, so an SVG or a bitmap could not be used at all. An item can now draw itself:iconBuilderis handed aLiquidGlassGlyphcarrying the already resolved color for the layer being drawn, the glyph box size, and whether that layer renders the selected state — so tinting withglyph.colorand switching artwork onglyph.selectedreproduces exactly whaticon+selectedIcondo. This matters on the glass-pill bar, which draws every tab twice per frame (one copy clipped inside the moving pill, one outside it): the builder runs once per layer with that layer's color, so custom artwork gets the same iOS-26 reveal as a built-in icon instead of sitting frozen while the pill sweeps past. Honored by every tier — the plain bar, the sliding bar, the glass-pill bar andLiquidGlassTabBar. UseLiquidGlassTabBarItem.custom(...)for asset-only tabs (noIconDataneeded), and mix builder items andIconDataitems freely in one bar. Custom content is boxed to the item style'siconSizeand scaled down if larger, so a glyph can never grow its cell and desync the pill geometry from the icon it reveals. No new package dependency — bring your own SVG/image library. - New —
LiquidGlassButton.childandLiquidGlassTabBarAction.child: both components hardcoded their content (an icon + label row; a single glyph), so anything else — an SVG, an image, a badge, two lines of text — was impossible.childreplaces that content entirely, withLiquidGlassButton.custom(...)/LiquidGlassTabBarAction.custom(...)for the content-only form. The widget is centered and clipped to the glass shape and never sizes its host: the button stays on itswidth/heightand the action stays asize-diameter circle, so the lens geometry holds. A bareIcon/Textinside inherits the host'sforegroundColor(and the button'sfontSize/fontWeight/iconSize), so it matches the built-in look by default; give the widget its own color to paint it yourself. - Additive release: no field types changed and no new required parameters, so existing code compiles and renders identically.
- New — SVG / PNG / any-widget icons in the nav and tab bars (
-
3.3.118 Jul 2026Release notes
Open source →- Fix —
flutter build webfails on Flutter 3.44+ (impellerc failure: Compilation failed for target: SkSL): Flutter 3.44 started validating every shader in the package manifest against SkSL at build time, and the Impeller-only entries use the hardware-derivative gradient (dFdx(float)) — invalid SkSL — so the web build died even though those entries are never loaded on web. The derivative path is now guarded behind impellerc'sSKIA_GRAPHICS_BACKENDdefine: when a shader is compiled for the SkSL target it falls back to the analytic (derivative-free) gradient, so all entries compile clean. Native Impeller output is byte-identical — real devices keep the cheap 1-tap derivative path — and the runtime per-backend shader selection is unchanged.
- Fix —
-
3.3.014 Jul 2026Release notes
Open source →- New —
OpticalBorder.lightSpread: controls how far the optical rim's directional highlight wraps around the perimeter, independent of the always-on ambient ring.0.0= tight, concentrated glint hugging the light axis,0.5= default (identical to the previous fixed falloff, so existing code renders unchanged),1.0= highlight wraps almost all the way around. Below0.5the angular window of each light lobe narrows — the bright core itself tightens, not just the dim tail. Applies toLiquidGlassLens, the border-only painter, and theLiquidGlassBlendermetaball rim.
- New —
-
3.2.228 Jun 2026Release notes
Open source →- Fix — vertically mirrored refraction on OpenGL ES (emulators): Flutter's OpenGLES coordinate-system unification (engine after 3.44.0) removed the framebuffer Y-flip, which turned the shaders' manual sampler/derivative Y-flips into a double flip — a symmetrical Y-axis refraction artifact visible on Android emulators that fall back to OpenGL ES (real Vulkan/Metal devices were never affected). All four
IMPELLER_TARGET_OPENGLESflips are now guarded by#ifndef IMPELLER_OPENGLES_UNFLIPPED_DEPRECATED, so they apply only on older engines and are skipped once the engine sets the macro. Skia/web and Vulkan/Metal are unchanged.
- Fix — vertically mirrored refraction on OpenGL ES (emulators): Flutter's OpenGLES coordinate-system unification (engine after 3.44.0) removed the framebuffer Y-flip, which turned the shaders' manual sampler/derivative Y-flips into a double flip — a symmetrical Y-axis refraction artifact visible on Android emulators that fall back to OpenGL ES (real Vulkan/Metal devices were never affected). All four
-
3.2.127 Jun 2026Release notes
Open source →- Fix — bottom nav bar tab selection on web: clicking a tab on web could select the wrong tab (the neighbour), walk toward the target one click at a time, or not change selection at all — most noticeable with a trackpad. A quick click was being routed through the hold-to-drag path and committing to the glide's lagging position instead of the cell that was pressed. A click now commits to the pressed tab. The tap-and-hold drag gesture is unchanged.
-
3.2.027 Jun 2026Release notes
Open source →- Per-backend single-lens shaders: the shape-gradient method now differs by engine. Impeller keeps the hardware-derivative (
dFdx) gradient, while Skia/web loads a separateliquid_glass_skia.frag/liquid_glass_border_skia.fragthat selects an analytic gradient instead —dFdxis invalid SkSL, so the previous shared program was only correct on Impeller. Programs are cached per backend and resolved automatically; the frosted fallback now also covers the brief async load when a lens mounts on a backend that wasn't preloaded. LiquidGlassShadersAPI (backward-compatible):ensureLoaded,isLoaded/isLoadedFor,createMainShader, andcreateBorderShadernow accept an optionalimpellerflag, defaulting to the engine's native backend (ui.ImageFilter.isShaderFilterSupported). Existing no-argument calls keep working.- Metaball blend on Skia — quality + blur: the merged smooth-union field now propagates an analytic gradient (the h-weighted blend of the per-lens gradients) on Skia instead of the heavier 5-tap, and in-shader blur is re-enabled on the Skia blend path. Chromatic aberration is applied per blur tap, so CA lands before the blur (matching Impeller's order).
- Blender Impeller perf — tight engine-blur clip: on the Impeller engine-blur path the costly backdrop + blur pass is now clipped to the tight glass region (the blob union plus rim/blur/refraction/bridge margin) instead of the whole surface. The shader is packed in that clip-local frame — lens centres, resolution, and the sampling window all share one coordinate space — so the blob stays locked to the lenses while the expensive pass only covers the glass.
- New —
LiquidGlassBlender.debugClipBounds(off by default): draws a magenta outline around the backdrop clip region on both backends, to verify the costly pass hugs the merged glass. Diagnostic only — it costs a stroked draw every frame; keep itfalsein production. - Example: added a liquid-glass showcase/menu page and an image-backed lens demo to the gallery.
- Per-backend single-lens shaders: the shape-gradient method now differs by engine. Impeller keeps the hardware-derivative (
-
3.1.027 Jun 2026Release notes
Open source →- New —
LiquidGlassBlender: blend two to sixLiquidGlassLensdescendants into one liquid surface. Neighbouring lenses fuse with a smooth metaball bridge as they meet and pull apart as they separate, while each member keeps its own corner style. Place it inside aLiquidGlassViewand it works on both backends — Impeller samples the live backdrop, Skia refracts the captured background. - Metaball gradient split: the merged-field normal uses the hardware-derivative 1-tap on Impeller and a 5-tap central difference on Skia (whose SkSL has no
dFdx), selected per backend so the blend loads and renders on both. - Single-lens shaders now use the exact 5-tap gradient for every corner style.
- Chromatic aberration on the Skia blend path is now applied after the blur (matching Impeller's order); in-shader blur is disabled on the Skia blend path for now.
- Note: the blend is not optimized for Skia yet — it works on the Skia capture path but can be heavy there; best performance is on Impeller for now.
- New —
-
3.0.018 Jun 2026Release notes
Open source →- New — lens anywhere:
LiquidGlassLensis a layout-driven lens you can drop anywhere in the widget tree (no position/size params; size comes from layout). It supports both Impeller and Skia automatically, resolving the best render path for the running engine: on Impeller it refracts the live backdrop with noLiquidGlassViewand no background widget at all; on Skia it refracts an ancestorLiquidGlassView's captured background, and gracefully degrades to a frosted look when neither is available. - New:
LiquidGlassStyle— one shared descriptor (shape + appearance + refraction) for every glass surface (lens, components, nav pill), withcopyWith(...)andmerge(...). - New — jelly motion: the
LiquidGlassSlider,LiquidGlassToggle, andLiquidGlassBottomNavBarnow share an iOS-style jelly spring — squash/stretch with a direction-memory spring and momentum-sided squash. The slider/toggle thumb stretches toward its travel and snaps back, and the nav bar's selection pill springs between items. Exposed standalone as the reusableLiquidGlassJellywidget. - New components:
LiquidGlassSlider,LiquidGlassToggle,LiquidGlassAppBar,LiquidGlassScaffold(owns the glass pipeline),LiquidGlassDraggable, andLiquidGlassJellyjoin the existingLiquidGlassButton,LiquidGlassBottomNavBar, andLiquidGlassTabBar. - Migration: the old position-driven lens API (
LiquidGlass) is superseded byLiquidGlassLensand the drop-in components; write new code againstLiquidGlassLens. - Breaking: removed the
LiquidGlassSearchBarcomponent. - Breaking:
LiquidGlassAppIconandLiquidGlassDockare no longer part of the public API (kept internally for maintenance). - Rewrote the README around the lens-anywhere API and consolidated the example into a single
main.dartgallery whose home menu opens each demo as its own route. - New shape:
LiquidGlassShape.continuousRoundedRectangle(...)— an Apple capsule-style continuous rounded rectangle, now the default shape; it collapses to a clean capsule at full corner radius. - Breaking: removed
SuperellipseShape; its L^n squircle now lives onLiquidGlassShape.squircle(...)(same iOS-style continuous-curvature look) and ships with its own exact, shader-matchedClipPathclipper (viaclipQuality: LiquidGlassClipQuality.exact). The old superellipse had no exact Flutter clip path, which forced a rectangle clip and capped its blur; the squircle's dedicated clipper now matches the SDF the shader draws and blurs correctly at any sigma. - Shapes are now selected via the
LiquidGlassShape.roundedRectangle(...)/.squircle(...)/.continuousRoundedRectangle(...)convenience constructors (a singlecornerStylevocabulary) instead of separateRoundedRectangleShape/SuperellipseShapeclasses. - Simplified the shaders: removed the superellipse SDF branch and the
u_shapeTypeuniform, so every lens now uses the analytic rounded-rect path. - Added
LiquidGlassView.regionCapture(off by default): per-lens region capture on the Skia sync path — each capture grabs only every lens's own rect (+ margin) instead of the whole background. A performance win when lenses cover a small part of a large background; no effect on Impeller.
- New — lens anywhere:
-
2.0.130 May 2026Release notes
Open source →- Fixed lens position being clamped to the parent bounds even when
outOfBoundaries: true. A lens moved past the parent's edge (in any direction) now keeps its true position instead of being pinned, so spacing between lenses stays correct.
- Fixed lens position being clamped to the parent bounds even when
-
2.0.030 May 2026Release notes
Open source →- Added optical border mode (
OpticalBorder) — Apple-style, SDF-based rim lighting with background-tinted highlights, dual-sided specular reflections, and a lens height profile — alongside the existingClassicBorder. - Added new ready-made components:
LiquidGlassButton,LiquidGlassSearchBar,LiquidGlassAppIcon,LiquidGlassDock,LiquidGlassTabBar, andLiquidGlassBottomNavBar. - Improved rendering stability on release/profile builds with Impeller.
- Fixed lens and border content rendering upside down on Impeller's OpenGL ES backend (older Android devices) by inverting the texture sample Y-axis under
IMPELLER_TARGET_OPENGLES. - Breaking:
oneSideLightIntensityanddoubleSideLightIntensitymoved from the shape toClassicBorder. Pass them viaborderType: ClassicBorder(...)instead of directly on the shape.
- Added optical border mode (
-
1.1.127 Dec 2025Release notes
Open source →- Formatted the dart files and changed the size of the thumbnail of screenshot.
-
1.1.027 Dec 2025Release notes
Open source →- Added new refraction modes: shape refraction and radial refraction.
- Added new light modes: edge and radial.
- Added chromatic aberration support.
- Added one side light intensity support.
- Added saturation control.
- Updated magnification behavior to apply to the entire lens area rather than only the distortion region.
- Improved and optimized shader code.
- Removed
highDistortionOnCurves; the same effect can now be achieved by increasingdistortionand settingdistortionWidthto half of the smallest lens dimension.
-
1.0.024 Nov 2025Release notes
Open source →Initial Stable Release – Liquid Glass Easy
- First official release of the
liquid_glass_easyFlutter package. - Provides real-time liquid glass lens effects with smooth distortion, magnification, and refraction.
- Built with shader-based rendering for high performance and flexibility.
- Includes
LiquidGlassViewandLiquidGlasswidgets for quick and easy integration into any UI. - Example app included to demonstrate usage, configuration, and visual styles.
- Ready for production and pub.dev distribution.
- First official release of the