Liquid Glass Easy

Using it efficiently

Pick the right piece, and the cost mostly takes care of itself.

Most glass performance problems are not tuning problems. They are a widget doing a job a cheaper widget was built for. This page is that decision.

Start from what you are building#

What you wantReach forWhy
Glass that has to be cheap, and ready to shipLite glassThe most efficient piece here: no shader, no capture, no lens budget. Frost, tint and a lit rim, drawn from plain canvas geometry. On Skia and the web, put the whole app on it (LiquidGlassEngine.liteGlassOnSkia = true) and ship — there is no background to capture and no performance to worry about. On either engine, a tinted button or a card with a rim that does not need to bend anything sets liteGlass: true on its style, instead of a backdrop shader on Impeller or a captured background on Skia.
One panel floating over a pageLiquidGlassLensOne read, one shader — two passes on Impeller once it blurs. Nothing beats it for refraction.
A bar, a page, and tabsLiquidGlassScaffoldOn Skia it captures the body once for all the lenses under it. On Impeller there is no capture at all, and it batches its chrome into one read. Adaptivity is wired in on both.
Many glassesBatchMany lenses at the cost of one read — on Impeller, and lite glass too. On Skia the view already captures once for all of them, so a batch is inert there, not wrong.
A cluster that should read as one panelBlend with smoothness: 0One surface, one material, one read — up to eight members. LiquidGlassGroup was this, and is deprecated.
Two shapes that should melt togetherBlendThe metaball bridge is the whole point of it.
A control that resizes as its content changesMorphIt measures the child. The two blobs are one blender pass, but a heavier shader than a lens's — LiquidGlassMorphMotion.plain is one lens on one spring, when you do not need the waist.
A slider or a switchLiquidGlassSlider / LiquidGlassSwitchSelf-contained. On Impeller the thumb reads the live backdrop and nothing is captured; on Skia their own small view takes one snapshot of the track and runs live only while the thumb is held. Your page is never captured.

Glass on Impeller: what it costs, in order#

Glass is floating chrome: bars, controls and buttons that float over the content, with the content scrolling underneath. Keep it out of the content itself — not inside a scrolling list or grid. That is what the material is for, and it is also what keeps it cheap.

On Impeller, the factor that decides the cost is how many blurred glasses are on screen. That is an Impeller cost, not a cost of this package's shaders: every blur is a pass over the backdrop. So even lite glass, which uses no shader at all, needs a batch once a page has many blurred surfaces. From the cheapest setup to the most expensive:

SetupCost
1. Batch, blur `0`The most efficient. One shared read and no blur at all: a page takes many glasses with no raster cost to speak of.
2. Batch, shader blurSet LiquidGlassConfiguration.batchBlurMethod to shader. Each glass blurs inside its own shader, so the batch keeps its single read. The cost grows with the number of glasses and with the sigma — see the blur advice below.
3. Batch, engine blur, separate passThe default. Not a rule — this can change once the Flutter team fixes the single pass bug (see Batch). With many glasses it is not effective. With a few glasses it has not been tested yet, so which one wins there is still open.

How much blur#

Blur is there to soften the backdrop, not to hide it. A high sigma keeps the frame rate but washes out the refraction, and the glass stops reading as glass — with a shader blur and with an engine blur alike. The glass looks right when the blur leaves the refraction clear.

  • `2`–`5` in every case, and 2–3 by preference, with the style's color tint carrying the frost.
  • Many glasses: `2`. Tested on the example app's Social Feed page, which floats a lot of glass over a scrolling feed: the raster time shows no spikes, and its maximum and average stay steady once scrolling starts.

The one question that matters on Skia#

Does the background actually move? On Impeller a lens reads the live backdrop and there is no capture to think about. On Skia and the web the expensive thing is rasterizing the background so the shader has something to read — and doing that sixty times a second for a gradient that never changes is the single most common waste in a glass UI.

Dart
// A background that never changes: one snapshot, refracted forever.
final controller = LiquidGlassViewController();

LiquidGlassView(
  controller: controller,
  backgroundWidget: const MyGradient(),
  realTimeCapture: false,
  child: const MyGlassUI(),
);

// …and again only when the background genuinely changed:
await controller.captureOnce();
  • A painted gradient, an image, a static illustration → capture once.
  • A scrolling list, a video, an animating background → keep it live.
  • A background that changes rarely → capture once, then captureOnce() again when it actually changes.

A checklist before you ship#

  • Is anything capturing that does not need to? One LiquidGlassView per screen, not one per widget. The slider and the switch bring their own small view: a view around the page is fine, one around each control is not needed. And if the page can live without refraction, lite glass captures nothing at all.
  • Is `pixelRatio` still at `1.0`? The glass bends and blurs what it reads, so it hides a soft capture well. 0.7 is usually invisible and meaningfully cheaper.
  • Is the blur doing work you cannot see? On Skia, blur is the most expensive look knob on the capture path, and above roughly 7 it stops looking like a real backdrop blur anyway.
  • Is `dispersion` on where nobody will see the fringe? At 0 (the default) the bending band costs one backdrop read per pixel; any other value adds reads in that band.
  • Is the morph pill earning its second pipeline? LiquidGlassPillMode.impellerOnly gives you the full pill where it is free and the light highlight everywhere else — unless the app is on lite glass, where both costs nothing on Skia either.
  • Are lenses overlapping inside a batch? They must not: glass in a batch cannot see other glass above or below it. A glass button in a pop-up refracts the page under the pop-up, not the pop-up. Pull it out with LiquidGlassBatch.exclude, or give a group of them a LiquidGlassBatch of their own inside the pop-up — see Batch.
  • Is the whole app inside one batch? It should not be. A batch is per page at most.

Mistakes that look like bugs#

SymptomCause
The glass is a flat frosted blur with no bend.On Skia or the web with no ancestor LiquidGlassView. There is nothing to refract, so it degrades — and says so once in the debug console. The same look with no notice is lite glass by choice, or the first lens of the app while its shader compiles.
The glass refracts an old version of the page.realTimeCapture: false with no captureOnce() after the background changed.
Two lenses in a batch look like one.They overlap. Every member samples the same copy, taken before any of them painted, so the upper one cannot see the lower one’s glass.
A lens inside a list goes black while the list is pulled past its end (Impeller).Android’s stretch overscroll renders the scrollable into its own texture, which holds what the list painted and not the page under it. The lens keeps its place and refracts that — black wherever the list is empty behind it, for as long as the pull lasts. If it shows, drop the overscroll indicator on scrollables that hold lenses.
The morph does not animate when the content changes.The children have no keys, so a child that kept its type was not seen as new.

Free by construction#

  • touch: null — the default — adds no listener and no ticker.
  • visibility: false removes the backdrop cost instantly, and the child with it.
  • Adaptivity samples only while something adaptive is on screen, at a few kilobytes and at most eight times a second — and not at all while no frames are being drawn.
  • LiquidGlassSlider and LiquidGlassSwitch never capture your page: nothing at all on Impeller, one snapshot of their own track on Skia.
  • On Impeller on Android and iOS a lens bakes its outline into a texture once and reads it back while it only moves; a shape that is still changing is baked once it holds still. LiquidGlassConfiguration.shapeCache = false turns it off.
  • Lite glass — one lens, or the whole app per engine — compiles no shader, takes no capture and holds no lens slot.

Comments

Comments are GitHub Discussions — reply from either place.