Skip to main content

macos_liquid_glass_ui 0.9.0 copy "macos_liquid_glass_ui: ^0.9.0" to clipboard
macos_liquid_glass_ui: ^0.9.0 copied to clipboard

PlatformmacOS

Flutter widgets and themes implementing the current macOS design language.

example/lib/main.dart

import 'dense_content.dart';
import 'document_inspector.dart';
import 'transient_surfaces.dart';
import 'liquid_glass.dart';

import 'package:flutter/cupertino.dart' hide OverlayVisibilityMode;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart' show ThemeMode, TimeOfDay;
import 'package:flutter/services.dart';
import 'package:macos_liquid_glass_ui/macos_liquid_glass_ui.dart';

Future<void> main() async {
  if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) {
    await const MacosWindowUtilsConfig().apply();
  }
  runApp(const ShellGallery());
}

/// A public-import-only application shell using the retained upstream APIs.
class ShellGallery extends StatefulWidget {
  const ShellGallery({super.key});

  @override
  State<ShellGallery> createState() => _ShellGalleryState();
}

class _ShellGalleryState extends State<ShellGallery> {
  ThemeMode _appearance = ThemeMode.system;

  @override
  Widget build(BuildContext context) => MacosApp(
    title: 'macOS UI Library',
    debugShowCheckedModeBanner: false,
    themeMode: _appearance,
    home: _LibraryWindow(
      onAppearanceChanged: () => setState(() {
        final isDark =
            _appearance == ThemeMode.dark ||
            (_appearance == ThemeMode.system &&
                WidgetsBinding.instance.platformDispatcher.platformBrightness ==
                    Brightness.dark);
        _appearance = isDark ? ThemeMode.light : ThemeMode.dark;
      }),
    ),
  );
}

class _LibraryWindow extends StatefulWidget {
  const _LibraryWindow({required this.onAppearanceChanged});

  final VoidCallback onAppearanceChanged;

  @override
  State<_LibraryWindow> createState() => _LibraryWindowState();
}

class _LibraryWindowState extends State<_LibraryWindow> {
  int _selection = 0;
  MacosWindowScope? _windowScope;
  void _toggleSidebar() => _windowScope?.toggleSidebar();
  static const _pages = [
    'Application shell',
    'Typography',
    'Colour roles',
    'Value controls',
    'Buttons',
    'Text and search',
    'Documents',
    'Sliders and selectors',
    'Menus and dialogs',
    'Liquid Glass',
    'Document inspector',
  ];
  void _showShell() => setState(() => _selection = 0);
  static const _inset = MacosChromeData.contentInset;
  static const _gap = MacosChromeData.gap;

