g1455

Card

GlassCard is a rounded glass panel for a few floating groups of content, such as widgets on a wallpaper, with a legible label colour for its children.

GlassCard is the same panel as a Bar with a 24 px corner instead of a capsule and 16 px of padding all round. Its children get a legible label colour, black or white, picked against the glass over what is behind it.

When to use

  • A few floating panels over a wallpaper or a photo: weather and calendar widgets, a now-playing card, a settings group on a lock-screen style page.
  • A panel that holds controls: switches, sliders, a button.
  • Not for every item of a list or a feed. Apple keeps Liquid Glass in the navigation and controls layers, out of the content layer. Each card is one more surface, and the cost grows faster than the number of surfaces. A feed of glass cards is the most expensive thing you can build with this package. If you really need it, check the count with GlassLedger.
  • Not over a plain, flat background. Glass over one flat colour looks like a grey box. Use an ordinary Card or Container there.

Usage

GlassCard(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.start,
    children: <Widget>[
      const Text('Battery', style: TextStyle(fontWeight: FontWeight.w600)),
      const Text('82% - about 9 hours left'),
      GlassButton(onPressed: () {}, child: const Text('Details')),
    ],
  ),
)

A card sizes itself to its child, like a Container with padding. Give it a width with a SizedBox, ConstrainedBox or the layout around it.

Choosing a finish

The card uses the host's finish unless you pass one. Over a busy photo, GlassFinish.frosted blurs the most and is easiest to read; GlassFinish.clear shows the most of the photo and is the hardest to read on. Try them in the demo above, and see Finishes.

Gotchas

  • A bar or tab bar floating above glass cards does not show those cards unless it is wrapped in GlassAbove. Controls written inside a card refract it automatically.
  • Secondary text in a card: derive it from DefaultTextStyle.of(context).style.color (for example at 70% alpha) rather than a fixed grey, so it follows the label colour the card chose.

Code

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

/// A weather widget over a wallpaper.
/// Assumes a GlassHost above the navigator (MaterialApp.builder).
class WeatherCard extends StatelessWidget {
  const WeatherCard({super.key});

  static const List<(String, IconData, int)> _hours = <(String, IconData, int)>[
    ('Now', Icons.wb_sunny, 21),
    ('14', Icons.wb_sunny, 23),
    ('15', Icons.wb_cloudy, 24),
    ('16', Icons.water_drop, 19),
  ];

  @override
  Widget build(BuildContext context) => SizedBox(
    width: 320,
    child: GlassCard(
      borderRadius: const BorderRadius.all(Radius.circular(28)),
      padding: const EdgeInsets.all(20),
      child: Builder(
        // Under the card, so it reads the label colour the card picked.
        builder: (BuildContext context) {
          final Color? ink = DefaultTextStyle.of(context).style.color;
          return Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              const Text('Lisbon', style: TextStyle(fontSize: 17, fontWeight: FontWeight.w600)),
              const Text('21°', style: TextStyle(fontSize: 48, fontWeight: FontWeight.w300)),
              Text('Mostly sunny · H:24° L:16°', style: TextStyle(color: ink?.withValues(alpha: 0.7))),
              const SizedBox(height: 16),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  for (final (String hour, IconData icon, int temp) in _hours)
                    Column(children: <Widget>[Text(hour), Icon(icon, size: 20), Text('$temp°')]),
                ],
              ),
            ],
          );
        },
      ),
    ),
  );
}

API

Parameter Type Default Description
child Widget required The content. Gets the card's label colour.
borderRadius BorderRadius BorderRadius.all(Radius.circular(24)) Corner radii.
padding EdgeInsets EdgeInsets.all(16) Space between the glass and the content.
finish GlassFinish? null The material. Null takes the theme's.
key Key? null