Skip to main content

glassy_ui_ios 0.3.1 copy "glassy_ui_ios: ^0.3.1" to clipboard
glassy_ui_ios: ^0.3.1 copied to clipboard

PlatformiOS

90 native iOS components for Flutter built with SwiftUI and UIKit. Real Liquid Glass on iOS 26+, classic styling on iOS 17-18, customizable from Dart.

glassy_ui_ios: 90 native iOS components for Flutter

pub version pub likes pub points downloads iOS 17+ MIT license

Real SwiftUI & UIKit components in your Flutter app.
Apple's Liquid Glass on iOS 26, automatic fallbacks on iOS 17–18.

Installation · Quick start · Widget examples · All 90 components · FAQ

See it in action #

Glass morphing
Glass morphing
glassMorphing
Floating action button
Floating action button
floatingActionButton
Glass transition
Glass transition
glassTransition
Download progress
App Store download
downloadProgress

Catalog Glass effect container Media controls Toolbar

Why glassy_ui_ios? #

Flutter draws every pixel itself, so Cupertino-style widgets can only imitate iOS. Liquid Glass is rendered by the system, with real-time refraction and morphing, and can't be faithfully recreated in Flutter. glassy_ui_ios embeds Apple's own components instead.

glassy_ui_ios Flutter look-alike widgets
Rendering ✅ Real SwiftUI / UIKit Recreated in Flutter
Liquid Glass (refraction, morphing) ✅ Native glassEffect ❌ Blur approximation
Matches every iOS update ✅ Automatically Needs a package update
System sheets, menus, popovers, haptics ✅ Native behavior Imitated
VoiceOver & Dynamic Type ✅ Built in Partial
iOS 17–18 support ✅ Classic materials fallback ✅
Customizable from Dart ✅ Colors, images, radius, fonts, glass ✅

Features #

  • 90 components: navigation bars, toolbars, glass buttons, tab bars, sidebars, split views, sheets, alerts, menus, pickers, progress, media and camera controls, and Liquid Glass effects. See the gallery below.
  • Truly native: SwiftUI and UIKit render each component, so system sheets, menus, popovers and haptics behave exactly like iOS.
  • Adaptive design: glassEffect, .glass / .glassProminent, GlassEffectContainer and glass morphing on iOS 26+, with material fallbacks on iOS 17–18.
  • Configurable from Dart: title, images (SF Symbols, assets, bytes), items, values, colors, background, corner radius, glass variant, fonts and light/dark. Changes apply live.
  • Two-way communication: every interaction comes back to Dart as a NativeEvent, and Dart drives components with trigger(), reset() and custom commands.
  • Extensible: register your own SwiftUI views and use them from Dart.
  • Copy-paste examples: every component comes with ready-to-use Dart code in the example app.
  • Fully documented: every public API has dartdoc, including each component's config fields, commands and events.
  • Swift Package Manager and a privacy manifest are included.

Requirements #

Minimum
iOS 17.0 (Liquid Glass needs iOS 26)
Flutter 3.27
Xcode 26 (to compile the Liquid Glass APIs)
Integration Swift Package Manager

Installation #

flutter pub add glassy_ui_ios

or add it to pubspec.yaml yourself:

dependencies:
  glassy_ui_ios: ^0.3.1

This plugin is distributed with Swift Package Manager, so turn it on for your app in pubspec.yaml:

flutter:
  config:
    enable-swift-package-manager: true

You can also turn it on globally with flutter config --enable-swift-package-manager.

Then set the iOS deployment target to 17.0: in Xcode, select Runner → General → Minimum Deployments. If your app still has a Podfile, set platform :ios, '17.0' there as well.

Quick start #

import 'package:glassy_ui_ios/glassy_ui_ios.dart';

SizedBox(
  height: 400,
  child: NativeComponent(
    type: NativeComponentType.prominentGlassButton,
    config: const NativeComponentConfig(
      title: 'Continue',
      image: NativeImage.symbol('arrow.right'),
      style: NativeStyle(
        tintColor: Colors.teal,
        cornerRadius: 20,
        backgroundGradient: [Colors.indigo, Colors.pink],
      ),
    ),
    onEvent: (event) => debugPrint('${event.name}: ${event.value}'),
  ),
);

NativeComponent sizes itself to its parent, like any platform view. Give it bounded constraints: an Expanded, a SizedBox, or a full page.

Glass widgets #

Every everyday control is also a typed Flutter widget: named parameters, typed callbacks, a normal widget size, and a tint and light/dark appearance taken from your app's Theme. They all start with Glass, so autocomplete lists them:

