resolveQuality static method

GlassQuality resolveQuality(
  1. BuildContext context, {
  2. GlassQuality? widgetQuality,
  3. 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: [
    GlassBottomBar(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: GlassBottomBar(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
GlassBottomBar 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:     GlassBottomBar → 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;
}