liquid_glass_hig 0.1.0-beta.3 copy "liquid_glass_hig: ^0.1.0-beta.3" to clipboard
liquid_glass_hig: ^0.1.0-beta.3 copied to clipboard

iOS 26 Liquid Glass UI primitives for Flutter — containers, sheets, nav bars, controls. Pixel-matched to Apple's HIG. Material 3 fallback on Android.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:liquid_glass_hig/liquid_glass.dart';

void main() {
  runApp(const LiquidGlassDemoApp());
}

class LiquidGlassDemoApp extends StatefulWidget {
  const LiquidGlassDemoApp({super.key});

  @override
  State<LiquidGlassDemoApp> createState() => _LiquidGlassDemoAppState();
}

class _LiquidGlassDemoAppState extends State<LiquidGlassDemoApp> {
  LiquidGlassPreset _preset = LiquidGlassPreset.frosted;
  int _tab = 2; // land on Components page

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'liquid_glass demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.indigo,
          brightness: Brightness.dark,
        ),
      ),
      builder: (context, child) {
        final theme = LiquidGlassTheme.fromColorScheme(
          Theme.of(context).colorScheme,
          preset: _preset,
        );
        return LiquidGlassThemeScope(theme: theme, child: child!);
      },
      home: Stack(
        children: [
          const _Wallpaper(),
          DemoShell(
            preset: _preset,
            onPresetChanged: (p) => setState(() => _preset = p),
            tabIndex: _tab,
            onTabChanged: (i) => setState(() => _tab = i),
          ),
        ],
      ),
    );
  }
}

class _Wallpaper extends StatelessWidget {
  const _Wallpaper();

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _WallpaperPainter(),
      size: Size.infinite,
    );
  }
}

class _WallpaperPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // Base gradient (the wash).
    final base = Paint()
      ..shader = const LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [
          Color(0xFFFF6A88),
          Color(0xFFFF99AC),
          Color(0xFFFFCB6B),
        ],
      ).createShader(Offset.zero & size);
    canvas.drawRect(Offset.zero & size, base);

    // Overlapping coloured blobs — these are what the glass will refract.
    final blobs = <(Offset, double, Color)>[
      (Offset(size.width * 0.20, size.height * 0.18), size.width * 0.55, const Color(0xFF6B5BFF)),
      (Offset(size.width * 0.85, size.height * 0.30), size.width * 0.45, const Color(0xFFFFB400)),
      (Offset(size.width * 0.10, size.height * 0.62), size.width * 0.50, const Color(0xFF00C2FF)),
      (Offset(size.width * 0.80, size.height * 0.78), size.width * 0.55, const Color(0xFFFF3FA5)),
      (Offset(size.width * 0.55, size.height * 0.50), size.width * 0.40, const Color(0xFF8AFF6B)),
    ];
    for (final (center, radius, color) in blobs) {
      final p = Paint()
        ..shader = RadialGradient(
          colors: [color.withValues(alpha: 0.85), color.withValues(alpha: 0.0)],
        ).createShader(Rect.fromCircle(center: center, radius: radius));
      canvas.drawCircle(center, radius, p);
    }

    // Diagonal highlight strip to break up the field further.
    final highlight = Paint()
      ..shader = LinearGradient(
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
        colors: [
          Colors.white.withValues(alpha: 0.18),
          Colors.white.withValues(alpha: 0),
          Colors.black.withValues(alpha: 0.12),
        ],
        stops: const [0, 0.55, 1],
      ).createShader(Offset.zero & size);
    canvas.drawRect(Offset.zero & size, highlight);
  }

  @override
  bool shouldRepaint(covariant _WallpaperPainter oldDelegate) => false;
}

class DemoShell extends StatefulWidget {
  const DemoShell({
    super.key,
    required this.preset,
    required this.onPresetChanged,
    required this.tabIndex,
    required this.onTabChanged,
  });

  final LiquidGlassPreset preset;
  final ValueChanged<LiquidGlassPreset> onPresetChanged;
  final int tabIndex;
  final ValueChanged<int> onTabChanged;

