Liquid Glass Easy

التكيّف والوضوح

زجاج يقرأ ما خلفه فيقلب لونه ولون محتواه، إضافة إلى حافة التمرير التي تُبقي العناصر العائمة واضحة.

صنف

LiquidGlassAdaptivity#

الدليل: التكيّف →

iOS-style adaptivity: the glass tint and the lens content switch between two palettes depending on whether the background behind the lens is dark or light.

Attach it to a style and LiquidGlassLens does the rest:

How the verdict is decided, in priority order:

1. permanentBrightness, when set — a manual override; no sampling runs at all. 2. Automatic sampling, when the lens sits inside a LiquidGlassView: the view captures its backgroundWidget at a very low pixel ratio and frame rate (see LiquidGlassView.adaptiveSampling), converts the sampled sRGB pixels to perceptual CIE L* lightness, and classifies the area with temporal smoothing and hysteresis. 3. brightnessFallback, as a last resort (a standalone Impeller lens has no readable backdrop) — the app theme's brightness by default, or the platform's.

What gets tinted. The glass fill uses the active glassColorOn* (overriding appearance.color while adaptivity is enabled). The child is wrapped in IconTheme + DefaultTextStyle carrying the active contentColorOn*, so any Icon or Text that doesn't hardcode its own color adapts automatically. Both animate together over duration on every flip.

Dart
LiquidGlassStyle(
  adaptivity: LiquidGlassAdaptivity(
    glassColorOnDark: Color(0x33000000),
    contentColorOnDark: Colors.white,
    glassColorOnLight: Color(0x40FFFFFF),
    contentColorOnLight: Color(0xFF1C1C1E),
  ),
)

المُنشئات

const LiquidGlassAdaptivity({ this.glassColorOnDark = const Color(0x26000000), this.contentColorOnDark = const Color(0xFFFFFFFF), this.glassColorOnLight = const Color(0x3DFFFFFF), this.contentColorOnLight = const Color(0xFF1C1C1E), this.duration = const Duration(milliseconds: 300), this.permanentBrightness, this.initialBrightness, this.brightnessFallback = LiquidGlassBrightnessFallback.appTheme, this.link, this.controller, this.darkBelow = 0.60, this.lightAbove = 0.60, this.continuousGlassColor = false, })
المعاملالنوعالافتراضيالوصف
glassColorOnDarkColorconst Color(0x26000000)Glass tint used while the background behind the lens is dark. Defaults to a dark veil, not a light one: the glass AGREES with the background — dark page, dark surface — and it is the content that contrasts. Inverting the two reads as a bright chip stamped on a dark page rather than as glass sitting in it.
contentColorOnDarkColorconst Color(0xFFFFFFFF)Content (icons/text) color used while the background is dark. Contrasts the surface, so it is the light one of the pair.
glassColorOnLightColorconst Color(0x3DFFFFFF)Glass tint used while the background behind the lens is light. The mirror of glassColorOnDark: a light veil over a light page.
contentColorOnLightColorconst Color(0xFF1C1C1E)Content (icons/text) color used while the background is light. Contrasts the surface, so it is the dark one of the pair.
durationDurationconst Duration(milliseconds: 300)How long the flip between the two palettes animates.
permanentBrightnessBrightness?Manual verdict override. When non-null, no background sampling runs and the palettes follow this value directly (still animated). Brightness.dark means "the background is dark".
initialBrightnessBrightness?The palette to assume before the first verdict arrives — the developer's guess for what the widget mounts over. Unlike permanentBrightness it is a placeholder, not a verdict: sampling (or a link) still runs and takes over. When the first verdict confirms the guess nothing moves at all; when it differs, the palettes animate into the truth. null (the default) assumes brightnessFallback — a dark-themed app gets the dark palette from the very first frame.
brightnessFallbackLiquidGlassBrightnessFallbackLiquidGlassBrightnessFallback.appThemeWhere the verdict comes from once everything else in the chain has come up empty. Defaults to LiquidGlassBrightnessFallback.appTheme; switch to LiquidGlassBrightnessFallback.platform to track the OS instead, which is also what you want outside a MaterialApp.
linkLiquidGlassAdaptivityLink?Optional verdict link (see LiquidGlassAdaptivityLink). On a consumer widget (lens, scroll edge, nav bar, …) a link always means follow it: the widget never samples and mirrors the link's verdict, flipping at the same instant as its publisher. On a LiquidGlassAdaptiveArea a link always means publish to it: the area broadcasts its verdict there for followers that can't sit inside the area's subtree. (Consumers inside an area follow it automatically — no link needed.)
controllerLiquidGlassAdaptivityController?Optional runtime pause/resume switch (see LiquidGlassAdaptivityController). While disabled the current palette holds, sampling stops, and link/sampled verdicts are ignored; a manual permanentBrightness still applies. null (the default) = always enabled.
darkBelowdouble0.60Mean perceptual-background-lightness threshold below which the verdict flips to dark. The signal is normalized CIE L*: 0 is black, 1 is white, and 0.5 is the neutral dark/light split. Defaults to `0.60`, the same value as lightAbove — equal thresholds mean no band at all, just a split, and the verdict follows which side of it the background is on. The split sits ABOVE the neutral 0.5 deliberately. Glass reads as glass by agreeing with what is behind it, and over a genuinely mid-grey backdrop the smoked palette carries content better than the milky one does — so the band from 0.5 to 0.6 is handed to the dark palette rather than split down the middle. Set both to 0.5 for the strictly neutral behaviour. Pulling the two apart opens an anti-strobe hysteresis band: inside it the current verdict holds, so a background hovering near the split cannot chatter between palettes. Worth doing for content that averages near mid-grey. Even with the band closed the driver smooths the signal and waits for two confirming samples before it flips, and a background landing exactly on the split holds rather than flipping — the band is a second line of defence, not the only one.
lightAbovedouble0.60Background-lightness threshold above which the verdict flips to light. See darkBelow for the band the two form when they differ.
continuousGlassColorboolfalseWhen true the glass tint tracks the background continuously — gliding through the whole range between glassColorOnLight and glassColorOnDark instead of switching between them. The content colors keep their binary flip either way, so text and icons never sit at a muddy half-tone. The glide runs on perceptual lightness: kContinuousFloor maps to the fully dark tint and kContinuousCeiling to the fully light one, with the ends clamped. Sample-to-sample jitter below a small delta is ignored, so a still background costs nothing. Needs a lightness source — its own sampling, or a followed link whose publisher samples. With only a manual permanentBrightness or the platform brightness there is nothing to glide along, and the tint falls back to the binary flip.

