skip to content

In Flutter, which Clip value triggers a saveLayer, how do the other Clip values compare in cost, and what do most widgets default to?

level: middleimportance: nice to knowfreq 28%

answer

  1. four values in the Clip enum
  2. hardEdge is fastest but jagged
  3. antiAlias can bleed at edges
  4. most widgets default to none
  5. borderRadius instead of a clip

basics

~10 s

Only Clip.antiAliasWithSaveLayer adds a saveLayer; it is very slow. Clip.hardEdge is the fastest real clip, Clip.antiAlias is smoother and a bit slower, and Clip.none, the default for most widgets, costs nothing.

solid answer

~40 s

The `Clip` enum has four values. `Clip.none` does not clip and is the default for most widgets, including `Container` and, with no theme override, `Card`. `Clip.hardEdge` is the fastest real clip but leaves curves jagged. `Clip.antiAlias` smooths the edge and is only a little slower, at the price of possible bleeding-edge artifacts. `Clip.antiAliasWithSaveLayer` clips with anti-aliasing and then allocates an offscreen buffer for everything drawn inside, which the docs call very slow; use it only when an image over a contrasting background shows fringes. Clip widgets pick their own defaults: `ClipRect` and `Stack` use `hardEdge`, `ClipRRect` and `ClipPath` use `antiAlias`. For a rounded colored box, `BoxDecoration.borderRadius` avoids a clip layer entirely.

code

dart · 16 lines
dart
import 'package:flutter/material.dart';

// Clip layer: ClipRRect defaults to Clip.antiAlias.
Widget clippedCard(Widget child) => ClipRRect(
      borderRadius: BorderRadius.circular(12),
      child: ColoredBox(color: Colors.blueGrey, child: child),
    );

// No clip layer: the decoration paints rounded corners itself.
Widget decoratedCard(Widget child) => DecoratedBox(
      decoration: BoxDecoration(
        color: Colors.blueGrey,
        borderRadius: BorderRadius.circular(12),
      ),
      child: child,
    );

go deeper

for a junior

Recall the four Clip values and that antiAliasWithSaveLayer is the slow one; most widgets do not clip unless you ask.

for a middle

Explain the fidelity versus cost order of the values and why the saveLayer variant adds an offscreen pass.

for a senior

Audit a codebase for needless clips and saveLayer clips, replace them with decorations, and confirm with the clip-layer toggle and profile timings.

for a principal

Bake the cheap defaults into shared components so product teams rarely need to choose a clipBehavior themselves.

## The four Clip values Many Flutter widgets take a `clipBehavior` parameter of type `Clip` (from `dart:ui`). It decides whether content that overflows the widget's shape is cut off, and how. | Value | What it does | Relative cost | |---|---|---| | `Clip.none` | No clipping at all | Free | | `Clip.hardEdge` | Clips without anti-aliasing; curves and diagonal edges look jagged | Fastest real clip | | `Clip.antiAlias` | Clips with smoothed edges; can show faint bleeding-edge artifacts | A little slower than `hardEdge` | | `Clip.antiAliasWithSaveLayer` | Anti-aliased clip followed by a `saveLayer`: everything inside is drawn to an offscreen buffer, then clipped and composited back | Very slow; use rarely | The engine's own documentation for the enum says `antiAliasWithSaveLayer` "is very slow", has no bleeding-edge artifacts, but "changes the semantics as it introduces an offscreen buffer". It recommends preferring `antiAlias` wherever possible. ## Why a saveLayer after a clip is so costly A **`saveLayer`** allocates an offscreen render target, draws the clipped content into it and composites it back. The Flutter docs note that on older devices a single render-target switch can cost about a millisecond. The clip itself is not free either: a clip applies to every draw that follows until it is restored, so one clip can slow down hundreds of draw operations. ## Defaults you should know Flutter unified `clipBehavior` years ago and switched most widgets to **not clip** by default, because an older design added a `saveLayer` after almost every non-rectangular clip in Material widgets such as `Card`, `Chip` and buttons. Removing those was reported to nearly halve the gallery app's average raster time. Current defaults worth remembering: - **`Clip.none`** for most widgets, including `Container`; `Card` falls back to `CardThemeData.clipBehavior` and then to `Clip.none`. - **`Clip.hardEdge`** for `ClipRect`, `Stack`, `SingleChildScrollView`, `ListWheelScrollView` and `EditableText`. - **`Clip.antiAlias`** for `ClipRRect` and `ClipPath`. No framework widget defaults to `antiAliasWithSaveLayer`; it only appears when someone asks for it. ## Picking the cheapest clip that looks right 1. **Do you need a clip at all?** If children never overflow, leave `Clip.none`. 2. **Rounded colored box?** Use `BoxDecoration(borderRadius: ...)` on a `Container` or `DecoratedBox` instead of `ClipRRect`. A decoration image is also cut to that radius while it is painted, with no clip layer. 3. **Axis-aligned rectangle or tiny radius?** `Clip.hardEdge` looks the same and is cheapest. 4. **Curves or diagonals that must look smooth?** `Clip.antiAlias`. 5. **Visible fringes where a picture meets a very different background color?** Only then consider `Clip.antiAliasWithSaveLayer`, after checking whether the background can be removed from under the image instead. ## Spotting clip cost In a debug build the DevTools Performance page offers a **Render Clip layers** switch, backed by the `debugDisableClipLayers` flag, which skips clip layers when building the scene. If raster time falls sharply with it off, clipping is a meaningful share of the cost. Confirm any fix with profile-mode timings on a real device, and search the code for `antiAliasWithSaveLayer`: it is rare enough that each use deserves a justification.

  • When is Clip.antiAliasWithSaveLayer genuinely justified in Flutter?
    When an anti-aliased clip over content whose color contrasts sharply with what lies beneath, typically an image over a very different background, shows faint fringes that `Clip.antiAlias` cannot hide. The docs first suggest removing the background from under the image; only if the artifact remains is the offscreen buffer worth paying for.
  • Does a Flutter Card clip its child by default?
    No. `Card.clipBehavior` is null by default, so it uses `CardThemeData.clipBehavior` and, if that is also unset, `Clip.none`. If an image inside should follow the rounded corners, set `clipBehavior: Clip.antiAlias` on that card, or round the image itself.

saying these in an interview costs you the question

  • Clip.antiAlias allocates an offscreen buffer just like antiAliasWithSaveLayer.
  • Material widgets like Card clip their children by default.
  • Clipping is free as long as it does not call saveLayer.
  • Clip.hardEdge is slower than Clip.antiAlias because it is more precise.
  • ClipRRect is the cheapest way to round a plain colored box.