  @override
  State<DemoShell> createState() => _DemoShellState();
}

class _DemoShellState extends State<DemoShell> {
  final ScrollController _scroll = ScrollController();

  @override
  void dispose() {
    _scroll.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Widget page;
    String title;
    switch (widget.tabIndex) {
      case 0:
        title = 'Settings';
        page = _SettingsPage(scroll: _scroll);
        break;
      case 1:
        title = 'Now Playing';
        page = _MusicPage(scroll: _scroll);
        break;
      case 2:
        title = 'Components';
        page = _ComponentsPage(
          scroll: _scroll,
          preset: widget.preset,
          onPresetChanged: widget.onPresetChanged,
        );
        break;
      default:
        title = 'Settings';
        page = _SettingsPage(scroll: _scroll);
    }

    return Scaffold(
      backgroundColor: Colors.transparent,
      extendBodyBehindAppBar: true,
      extendBody: true,
      appBar: GlassScrollAwareNavigationBar(
        scrollController: _scroll,
        title: title,
      ),
      body: page,
      bottomNavigationBar: GlassTabBar(
        items: const [
          GlassTabItem(
            icon: Icons.settings_outlined,
            selectedIcon: Icons.settings,
            label: 'Settings',
          ),
          GlassTabItem(
            icon: Icons.play_circle_outline,
            selectedIcon: Icons.play_circle,
            label: 'Music',
          ),
          GlassTabItem(
            icon: Icons.widgets_outlined,
            selectedIcon: Icons.widgets,
            label: 'Components',
          ),
        ],
        currentIndex: widget.tabIndex,
        onTap: widget.onTabChanged,
      ),
    );
  }
}

class _SettingsPage extends StatefulWidget {
  const _SettingsPage({required this.scroll});
  final ScrollController scroll;

  @override
  State<_SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<_SettingsPage> {
  bool _airplane = false;
  bool _wifi = true;
  bool _bluetooth = true;

  @override
  Widget build(BuildContext context) {
    return ListView(
      controller: widget.scroll,
      padding: EdgeInsets.only(
        top: MediaQuery.paddingOf(context).top + kToolbarHeight + 8,
        bottom: 100,
      ),
      children: [
        GlassSection(
          rows: [
            GlassSectionRow(
              title: 'Airplane Mode',
              leading: const Icon(Icons.airplanemode_active, color: Colors.orange),
              trailing: GlassToggle(
                value: _airplane,
                onChanged: (v) => setState(() => _airplane = v),
              ),
            ),
            GlassSectionRow(
              title: 'Wi-Fi',
              leading: const Icon(Icons.wifi, color: Colors.blue),
              trailing: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Text('HomeNet'),
                  const SizedBox(width: 6),
                  GlassToggle(
                    value: _wifi,
                    onChanged: (v) => setState(() => _wifi = v),
                  ),
                ],
              ),
              onTap: () {},
            ),
            GlassSectionRow(
              title: 'Bluetooth',
              leading: const Icon(Icons.bluetooth, color: Colors.blue),
              trailing: GlassToggle(
                value: _bluetooth,
                onChanged: (v) => setState(() => _bluetooth = v),
              ),
            ),
            GlassSectionRow(
              title: 'Cellular',
              leading: const Icon(Icons.network_cell, color: Colors.green),
              trailing: const Text('Off'),
              onTap: () {},
            ),
          ],
        ),
        GlassSection(
          title: 'general',
          rows: [
            GlassSectionRow(
              title: 'About',
              leading: const Icon(Icons.info_outline, color: Colors.grey),
              onTap: () {},
            ),
            GlassSectionRow(
              title: 'Software Update',
              leading: const Icon(Icons.system_update, color: Colors.indigo),
              trailing: const Text('iOS 26.1'),
              onTap: () {},
            ),
            GlassSectionRow(
              title: 'AirDrop',
              leading: const Icon(Icons.share, color: Colors.blue),
              onTap: () => showGlassActionSheet<void>(
                context: context,
                title: 'AirDrop',
                message: 'Choose who can send you content.',
                actions: [
                  GlassActionSheetAction(
                    label: 'Receiving Off',
                    onPressed: () => Navigator.pop(context),
                  ),
                  GlassActionSheetAction(
                    label: 'Contacts Only',
                    onPressed: () => Navigator.pop(context),
                  ),
                  GlassActionSheetAction(
                    label: 'Everyone for 10 Minutes',
                    onPressed: () => Navigator.pop(context),
                  ),
                ],
                cancel: GlassActionSheetAction(
                  label: 'Cancel',
                  onPressed: () => Navigator.pop(context),
                ),
              ),
            ),
          ],
          footer: 'Updates download in the background.',
        ),
        GlassSection(
          title: 'danger zone',
          rows: [
            GlassSectionRow(
              title: 'Reset',
              leading: const Icon(Icons.refresh, color: Colors.red),
              onTap: () => showGlassDialog<void>(
                context: context,
                title: 'Reset all settings?',
                message:
                    'This will return every setting on this device to its default. Your data will not be touched.',
                actions: [
                  GlassDialogAction(
                    label: 'Cancel',
                    onPressed: () => Navigator.pop(context),
                  ),
                  GlassDialogAction(
                    label: 'Reset',
                    variant: GlassButtonVariant.primary,
                    onPressed: () => Navigator.pop(context),
                  ),
                ],
              ),
            ),
          ],
        ),
      ],
    );
  }
}