  @override
  Widget build(BuildContext context) {
    final theme = MacosTheme.of(context);
    final chrome = MacosChromeData.of(context);
    final preferences = MacosAccessibility.of(context);
    final window = MacosWindow(
      disableWallpaperTinting: true,
      backgroundColor: chrome.canvas,
      sidebar: Sidebar(
        minWidth: MacosChromeData.sidebarMinWidth,
        maxWidth: MacosChromeData.sidebarMaxWidth,
        startWidth: MacosChromeData.sidebarStartWidth,
        decoration: BoxDecoration(color: chrome.sidebar),
        builder: (context, controller) => SidebarItems(
          scrollController: controller,
          currentIndex: _selection,
          onChanged: (value) => setState(() => _selection = value),
          items: const [
            SidebarItem(
              leading: MacosIcon(CupertinoIcons.macwindow),
              label: Text('Application shell'),
            ),
            SidebarItem(
              label: Text('Foundations'),
              section: true,
              expandDisclosureItems: true,
              disclosureItems: [
                SidebarItem(
                  leading: MacosIcon(CupertinoIcons.textformat),
                  label: Text('Typography'),
                ),
                SidebarItem(
                  label: Text('Appearance'),
                  expandDisclosureItems: true,
                  disclosureItems: [
                    SidebarItem(
                      leading: MacosIcon(CupertinoIcons.paintbrush),
                      label: Text('Colour roles'),
                    ),
                  ],
                ),
              ],
            ),
            SidebarItem(
              label: Text('Value controls'),
              leading: MacosIcon(CupertinoIcons.checkmark_circle),
            ),
            SidebarItem(
              leading: MacosIcon(CupertinoIcons.cursor_rays),
              label: Text('Buttons'),
            ),
            SidebarItem(
              label: Text('Text and search'),
              leading: MacosIcon(CupertinoIcons.search),
            ),
            SidebarItem(
              label: Text('Documents'),
              leading: MacosIcon(CupertinoIcons.doc_text),
            ),
            SidebarItem(
              label: Text('Sliders and selectors'),
              leading: MacosIcon(CupertinoIcons.slider_horizontal_3),
            ),
            SidebarItem(
              label: Text('Menus and dialogs'),
              leading: MacosIcon(CupertinoIcons.rectangle_on_rectangle),
            ),
            SidebarItem(
              label: Text('Liquid Glass'),
              leading: MacosIcon(CupertinoIcons.layers),
            ),
            SidebarItem(
              label: Text('Document inspector'),
              leading: MacosIcon(CupertinoIcons.info_circle),
            ),
          ],
        ),
      ),
      child: Builder(
        builder: (context) {
          void toggleSidebar() => MacosWindowScope.of(context).toggleSidebar();
          _windowScope = MacosWindowScope.of(context);
          return Focus(
            autofocus: true,
            child: MacosScaffold(
              toolBar: ToolBar(
                title: Text(_pages[_selection]),
                titleWidth: 200,
                decoration: BoxDecoration(color: chrome.toolbar),
                leading: MacosIconButton(
                  semanticLabel: 'Toggle sidebar',
                  icon: const MacosIcon(CupertinoIcons.sidebar_left),
                  onPressed: toggleSidebar,
                ),
                actions: [
                  ToolBarIconButton(
                    label: 'Change appearance',
                    tooltipMessage: 'Change appearance (⌥⌘L)',
                    icon: const MacosIcon(CupertinoIcons.circle_lefthalf_fill),
                    showLabel: false,
                    onPressed: widget.onAppearanceChanged,
                  ),
                  ToolBarIconButton(
                    label: 'Show application shell',
                    tooltipMessage: 'Show application shell (⌥⌘1)',
                    icon: const MacosIcon(CupertinoIcons.macwindow),
                    showLabel: false,
                    onPressed: _showShell,
                  ),
                  const ToolBarIconButton(
                    label: 'Export selection',
                    icon: MacosIcon(CupertinoIcons.square_arrow_up),
                    showLabel: false,
                  ),
                ],
              ),
              children: [
                ContentArea(
                  builder: (context, controller) => _selection == 9
                      ? const LiquidGlassPage()
                      : ColoredBox(
                          color: chrome.canvas,
                          child: ListView(
                            controller: controller,
                            padding: const EdgeInsets.all(_inset),
                            children: [
                              Semantics(
                                header: true,
                                child: Text(
                                  _pages[_selection],
                                  style: theme.typography.largeTitle,
                                ),
                              ),
                              const SizedBox(height: _gap),
                              Text(
                                'A desktop workspace, built in Flutter.',
                                style: theme.typography.headline,
                              ),
                              const SizedBox(height: _inset),
                              if (_selection == 0) ...[
                                const Text('Window, navigation and content'),
                                const SizedBox(height: _gap),
                                const Text(
                                  'Resize the sidebar, hide it with ⌥⌘S, or switch appearance with ⌥⌘L. '
                                  'Native window controls remain owned by macOS. '
                                  'Open Liquid Glass for the native floating command surface. '
                                  'Use arrows, Home and End to explore the navigation tree. '
                                  'Resize the window to reach commands in More toolbar commands.',
                                ),
                                const SizedBox(height: _inset),
                                Wrap(
                                  spacing: _gap,
                                  runSpacing: _gap,
                                  children: [
                                    PushButton(
                                      controlSize: ControlSize.large,
                                      onPressed: () =>
                                          setState(() => _selection = 1),
                                      child: const Text('Explore typography'),
                                    ),
                                    PushButton(
                                      controlSize: ControlSize.large,
                                      onPressed: () =>
                                          setState(() => _selection = 9),
                                      child: const Text('Open Liquid Glass'),
                                    ),
                                  ],
                                ),
                                const SizedBox(height: _inset),
                                Text(
                                  'System accessibility',
                                  style: theme.typography.title2,
                                ),
                                const SizedBox(height: _gap),
                                for (final entry in {
                                  'Preference adapter': preferences.available
                                      ? 'Connected'
                                      : 'Safe fallback',
                                  'Transparency': preferences.reduceTransparency
                                      ? 'Reduced'
                                      : 'Allowed; shell remains opaque',
                                  'Motion':
                                      MediaQuery.disableAnimationsOf(context)
                                      ? 'Reduced'
                                      : 'Standard',
                                  'Contrast': MediaQuery.highContrastOf(context)
                                      ? 'Increased'
                                      : 'Standard',
                                  'Window': chrome.isActive
                                      ? 'Active'
                                      : 'Inactive',
                                }.entries)
                                  Padding(
                                    padding: const EdgeInsets.symmetric(
                                      vertical: _gap / 2,
                                    ),
                                    child: Wrap(
                                      spacing: _gap,
                                      runSpacing: _gap / 2,
                                      children: [
                                        Text(
                                          entry.key,
                                          style: theme.typography.headline,
                                        ),
                                        Text(
                                          entry.value,
                                          style: TextStyle(
                                            color: chrome.secondaryForeground,
                                          ),
                                        ),
                                      ],
                                    ),
                                  ),
                              ] else if (_selection == 1) ...[
                                for (final entry in {
                                  'Large title': theme.typography.largeTitle,
                                  'Title': theme.typography.title1,
                                  'Headline': theme.typography.headline,
                                  'Body — The quick brown fox jumps over the lazy dog.':
                                      theme.typography.body,
                                  'Caption — 8 November 2026':
                                      theme.typography.caption1,
                                }.entries)
                                  Padding(
                                    padding: const EdgeInsets.only(
                                      bottom: _inset,
                                    ),
                                    child: Text(entry.key, style: entry.value),
                                  ),
                              ] else if (_selection == 10) ...[
                                const DocumentInspector(),
                              ] else if (_selection == 8) ...[
                                const TransientSurfacesPage(),
                              ] else if (_selection == 7) ...[
                                const _SelectorsPage(),
                              ] else if (_selection == 6) ...[
                                const DocumentBrowser(),
                              ] else if (_selection == 5) ...[
                                const _FieldsPage(),
                              ] else if (_selection == 4) ...[
                                const _ButtonsPage(),
                              ] else if (_selection == 3) ...[
                                const _ValueControlsPage(),
                              ] else ...[
                                const Text(
                                  'Surfaces and separators follow MacosTheme. Text and controls retain their existing semantic theme roles.',
                                ),
                                const SizedBox(height: _inset),
                                for (final role in {
                                  'Document canvas': chrome.canvas,
                                  'Sidebar': chrome.sidebar,
                                  'Separator': chrome.separator,
                                  'Focus ring': chrome.focusRing,
                                  'Selection': chrome.selection,
                                  'Hover': chrome.hover,
                                  'Pressed': chrome.pressed,
                                }.entries)
                                  Padding(
                                    padding: const EdgeInsets.only(
                                      bottom: _gap,
                                    ),
                                    child: Row(
                                      children: [
                                        Container(
                                          width: 32,
                                          height: 32,
                                          color: role.value,
                                        ),
                                        const SizedBox(width: _gap),
                                        Expanded(child: Text(role.key)),
                                      ],
                                    ),
                                  ),
                              ],
                            ],
                          ),
                        ),
                ),
              ],
            ),
          );
        },
      ),
    );
    final shortcuts = CallbackShortcuts(
      bindings: {
        const SingleActivator(LogicalKeyboardKey.digit1, meta: true, alt: true):
            _showShell,
        const SingleActivator(LogicalKeyboardKey.keyS, meta: true, alt: true):
            _toggleSidebar,
        const SingleActivator(LogicalKeyboardKey.keyL, meta: true, alt: true):
            widget.onAppearanceChanged,
      },
      child: window,
    );
    if (!kIsWeb && defaultTargetPlatform == TargetPlatform.macOS) {
      return PlatformMenuBar(
        menus: [
          PlatformMenu(
            label: 'macOS UI Library',
            menus: [
              PlatformProvidedMenuItem(type: PlatformProvidedMenuItemType.quit),
            ],
          ),
          PlatformMenu(
            label: 'View',
            menus: [
              PlatformMenuItem(
                label: 'Toggle Sidebar',
                shortcut: const SingleActivator(
                  LogicalKeyboardKey.keyS,
                  meta: true,
                  alt: true,
                ),
                onSelected: _toggleSidebar,
              ),
              PlatformMenuItem(
                label: 'Show Application Shell',
                shortcut: const SingleActivator(
                  LogicalKeyboardKey.digit1,
                  meta: true,
                  alt: true,
                ),
                onSelected: _showShell,
              ),
              const PlatformMenuItem(label: 'Export Selection'),
              PlatformProvidedMenuItem(
                type: PlatformProvidedMenuItemType.toggleFullScreen,
              ),
              PlatformMenuItem(
                label: 'Change Appearance',
                shortcut: const SingleActivator(
                  LogicalKeyboardKey.keyL,
                  meta: true,
                  alt: true,
                ),
                onSelected: widget.onAppearanceChanged,
              ),
            ],
          ),
        ],
        child: shortcuts,
      );
    }
    return shortcuts;
  }
}

