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
childThe 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.
useEngineBlurtrueLet the engine blur the merged surface instead of the shader.

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 style.shape.borderWidth draws 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; set it to 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 LiquidGlassViewanywhere: 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.

Comments

Comments are GitHub Discussions — reply from either place.