class _MusicPage extends StatefulWidget {
  const _MusicPage({required this.scroll});
  final ScrollController scroll;

  @override
  State<_MusicPage> createState() => _MusicPageState();
}

class _MusicPageState extends State<_MusicPage> {
  String _filter = 'all';

  @override
  Widget build(BuildContext context) {
    final theme = LiquidGlassThemeScope.of(context);
    return ListView(
      controller: widget.scroll,
      padding: EdgeInsets.only(
        top: MediaQuery.paddingOf(context).top + kToolbarHeight + 8,
        bottom: 100,
      ),
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
          child: GlassSearchBar(
            placeholder: 'Find a song',
            onChanged: (_) {},
          ),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
          child: GlassSegmentedControl<String>(
            segments: const {
              'all': Text('All'),
              'downloaded': Text('Downloaded'),
              'recent': Text('Recent'),
            },
            groupValue: _filter,
            onValueChanged: (v) => setState(() => _filter = v),
          ),
        ),
        GlassPanel(
          title: 'now playing',
          child: Row(
            children: [
              Container(
                width: 64,
                height: 64,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(8),
                  gradient: const LinearGradient(
                    colors: [Color(0xFFFFD86A), Color(0xFFFF6A95)],
                  ),
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(
                      'Glasshouse',
                      style: TextStyle(
                        color: theme.foreground,
                        fontSize: 16,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    Text(
                      'The Refractions',
                      style: TextStyle(
                        color: theme.mutedForeground,
                        fontSize: 13,
                      ),
                    ),
                  ],
                ),
              ),
              IconButton(
                icon: const Icon(Icons.pause_circle_filled),
                color: theme.accent,
                iconSize: 32,
                onPressed: () {},
              ),
            ],
          ),
        ),
        const _FavoritesChips(),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: GlassButton(
            label: 'Add to library',
            icon: Icons.add,
            variant: GlassButtonVariant.primary,
            expand: true,
            onPressed: () {},
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
          child: GlassButton(
            label: 'Share',
            icon: Icons.ios_share,
            variant: GlassButtonVariant.secondary,
            expand: true,
            onPressed: () => showGlassBottomSheet<void>(
              context: context,
              builder: (sheetContext) => const _ShareSheet(),
            ),
          ),
        ),
      ],
    );
  }
}

class _FavoritesChips extends StatelessWidget {
  const _FavoritesChips();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: Wrap(
        spacing: 8,
        runSpacing: 8,
        children: const [
          GlassChip(label: 'Pop', icon: Icons.tag, selected: true),
          GlassChip(label: 'Jazz', icon: Icons.tag),
          GlassChip(label: 'Lo-Fi', icon: Icons.tag),
          GlassChip(label: 'Electronic', icon: Icons.tag),
        ],
      ),
    );
  }
}