class _ButtonsPage extends StatefulWidget {
  const _ButtonsPage();

  @override
  State<_ButtonsPage> createState() => _ButtonsPageState();
}

class _ButtonsPageState extends State<_ButtonsPage> {
  bool _expanded = false;
  String _result = 'No button action yet';
  void _record(String result) => setState(() => _result = result);

  @override
  Widget build(BuildContext context) {
    final theme = MacosTheme.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          'Tab between commands, then use Enter or Space. '
          'Hover and hold to inspect feedback. Secondary click does not activate.',
        ),
        const SizedBox(height: MacosChromeData.contentInset),
        Text('Push buttons', style: theme.typography.title2),
        const SizedBox(height: MacosChromeData.gap),
        Wrap(
          spacing: MacosChromeData.gap,
          runSpacing: MacosChromeData.gap,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: [
            for (final size in ControlSize.values)
              PushButton(
                controlSize: size,
                onPressed: () => _record('Saved with ${size.name} button'),
                child: Text('Save ${size.name}'),
              ),
            PushButton(
              controlSize: ControlSize.regular,
              secondary: true,
              onPressed: () => _record('Cancelled'),
              child: const Text('Cancel'),
            ),
            const PushButton(
              controlSize: ControlSize.regular,
              child: Text('Save unavailable'),
            ),
            PushButton(
              controlSize: ControlSize.large,
              onPressed: () => _record('Document and attachments saved'),
              child: const Text(
                'Save document and all attached reference material',
              ),
            ),
          ],
        ),
        const SizedBox(height: MacosChromeData.contentInset),
        Text('Icon, disclosure and help', style: theme.typography.title2),
        const SizedBox(height: MacosChromeData.gap),
        Wrap(
          spacing: MacosChromeData.gap,
          runSpacing: MacosChromeData.gap,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: [
            MacosIconButton(
              semanticLabel: 'Add attachment',
              icon: const MacosIcon(CupertinoIcons.add),
              onPressed: () => _record('Attachment added'),
            ),
            const MacosIconButton(
              semanticLabel: 'Remove attachment unavailable',
              icon: MacosIcon(CupertinoIcons.minus),
            ),
            MacosDisclosureButton(
              semanticLabel: 'Attachment details',
              isPressed: _expanded,
              onPressed: () => setState(() => _expanded = !_expanded),
            ),
            const MacosDisclosureButton(semanticLabel: 'Details unavailable'),
            HelpButton(
              semanticLabel: 'Attachment help',
              onPressed: () =>
                  _record('Choose Add attachment to include a file.'),
            ),
            const HelpButton(semanticLabel: 'Help unavailable'),
          ],
        ),
        if (_expanded) ...[
          const SizedBox(height: MacosChromeData.gap),
          const Text('Attachments are included when you save this document.'),
        ],
        const SizedBox(height: MacosChromeData.contentInset),
        Semantics(liveRegion: true, child: Text(_result)),
      ],
    );
  }
}