Scaffold(
  extendBody: true, // pages scroll under the glass bar
  appBar: AppBar(
    title: const Text('Home'),
    actions: [
      GlassIconButton(
        icon: const NativeImage.symbol('person.crop.circle'),
        onPressed: openProfile,
      ),
    ],
  ),
  body: ListView(
    padding: const EdgeInsets.all(16),
    children: [
      GlassSegmentedControl(
        items: const [NativeItem(title: 'Day'), NativeItem(title: 'Week')],
        selectedIndex: range,
        onChanged: (i) => setState(() => range = i),
      ),
      GlassSwitch(
        label: 'Wi-Fi',
        value: wifi,
        onChanged: (v) => setState(() => wifi = v),
      ),
      GlassSlider(value: volume, onChanged: (v) => setState(() => volume = v)),
      GlassMenuButton(
        title: 'Choose an option',
        width: 260,
        items: const [NativeItem(title: 'Option 1'), NativeItem(title: 'Option 2')],
        onSelected: (id) => NativeDialog.alert(title: 'Selected', message: id),
      ),
    ],
  ),
  bottomNavigationBar: GlassTabBar(
    items: const [
      NativeItem.symbol('Home', 'house.fill'),
      NativeItem.symbol('Profile', 'person.crop.circle.fill', badge: '2'),
    ],
    selectedIndex: index,
    onChanged: (i) => setState(() => index = i),
  ),
);
Group Widgets
Navigation GlassTabBar (also expandable), GlassToolbar, GlassBackButton, GlassCloseButton
Buttons GlassButton, GlassIconButton, GlassFloatingActionButton (expands into actions), GlassAvatarButton, GlassImageButton
Menus GlassMenuButton (submenus, overflow), GlassPopupButton
Controls GlassSegmentedControl, GlassSwitch, GlassStepper, GlassSlider, GlassPageIndicator, GlassCheckbox
Input GlassTextField (secure, multiline, keyboards), GlassSearchBar, GlassDatePicker (compact, wheel, calendar), GlassWheelPicker, GlassColorPicker
Progress & media GlassProgressIndicator (bar, ring, spinner), GlassPlayPauseButton, GlassMediaControls

Native UI that appears over the app is one call. Each returns what the user chose:

Call Native UI
NativeDialog.alert / .actionSheet / .prompt UIAlertController, with a text field for prompt
NativeSheet.show Sheet with medium / large detents listing options
NativeToast.show Glass toast at the top or bottom; touches pass through
NativeShare.share UIActivityViewController

On Android and other platforms most widgets fall back to their Material equivalent (Switch, Slider, SegmentedButton, TextField, MenuAnchor…), and every widget takes a fallback to show your own instead. Pass context: to the dialog, sheet and toast calls to get Material dialogs, bottom sheets and snack bars there; this also makes the iOS versions follow your app's light/dark theme.

Each widget is a platform view, which takes every touch inside its bounds, so give it the size it needs (width / height). Inside scroll views the widgets only claim taps, so drags still scroll the page; sliders also claim horizontal drags, and wheels and calendars claim all drags.

Copy-paste code for each one is in Widget examples below; the example app shows them all on its Widgets tab (example/lib/widgets_gallery.dart).

Controlling a component from Dart #

late NativeComponentController controller;

NativeComponent(
  type: NativeComponentType.halfSheet,
  onCreated: (c) => controller = c,
);

await controller.trigger();          // primary action: present, toggle, next…
await controller.reset();            // back to the initial state
await controller.send('custom', 42); // custom commands for your own components
controller.events.listen(print);     // stream of NativeEvent

NativeComponentType.triggerLabel describes what trigger() does for each component.

Live updates #

Put the config in state and change it. The native view updates in place without being recreated:

setState(() {
  config = config.copyWith(
    title: 'Updated',
    style: config.style.copyWith(tintColor: Colors.orange, glass: NativeGlass.clear),
  );
});

Widget examples #

Copy-paste examples for every Glass… widget and native presenter, grouped like the table above. Each snippet goes in a build method; the ones with a value keep it in your State and call setState in the callback. Every snippet also works on Android, where it shows the Material fallback.

GlassTabBar

A floating glass tab bar. Only the bar is native; the pages stay Flutter widgets.

int tab = 0;

Scaffold(
  extendBody: true, // pages scroll under the glass bar
  body: IndexedStack(index: tab, children: pages),
  bottomNavigationBar: GlassTabBar(
    items: const [
      NativeItem.symbol('Home', 'house.fill'),
      NativeItem.symbol('Search', 'magnifyingglass'),
      NativeItem.symbol('Inbox', 'tray.fill', badge: '3'),
      NativeItem.symbol('Profile', 'person.crop.circle.fill'),
    ],
    selectedIndex: tab,
    onChanged: (i) => setState(() => tab = i),
    expandable: false, // true: only the selected tab shows its title
    fallback: NavigationBar(
      selectedIndex: tab,
      onDestinationSelected: (i) => setState(() => tab = i),
      destinations: const [
        NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
        NavigationDestination(icon: Icon(Icons.search), label: 'Search'),
        NavigationDestination(icon: Icon(Icons.inbox), label: 'Inbox'),
        NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
      ],
    ),
  ),
);

Placed anywhere other than the bottom of the screen, pass safeArea: false.

GlassToolbar

A glass capsule of buttons. Items with an icon show the icon; the destructive role is red.

