g1455

Tiers & fallbacks

Full glass, a cheap translucent fill, or opaque: the rung for reduce transparency, low-end devices and tests.

Glass comes in three rungs. The package never switches between them on its own: you decide, and GlassTierPolicy turns your signals into a choice.

Tier What is drawn Captures
GlassTier.full Real glass: refraction, blur, tint and rim. Yes
GlassTier.cheap The same shape and rim, with the tint laid straight over what is behind. No blur, no refraction. No
GlassTier.opaque A solid fill matching the glass's average look over the declared backdrop. No

When to use

  • Opaque for the operating system's Reduce Transparency setting. That is what the setting asks for.
  • Cheap as a ceiling on low-end devices, or for a long list of cards below full-glass bars.
  • Pinned tiers for tests, screenshots and benchmarks.
  • Don't expect auto-detection. Flutter doesn't expose Reduce Transparency, so read it natively.

Usage

GlassHost(
  backdrop: const Color(0xFF101014), // the opaque tier fills with this
  tier: GlassTierPolicy(
    reduceTransparency: reduceTransparency, // read natively by the app
    ceiling: lowEndDevice ? GlassTier.cheap : null,
  ).choose(),
  child: child,
)

choose() resolves in this order: pinned, then reduceTransparency (opaque), then ceiling, then full. The GlassTierChoice it returns carries the tier and a GlassTierReason for reporting.

A tier for a subtree

The host's tier applies to the whole screen. To run one part of it on another rung, nest a GlassTheme below the host. The demo above does exactly that around its stage:

GlassTheme(
  data: GlassTheme.of(context).copyWith(
    tier: const GlassTierPolicy(ceiling: GlassTier.cheap).choose(),
  ),
  child: cardList, // cheap cards under full-glass bars
)

Behaviour

  • Below the full tier nothing is captured, so the cheap and opaque rungs cost no capture at all.
  • Below full, groups stop fusing (no bridges between members) and ripples are off.
  • The cheap and opaque rungs draw even without a host above, as flat panels.

Warning

With opaque, declare backdrop on the host or the theme. Without it the fill is the tint alone, which is wrong, and you get a debug error when the rung is painted.

Warning

pinned overrides everything, the user's Reduce Transparency setting included.

Code

import 'package:flutter/material.dart';
import 'package:g1455/g1455.dart';

/// The app's tier from what the app knows about the device and the user.
class TieredApp extends StatelessWidget {
  const TieredApp({
    super.key,
    required this.reduceTransparency,
    required this.lowEndDevice,
    required this.home,
  });

  final bool reduceTransparency; // read natively: Flutter does not pass it on
  final bool lowEndDevice; // your own device table or benchmark
  final Widget home;

  @override
  Widget build(BuildContext context) => MaterialApp(
    builder: (BuildContext context, Widget? child) => GlassHost(
      backdrop: const Color(0xFF101014), // needed by the opaque tier
      tier: GlassTierPolicy(
        reduceTransparency: reduceTransparency,
        ceiling: lowEndDevice ? GlassTier.cheap : null,
      ).choose(),
      child: child!,
    ),
    home: home,
  );
}

/// Full-glass bar over a list of cards held at the cheap tier.
class CheapCards extends StatelessWidget {
  const CheapCards({super.key});

  @override
  Widget build(BuildContext context) => Stack(
    children: <Widget>[
      GlassTheme(
        data: GlassTheme.of(context).copyWith(
          tier: const GlassTierChoice(GlassTier.cheap, GlassTierReason.deviceCeiling),
        ),
        child: ListView(
          padding: const EdgeInsets.fromLTRB(16, 88, 16, 16),
          children: <Widget>[
            for (var i = 0; i < 20; i++)
              Padding(
                padding: const EdgeInsets.only(bottom: 12),
                child: GlassCard(child: Text('Card $i')),
              ),
          ],
        ),
      ),
      const Positioned(
        top: 24,
        left: 16,
        right: 16,
        child: GlassAbove(child: GlassBar(child: Text('Inbox'))),
      ),
    ],
  );
}

API

GlassTierPolicy

Parameter Type Default Description
pinned GlassTier? null Forces a tier. Overrides everything, including the user's accessibility setting.
reduceTransparency bool false The OS Reduce Transparency switch, as your app read it. Gives opaque.
ceiling GlassTier? null The richest tier this device should run, e.g. cheap on low-end hardware.

choose() returns a GlassTierChoice, resolving pinned, then reduceTransparency, then ceiling, then full.

GlassTierChoice

Parameter Type Default Description
tier GlassTier required (positional) The rung drawn.
reason GlassTierReason required (positional) Why, for reporting.

GlassTierChoice.byDefault is (GlassTier.full, GlassTierReason.byDefault).

Enums

Name Values Description
GlassTier full, cheap, opaque The rungs. readsBackdrop is true only for full.
GlassTierReason byDefault, pinnedByHost, reduceTransparency, deviceCeiling Why a rung was chosen. Reporting only.