الخصائص

الاسمالنوعالوصف
isNoneboolWhether this is the none opt-out sentinel.

الدوال

التوقيعالوصف
LiquidGlassAdaptivity copyWith({ Color? glassColorOnDark, Color? contentCol…Returns a copy with the given fields replaced.

الأعضاء الساكنة

التوقيعالوصف
double kContinuousFloorBackground lightness mapping to the fully dark glass tint.
double kContinuousCeilingBackground lightness mapping to the fully light glass tint.
LiquidGlassAdaptivity noneThe opt-out sentinel: a widget whose adaptivity is none is not adaptive at all — it keeps its plain style even inside a LiquidGlassAdaptiveArea (which it would otherwise inherit from).

معرَّف في lib/src/widgets/utils/liquid_glass_adaptivity.dart

صنف

LiquidGlassAdaptiveSampling#

الدليل: التكيّف →

Tuning for the background sampling that powers LiquidGlassAdaptivity inside a LiquidGlassView.

The sampler is a second, tiny capture of the view's backgroundWidget — completely separate from the rendering capture. It runs only while at least one descendant lens has adaptivity enabled, is self-paced (a new sample never starts before the previous readback finished), and reads back a few kilobytes per sample. It is also idle-gated: while no frames are being rendered (a fully static screen), no captures run at all — pixels can only change through a rendered frame, so a frameless stretch is guaranteed unchanged.

المُنشئات

const LiquidGlassAdaptiveSampling({ this.pixelRatio = 0.05, this.frameLimit = 8, this.minimumRegionSamples = 8, })
المعاملالنوعالافتراضيالوصف
pixelRatiodouble0.05Pixel ratio of the sampling capture — same meaning as LiquidGlassView.pixelRatio, just much lower. The default (0.05) turns a 400 px-wide background into a ~20 px sample. The sampler may raise it enough to satisfy minimumRegionSamples for a small client.
frameLimitdouble8Maximum samples per second. The default (8) is plenty for a color flip that animates over ~250 ms.
minimumRegionSamplesint8Minimum captured pixels across the shortest visible dimension of every registered region. This keeps a small lens from deciding from only two or three texels even when the full backdrop is large.

معرَّف في lib/src/widgets/utils/liquid_glass_adaptivity.dart

صنف

LiquidGlassAdaptiveArea#

يرث من StatefulWidgetالدليل: التكيّف →

An invisible region that drives adaptivity for everything inside it — the group form of LiquidGlassStyle.adaptivity, with zero wiring.

The area paints nothing. Its laid-out bounds are the measured region: through the enclosing LiquidGlassView's luminance sampler it reads the background under itself, decides one dark/light verdict (hysteresis, animated flips — the same machine every adaptive widget uses), and every adaptive descendant follows it automatically. One region, one verdict, lockstep flips for the whole cluster:

What descendants do. A descendant with no adaptivity of its own inherits the area's (palettes, duration, everything). A descendant with its own adaptivity keeps its palettes but still follows the area's verdict — unless it carries an explicit permanentBrightness (manual always wins) or its own link (an explicit channel wins over the enclosing area).

Outside followers. Give the area's adaptivity a LiquidGlassAdaptivity.link and the area publishes its verdict there, so widgets that can't sit inside the subtree (other overlays, pipelines) can follow the same verdict by carrying the same link.

Verdict sources, in the usual priority order: a manual permanentBrightness on adaptivity; else sampling through the enclosing LiquidGlassView (opt in with its adaptiveSampling); else the platform brightness. Without a view (standalone Impeller) the area still works — it just can't sample.

Dart
Positioned(
  left: 0, right: 0, bottom: 0, height: 160,
  child: LiquidGlassAdaptiveArea(
    adaptivity: LiquidGlassAdaptivity(...),
    child: Stack(children: [myNavBar, mySidePill]),
  ),
)

المُنشئات

const LiquidGlassAdaptiveArea({ super.key, required this.adaptivity, required this.child, this.systemChrome = LiquidGlassSystemChrome.none, this.debugBounds = false, })
المعاملالنوعالافتراضيالوصف
keyKey?
adaptivityمطلوبLiquidGlassAdaptivityThe area's adaptivity: the palettes/duration descendants inherit, the optional manual verdict, and the optional publish link.
childمطلوبWidgetThe subtree the verdict applies to. Laid out as-is — the area adds no visuals and no constraints of its own.
systemChromeLiquidGlassSystemChromeLiquidGlassSystemChrome.noneAlso drive the system bars' icon brightness from this area's verdict (see LiquidGlassSystemChrome) — dark backdrop → light icons, flipping with the glass. Off by default. The area must cover the bar's probe point for this to work: the bottom-center of the screen for LiquidGlassSystemChrome .navigationBar, the top-center for .statusBar — an edge-pinned band qualifies, a floating pill does not.
debugBoundsboolfalseDebug: draw a cyan outline around the exact rect this area samples (its laid-out bounds). Cyan on purpose — the blender's debugClipBounds is magenta, so both overlays can be on at once. Off by default.

الأعضاء الساكنة

التوقيعالوصف
LiquidGlassAdaptiveAreaScope? maybeOf(BuildContext context)The enclosing area's scope, or null when there is none. Adaptive widgets use this to inherit the area's adaptivity and follow its verdict.

معرَّف في lib/src/widgets/components/liquid_glass_adaptive_area.dart

صنف

LiquidGlassAdaptiveAreaScope#

يرث من InheritedWidget

What a LiquidGlassAdaptiveArea exposes to its descendants: the adaptivity to inherit and the link carrying the area's verdict.

المُنشئات

const LiquidGlassAdaptiveAreaScope({ super.key, required this.adaptivity, this.link, required super.child, })
المعاملالنوعالافتراضيالوصف
keyKey?
adaptivityمطلوبLiquidGlassAdaptivityThe area's adaptivity, inherited by descendants that have none.
linkLiquidGlassAdaptivityLink?The link the area publishes its verdict on; descendants follow it. null means config only: descendants inherit adaptivity's palettes but resolve their OWN verdict from their own backdrop. The scaffold hands its chrome palettes down this way, so an app bar is judged by the pixels behind the app bar rather than by a strip it does not cover.
childمطلوب

معرَّف في lib/src/widgets/components/liquid_glass_adaptive_area.dart

صنف

LiquidGlassAdaptiveContent#

يرث من StatefulWidgetالدليل: التكيّف →

Makes bare content adaptive — icons and text that sit directly on the page with no glass behind them (a hero title over a photo, a floating caption) follow the animated adaptive content color exactly like a lens child would.

A LiquidGlassLens installs the adaptive content color only for its own child; content outside any lens has no way to adapt. This wrapper closes that gap: it runs the same verdict machine and installs the animated IconTheme + DefaultTextStyle over child, so any Icon or Text inside that doesn't hardcode a color adapts automatically.

Verdict sources — the standard consumer chain: adaptivity's manual permanentBrightness → its explicit link → the enclosing LiquidGlassAdaptiveArea → own sampling of this widget's bounds through the enclosing LiquidGlassView (needs its adaptiveSampling) → platform brightness. With adaptivity null the enclosing area's config is inherited entirely; with no area either, the widget is inert and just returns child.

Dart
LiquidGlassAdaptiveContent(
  child: Text('Thailand Trip'),   // no explicit color
)

المُنشئات

const LiquidGlassAdaptiveContent({ super.key, this.adaptivity, this.child, this.builder, })
المعاملالنوعالافتراضيالوصف
keyKey?
adaptivityLiquidGlassAdaptivity?The adaptivity to apply. null (the default) inherits the enclosing LiquidGlassAdaptiveArea's config — palettes, duration, verdict. LiquidGlassAdaptivity.none opts out entirely.
childWidget?The content the animated color applies to. Bare Icon/Text (no explicit color) adapt automatically. Optional when builder is given; when both are set builder wins (and may render it).
builderWidget Function( BuildContext context, Color color, Brightness brightness)?Builds fully custom adaptive content instead of relying on the IconTheme/DefaultTextStyle install over child. Called with the animated content color (the exact color the widget would otherwise push into those themes) and the current dark/light brightness verdict — so content that ignores those themes (an SvgPicture, a CustomPaint) can still adapt: tint with color, or swap assets on brightness. Rebuilt every frame of a palette flip, exactly like the theme install.

معرَّف في lib/src/widgets/components/liquid_glass_adaptive_content.dart

صنف

LiquidGlassAdaptivityController#

يرث من ChangeNotifierالدليل: التكيّف →

Runtime pause/resume switch for LiquidGlassAdaptivity — attach one via LiquidGlassAdaptivity.controller and every widget carrying that config obeys it. Share one instance across configs for a page-wide master switch.

Disabled = hold, not off. The palettes stay applied, frozen at the current verdict (at mount that's the initialBrightness guess), and background sampling stops completely — zero captures while disabled. A manual permanentBrightness still applies (it is an explicit override, not adaptation). On enable sampling resumes and the next verdict animates in; adaptOnce instead takes a single on-demand look while staying disabled. For a permanent opt-out use LiquidGlassAdaptivity.none instead.

The classic use is entry control — mount frozen on the guess, adapt only once the page has settled:

Owned by the developer — dispose it like any controller.

Dart
final adaptCtrl = LiquidGlassAdaptivityController(enabled: false);
// ...adaptivity: LiquidGlassAdaptivity(controller: adaptCtrl, ...)
WidgetsBinding.instance.addPostFrameCallback((_) => adaptCtrl.enable());

المُنشئات

LiquidGlassAdaptivityController({bool enabled = true})
المعاملالنوعالافتراضيالوصف
enabledbooltrueWhether adaptivity is currently allowed to follow the background.

الخصائص

الاسمالنوعالوصف
refreshGenerationintMonotonic count of adaptOnce requests. Each attached widget compares it against the generation it last served, so every widget answers a request exactly once, at its own delivery pace.

الدوال

التوقيعالوصف
void enable()Resumes adapting; the next verdict animates in.
void disable()Pauses adapting; the current palette holds and sampling stops.
void adaptOnce()While disabled, requests a single adaptation: every widget holding this controller takes exactly one look at its background, animates to the resulting verdict, and freezes again — enabled stays false throughout. Sampling runs for just that one capture. The classic use is event-driven adaptation — hold during a scroll, adapt once it settles: A verdict that confirms the current palette moves nothing — the look still counts as taken. Followers of a link complete their look on the group's next delivery (the publishing area re-delivers even an unchanged verdict). While enabled this is a no-op: continuous adaptation is already live.

معرَّف في lib/src/widgets/utils/liquid_glass_adaptivity.dart

تعداد

LiquidGlassBrightnessFallback#

الدليل: التكيّف →

Where a verdict comes from when there is nothing better: no permanentBrightness, no link to follow, no enclosing area, no sampler, and no initialBrightness guess.

It is the bottom of the chain, so it only decides anything for glass that cannot read its backdrop — a lens over a live Impeller backdrop, or any surface in a view that never opted into adaptiveSampling.

القيم

القيمةالوصف
appThemeTheme.of(context).brightness — the app's own dark/light, the one a MaterialApp.themeMode switch drives. The default: an app that themes itself dark is overwhelmingly likely to be painting dark backgrounds, which makes it the better guess at what the glass is sitting on. The catch: Theme has no maybeOf, so with no `Theme` ancestor — a bare WidgetsApp, a CupertinoApp, a widget test with no MaterialAppTheme.of silently returns Flutter's fallback theme and this reads Brightness.light rather than following the device. Use platform there.
platformMediaQuery.platformBrightness — the OS-level dark mode setting, which MaterialApp.themeMode does not affect. Correct everywhere, including outside Material, and the right choice when the glass should track the device rather than the app.

معرَّف في lib/src/widgets/utils/liquid_glass_adaptivity.dart

تعداد

LiquidGlassSystemChrome#

Which system bars an adaptive widget's verdict also drives — the glass and the OS chrome flip together, like Android's own gesture pill.

Honored by LiquidGlassAdaptiveArea.systemChrome and LiquidGlassScaffold.systemChrome. Implemented declaratively with Flutter's AnnotatedRegion, so the influence exists exactly while the widget covers the system bar's probe point (bottom-center of the screen for the navigation bar, top-center for the status bar) and vanishes on unmount — no global state, nothing to undo.

Only the icon brightness of the bars is driven; their colors are never touched. With none no AnnotatedRegion exists in the tree at all — the feature is completely off. LiquidGlassAdaptiveArea defaults to none; LiquidGlassScaffold defaults to statusBar.

القيم

القيمةالوصف
noneSystem bars untouched.
statusBarDrive the status bar's icon brightness.
navigationBarDrive the navigation bar's icon (and gesture hint) brightness.
bothDrive both bars.

معرَّف في lib/src/widgets/utils/liquid_glass_adaptivity.dart

صنف

LiquidGlassScrollEdge#

يرث من StatefulWidgetالدليل: حافة التمرير →

iOS-style scroll edge treatment: a fading dim band pinned over a scrollable's edge so floating glass elements (nav pills, app bars) stay legible over whatever scrolls beneath — the "subtle dimming" variant of Apple's scroll edge effect, without any blur of its own.

Place it inside a LiquidGlassView's child, pinned to an edge, under your floating glass elements:

Adaptivity (optional). With adaptivity set and an ancestor LiquidGlassView, the band registers with the view's shared luminance sampler (same one LiquidGlassLens uses — no extra captures) and animates between two tints: glassColorOnDark while the background under the band is dark, glassColorOnLight while it is light. A child placed in the band gets the matching contentColorOn* through IconTheme/DefaultTextStyle, exactly like the lens. Without a view, the verdict falls back to adaptivity.permanentBrightness, then platform brightness. With adaptivity left null the band is a static fade of color — no sampling, no animation, no cost beyond one gradient.

Blur. blur blurs the backdrop under the band as exactly one `BackdropFilter` — one backdrop read, one blur pass, at any sigma and on every backend. It defaults to 5; pass 0 to switch it off, and then no filter exists in the tree at all. Inside a LiquidGlassBatch that pass joins the batch: it reads the batch's one copy of the backdrop instead of taking its own, like every lens in there. The band is pinned over what scrolls under it, so it is an overlap in the batch's sense — see LiquidGlassBatch for what that costs and when to keep it out.

style decides only whether that pass is feathered: LiquidGlassScrollEdgeStyle.soft — the default — fades the blur out across the band with a dstIn ramp so it meets sharp content without a seam, while LiquidGlassScrollEdgeStyle.hard leaves it uniform and ends on a line. Neither costs more than the other.

curve shapes the tint's falloff; blurCurve shapes the blur's, and by default keeps nearly all of it across the first half of the band so chrome stays legible over what passes under it.

The fade itself never blocks touches: scrolls and taps pass through to the content beneath. Only child is hit-testable.

Dart
Positioned(
  left: 0, right: 0, bottom: 0, height: 140,
  child: LiquidGlassScrollEdge(
    edge: LiquidGlassEdge.bottom,
    adaptivity: LiquidGlassAdaptivity(...),
  ),
)

المُنشئات

const LiquidGlassScrollEdge({ super.key, this.edge = LiquidGlassEdge.bottom, this.height, this.color = const Color(0x8A000000), this.adaptivity, this.blur = 5, this.curve = Curves.easeInOut, this.blurCurve = defaultBlurCurve, this.style = LiquidGlassScrollEdgeStyle.soft, this.child, })
المعاملالنوعالافتراضيالوصف
keyKey?
edgeLiquidGlassEdgeLiquidGlassEdge.bottomThe screen edge the fade hugs (full strength there, transparent toward the content).
heightdouble?Band height. When null the band fills its parent's constraints (e.g. a Positioned with height:).
colorColorconst Color(0x8A000000)Fade tint when adaptivity is null (static mode). The alpha is the peak opacity at the very edge.
adaptivityLiquidGlassAdaptivity?Optional adaptive palettes. glassColorOnDark/glassColorOnLight become the fade tint; contentColorOn* tint child. null (default) = static fade, feature fully off.
blurdouble5Backdrop blur sigma applied under the fade, in logical pixels. Defaults to 5: a scroll edge exists to keep chrome legible over whatever passes under it, and a dim alone rarely does that over a busy photograph. Pass 0 to switch the blur off entirely — then no BackdropFilter exists in the tree at all and the band is a pure tint ramp.
curveCurveCurves.easeInOutShape of the TINT falloff from the edge (0 = at the edge) to the inner boundary (1 = fully transparent). The blur has its own — see blurCurve.
blurCurveCurvedefaultBlurCurveShape of the BLUR falloff — see defaultBlurCurve. Read it as "how much blur is left at this depth": the value is inverted, so a curve that rises late holds the blur late. Pass curve here to put the blur back on the tint's profile, a wider Interval to hold it even longer, or Curves.easeInOut for the falloff to start immediately. Separate from curve because the dim and the blur want different profiles — a tint that lingers reads as murk, a blur that lingers reads as depth.
styleLiquidGlassScrollEdgeStyleLiquidGlassScrollEdgeStyle.softHow the band meets the content — see LiquidGlassScrollEdgeStyle. Defaults to LiquidGlassScrollEdgeStyle.soft.
childWidget?Optional content pinned inside the band (title, actions). Adapts automatically when adaptivity is set; hit-testable, unlike the fade itself.

الأعضاء الساكنة

التوقيعالوصف
Curve defaultBlurCurveThe blur's default falloff: Curves.easeInQuart, which keeps nearly all of the blur across the first half of the band and spends the release over the second. A scroll edge exists so chrome stays legible over what scrolls under it, and the chrome occupies the stretch nearest the edge — so the blur has to be near full strength there, not already on its way down. Quartic easing gives that for free: t^4 has barely moved by mid-band (94% of the sigma is still there at 0.5, 76% at 0.7), then falls away over the rest. It trades a softer hold for a firmer landing than a held Interval would: about a third of the sigma is still present at 0.9 and the curve is at its steepest as it reaches the boundary. Over a busy feed that reads as depth; over flat content it can read as an edge, which is what blurCurve is there to retune.

معرَّف في lib/src/widgets/components/liquid_glass_scroll_edge.dart

تعداد

LiquidGlassEdge#

Which screen edge a LiquidGlassScrollEdge hugs. The fade is fully opaque at that edge and dissolves to transparent toward the content.

القيم

القيمةالوصف
top
bottom

معرَّف في lib/src/widgets/components/liquid_glass_scroll_edge.dart

تعداد

LiquidGlassScrollEdgeStyle#

How a LiquidGlassScrollEdge layers its backdrop blur. Both styles paint the same tint ramp; only the blur differs.

القيم

القيمةالوصف
softThe blur is strongest at the edge and fades to nothing by the inner boundary, so content eases toward sharp instead of falling off a wall. Seamless on every backend. The default. ONE backdrop pass, feathered from the inside by a dstIn ramp: the blur's alpha follows LiquidGlassScrollEdge.blurCurve, and because a backdrop layer composites over the untouched page, whatever alpha the ramp gives up comes back as sharp content. No clip boundary, so no seam — for the price of the single pass hard costs.
hardThe same single pass, UNFEATHERED: one uniform sigma across the whole band. The blur then stops dead where the band does, leaving a visible seam against unblurred content. Costs exactly what soft costs; the only difference is the ramp.

معرَّف في lib/src/widgets/components/liquid_glass_scroll_edge.dart

التعليقات

التعليقات هي مناقشات GitHub — يمكنك الرد من أيٍّ من المكانين.