GlassToolbar(
  width: 240,
  items: const [
    NativeItem.symbol('Share', 'square.and.arrow.up'),
    NativeItem.symbol('Favorite', 'heart'),
    NativeItem.symbol('Info', 'info.circle'),
    NativeItem.symbol('Delete', 'trash', role: NativeItemRole.destructive),
  ],
  onPressed: (index) => debugPrint('Toolbar button $index'),
)

GlassBackButton · GlassCloseButton

Both pop the current route unless you pass onPressed.

AppBar(
  leading: const GlassBackButton(),
  actions: [GlassCloseButton(onPressed: () => Navigator.pop(context))],
)

Buttons #

GlassButton

GlassButton(
  label: 'Glass',
  onPressed: () {},
)

GlassButton(
  label: 'Continue',
  icon: const NativeImage.symbol('arrow.right'),
  prominent: true, // filled glass
  size: NativeControlSize.large,
  onPressed: () {},
)

GlassIconButton

GlassIconButton(
  icon: const NativeImage.symbol('person.crop.circle'),
  semanticLabel: 'Profile',
  onPressed: () {},
  fallback: IconButton(icon: const Icon(Icons.person), onPressed: () {}),
)

GlassIconButton(
  icon: const NativeImage.symbol('plus'),
  prominent: true,
  onPressed: () {},
)

GlassFloatingActionButton

Without actions it calls onPressed; with actions it expands into labelled glass buttons.

Scaffold(
  floatingActionButton: GlassFloatingActionButton(
    actions: const [
      NativeItem.symbol('Photo', 'photo'),
      NativeItem.symbol('Note', 'note.text'),
      NativeItem.symbol('Reminder', 'checklist'),
    ],
    onActionSelected: (index) => debugPrint('Create $index'),
    fallback: FloatingActionButton(
      onPressed: () {},
      child: const Icon(Icons.add),
    ),
  ),
);

GlassAvatarButton

An avatar (initials or photo) that opens a native menu.

GlassAvatarButton(
  initials: 'RP',
  // image: const NativeImage.asset('assets/me.jpg'),
  size: 40,
  items: const [
    NativeItem.symbol('Profile', 'person'),
    NativeItem.symbol('Settings', 'gear'),
    NativeItem.symbol('Sign Out', 'rectangle.portrait.and.arrow.right',
        role: NativeItemRole.destructive),
  ],
  onSelected: (id) => debugPrint(id), // 'Profile', 'Settings', 'Sign Out'
)

GlassImageButton

Artwork with a glass badge, e.g. a video or album cover.

GlassImageButton(
  image: const NativeImage.asset('assets/cover.jpg'),
  badge: 'play.fill', // null hides the badge
  width: 160,
  height: 160,
  cornerRadius: 18,
  onPressed: () {},
)

GlassMenuButton

Entries support icons, the destructive role and nested children (submenus). onSelected gets the entry's id, or its title.

GlassMenuButton(
  title: 'File',
  width: 140, // platform views can't size themselves: fit the title
  items: const [
    NativeItem.symbol('New File', 'doc.badge.plus'),
    NativeItem(
      title: 'Export As',
      symbol: 'square.and.arrow.up',
      children: [NativeItem(title: 'PDF'), NativeItem(title: 'PNG')],
    ),
    NativeItem.symbol('Delete', 'trash', role: NativeItemRole.destructive),
  ],
  onSelected: (id) => debugPrint(id),
)

An icon-only "more" menu:

GlassMenuButton(
  icon: const NativeImage.symbol('ellipsis'),
  showChevron: false,
  prominent: false,
  width: 52,
  items: const [
    NativeItem.symbol('Select', 'checkmark.circle'),
    NativeItem.symbol('View Options', 'slider.horizontal.3'),
  ],
  onSelected: (id) => debugPrint(id),
)

A menu that shows an alert for the chosen option:

GlassMenuButton(
  title: 'Choose an option',
  width: 260,
  items: const [
    NativeItem(title: 'Option 1'),
    NativeItem(title: 'Option 2'),
    NativeItem(title: 'Option 3'),
  ],
  onSelected: (option) => NativeDialog.alert(
    title: 'Selected',
    message: 'You selected $option',
    context: context,
  ),
)

GlassPopupButton

Shows the current choice and opens a native picker menu.

int repeat = 0;

GlassPopupButton(
  label: 'Repeat',
  items: const [
    NativeItem(title: 'Never'),
    NativeItem(title: 'Daily'),
    NativeItem(title: 'Weekly'),
    NativeItem(title: 'Monthly'),
  ],
  selectedIndex: repeat,
  onChanged: (i) => setState(() => repeat = i),
)

Controls #

GlassSegmentedControl

int range = 1;

GlassSegmentedControl(
  items: const [
    NativeItem(title: 'Day'),
    NativeItem(title: 'Week'),
    NativeItem(title: 'Month'),
  ],
  selectedIndex: range,
  onChanged: (i) => setState(() => range = i),
)

GlassSwitch

bool wifi = true;

GlassSwitch(
  label: 'Wi-Fi', // omit for a bare switch
  value: wifi,
  onChanged: (v) => setState(() => wifi = v),
)