class _ShareSheet extends StatelessWidget {
  const _ShareSheet();

  @override
  Widget build(BuildContext context) {
    final theme = LiquidGlassThemeScope.of(context);
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Text(
            'Share Glasshouse',
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 17,
              fontWeight: FontWeight.w700,
              color: theme.foreground,
            ),
          ),
          const SizedBox(height: 16),
          Wrap(
            alignment: WrapAlignment.spaceEvenly,
            spacing: 12,
            children: const [
              _ShareIcon(icon: Icons.messenger_outline, label: 'Messages'),
              _ShareIcon(icon: Icons.mail_outline, label: 'Mail'),
              _ShareIcon(icon: Icons.copy, label: 'Copy'),
              _ShareIcon(icon: Icons.bookmark_outline, label: 'Save'),
            ],
          ),
          const SizedBox(height: 16),
          GlassButton(
            label: 'Close',
            expand: true,
            onPressed: () => Navigator.pop(context),
          ),
        ],
      ),
    );
  }
}

class _ShareIcon extends StatelessWidget {
  const _ShareIcon({required this.icon, required this.label});
  final IconData icon;
  final String label;

  @override
  Widget build(BuildContext context) {
    final theme = LiquidGlassThemeScope.of(context);
    return Column(
      children: [
        GlassCard(
          padding: const EdgeInsets.all(12),
          onTap: () {},
          child: Icon(icon, color: theme.accent),
        ),
        const SizedBox(height: 4),
        Text(label, style: TextStyle(fontSize: 11, color: theme.foreground)),
      ],
    );
  }
}

class _ComponentsPage extends StatefulWidget {
  const _ComponentsPage({
    required this.scroll,
    required this.preset,
    required this.onPresetChanged,
  });

  final ScrollController scroll;
  final LiquidGlassPreset preset;
  final ValueChanged<LiquidGlassPreset> onPresetChanged;

  @override
  State<_ComponentsPage> createState() => _ComponentsPageState();
}

class _ComponentsPageState extends State<_ComponentsPage> {
  bool _toggleA = true;
  bool _toggleB = false;
  String _filter = 'all';
  String _chip = 'pop';

