liquid_glass_renderer

repository·main·Indexed 19 days ago

https://github.com/whynotmake-it/flutter_liquid_glass

A Flutter package for creating high-fidelity 'liquid glass' or 'frosted glass' visual effects using shaders. It supports complex blending of glass shapes, interactive glow and stretch effects, and customizable refraction via LiquidGlassSettings. Requires the Impeller rendering engine; Skia, Web, Windows, and Linux are unsupported. Includes specialized widgets like LiquidGlassLayer, LiquidGlassBlendGroup, and a high-performance FakeGlass alternative.

Tokens
10.1K
Snippets
29
Records
38
Agent score
64%

What's inside liquid_glass_renderer

  1. How to use LiquidGlass effects

    main

    The liquid glass effect works by distorting the pixels of the content located behind the glass widget. To make the effect visible, you must place your glass widgets on top of other content, typically using a Stack.

    Basic Implementation Pattern

    1. Place your background content in a Stack.
    2. Add a LiquidGlassLayer to manage the rendering.
    3. Place LiquidGlass widgets inside the layer.
    Stack(
      children: [
        // 1. Background content
        MyBackgroundContent(),
    
        // 2. Layer for glass effects
        LiquidGlassLayer(
          // 3. Glass widgets
          child: LiquidGlass(
            shape: LiquidRoundedSuperellipse(borderRadius: 30),
            child: const SizedBox.square(dimension: 100),
          ),
        ),
      ],
    )
    Stack(
      children: [
        // 1. Your background content goes here
        MyBackgroundContent(),
    
        // 2. Create a layer for liquid glass effects
        LiquidGlassLayer(
          // 3. Add your LiquidGlass widgets here
          child: LiquidGlass(
            shape: LiquidRoundedSuperellipse(borderRadius: 30),
            child: const SizedBox.square(dimension: 100),
          ),
        ),
      ],
    )
  2. Add shadows to LiquidGlass widgets

    main

    Shadows can be added to any LiquidGlass widget (including .grouped(), .withOwnLayer(), .auto(), and FakeGlass) using the shadows parameter.

    Best Practices:

    • Use BlurStyle.outer to keep shadows evenly distributed around the edge.
    • Avoid offsets for a more natural look.
    • Combine a tight, subtle shadow with a softer, wider one for depth.
    LiquidGlass(
      shape: LiquidRoundedSuperellipse(borderRadius: 30),
      shadows: const [
        // Tight, subtle edge shadow
        BoxShadow(
          blurStyle: BlurStyle.outer,
          color: Color.from(alpha: 0.05, red: 0, green: 0, blue: 0),
          blurRadius: 2,
        ),
        // Softer, wider ambient shadow
        BoxShadow(
          blurStyle: BlurStyle.outer,
          color: Color.from(alpha: 0.1, red: 0, green: 0, blue: 0),
          blurRadius: 30,
        ),
      ],
      child: const SizedBox.square(dimension: 150),
    )
  3. Use FakeGlass for high-performance glass effects

    main

    When performance is critical or you don't need refraction, use FakeGlass. It uses backdrop filters instead of shaders, making it much lighter.

    Limitations:

    • Does not support thickness or refractiveIndex.

    Usage Options:

    1. Individual Widget: Wrap a specific widget in FakeGlass.
    2. Layer-wide: Set fake: true on a LiquidGlassLayer to make all its children use FakeGlass automatically.
    // Individual widget
    FakeGlass(
      shape: LiquidRoundedSuperellipse(borderRadius: 20),
      settings: const LiquidGlassSettings(
        blur: 10,
        glassColor: Color(0x33FFFFFF),
      ),
      child: const SizedBox(height: 100, width: 100),
    )
    
    // Layer-wide
    LiquidGlassLayer(
      fake: true,
      settings: const LiquidGlassSettings(blur: 10, glassColor: Color(0x33FFFFFF)),
      child: // ... all children will use FakeGlass
    )
  4. Performance best practices and limitations

    main

    The liquid glass effect is computationally intensive and requires the Impeller rendering engine. Skia is currently unsupported.

    Critical Limitations

    • Platform Support: Only works on Impeller. Web, Windows, and Linux are unsupported.
    • Memory: Animating shapes can cause temporary memory spikes due to a Flutter bug regarding texture disposal.
    • Blending Limits: A LiquidGlassBlendGroup supports a maximum of 16 shapes. Performance degrades significantly as you approach this limit.
    • Blur Artifacts: Blurring can introduce artifacts when blending shapes.

    Optimization Tips

    • Reuse Layers: Use a single LiquidGlassLayer for shapes that share the same settings. Creating many individual layers is expensive.
    • Minimize Area: Keep the area covered by LiquidGlassLayer and LiquidGlassBlendGroup as small as possible. If you have sparse shapes over a large area, split them into multiple smaller layers/groups.
    • Limit Animations: Moving shapes forces a re-render of the glass effect every frame. In a LiquidGlassBlendGroup, moving any shape forces all shapes in that group to re-render.
    • Use FakeGlass: For non-critical UI elements, swap LiquidGlass with FakeGlass to avoid expensive shaders.
  5. Add interactive effects with GlassGlow and LiquidStretch

    main

    Enhance your glass UI with interactive animations:

    • GlassGlow: Adds a responsive glow that follows user touches. Wrap your content with GlassGlow inside your glass widget. The GlassGlowLayer is automatically included by LiquidGlass.
    • LiquidStretch: Adds organic squash and stretch effects that respond to drag gestures, creating a jelly-like feel.
    // GlassGlow Example
    LiquidGlass(
      shape: LiquidRoundedSuperellipse(borderRadius: 20),
      child: GlassGlow(
        glowColor: Colors.white24,
        glowRadius: 1.0,
        child: const Text('Touch Me'),
      ),
    )
    
    // LiquidStretch Example
    LiquidStretch(
      stretch: 0.5,
      interactionScale: 1.05,
      child: LiquidGlass(
        shape: LiquidRoundedSuperellipse(borderRadius: 20),
        child: const Text('Stretchy'),
      ),
    )
  6. How to add a glow effect using GlassGlow and GlassGlowLayer

    main

    To create a glowing effect that responds to touch, you must use a pair of widgets: GlassGlowLayer and GlassGlow.

    1. GlassGlowLayer: Acts as the container that manages the glow state and paints the effect behind its children. It works similarly to how Material provides a surface for InkWell.
    2. GlassGlow: Must be placed as a descendant of a GlassGlowLayer. It listens for touch events (pointer down, move, up) and sends those coordinates to the parent layer to update the glow's position, color, and size.

    The glow effect uses a RadialGradient that fades from the specified glowColor at the center to fully transparent at the edges. It uses BlendMode.plus for additive blending.

    GlassGlowLayer(
      child: GlassGlow(
        glowColor: Colors.blue.withOpacity(0.5),
        glowRadius: 1.0,
        child: YourGlassWidget(),
      ),
    )
  7. How LiquidGlass layers and blending work

    main

    To achieve the liquid glass effect, the library uses a layered architecture:

    • LiquidGlassLayer: The top-level container that manages the rendering context for the glass effect. For optimal performance, you should wrap multiple LiquidGlass shapes in a single LiquidGlassLayer rather than giving each shape its own layer.
    • LiquidGlassBlendGroup: A specialized container used to group multiple LiquidGlass shapes so they can be blended together. This is required when using the LiquidGlass.grouped() constructor.
    • LiquidGlass.auto(): A convenience constructor that bridges the gap by searching for an existing LiquidGlassLayer or creating one if none is found.

    Performance Tip: Prefer placing a single LiquidGlassLayer ancestor in your widget tree and using the standard LiquidGlass constructor for all shapes within it to minimize the overhead of creating multiple layers.

  8. Configure glass appearance with LiquidGlassSettings

    main

    You can customize the appearance of glass widgets by providing LiquidGlassSettings to a LiquidGlassLayer, LiquidGlass.withOwnLayer(), or LiquidGlass.auto(). All glass widgets within that layer will share these settings.

    Key properties include:

    • glassColor: The color tint of the glass (alpha controls intensity).
    • thickness: Amount of background refraction (higher = more distortion).
    • blur: Background blur strength (0 = no blur).
    • refractiveIndex: Material refractive index (1.0 = no refraction, ~1.5 = realistic glass).
    • lightAngle & lightIntensity: Control the virtual light source direction and brightness.
    • ambientStrength: Intensity of ambient light on the glass.
    • outlineIntensity: Visibility of the glass edge.
    • saturation: Adjusts background color saturation (1.0 = no change, <1.0 = desaturated, >1.0 = more saturated).
    LiquidGlassLayer(
      settings: const LiquidGlassSettings(
        thickness: 10,
        glassColor: Color(0x1AFFFFFF),
        lightIntensity: 1.5,
        outlineIntensity: 0.5,
        saturation: 1.2,
      ),
      child: LiquidGlassBlendGroup(
        blend: 40, // Note: blend is on LiquidGlassBlendGroup, not settings
        child: // ... your LiquidGlass.grouped widgets
      ),
    )
  9. Define shapes for LiquidGlass using LiquidShape

    main
    The LiquidShape class is a sealed base class that extends OutlinedBorder. It is used to define the geometry for LiquidGlass widgets. You can use several built-in implementations to provide different geometric forms to your glass effects. All LiquidShape implementations are compatible with standard Flutter ShapeBorder operations like scale and copyWith.
  10. Use LiquidGlassLayer to render glass effects

    main

    The LiquidGlassLayer is the primary container for rendering liquid glass effects. It automatically detects and renders all LiquidGlass or LiquidGlassBlendGroup widgets within its subtree.

    Important Usage Rules:

    1. Layering: Do not place any other widgets between the LiquidGlassLayer and its LiquidGlass children. If you do, the glass effect will be rendered behind those widgets.
    2. Settings: All shapes within a single LiquidGlassLayer share the same LiquidGlassSettings.
    3. Performance: If you have multiple layers that share the same background blur, set useBackdropGroup: true to allow them to share a single BackdropGroup, which improves performance.
    4. Compatibility: The high-fidelity liquid glass effect requires Impeller. If you are using Skia, the layer will automatically fall back to FakeGlass effects, or you can manually set fake: true to avoid warnings.
    Widget build(BuildContext context) {
      return LiquidGlassLayer(
        settings: const LiquidGlassSettings(), // Shared settings for all shapes in this layer
        useBackdropGroup: true,               // Optimization for sharing blur
        child: Column(
          children: [
            LiquidGlass(
              shape: LiquidRoundedSuperellipse(borderRadius: 10),
              child: const SizedBox.square(dimension: 100),
            ),
            LiquidGlassBlendGroup(
              blend: 20,
              child: Row(
                children: [
                  LiquidGlass.grouped(
                    shape: const LiquidOval(),
                    child: const SizedBox.square(dimension: 100),
                  ),
                  LiquidGlass.grouped(
                    shape: const LiquidRoundedSuperellipse(borderRadius: 20),
                    child: const SizedBox.square(dimension: 100),
                  ),
                ],
              ),
            ),
          ],
        ),
      );
    }
  11. Use LiquidGlassBlendGroup to blend multiple shapes

    main

    The LiquidGlassBlendGroup widget is used to group multiple liquid glass shapes so they blend together into a single cohesive effect.

    To use it:

    1. Wrap your liquid glass shapes in a LiquidGlassBlendGroup.
    2. Ensure there is a parent LiquidGlassLayer in the widget tree to provide the rendering context.
    3. Use the blend property to control the intensity of the blending. A higher value increases the distance (in logical pixels) at which shapes start to merge.

    Note: There is a limit of LiquidGlassBlendGroup.maxShapesPerLayer (currently 16) shapes per layer.

    LiquidGlassBlendGroup(
      blend: 30.0, // Higher value = more blending distance
      child: Column(
        children: [
          LiquidGlass.grouped(shape: ...),
          LiquidGlass.grouped(shape: ...),
        ],
      ),
    )