GlassStepper

double guests = 2;

GlassStepper(
  label: 'Guests',
  value: guests,
  min: 1,
  max: 10,
  step: 1,
  onChanged: (v) => setState(() => guests = v),
)

GlassSlider

double brightness = 0.6;
double rating = 3;

GlassSlider(
  value: brightness,
  minIcon: 'sun.min',
  maxIcon: 'sun.max.fill',
  onChanged: (v) => setState(() => brightness = v),
  onChangeEnd: (v) => debugPrint('Saved $v'),
)

GlassSlider(
  value: rating,
  min: 1,
  max: 5,
  divisions: 4, // snaps to 1, 2, 3, 4, 5
  onChanged: (v) => setState(() => rating = v),
)

GlassPageIndicator

int page = 0;

GlassPageIndicator(
  count: 4,
  currentIndex: page,
  onChanged: (i) => setState(() => page = i), // tapped dot
)

GlassCheckbox

bool terms = false;

GlassCheckbox(
  label: 'I agree to the terms',
  value: terms,
  onChanged: (v) => setState(() => terms = v),
)

Input & pickers #

GlassSearchBar

final search = GlassTextFieldController(); // optional: clear(), focus(), unfocus()
String query = '';

GlassSearchBar(
  placeholder: 'Search photos',
  controller: search,
  onChanged: (q) => setState(() => query = q),
  onSubmitted: (q) => debugPrint('Search "$q"'),
)

GlassTextField

GlassTextField(
  placeholder: 'Email',
  keyboardType: GlassKeyboardType.email,
  onChanged: (text) => debugPrint(text),
  onSubmitted: (text) => debugPrint('Done: $text'),
)

const GlassTextField(placeholder: 'Password', obscureText: true)

const GlassTextField(placeholder: 'Notes', multiline: true)

GlassDatePicker

DateTime date = DateTime.now();

// Compact field with a label
GlassDatePicker(
  label: 'Date',
  value: date,
  onChanged: (d) => setState(() => date = d),
)

// Time only
GlassDatePicker(
  label: 'Time',
  value: date,
  mode: GlassDatePickerMode.time,
  onChanged: (d) => setState(() => date = d),
)

// Inline calendar, limited to the next year
GlassDatePicker(
  value: date,
  style: GlassDatePickerStyle.graphical, // or .wheel
  firstDate: DateTime.now(),
  lastDate: DateTime.now().add(const Duration(days: 365)),
  onChanged: (d) => setState(() => date = d),
)

GlassWheelPicker

int size = 1;

GlassWheelPicker(
  items: const [
    NativeItem(title: 'Small'),
    NativeItem(title: 'Medium'),
    NativeItem(title: 'Large'),
  ],
  selectedIndex: size,
  onChanged: (i) => setState(() => size = i),
)

GlassColorPicker

iOS only: pass a fallback for other platforms.

Color accent = Colors.indigo;

GlassColorPicker(
  label: 'Accent colour',
  value: accent,
  supportsOpacity: false,
  onChanged: (c) => setState(() => accent = c),
  fallback: const Text('Colour picker is available on iOS'),
)

Progress & media #

GlassProgressIndicator

double progress = 0.4;

GlassProgressIndicator(value: progress, label: 'Uploading') // bar

GlassProgressIndicator(
  value: progress,
  style: GlassProgressStyle.circular, // ring with percentage
)

const GlassProgressIndicator() // no value: spinner

GlassPlayPauseButton

bool playing = false;

GlassPlayPauseButton(
  isPlaying: playing,
  onChanged: (v) => setState(() => playing = v),
)

GlassMediaControls

GlassMediaControls(
  isPlaying: playing,
  onPlayPause: (v) => setState(() => playing = v),
  onPrevious: () => debugPrint('Previous'),
  onNext: () => debugPrint('Next'),
)

Alerts, sheets & toasts #

Pass context: so the iOS UI follows your app's light/dark theme and other platforms get the Material equivalent.

NativeDialog.alert

Returns the id (or title) of the tapped action.

final choice = await NativeDialog.alert(
  title: 'Delete photo?',
  message: 'This photo will be deleted from all your devices.',
  actions: const [
    NativeItem(title: 'Delete', role: NativeItemRole.destructive),
    NativeItem(title: 'Cancel', role: NativeItemRole.cancel),
  ],
  context: context,
);
if (choice == 'Delete') deletePhoto();

Without actions it shows a single "OK".

NativeDialog.actionSheet

final choice = await NativeDialog.actionSheet(
  title: 'You have unsaved changes',
  actions: const [
    NativeItem(title: 'Discard', role: NativeItemRole.destructive),
    NativeItem(title: 'Save Draft'),
    NativeItem(title: 'Cancel', role: NativeItemRole.cancel),
  ],
  context: context,
);

NativeDialog.prompt

Returns the entered text, or null when cancelled.

final name = await NativeDialog.prompt(
  title: 'Rename',
  placeholder: 'Name',
  initialValue: 'Holiday.jpg',
  confirmLabel: 'Save',
  context: context,
);
if (name != null) rename(name);