class _ValueControlsPage extends StatefulWidget {
  const _ValueControlsPage();
  @override
  State<_ValueControlsPage> createState() => _ValueControlsPageState();
}

class _ValueControlsPageState extends State<_ValueControlsPage> {
  bool _sync = false;
  bool? _attachments;
  String? _storage = 'local';
  final _controller = MacosTabController(length: 3);

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

  Widget _row(Widget control, String label) => Padding(
    padding: const EdgeInsets.only(bottom: MacosChromeData.gap),
    child: Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        control,
        const SizedBox(width: MacosChromeData.gap),
        Expanded(child: Text(label)),
      ],
    ),
  );

  @override
  Widget build(BuildContext context) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      const Text(
        'Use Tab, Space or Enter. Drag a switch; use arrows in the storage group and view segments.',
      ),
      const SizedBox(height: MacosChromeData.contentInset),
      for (final size in [
        ControlSize.mini,
        ControlSize.small,
        ControlSize.regular,
      ])
        _row(
          MacosSwitch(
            value: _sync,
            size: size,
            semanticLabel: 'Sync ${size.name}',
            onChanged: (value) => setState(() => _sync = value),
          ),
          'Sync documents — ${size.name}',
        ),
      _row(
        const MacosSwitch(
          value: true,
          onChanged: null,
          semanticLabel: 'Managed sync',
        ),
        'Sync managed by your organisation',
      ),
      _row(
        MacosCheckbox(
          value: _attachments,
          semanticLabel: 'Include attachments',
          onChanged: (value) => setState(() => _attachments = value),
        ),
        'Include attachments from selected documents (initially mixed)',
      ),
      _row(
        const MacosCheckbox(
          value: null,
          onChanged: null,
          semanticLabel: 'Managed attachments',
        ),
        'Mixed attachments — managed',
      ),
      RadioGroup<String>(
        groupValue: _storage,
        onChanged: (value) => setState(() => _storage = value),
        child: Column(
          children: [
            for (final entry in {
              'local': 'On this Mac',
              'cloud': 'In cloud storage',
              'archive': 'Archive unavailable',
            }.entries)
              _row(
                MacosRadioButton<String>(
                  value: entry.key,
                  groupValue: _storage,
                  semanticLabel: entry.value,
                  onChanged: entry.key == 'archive'
                      ? null
                      : (value) => setState(() => _storage = value),
                ),
                entry.value,
              ),
          ],
        ),
      ),
      const SizedBox(height: MacosChromeData.gap),
      MacosSegmentedControl(
        controller: _controller,
        tabs: const [
          MacosTab(label: 'Summary'),
          MacosTab(label: 'Standard'),
          MacosTab(label: 'Detailed'),
        ],
      ),
      const SizedBox(height: MacosChromeData.gap),
      ListenableBuilder(
        listenable: _controller,
        builder: (context, _) => Text(
          'Preview detail level: ${['Summary', 'Standard', 'Detailed'][_controller.index]}',
        ),
      ),
      Semantics(
        liveRegion: true,
        child: Text(
          'Sync ${_sync ? 'on' : 'off'}; attachments ${_attachments == null
              ? 'mixed'
              : _attachments!
              ? 'included'
              : 'excluded'}; storage $_storage',
        ),
      ),
    ],
  );
}