  @override
  Widget build(BuildContext context) {
    final theme = LiquidGlassThemeScope.of(context);

    return ListView(
      controller: widget.scroll,
      padding: EdgeInsets.only(
        top: MediaQuery.paddingOf(context).top + kToolbarHeight + 8,
        bottom: 100,
      ),
      children: [
        // Hero preset switcher — biggest CTA on the page.
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 8, 16, 4),
          child: GlassPanel(
            title: 'preset',
            margin: EdgeInsets.zero,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  'Switch the global theme to see thickness, refraction, and rim light react live.',
                  style: TextStyle(
                    fontSize: 12,
                    color: theme.mutedForeground,
                    height: 1.3,
                  ),
                ),
                const SizedBox(height: 12),
                GlassSegmentedControl<LiquidGlassPreset>(
                  segments: const {
                    LiquidGlassPreset.clear: Text('Clear'),
                    LiquidGlassPreset.frosted: Text('Frosted'),
                    LiquidGlassPreset.vibrant: Text('Vibrant'),
                  },
                  groupValue: widget.preset,
                  onValueChanged: widget.onPresetChanged,
                ),
              ],
            ),
          ),
        ),

        // Hero droplet gallery — the money shot.
        const _DropletGallery(),

        // Cards & containers.
        GlassPanel(
          title: 'cards & containers',
          child: Column(
            children: [
              Row(
                children: [
                  Expanded(
                    child: GlassCard(
                      padding: const EdgeInsets.symmetric(vertical: 24),
                      onTap: () {},
                      child: Center(
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Icon(Icons.touch_app, color: theme.accent, size: 22),
                            const SizedBox(height: 4),
                            Text(
                              'GlassCard',
                              style: TextStyle(
                                color: theme.foreground,
                                fontWeight: FontWeight.w600,
                                fontSize: 12,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: GlassContainer(
                      height: 84,
                      alignment: Alignment.center,
                      child: Text(
                        'GlassContainer',
                        style: TextStyle(
                          color: theme.foreground,
                          fontWeight: FontWeight.w600,
                          fontSize: 12,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),

        // Buttons.
        GlassPanel(
          title: 'buttons',
          child: Wrap(
            spacing: 8,
            runSpacing: 8,
            alignment: WrapAlignment.center,
            children: [
              GlassButton(
                label: 'Primary',
                icon: Icons.check,
                variant: GlassButtonVariant.primary,
                onPressed: () {},
              ),
              GlassButton(
                label: 'Secondary',
                variant: GlassButtonVariant.secondary,
                onPressed: () {},
              ),
              GlassButton(
                label: 'Tertiary',
                variant: GlassButtonVariant.tertiary,
                onPressed: () {},
              ),
              const GlassButton(label: 'Disabled'),
            ],
          ),
        ),

        // Toggles + chips.
        GlassPanel(
          title: 'toggles & chips',
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisSize: MainAxisSize.min,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    'Notifications',
                    style: TextStyle(color: theme.foreground, fontSize: 14),
                  ),
                  GlassToggle(
                    value: _toggleA,
                    onChanged: (v) => setState(() => _toggleA = v),
                  ),
                ],
              ),
              const SizedBox(height: 8),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    'Reduce motion',
                    style: TextStyle(color: theme.foreground, fontSize: 14),
                  ),
                  GlassToggle(
                    value: _toggleB,
                    onChanged: (v) => setState(() => _toggleB = v),
                  ),
                ],
              ),
              const SizedBox(height: 14),
              Wrap(
                spacing: 8,
                runSpacing: 8,
                children: [
                  for (final tag in ['pop', 'jazz', 'lo-fi', 'electronic'])
                    GlassChip(
                      label: tag,
                      icon: Icons.tag,
                      selected: _chip == tag,
                      onTap: () => setState(() => _chip = tag),
                    ),
                ],
              ),
            ],
          ),
        ),

        // Search bar + segmented control inline.
        GlassPanel(
          title: 'inputs',
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            mainAxisSize: MainAxisSize.min,
            children: [
              GlassSearchBar(
                placeholder: 'Search components',
                onChanged: (_) {},
              ),
              const SizedBox(height: 10),
              GlassSegmentedControl<String>(
                segments: const {
                  'all': Text('All'),
                  'inputs': Text('Inputs'),
                  'modals': Text('Modals'),
                },
                groupValue: _filter,
                onValueChanged: (v) => setState(() => _filter = v),
              ),
            ],
          ),
        ),

        // Modal triggers — launches each modal type.
        GlassPanel(
          title: 'modals',
          child: Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              GlassButton(
                label: 'Bottom sheet',
                icon: Icons.swipe_up,
                onPressed: () => showGlassBottomSheet<void>(
                  context: context,
                  builder: (sheetCtx) => Padding(
                    padding: const EdgeInsets.all(20),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text(
                          'Glass bottom sheet',
                          style: TextStyle(
                            fontSize: 18,
                            fontWeight: FontWeight.w700,
                            color: theme.foreground,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text(
                          'Drag down or tap outside to dismiss.',
                          textAlign: TextAlign.center,
                          style: TextStyle(
                            color: theme.mutedForeground,
                            fontSize: 13,
                          ),
                        ),
                        const SizedBox(height: 16),
                        GlassButton(
                          label: 'Close',
                          expand: true,
                          onPressed: () => Navigator.pop(sheetCtx),
                        ),
                      ],
                    ),
                  ),
                ),
              ),
              GlassButton(
                label: 'Dialog',
                icon: Icons.chat_bubble_outline,
                onPressed: () => showGlassDialog<void>(
                  context: context,
                  title: 'Delete this item?',
                  message:
                      'This will permanently remove it from your library.',
                  actions: [
                    GlassDialogAction(
                      label: 'Cancel',
                      onPressed: () => Navigator.pop(context),
                    ),
                    GlassDialogAction(
                      label: 'Delete',
                      variant: GlassButtonVariant.primary,
                      onPressed: () => Navigator.pop(context),
                    ),
                  ],
                ),
              ),
              GlassButton(
                label: 'Action sheet',
                icon: Icons.list_alt,
                onPressed: () => showGlassActionSheet<void>(
                  context: context,
                  title: 'Share via',
                  actions: [
                    GlassActionSheetAction(
                      label: 'Messages',
                      icon: Icons.chat_bubble_outline,
                      onPressed: () => Navigator.pop(context),
                    ),
                    GlassActionSheetAction(
                      label: 'Mail',
                      icon: Icons.mail_outline,
                      onPressed: () => Navigator.pop(context),
                    ),
                    GlassActionSheetAction(
                      label: 'Remove',
                      icon: Icons.delete_outline,
                      isDestructive: true,
                      onPressed: () => Navigator.pop(context),
                    ),
                  ],
                  cancel: GlassActionSheetAction(
                    label: 'Cancel',
                    onPressed: () => Navigator.pop(context),
                  ),
                ),
              ),
            ],
          ),
        ),

        // Floating toolbar.
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: Text(
            'FLOATING TOOLBAR',
            style: TextStyle(
              fontSize: 12,
              letterSpacing: 0.6,
              fontWeight: FontWeight.w600,
              color: theme.mutedForeground,
            ),
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 32),
          child: GlassToolbar(
            children: [
              IconButton(
                onPressed: () {},
                icon: Icon(Icons.format_bold, color: theme.foreground),
              ),
              IconButton(
                onPressed: () {},
                icon: Icon(Icons.format_italic, color: theme.foreground),
              ),
              IconButton(
                onPressed: () {},
                icon: Icon(Icons.format_underline, color: theme.foreground),
              ),
              IconButton(
                onPressed: () {},
                icon: Icon(Icons.link, color: theme.foreground),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

/// Three free-floating glass beads of different sizes/shapes — the visual
/// money shot of the Components page. Sits over the textured wallpaper so
/// the refraction reads at a glance.
class _DropletGallery extends StatelessWidget {
  const _DropletGallery();

  @override
  Widget build(BuildContext context) {
    final theme = LiquidGlassThemeScope.of(context);
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(0, 0, 0, 8),
            child: Text(
              'DROPLETS',
              style: TextStyle(
                fontSize: 12,
                letterSpacing: 0.6,
                fontWeight: FontWeight.w600,
                color: theme.mutedForeground,
              ),
            ),
          ),
          SizedBox(
            height: 140,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                // Small pill
                LiquidGlass(
                  borderRadius: BorderRadius.circular(60),
                  padding: const EdgeInsets.symmetric(
                    horizontal: 18,
                    vertical: 14,
                  ),
                  child: Icon(
                    Icons.water_drop,
                    color: theme.foreground,
                    size: 22,
                  ),
                ),
                // Medium square-ish
                LiquidGlass(
                  borderRadius: BorderRadius.circular(28),
                  padding: const EdgeInsets.all(20),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Icon(
                        Icons.bolt,
                        color: theme.accent,
                        size: 28,
                      ),
                      const SizedBox(height: 4),
                      Text(
                        'Glass',
                        style: TextStyle(
                          fontSize: 11,
                          fontWeight: FontWeight.w600,
                          color: theme.foreground,
                        ),
                      ),
                    ],
                  ),
                ),
                // Big round
                LiquidGlass(
                  borderRadius: BorderRadius.circular(60),
                  padding: const EdgeInsets.all(24),
                  child: Icon(
                    Icons.brightness_2,
                    color: theme.foreground,
                    size: 36,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
163
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

iOS 26 Liquid Glass UI primitives for Flutter — containers, sheets, nav bars, controls. Pixel-matched to Apple's HIG. Material 3 fallback on Android.

Repository (GitHub)
View/report issues

Topics

#ios #liquid-glass #cupertino #ui #design-system

License

BSD-3-Clause (license)

Dependencies

flutter, liquid_glass_renderer

More

Packages that depend on liquid_glass_hig