NativeSheet.show

Returns the id (or title) of the tapped item, or null when dismissed.

final target = await NativeSheet.show(
  title: 'Share to',
  message: 'Choose where to send the photo.',
  items: const [
    NativeItem.symbol('Messages', 'message.fill'),
    NativeItem.symbol('Mail', 'envelope.fill'),
    NativeItem(title: 'AirDrop', subtitle: 'Nearby devices', symbol: 'airplayaudio'),
  ],
  detents: const [NativeSheetDetent.medium, NativeSheetDetent.large],
  context: context,
);

NativeToast.show

A glass toast over the whole app; touches pass through it.

NativeToast.show(
  'Copied to clipboard',
  icon: 'doc.on.doc.fill',
  position: NativeToastPosition.top, // or .bottom
  duration: const Duration(seconds: 2),
  context: context,
);

NativeShare.share

Returns whether the user completed a share.

await NativeShare.share(
  text: 'Check out glassy_ui_ios',
  url: 'https://pub.dev/packages/glassy_ui_ios',
);

Full screen example #

A complete app using the tab bar, an app bar button, a menu and an alert:

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

void main() => runApp(const MaterialApp(home: HomePage()));

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int tab = 0;

  @override
  Widget build(BuildContext context) {
    final pages = [
      _Page(
        title: 'Home',
        onProfile: () => setState(() => tab = 1),
        child: GlassMenuButton(
          title: 'Choose an option',
          width: 260,
          items: const [NativeItem(title: 'Option 1'), NativeItem(title: 'Option 2')],
          onSelected: (option) => NativeDialog.alert(
            title: 'Selected',
            message: 'You selected $option',
            context: context,
          ),
        ),
      ),
      const _Page(title: 'Profile', child: Text('Profile Screen')),
    ];

    return Scaffold(
      extendBody: true,
      body: IndexedStack(index: tab, children: pages),
      bottomNavigationBar: GlassTabBar(
        items: const [
          NativeItem.symbol('Home', 'house.fill'),
          NativeItem.symbol('Profile', 'person.crop.circle.fill'),
        ],
        selectedIndex: tab,
        onChanged: (i) => setState(() => tab = i),
        fallback: NavigationBar(
          selectedIndex: tab,
          onDestinationSelected: (i) => setState(() => tab = i),
          destinations: const [
            NavigationDestination(icon: Icon(Icons.home), label: 'Home'),
            NavigationDestination(icon: Icon(Icons.person), label: 'Profile'),
          ],
        ),
      ),
    );
  }
}

class _Page extends StatelessWidget {
  const _Page({required this.title, required this.child, this.onProfile});

  final String title;
  final Widget child;
  final VoidCallback? onProfile;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
        actions: [
          if (onProfile != null)
            GlassIconButton(
              icon: const NativeImage.symbol('person.crop.circle'),
              semanticLabel: 'Profile',
              onPressed: onProfile!,
              fallback: IconButton(icon: const Icon(Icons.person), onPressed: onProfile),
            ),
          const SizedBox(width: 8),
        ],
      ),
      body: Center(child: child),
    );
  }
}

Configuration #

Every component reads the same NativeComponentConfig. Each one documents the fields it uses in its Swift source (ios/glassy_ui_ios/Sources/glassy_ui_ios/Components/).

Field Purpose
title, subtitle, message, placeholder Text content
image Main icon or artwork: NativeImage.symbol('star.fill'), NativeImage.asset('assets/cover.jpg'), NativeImage.bytes(data)
items Tabs, menu actions, segments, options, rows (List<NativeItem>; nest with children for submenus and columns)
value, min, max, step Initial value and range. DateTime and Color values are encoded automatically
enabled Disables the whole component
extra Component-specific options, e.g. {'detents': ['0.25', 'medium', 'large']}
style A NativeStyle (see below)

NativeStyle #

Property Description
tintColor Accent color for buttons, switches, sliders and selection
foregroundColor Primary text and icon color
backgroundColor / backgroundGradient Background behind the component
showBackdrop Colorful default backdrop that makes glass refraction visible
surfaceColor Card color when glass is disabled
cornerRadius Corner radius of cards, sheets and rounded glass
glass NativeGlass.regular, .clear or .none
glassTint, interactive Glass tint and touch reaction
padding, spacing Layout
fontSize, fontWeight, fontDesign Title typography
colorScheme Force light or dark
controlSize mini … extraLarge

NativeItem #

const NativeItem.symbol('Inbox', 'tray', badge: '3');
const NativeItem(id: 'delete', title: 'Delete', symbol: 'trash', role: NativeItemRole.destructive);
const NativeItem(title: 'Export', children: [NativeItem(title: 'PDF'), NativeItem(title: 'PNG')]);

Images from assets #

Declare the asset in your app's pubspec.yaml and pass its path:

NativeComponentConfig(image: const NativeImage.asset('assets/album.jpg'));

Events #

NativeEvent has a name, a value and a time. The names are consistent across components:

