resolveQuality static method
- BuildContext context, {
- GlassQuality? widgetQuality,
- GlassQuality fallback = GlassQuality.standard,
Resolves the effective GlassQuality for a widget.
Every glass widget calls this once per build to determine its actual rendering quality. The resolution follows a strict five-level priority chain. Read from highest to lowest priority:
Level 1 — Widget-level explicit quality
The quality: parameter on an individual widget.
GlassButton(quality: GlassQuality.minimal, ...) // always resolved first
Wins over: theme, inherited, and widget-class defaults. Subject to: GlassAdaptiveScope ceiling (see Level 3).
quality: GlassQuality.premium means "I want premium if the device can
handle it". The adaptive scope is the arbiter of device capability and
will cap this down when necessary. This is the correct mental model — you
are expressing intent, not issuing a guarantee.
Level 2 — Inherited ancestor quality
The GlassQuality provided by the nearest AdaptiveLiquidGlassLayer ancestor via InheritedLiquidGlass. Only consulted if Level 1 is null.
AdaptiveLiquidGlassLayer(
quality: GlassQuality.standard, // all descendants without explicit quality get standard
child: ...,
)
Level 3 — GlassAdaptiveScope ceiling (applied uniformly to all levels)
If a GlassAdaptiveScope ancestor is present, the quality resolved at any of Levels 1, 2, 4, or 5 is capped to the scope's GlassAdaptiveScopeData.effectiveQuality. The scope never raises quality — it only lowers it.
// Scope decides device can sustain standard.
// A premium widget gets standard. A minimal widget stays minimal.
GlassAdaptiveScope(
child: Column(children: [
GlassTabBar.bottom(quality: GlassQuality.premium),// → standard (capped)
GlassButton(quality: GlassQuality.minimal), // → minimal (not raised)
GlassButton(), // → standard (from fallback, capped)
]),
)
To bypass the scope ceiling on a specific subtree — for a hero widget you've verified runs well on the target device — wrap it in its own locked scope:
GlassAdaptiveScope(
minQuality: GlassQuality.premium, // floor = premium
maxQuality: GlassQuality.premium, // ceiling = premium → locked
child: GlassTabBar.bottom(quality: GlassQuality.premium),
)
Level 4 — GlassTheme quality
The quality field of the GlassThemeVariant from the nearest GlassTheme
ancestor. Controls all widgets in the subtree that don't have a
widget-level or inherited quality set.
GlassTheme(
data: GlassThemeData(
light: GlassThemeVariant(quality: GlassQuality.standard), // all widgets → standard
dark: GlassThemeVariant(quality: GlassQuality.standard),
),
child: ...,
)
Default theme variants (GlassThemeVariant.light and
GlassThemeVariant.dark) have quality: null, so theme quality is
transparent by default and falls through to Level 5.
Level 5 — Widget-class default (the fallback parameter)
The last resort. Each widget class hard-codes the quality that makes sense for its role when nothing else is specified:
| Widget class | Default quality | Rationale |
|---|---|---|
| GlassTabBar.bottom | premium |
Static footer — full quality expected |
| GlassToolbar | premium |
Static surface |
| GlassTabBar | standard |
May be in a scrollable context |
| GlassButton | standard |
Interactive, potentially many on screen |
| GlassTextField | standard |
Interactive |
| GlassSlider | standard |
Animated during interaction |
| GlassDialog | standard |
Overlay |
| GlassSheet | standard |
Overlay |
| GlassContainer | standard |
General purpose container |
Widgets pass their own default as the fallback parameter when calling
resolveQuality. You do not set this — it is an implementation detail.
Complete priority summary
Highest priority
1. widget explicit quality: GlassButton(quality: GlassQuality.minimal)
2. inherited ancestor: AdaptiveLiquidGlassLayer(quality: ...)
── GlassAdaptiveScope ceiling applied to levels 1 & 2 above ──
3. theme quality: GlassThemeVariant(quality: GlassQuality.standard)
4. widget-class default: GlassTabBar.bottom → premium, GlassButton → standard
── GlassAdaptiveScope ceiling applied to levels 3 & 4 above ──
Lowest priority
Note: Level 2 (AdaptiveLiquidGlassLayer) is a lower-level internal mechanism. In most apps you will only interact with Levels 1, 3, 4, and the scope ceiling.
Implementation
static GlassQuality resolveQuality(
BuildContext context, {
GlassQuality? widgetQuality,
GlassQuality fallback = GlassQuality.standard,
}) {
// Read the adaptive ceiling once — applied uniformly to every step.
final adaptiveData = GlassAdaptiveScopeData.maybeOf(context);
// Step 1: Widget-level quality wins over theme and inherited.
//
// It is NOT exempt from the adaptive scope. `quality: GlassQuality.premium`
// means "I want premium if the device can handle it." GlassAdaptiveScope
// is the arbiter of device capability and caps the result when necessary.
//
// To unconditionally force a quality floor on a specific subtree regardless
// of the enclosing ceiling, wrap it with:
// GlassAdaptiveScope(minQuality: GlassQuality.premium, child: ...)
if (widgetQuality != null) {
return adaptiveData != null
? _applyCeiling(widgetQuality, adaptiveData.effectiveQuality)
: widgetQuality;
}
// Step 2: inherited ancestor quality (e.g. from AdaptiveLiquidGlassLayer).
//
// SKIP when:
// - Inside a GlassIsolationScope with `isolated: true` — isolated surfaces
// create their own glass layers and should not inherit from the page layer.
// - A GlassIsolationScope provides a `defaultQuality` — the scope is
// explicitly overriding the ambient quality (e.g. GlassScaffold wraps bars
// with `defaultQuality: premium`). Without this guard, the page-level
// `standard` quality would short-circuit before the scope's premium hint
// at Step 4 can take effect.
final scopeDefault = GlassIsolationScope.defaultQualityOf(context);
GlassQuality? resolved;
if (!GlassIsolationScope.isIsolated(context) && scopeDefault == null) {
final inherited =
context.dependOnInheritedWidgetOfExactType<InheritedLiquidGlass>();
if (inherited != null) resolved = inherited.quality;
}
// Step 3: cap inherited quality by adaptive ceiling.
if (adaptiveData != null && resolved != null) {
resolved = _applyCeiling(resolved, adaptiveData.effectiveQuality);
}
if (resolved != null) return resolved;
// Step 4: theme-level quality, capped by adaptive ceiling.
//
// scopeDefault was already resolved above (Step 2). GlassScaffold sets
// `defaultQuality: premium` on bar wrappers. This ensures buttons in bars
// default to premium without requiring explicit quality.
// GlassAdaptiveScope ceiling still caps this on low-end devices.
final themeData = GlassThemeData.of(context);
GlassQuality result =
themeData.qualityFor(context) ?? scopeDefault ?? fallback;
if (adaptiveData != null) {
result = _applyCeiling(result, adaptiveData.effectiveQuality);
}
return result;
}