class _FieldsPage extends StatefulWidget {
  const _FieldsPage();
  @override
  State<_FieldsPage> createState() => _FieldsPageState();
}

class _FieldsPageState extends State<_FieldsPage> {
  final _name = TextEditingController(text: 'Quarterly report');
  final _reference = TextEditingController(text: 'REPORT-2026-11');
  String? _error;
  String _status = 'Choose a document or enter a search query.';
  @override
  void dispose() {
    _name.dispose();
    _reference.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final chrome = MacosChromeData.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text('Document name'),
        const SizedBox(height: MacosChromeData.gap),
        Semantics(
          label: 'Document name',
          child: MacosTextField(
            controller: _name,
            placeholder: 'Enter a descriptive document name',
            clearButtonMode: OverlayVisibilityMode.editing,
            decoration: _error == null
                ? kDefaultRoundedBorderDecoration
                : BoxDecoration(
                    color: chrome.canvas,
                    border: Border.all(color: MacosColors.systemRedColor),
                    borderRadius: BorderRadius.circular(
                      MacosChromeData.controlRadius,
                    ),
                  ),
            onChanged: (_) => setState(() => _error = null),
            onSubmitted: (_) => _save(),
          ),
        ),
        if (_error != null) Semantics(liveRegion: true, child: Text(_error!)),
        const SizedBox(height: MacosChromeData.gap),
        PushButton(
          controlSize: ControlSize.regular,
          onPressed: _save,
          child: const Text('Save name'),
        ),
        const SizedBox(height: MacosChromeData.contentInset),
        const Text('Reference (read only; select and copy)'),
        Semantics(
          label: 'Document reference',
          child: MacosTextField(controller: _reference, readOnly: true),
        ),
        const SizedBox(height: MacosChromeData.gap),
        const Text('Managed by your organisation'),
        Semantics(
          label: 'Managed folder',
          child: MacosTextField(placeholder: 'Team documents', enabled: false),
        ),
        const SizedBox(height: MacosChromeData.contentInset),
        const Text('Find a document'),
        Semantics(
          label: 'Find a document',
          child: MacosSearchField(
            placeholder: 'Search titles; use arrows and Enter to choose',
            results: const [
              SearchResultItem('Quarterly report'),
              SearchResultItem('Project brief'),
              SearchResultItem('Meeting notes'),
            ],
            emptyWidget: const Padding(
              padding: EdgeInsets.all(MacosChromeData.gap),
              child: Text('No matching documents'),
            ),
            onChanged: (query) => setState(
              () => _status = query.isEmpty
                  ? 'Search cleared'
                  : 'Searching for $query',
            ),
            onSubmitted: (query) =>
                setState(() => _status = 'Search submitted: $query'),
            onResultSelected: (result) =>
                setState(() => _status = 'Opened ${result.searchKey}'),
          ),
        ),
        const SizedBox(height: MacosChromeData.gap),
        Semantics(liveRegion: true, child: Text(_status)),
      ],
    );
  }

  void _save() => setState(() {
    _error = _name.text.trim().isEmpty
        ? 'Enter a document name before saving.'
        : null;
    if (_error == null) _status = 'Saved ${_name.text}';
  });
}