Event Sent when
change A value changed (slider, toggle, picker, tab…)
select An item, row or menu entry was chosen (value = item id)
tap / action A button or action was pressed
presented / dismissed A sheet or overlay appeared or disappeared
complete A progress indicator finished

Custom native components #

Any SwiftUI view in your app can become a component. Make it conform to NativeComponent and register it:

import glassy_ui_ios
import SwiftUI

struct RatingView: NativeComponent {
  static let componentId = "rating"
  @EnvironmentObject private var context: ComponentContext
  @Environment(\.nativeConfig) private var config
  @State private var rating = 0

  var body: some View {
    ComponentScaffold {
      GlassCard(config.title ?? "Rate") {
        HStack {
          ForEach(1...5, id: \.self) { star in
            Button {
              rating = star
              context.emit("change", star)
            } label: {
              Image(systemName: star <= rating ? "star.fill" : "star")
            }
          }
        }
      }
    }
    .seed($rating, from: config.int(or: 0))
    .onNativeCommand { if $0.name == "reset" { rating = 0 } }
  }
}

// AppDelegate, after GeneratedPluginRegistrant.register(...)
NativeComponentRegistry.register(RatingView.self)
NativeComponent.custom(id: 'rating', config: const NativeComponentConfig(title: 'Rate us'));

These building blocks are available to your views: adaptiveGlass, nativeGlass, nativeButtonStyle, AdaptiveGlassContainer, adaptiveGlassID, adaptiveGlassUnion, ComponentScaffold, ComponentBackground, GlassCard, NativeIcon, NativeArtwork, NativeItemLabel, NativeItemButton, CloseButton, GlassIconButton and GlassSupport.isAvailable.

System helpers #

final info = await NativeSystem.info(); // iOS version, liquidGlass, registered ids
await NativeSystem.haptic(NativeHaptic.success);

Screenshots were taken on an iPhone simulator running iOS 27 (Liquid Glass), using each component's default configuration. The example app contains all 90 components, a live Customize panel, and a Code button with copy-paste ready code for each component (also in example/lib/snippets.dart).

Navigation Bar / App Bar
Navigation Bar / App Bar
NativeComponentType.navBar
NavigationStack + .toolbar — glass bar on iOS 26
Back Button
Back Button
NativeComponentType.backButton
NavigationStack(path:) system back button
Close Button
Close Button
NativeComponentType.closeButton
Button(role: .close) / xmark.circle.fill
Navigation Title
Navigation Title
NativeComponentType.navTitle
.navigationTitle + .toolbarTitleMenu
Large Navigation Title
Large Navigation Title
NativeComponentType.largeNavTitle
.navigationBarTitleDisplayMode(.large)
Toolbar
Toolbar
NativeComponentType.toolbar
ToolbarItemGroup + ToolbarSpacer (iOS 26)
Floating Toolbar
Floating Toolbar
NativeComponentType.floatingToolbar
GlassEffectContainer + .glassEffect / material capsule
Bottom Toolbar
Bottom Toolbar
NativeComponentType.bottomToolbar
ToolbarItem(placement: .bottomBar)
Toolbar Action Button
Toolbar Action Button
NativeComponentType.toolbarActionButton
.confirmationAction + .glassProminent

Buttons (9) #

Glass Button
Glass Button
NativeComponentType.glassButton
.buttonStyle(.glass) / .bordered
Prominent Glass Button
Prominent Glass Button
NativeComponentType.prominentGlassButton
.buttonStyle(.glassProminent) / .borderedProminent
Image Button
Image Button
NativeComponentType.imageButton
Button with image label + custom ButtonStyle
Circular Icon Button
Circular Icon Button
NativeComponentType.circularIconButton
.buttonBorderShape(.circle) + glass
SF Symbols Button
SF Symbols Button
NativeComponentType.sfSymbolsButton
.symbolEffect, rendering modes, variableValue
Plus Button
Plus Button
NativeComponentType.plusButton
Glass circle + .numericText transition
Minus Button
Minus Button
NativeComponentType.minusButton
Glass circle + .numericText transition
Floating Action Button
Floating Action Button
NativeComponentType.floatingActionButton
Glass morphing with glassEffectID
Avatar Action Button
Avatar Action Button
NativeComponentType.avatarActionButton
Toolbar Menu with avatar label

Controls (7) #

Segmented Control
Segmented Control
NativeComponentType.segmentedControl
Picker(.segmented)
Toggle / Switch
Toggle / Switch
NativeComponentType.toggle
Toggle(.switch)
Stepper
Stepper
NativeComponentType.stepper
Stepper(value:in:step:)
Slider
Slider
NativeComponentType.slider
Slider with min/max value labels
Slider with Tick Marks
Slider with Tick Marks
NativeComponentType.sliderTicks
Slider(tick:) iOS 26 / drawn ticks
Page Indicator
Page Indicator
NativeComponentType.pageIndicator
TabView(.page) + .indexViewStyle
Checkbox-Style Control
Checkbox-Style Control
NativeComponentType.checkbox
Custom ToggleStyle with SF Symbols

