Liquid Glass Easy

Blend

Two to eight lenses, fused into one liquid surface.

Wrap lenses in a LiquidGlassBlender and they are drawn with one shader pass: one backdrop read and one material for every lens under it, in place of a pass and a read per lens. That sharing is the base of the widget, and it is there whatever smoothness says. Above zero the silhouettes merge too: as two neighbours approach, a smooth metaball bridge grows between them; pull them apart and it snaps. Each member keeps its own corner style through the merge — a circle stays a circle, a squircle stays a squircle — while the blender owns the material: one style, one rim, one piece of glass.

Dart
LiquidGlassView(
  backgroundWidget: myBackground,
  child: LiquidGlassBlender(
    smoothness: 56,
    style: const LiquidGlassStyle(
      shape: LiquidGlassShape.continuousRoundedRectangle(
        cornerRadius: 36,
      ),
    ),
    child: Stack(
      children: const [
        Positioned(
          left: 40,
          top: 80,
          child: SizedBox(
            width: 120,
            height: 120,
            child: LiquidGlassLens(),
          ),
        ),
        Positioned(
          left: 120,
          top: 110,
          child: SizedBox(
            width: 100,
            height: 100,
            child: LiquidGlassLens(),
          ),
        ),
      ],
    ),
  ),
)
ParameterDefaultWhat it does
child—The subtree holding the member lenses. They can be at any depth.
styleLiquidGlassStyle()The look of the merged surface. The group is one piece of glass, so it has one style.
smoothness48How eagerly neighbours fuse — the headline knob. Low keeps shapes distinct until they nearly touch; high grows a bridge from further away. 0 is no bridge at all: hard outlines on the one shared pass, the former LiquidGlassGroup.
useEngineBlurtrueThe engine's Gaussian blurs the merged surface, composed into the blender's one pass (single pass). false blurs inside the shader instead.

Members are found by walking the subtree, so the lenses do not need to be direct children. Anything that is not a LiquidGlassLens — the controls in the demo, for instance — simply is not a member and never joins the merge.

The rim is the evidence#

The group's lights — style.specular and style.glow — draw one outline around the merged silhouette, not around each member. That single unbroken line running around two bodies and the bridge between them is what makes the fusion legible; give the light thickness: 0 and the rim is suppressed outright rather than drawn as a hairline. Turn it off in the demo and the union is still there in the refraction — just much harder to read.

Where a blend can run#

This is the same rule the whole package follows, and blending is the strictest case of it: the merged surface is a shader that samples the pixels behind the group, so it can only exist where those pixels can be reached.

Where the blender sitsWhat happens
Inside a LiquidGlassView — anywhere: iOS, Android, web, desktopWorks, and you write it once. The view resolves the engine for you: on Impeller the members read the live backdrop and no capture is taken at all; on Skia they refract the view's captured backgroundWidget. Same widget tree, no branch.
No view, on ImpellerWorks. The live backdrop is always available there, so the group fuses over whatever your app painted behind it.
No view, on Skia / webThere is nothing to sample, so the merge cannot happen. Each member degrades to its own frosted glass — blur, tint, rim — and they stop fusing. A one-time debug notice says so.

The shader itself also differs per engine, and that is handled for you: Impeller loads a derivative-based one-tap gradient, Skia loads a five-tap variant with no dFdx in it, because Skia cannot compile the first. Flipping backend reloads the right program.

Keep the members close#

By default the blender blurs with the engine in a single pass (useEngineBlur: true), and it draws every lens under it with one shader. That shader, and its backdrop read, cover one rectangle: the area that holds all the members. The blender works that area out on its own from where the members are, and it grows as they spread apart.

So a blender is a group of glasses in one area, not a wrapper for the whole page. Put one glass at the top of the screen and another at the bottom inside the same blender, and the pass covers everything from top to bottom — the whole screen — to draw two small pieces of glass. The same happens when one member moves far from the others: the area stretches to keep every member inside it, and the cost grows with it.

Comments

Comments are GitHub Discussions — reply from either place.