Liquid Glass Easy

Lite glass

The material without the shader: frost, tint and rim, and nothing to compile.

A lens gets its look from a fragment shader: every pixel measures its distance to the shape, reads the background under it, bends it and lights itself. Lite glass gives up the bending — the half that needs a shader — and keeps everything else: the frost, the tint, and a rim lit by the same model the shader runs, evaluated on the Dart side and drawn as one triangle mesh from plain canvas geometry. No FragmentProgram, nothing to warm up, no capture on Skia, no slot in the lens budget on Impeller.

Three ways in#

It is one material with three doors. Flip the whole app, flip one lens, or use the widget directly on a surface that was never a lens.

Dart
// 1. Every lens in the app — set once, before the first lens builds.
LiquidGlassEngine.liteGlassOnSkia = true;      // Skia and the web
LiquidGlassEngine.liteGlassOnImpeller = true;  // Impeller

// 2. This lens only, on either engine — the value is its rim's pickup.
LiquidGlassLens(
  style: const LiquidGlassStyle(liteGlass: LiquidGlassLitePickup.backdrop),
  child: child,
)

// 3. The widget itself, for a card, a row, a sheet.
LiquidGlassLite(
  shape: const LiquidGlassShape(cornerRadius: 22, borderWidth: 1.5),
  blur: const LiquidGlassBlur(sigmaX: 3, sigmaY: 3),
  color: const Color(0x33FFFFFF),
  child: child,
)

With an engine switch on, a LiquidGlassView takes no glass capture at all — the adaptive sampler is not a capture and keeps running — and every lens beneath it draws lite glass, the same thing a lens outside any view already draws. That is the way to put the material on a page that has too many lenses for the shader, or on a device that has no time for it, without touching a single lens.

What a lens keeps#

A lens drawn lite keeps its shape, its appearance's color, blur and shadow, and its refraction's magnification as a flat zoom in the frost's own filter. The distortion band, the chromatic aberration and the edge bend are gone — there is no refraction in any mode. The rim stays, at the same numbers: borderWidth, lightColor, lightDirection, lightIntensity, and the OpticalBorder or ClassicBorder parameters mean what they mean on a real lens. The silhouette is the outline a lens clips to, so all three corner styles are reproduced exactly.

What still works, and what does not#

Lite glass changes how a lens is drawn, not what the tree around it does. Everything that never needed the shader keeps working; the two things that are the shader do not.

WhatUnder lite glass
Every componentWorks the same, with no refraction. The slider, the switch, the button, the FAB, the app bar, the tab bar, the dialog, the sheet, the scaffold, the draggable and the motion pill all keep their behaviour, their motion and their shadow; their glass is frost, tint and rim. The tab bar's pillStyle.mode: both is fine on Skia here, since there is no capture to double.
touch / LiquidGlassFlexWorks. The press swell and the drag deform scale the lite outline and its child exactly as they scale the shader's.
adaptivityWorks. The adaptive sampler is not a glass capture and keeps running under the engine switch, so tint and content still flip with the background — on a lens, in an area, through a link, and on the scaffold's system bars.
LiquidGlassScrollEdgeUnchanged. The band never used the shader.
appearance.shadowKept. The contact shadow is drawn around the lite outline as it is around the shader's.
LiquidGlassMorphRuns as `plain`. One lens whose outline springs from the old shape to the new; motion is ignored, there is no second blob and no neck, because two lite surfaces cannot be merged into one.
LiquidGlassBlenderDoes not blend. There is no merged pass: every member paints solo as its own lite surface, with its own outline and its own read. No metaball, no shared material.
LiquidGlassBatchInert. There is no shader pass to share a read between; each lite frost is its own filter.
refractionGone, except `magnification`. distortion, distortionWidth, chromaticAberration and the refraction type do nothing; magnification survives as a flat zoom inside the frost's filter.
LiquidGlassShadersNothing to compile. ensureLoaded() is harmless and unnecessary; a LiquidGlassView takes no glass capture.

Where the rim gets its colour#

The shader's rim samples the background under each pixel and tints its highlight with it, so over a photograph the rim is red where it crosses red and blue where it crosses blue. LiquidGlassLite.pickup — and liteGlass on a lens's style, which is the same value — is the lite answer to that, and it is a choice of cost:

LiquidGlassLitePickupThe rimCost
noneWhite light, painted over the child. What the shader gives over a neutral background.One drawVertices
blendOverlaid on what is under it, so the hue comes through, then the rim's own lightColor and borderColor are multiplied in, so a grey light is a grey rim over white.Blend modes, no read
surfaceblend's rim painted over the finished surface — frost, tint and child — instead of under them, so a tinted glass tints its rim, and a label under the edge shows along it.Blend modes, no read
backdropThe default. Cut out of a brightened copy of the background, so the colour varies along the rim as the shader's does. The tint sits under it and the child over it, and neither enters the read.One backdrop read