Tabs & Structure (10) #

Tab Bar
Tab Bar
NativeComponentType.tabBar
TabView + .tabItem
Floating Tab Bar
Floating Tab Bar
NativeComponentType.floatingTabBar
.tabBarMinimizeBehavior(.onScrollDown) / custom capsule
Tab Bar Item
Tab Bar Item
NativeComponentType.tabBarItem
.tabItem + .badge
Expandable Tab Bar
Expandable Tab Bar
NativeComponentType.expandableTabBar
Glass capsules that expand with glassEffectID
Search Tab
Search Tab
NativeComponentType.searchTab
Tab(role: .search) iOS 18+ / tabItem
Sidebar
Sidebar
NativeComponentType.sidebar
NavigationSplitView + .listStyle(.sidebar)
Navigation Stack
Navigation Stack
NativeComponentType.navigationStack
NavigationStack(path:) + navigationDestination
Split View
Split View
NativeComponentType.splitView
Three-column NavigationSplitView
Floating Accessory View
Floating Accessory View
NativeComponentType.floatingAccessory
.tabViewBottomAccessory (iOS 26) / safeAreaInset
Inspector Panel
Inspector Panel
NativeComponentType.inspector
.inspector(isPresented:)

Sheets, Dialogs & Overlays (11) #

Bottom Sheet
Bottom Sheet
NativeComponentType.bottomSheet
Sheet with .height detent + background interaction
Half-Screen Sheet
Half-Screen Sheet
NativeComponentType.halfSheet
.presentationDetents([.medium])
Full-Screen Sheet
Full-Screen Sheet
NativeComponentType.fullSheet
.fullScreenCover
Detented Sheet
Detented Sheet
NativeComponentType.detentedSheet
Detents with selection binding
Sheet Drag Indicator
Sheet Drag Indicator
NativeComponentType.dragIndicator
.presentationDragIndicator
Alert Dialog
Alert Dialog
NativeComponentType.alert
.alert with roles + text field
Confirmation Dialog
Confirmation Dialog
NativeComponentType.confirmationDialog
.confirmationDialog — popover on iOS 26
Action Sheet
Action Sheet
NativeComponentType.actionSheet
UIKit UIAlertController(.actionSheet)
Popover
Popover
NativeComponentType.popover
.popover + .presentationCompactAdaptation
Toast / Notification Overlay
Toast / Notification Overlay
NativeComponentType.toast
Glass capsule overlay with transition
Confirmation Popup
Confirmation Popup
NativeComponentType.confirmationPopup
Custom glass card modal
Popup Menu
Popup Menu
NativeComponentType.popupMenu
Menu + Picker (pop-up button)
Dropdown Menu
Dropdown Menu
NativeComponentType.dropdownMenu
Menu with sections, Picker and Toggle
Context Menu
Context Menu
NativeComponentType.contextMenu
.contextMenu with preview
Overflow Menu
Overflow Menu
NativeComponentType.overflowMenu
Toolbar Menu with ellipsis
Nested Submenu
Nested Submenu
NativeComponentType.nestedSubmenu
Menu inside Menu
Share Menu
Share Menu
NativeComponentType.shareMenu
ShareLink + UIActivityViewController
Text Selection Menu
Text Selection Menu
NativeComponentType.textSelectionMenu
UITextView editMenuForTextIn

Input & Pickers (10) #

Text Field
Text Field
NativeComponentType.textField
TextField styles + glass field
Secure Password Field
Secure Password Field
NativeComponentType.secureField
SecureField with reveal + strength
Search Field
Search Field
NativeComponentType.searchField
.searchable + .searchSuggestions
Multiline Text Editor
Multiline Text Editor
NativeComponentType.textEditor
TextEditor + FocusState
Date Picker
Date Picker
NativeComponentType.datePicker
DatePicker(.graphical / .compact)
Time Picker
Time Picker
NativeComponentType.timePicker
DatePicker(.hourAndMinute, .wheel)
Wheel Picker
Wheel Picker
NativeComponentType.wheelPicker
Picker(.wheel) multi-column
Menu Picker
Menu Picker
NativeComponentType.menuPicker
Picker(.menu)
Color Picker
Color Picker
NativeComponentType.colorPicker
ColorPicker (returns hex to Dart)
Floating Search Bar
Floating Search Bar
NativeComponentType.floatingSearchBar
Glass search capsule over content

Progress (7) #

Circular Progress Indicator
Circular Progress Indicator
NativeComponentType.circularProgress
Gauge(.accessoryCircularCapacity) + ring
Linear Progress Bar
Linear Progress Bar
NativeComponentType.linearProgress
ProgressView(value:)
Determinate Progress Bar
Determinate Progress Bar
NativeComponentType.determinateProgress
ProgressView(value:total:) + Gauge
Indeterminate Loading Indicator
Indeterminate Loading Indicator
NativeComponentType.indeterminateLoading
ProgressView() + animated bar
Download Progress Indicator
Download Progress Indicator
NativeComponentType.downloadProgress
App Store-style ring button
Upload Progress Indicator
Upload Progress Indicator
NativeComponentType.uploadProgress
Per-file ProgressView
Activity Indicator
Activity Indicator
NativeComponentType.activityIndicator
UIKit UIActivityIndicatorView