class _SelectorsPage extends StatefulWidget {
  const _SelectorsPage();
  @override
  State<_SelectorsPage> createState() => _SelectorsPageState();
}

class _SelectorsPageState extends State<_SelectorsPage> {
  double _volume = 50;
  String _quality = 'balanced';
  Color _colour = CupertinoColors.systemBlue;
  DateTime _date = DateTime(2026, 11, 8);
  TimeOfDay _time = const TimeOfDay(hour: 10, minute: 30);
  bool _enabled = true;

  @override
  Widget build(BuildContext context) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text('Export settings', style: MacosTheme.of(context).typography.title2),
      const SizedBox(height: MacosChromeData.gap),
      Wrap(
        spacing: MacosChromeData.gap,
        crossAxisAlignment: WrapCrossAlignment.center,
        children: [
          MacosSwitch(
            value: _enabled,
            onChanged: (value) => setState(() => _enabled = value),
          ),
          const Text('Enable export controls'),
        ],
      ),
      const SizedBox(height: MacosChromeData.gap),
      Text('Volume: ${_volume.round()} percent'),
      MacosSlider(
        value: _volume,
        min: 0,
        max: 100,
        semanticLabel: 'Export volume',
        onChanged: _enabled ? (value) => setState(() => _volume = value) : null,
      ),
      const SizedBox(height: MacosChromeData.gap),
      const Text('Quality'),
      MacosSlider(
        value: _quality == 'balanced' ? 1 : 2,
        min: 1,
        max: 2,
        splits: 2,
        discrete: true,
        semanticLabel: 'Export quality',
        onChanged: _enabled
            ? (value) =>
                  setState(() => _quality = value == 1 ? 'balanced' : 'best')
            : null,
      ),
      MacosPopupButton<String>(
        value: _quality,
        items: const [
          MacosPopupMenuItem(value: 'balanced', child: Text('Balanced')),
          MacosPopupMenuItem(value: 'best', child: Text('Best quality')),
          MacosPopupMenuItem(
            value: 'managed',
            enabled: false,
            child: Text('Managed by policy'),
          ),
        ],
        onChanged: _enabled
            ? (value) => setState(() => _quality = value!)
            : null,
      ),
      const SizedBox(height: MacosChromeData.gap),
      const Text('Highlight colour'),
      MacosColorWell(
        color: _colour,
        enabled: _enabled,
        semanticLabel: 'Export highlight colour',
        onColorSelected: (value) => setState(() => _colour = value),
      ),
      const SizedBox(height: MacosChromeData.gap),
      Text(
        'Scheduled: ${_date.year}-${_date.month}-${_date.day} at ${_time.format(context)}',
      ),
      Wrap(
        spacing: MacosChromeData.contentInset,
        runSpacing: MacosChromeData.gap,
        children: [
          MacosDatePicker(
            initialDate: _date,
            enabled: _enabled,
            onDateChanged: (value) => setState(() => _date = value),
          ),
          MacosTimePicker(
            initialTime: _time,
            enabled: _enabled,
            onTimeChanged: (value) => setState(() => _time = value),
          ),
        ],
      ),
      const SizedBox(height: MacosChromeData.contentInset),
      const Text('Example export progress: 35 percent'),
      const ProgressBar(value: 35, semanticLabel: 'Example export'),
      const SizedBox(height: MacosChromeData.gap),
      const Wrap(
        spacing: MacosChromeData.gap,
        crossAxisAlignment: WrapCrossAlignment.center,
        children: [
          ProgressCircle(value: 35, semanticLabel: 'Example indexing'),
          ProgressCircle(semanticLabel: 'Example connection pending'),
          Text('Waiting for connection'),
        ],
      ),
    ],
  );
}
0
likes
140
points
11
downloads

Documentation

API reference

Publisher

verified publisherjatatech.com

Weekly Downloads

Flutter widgets and themes implementing the current macOS design language.

License

MIT (license)

Dependencies

appkit_ui_element_colors, equatable, flutter, gradient_borders, macos_window_utils, vector_math

More

Packages that depend on macos_liquid_glass_ui

Packages that implement macos_liquid_glass_ui