ambientColor is the fourth way, and it is orthogonal: hand the widget one colour — a sampled backdrop, or whatever the surface sits on — and it goes through the shader's own highlight math, tinting the whole rim at once. Free, but one tone for the whole outline, and only the painted modes take it.

What it costs#

  • The frost is one backdrop read. Blur and magnification go through one filter, so adding magnification to a blur adds no read.
  • A `backdrop` rim is a second read. Blur at zero, magnification at one and that rim is one read, for the rim.
  • `none`, `blend` and `surface` read nothing. With blur at zero and magnification at one, the whole widget is a few drawVertices calls — put a fill of your own behind it and it is nearly free. That is the shape to reach for in a long list.

LiquidGlassLite

API reference →
const LiquidGlassLite({ super.key, this.shape = const LiquidGlassShape(), this.borderAlpha = 1.0, this.blur = const LiquidGlassBlur(sigmaX: 3, sigmaY: 3), this.magnification = 1.0, this.pickup = LiquidGlassLitePickup.backdrop, this.color, this.blendFloor = 0.35, this.ambientColor, this.shapeScale = const Offset(1.0, 1.0), this.child, })
ParameterTypeDefaultDescription
keyKey?
shapeLiquidGlassShapeconst LiquidGlassShape()The geometry and lighting to draw, read exactly as a lens reads it.
borderAlphadouble1.0A global multiplier on the rim's opacity, for fading it in and out. The lens uses the same knob for its own show/hide animation.
blurLiquidGlassBlurconst LiquidGlassBlur(sigmaX: 3, sigmaY: 3)How much the surface frosts what is behind it, inside its own outline. This is the glass itself rather than its edge: a backdrop blur clipped to the shape, painted under child, the same thing a lens' appearance blur does. It defaults to a light 3 — pass const LiquidGlassBlur() for a bare rim over whatever is already there. It costs a backdrop read, and with LiquidGlassLitePickup.backdrop the rim's own read comes on top of it: two reads for a surface that carries both. That is what a blurred lens costs too, so a screen with many of them wants LiquidGlassBlur() here, or LiquidGlassLitePickup.surface there, or both.
magnificationdouble1.0How much the surface magnifies what is behind it: the backdrop scaled about the shape's centre, evenly, across the whole surface. This is the flat-slab half of a lens' refraction — the content inside the outline stops lining up with the content outside it. It rides in the blur's own filter, so it is free when blur is on. 1.0 is flat.
pickupLiquidGlassLitePickupLiquidGlassLitePickup.backdropWhether — and how — the rim takes its colour from what is behind it. See LiquidGlassLitePickup.
colorColor?The glass's own tint, LiquidGlassAppearance.color's counterpart: a flat fill of the shape between the frost and child. Under LiquidGlassLitePickup.backdrop and LiquidGlassLitePickup.blend the rim sits over it and never takes colour from it, as on a lens: laid beneath the rim inside the read's own layer, or painted around the rim, cut by the rim's alpha. Under LiquidGlassLitePickup.surface and LiquidGlassLitePickup.none it is a plain fill under the child, and the rim is painted over it. null, or fully transparent, paints nothing.
blendFloordouble0.35How much plain white rim survives underneath LiquidGlassLitePickup.blend and LiquidGlassLitePickup.surface. The blend there is overlay, and overlay multiplies: over black there is nothing to multiply, so the rim would disappear exactly where it shows most. This is the plain rim screened over the blend afterwards, showing through wherever the blend came out empty. It is a straight trade — every bit of floor buys visibility on black with tint on colour. Measured on a red field and a black one, as how far the rim's red runs ahead of its blue, and how bright it is over black: The default now holds MORE colour than LiquidGlassLitePickup.backdrop does (104 at 70) — and that one reads the backdrop to get there. Ignored by the other pickup modes.
ambientColorColor?The background color the highlight is tinted with. The shader samples this per pixel from the refracted backdrop; here it is one color for the whole rim. Null keeps the shader's untinted behavior — a white highlight scaled by LiquidGlassShape.lightColor.
shapeScaleOffsetconst Offset(1.0, 1.0)The deformation applied to the outline, matching a lens' shape scale: the shape is built at rest size and stretched, so a squeezed circle becomes an ellipse instead of keeping a fixed corner radius.
childWidget?The content the rim is drawn over. When null the widget expands to its constraints and draws the rim alone.

Comments

Comments are GitHub Discussions — reply from either place.