Media & Camera (11) #

Gallery Toolbar
Gallery Toolbar
NativeComponentType.galleryToolbar
Grid + bottomBar selection toolbar
Photo Viewer Toolbar
Photo Viewer Toolbar
NativeComponentType.photoViewerToolbar
Glass top/bottom chrome
Media Playback Controls
Media Playback Controls
NativeComponentType.mediaControls
Now Playing glass card
Play / Pause Button
Play / Pause Button
NativeComponentType.playPause
.contentTransition(.symbolEffect(.replace))
Skip Forward / Backward Button
Skip Forward / Backward Button
NativeComponentType.skipButtons
gobackward.15 / goforward.30 + bounce
Volume Slider
Volume Slider
NativeComponentType.volumeSlider
Slider + MPVolumeView
Full-Screen Media Close Button
Full-Screen Media Close Button
NativeComponentType.mediaClose
Glass xmark over media
Picture-in-Picture Controls
Picture-in-Picture Controls
NativeComponentType.pipControls
Draggable mini player + AVKit support check
Camera Controls
Camera Controls
NativeComponentType.cameraControls
Shutter, modes, flash, flip
Zoom Controls
Zoom Controls
NativeComponentType.zoomControls
Zoom pills + MagnifyGesture
Crop Controls
Crop Controls
NativeComponentType.cropControls
Aspect ratios, straighten dial, rotate

Liquid Glass (9) #

Scroll-Edge Glass Effect
Scroll-Edge Glass Effect
NativeComponentType.scrollEdge
.scrollEdgeEffectStyle(.soft / .hard)
Glass Background
Glass Background
NativeComponentType.glassBackground
Glass.regular / .clear / .tint
Custom Glass Shape
Custom Glass Shape
NativeComponentType.customGlassShape
.glassEffect(in: customShape)
Glass Button Group
Glass Button Group
NativeComponentType.glassButtonGroup
.glassEffectUnion
Glass Effect Container
Glass Effect Container
NativeComponentType.glassContainer
GlassEffectContainer(spacing:) blending
Glass Morphing Animation
Glass Morphing Animation
NativeComponentType.glassMorphing
.glassEffectID morphing
Glass Transition
Glass Transition
NativeComponentType.glassTransition
.glassEffectTransition(.materialize)
App Icon with Layered Glass Styling
App Icon with Layered Glass Styling
NativeComponentType.appIcon
Layered icon: default / dark / clear / tinted
Widget with Clear or Tinted Appearance
Widget with Clear or Tinted Appearance
NativeComponentType.widgetAppearance
Full colour / clear / tinted rendering

Limitations #

  • Glass refracts only native content inside the platform view, not the Flutter widgets behind it. That's why components draw their own backdrop (NativeStyle.showBackdrop).
  • iOS only. On other platforms NativeComponent shows its fallback widget.
  • Picture-in-picture and camera controls are UI only: no video asset and no camera session are used. MPVolumeView renders only on physical devices.
  • Every component is a platform view, so prefer a few large components per screen over many small ones.

FAQ #

Does it work on Android, web or desktop?

No. The components are Apple's own, so they only exist on iOS. On other platforms NativeComponent renders its fallback widget, so you can show a Material version there.

Do my users need iOS 26?

No. The minimum is iOS 17. On iOS 26+ components use Liquid Glass. On iOS 17–18 the same components use classic materials (.regularMaterial, .bordered…) automatically. Check at runtime with NativeSystem.info() → liquidGlass.

My app still uses CocoaPods. Can I use this package?

Yes. Turn on Swift Package Manager for your app (see Installation). Flutter then integrates this plugin through SPM, while your other CocoaPods plugins keep working.

How do I get the code for a specific component?

Run the example app and tap the Code button on any component, or browse example/lib/snippets.dart. Every snippet is compiled and tested against the current API.

What about performance?

Each component is a platform view. That's fine for whole screens, bars, sheets and cards. Prefer a few larger components per screen over dozens of tiny ones in a scrolling list.

Can I add my own SwiftUI views?

Yes. See Custom native components.

Support the project #

If glassy_ui_ios saves you time, please give it a 👍 on pub.dev and a ⭐ on GitHub. It helps other Flutter developers find it.

Found a bug or want a component? Open an issue. Pull requests are welcome; see CONTRIBUTING.

License #

MIT. See LICENSE.

1
likes
160
points
--
downloads
screenshot

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

90 native iOS components for Flutter built with SwiftUI and UIKit. Real Liquid Glass on iOS 26+, classic styling on iOS 17-18, customizable from Dart.

Repository (GitHub)
View/report issues
Contributing

Topics

#ios #liquid-glass #swiftui #ui #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on glassy_ui_ios

Packages that implement glassy_ui_ios