Liquid Glass Widgets — AI Agent Guide
This skill defines the architecture, design principles, API dictionary, and strict implementation rules for building Flutter applications with liquid_glass_widgets.
Follow this guide strictly when writing, refactoring, or reviewing UI code to ensure high visual fidelity, 60/120 fps GPU performance, and zero hallucinations.
1. Golden Architectural Rules (Mandatory)
Rule 1: Glass is a Platter, Not a Wrapper
Glass in the iOS 26 design system is reserved for the navigation and control layer — floating chrome that sits above content.
- DO use glass for: Navigation bars (
GlassAppBar), tab bars (GlassTabBar), floating buttons (GlassButton,GlassIconButton), toolbars, modal sheets (GlassModalSheet), dialogs (GlassDialog), and standalone floating cards (GlassCard). - DO NOT use glass for: Full-screen backgrounds, dense list view items, video players, image galleries, or text article bodies. Backgrounds should be rich wallpapers or gradients; content cards should be clean and readable.
Rule 2: NEVER Nest Refractive Glass inside Refractive Glass
Placing refractive glass widgets inside another refractive glass container is an optical and performance anti-pattern.
- CRITICAL ANTI-PATTERN: Placing
GlassButton,GlassIconButton,GlassSegmentedControl,GlassSlider, orGlassSwitchinside aGlassCard,GlassGroupedSection, orGlassContainer. - Why? Interactive glass controls provide their own refractive surface. Nesting creates severe double-refraction distortion, clips spring jelly animations, and wastes GPU fill-rate.
- Correct Pattern:
- In a
GlassCardorGlassGroupedSection: Use standard text, icons, and non-refractive controls (orGlassListTilewhich is specifically designed without an internal glass layer). - Outside the card / on the page platter: Use
GlassSegmentedControl,GlassButton,GlassSlider, or standaloneGlassSwitch(useOwnLayer: true).
- In a
Rule 3: Single Public Import
- ALWAYS:
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart'; - NEVER import
package:liquid_glass_widgets/src/.... - NEVER instantiate
LiquidGlass(...)directly — it is an internal Impeller-only renderer. Always useAdaptiveGlassor the high-levelGlass*widgets.
Rule 4: Ban Obsolete Pre-1.0 APIs
GlassBottomBarandGlassSearchableBottomBarwere deleted in v1.0.0.- ALWAYS USE:
GlassTabBar.bottom(...)orGlassTabBar.searchable(...).
Rule 5: Honor Quality Tiers
GlassQuality.standard: Default for 95% of controls, cards, and input fields. Uses a highly optimized single-pass fragment shader.GlassQuality.premium: Multi-pass Impeller shader with chromatic dispersion, realistic refraction, and dynamic specular highlights. Reserved for persistent navigation bars (GlassAppBar,GlassTabBar) and hero surfaces.GlassScaffoldautomatically promotes its bars to premium.GlassQuality.minimal: Lightweight shader-free fallback (BackdropFilteronly). Use for ultra-dense scrolling lists or low-power modes.
2. App Lifecycle & Initialization
In main.dart, always initialize the shader pipeline before runApp(), and wrap the application with LiquidGlassWidgets.wrap().
dartimport 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:liquid_glass_widgets/liquid_glass_widgets.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Pre-warms fragment shaders in parallel to prevent first-frame white flashes or shader jank await LiquidGlassWidgets.initialize(); runApp( LiquidGlassWidgets.wrap( adaptiveQuality: true, // Dynamically benchmarks device raster budget and scales quality respectSystemAccessibility: true, // Automatically degrades on Reduce Motion / Reduce Transparency // CRITICAL FOR MATERIAL APPS: Bridges Material ThemeMode without importing flutter/material in the engine brightnessResolver: Theme.maybeBrightnessOf, // GlassThemeData.simple is the recommended constructor — applies the same settings to // both light and dark, overlaid on the library's built-in per-mode defaults. // Use GlassThemeData(light: ..., dark: ...) only for fine-grained per-mode control. theme: GlassThemeData.simple( blur: 12, thickness: 25, quality: GlassQuality.standard, ), child: const MyApp(), ), ); }
3. Screen Architecture: GlassScaffold
For any screen featuring liquid glass navigation bars or floating surfaces, always use GlassScaffold instead of Flutter's built-in Scaffold.
GlassScaffold solves complex compositing challenges automatically:
- Guaranteed Z-Ordering: App bars and bottom bars paint above scrolling body content, preventing body cards from overlapping navigation buttons.
- Edge Fading: Content fades smoothly as it scrolls under bars (
.scrollEdgeEffectStyle(.soft)matching iOS 26). - Content-Aware Brightness: Dynamically flips app bar icons and typography between light and dark depending on the luminosity of the content scrolling underneath.
- Auto-Padding: Automatically accounts for safe areas and bar heights.
dartclass HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { int _selectedTab = 0; @override Widget build(BuildContext context) { return GlassScaffold( // 1. Wallpaper background (image, gradient, or solid) background: Image.asset('assets/wallpaper.jpg', fit: BoxFit.cover), contentAwareBrightness: true, // 2. Navigation Bar // GlassAppBar uses `actions` (List<Widget>), not `trailing`. appBar: GlassAppBar( title: const Text('Dashboard'), actions: [ GlassIconButton( icon: const Icon(CupertinoIcons.bell_fill), onPressed: () => _openNotifications(), ), ], ), // 3. Unified Bottom Tab Bar bottomBar: GlassTabBar.bottom( selectedIndex: _selectedTab, onTabSelected: (index) => setState(() => _selectedTab = index), tabs: const [ GlassTab(icon: Icon(CupertinoIcons.house_fill), label: 'Home'), GlassTab(icon: Icon(CupertinoIcons.chart_bar_fill), label: 'Stats'), GlassTab(icon: Icon(CupertinoIcons.gear_alt_fill), label: 'Settings'), ], ), // 4. Scrollable Body Content body: ListView( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20), children: [ // Content cards stay opaque or use GlassCard for grouping GlassGroupedSection( header: const Text('ACCOUNT'), children: [ GlassListTile( leading: const Icon(CupertinoIcons.person_fill), title: const Text('Profile'), trailing: GlassListTile.chevron, onTap: () {}, ), GlassListTile( leading: const Icon(CupertinoIcons.lock_shield_fill), title: const Text('Privacy & Security'), trailing: GlassListTile.chevron, onTap: () {}, ), ], ), ], ), ); } }
4. Component Dictionary & Substitution Table
When generating Flutter UI, replace standard Flutter widgets with their native liquid glass counterparts:
| Flutter Standard Widget | Liquid Glass Replacement | Key Usage Notes |
|---|---|---|
Scaffold | GlassScaffold | Handles z-order, edge fading, content-aware brightness. |
AppBar / CupertinoNavigationBar | GlassAppBar | Transparent iOS 26 style by default. Supports .pinned for gel morph. |
BottomNavigationBar / CupertinoTabBar | GlassTabBar.bottom | Unified glass navigation pill. GlassBottomBar is deleted. |
| Bottom nav that collapses to selected tab on scroll | GlassTabBar.minimizable | Minimizes to a single pill on scroll; supports an optional trailing action button. |
ElevatedButton / CupertinoButton | GlassButton | Refractive button with squeeze & stretch physics and touch glow. |
IconButton | GlassIconButton | Refractive circular/squircle button with onPressed. |
PopupMenuButton / Context Menu | GlassPullDownButton | Combined trigger button and GlassMenu. |
CupertinoSegmentedControl | GlassSegmentedControl<T> | Fluid animated glass indicator with jelly physics. Do NOT wrap in GlassCard. |
Switch / CupertinoSwitch | GlassSwitch | Fluid spring jump animation. Use useOwnLayer: true when standalone. |
Slider / CupertinoSlider | GlassSlider | Glass track with draggable jelly thumb and haptic snaps. |
Card | GlassCard | Glass container for grouping content. Content inside stays opaque. |
Grouped ListView | GlassGroupedSection | Inset grouped list container with automatic dividers between GlassListTiles. |
ListTile | GlassListTile | Grouped row item. Shares parent glass layer inside GlassGroupedSection. |
Divider | GlassDivider | Refractive subtle separator line. |
TextField / CupertinoTextField | GlassTextField | Input field with glowing refractive focus border. |
showModalBottomSheet | GlassModalSheet.show | Multi-detent fluid sheet (peek, half, full) with spring physics. |
showDialog / CupertinoAlertDialog | GlassDialog.show | Liquid glass modal alert with GlassDialogAction buttons. |
SnackBar / Banner | GlassToast | Floating glass HUD banner. |
5. Overlays: Modal Sheets, Dialogs & Transitions
GlassModalSheet
Presents an iOS 26 fluid modal sheet with detents:
dartGlassModalSheet.show( context: context, initialState: GlassSheetState.half, halfSize: 0.5, peekSize: 90.0, builder: (sheetContext) { return ListView( padding: const EdgeInsets.all(20), children: const [ Text( 'Modal Sheet Content', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ], ); }, );
GlassDialog
Presents a native alert dialog:
dartGlassDialog.show( context: context, title: 'Discard Draft?', message: 'This action cannot be reversed.', actions: [ GlassDialogAction( label: 'Cancel', onPressed: () => Navigator.of(context).pop(), ), GlassDialogAction( label: 'Discard', isDestructive: true, onPressed: () { Navigator.of(context).pop(); _discardDraft(); }, ), ], );
GlassMenu Submenus
Give a GlassMenuItem a submenu list to open a native iOS-style card over the parent (the parent recedes and dims; tapping the card's header or the exposed parent collapses one level). Submenus nest. Works in GlassMenu and GlassPullDownButton:
dartGlassPullDownButton( icon: const Icon(CupertinoIcons.ellipsis_circle), items: [ GlassMenuItem( title: 'Share…', icon: const Icon(CupertinoIcons.share), // onTap is required but never called on a submenu row: // activating it opens the card instead. onTap: () {}, submenu: [ GlassMenuItem(title: 'Messages', icon: const Icon(CupertinoIcons.chat_bubble_fill), onTap: _sendMessage), GlassMenuItem(title: 'Mail', icon: const Icon(CupertinoIcons.mail_solid), onTap: _sendMail), ], ), const GlassMenuDivider(), GlassMenuItem(title: 'Delete', icon: const Icon(CupertinoIcons.trash), isDestructive: true, onTap: _delete), ], )
- A submenu row shows a chevron automatically unless you pass
trailing. - Do not build nested menus by opening a second
GlassMenufrom an item'sonTap; usesubmenu.
GlassMaterialize
For widgets appearing or disappearing dynamically, use GlassMaterialize to match iOS 26's progressive defogging transition:
dartGlassMaterialize( visible: _showFilterPill, // GlassChip.label is a String, not a Widget child: GlassChip( label: 'Active Filters', onDeleted: () => setState(() => _showFilterPill = false), ), )
6. Advanced Navigation: Gel Morph with GlassNavigationShell
To achieve the signature iOS 26 navigation bar transition — where the capsule swells, bounces, and cross-fades icons between routes:
- Wrap the Navigator in
GlassNavigationShell(typically inCupertinoApp.builder):
dartCupertinoApp( builder: (context, child) => GlassNavigationShell( swipeCommitTransition: GlassSwipeCommitTransition.crossFade, // optional: plain cross-fade on back-swipe verticalBarBehavior: GlassVerticalBarBehavior.automatic, // optional: iPhone Duo vertical strip child: child!, ), home: const HomeScreen(), )
- Use
GlassAppBar.pinnedon destination screens. Items are declared asGlassBarItemdata (not arbitrary widgets), which allows the shell to hoist them above theNavigatorand morph them across routes:
dartGlassAppBar.pinned( title: const Text('Detail'), actions: [ // GlassBarItem.icon uses `onTap` (not `onPressed`) GlassBarItem.icon( icon: const Icon(CupertinoIcons.square_and_arrow_up), onTap: () => _share(), ), // Tinted capsule (iOS 26 prominent action): set tintColor + separate background. // The capsule fills with the colour; foreground icon auto-flips to white/black. GlassBarItem.icon( icon: const Icon(CupertinoIcons.checkmark), onTap: _save, background: GlassBarItemBackground.separate, tintColor: CupertinoColors.activeBlue, ), ], )
Note: Without
GlassNavigationShell,GlassAppBar.pinnedfalls back gracefully — items render inside the bar in-route, so all screens work either way.
iPhone Duo: Under the shell, pinned bars,
GlassTabBar.bottom/.searchable,GlassToolbarandGlassLargeTitlemove into the vertical bar strip on their own; pass a large title's controller to itsGlassAppBar.pinned, which draws the title there. AGlassAppBar.pinnedinside aGlassModalSheetfollows the sheet, whoseplacementmirrorspresentationPlacement(_:). UseGlassBarItem.custom(axisBehavior: GlassBarItemAxisBehavior.verticalPreferred)for custom content that should go with them, and readGlassVerticalBar.maybeOf(context)to fit a bar the package does not draw.
7. Common Agent Pitfalls (Checklist before outputting code)
- Did you import the public API only? (
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';). Never importsrc/. - Did you call
await LiquidGlassWidgets.initialize()inmain()? Shaders must be pre-warmed. - Did you wrap your app in
LiquidGlassWidgets.wrap()? Required for theme and accessibility scaling. - Are you using
GlassScaffoldinstead ofScaffold? Prevents z-index glitches with glass nav bars. - Did you avoid nesting glass in glass? Never put
GlassButton,GlassSlider, orGlassSwitchdirectly insideGlassCardorGlassGroupedSection. - Did you use
GlassTabBar.bottominstead ofGlassBottomBar?GlassBottomBaris deleted. -
GlassButtonusesonTap, notonPressed. (GlassButton(onTap: () {})) — the opposite ofElevatedButton. -
GlassIconButtonusesonPressed, notonTap. (GlassIconButton(onPressed: () {})). -
GlassAppBarusesactions: [...](a List), nottrailing:. There is notrailingparameter. -
GlassChip.labelis aString, not aWidget. Passlabel: 'text', notlabel: Text('text'). -
GlassBarItem.iconusesonTap(required), notonPressed. - Nested menus use
GlassMenuItem(submenu: [...]). The parent row still needsonTap: () {}(required, never called). Don't open a secondGlassMenufromonTap. -
tintColoronGlassBarItemrequiresbackground: GlassBarItemBackground.separate. A shared-background item cannot be tinted (asserts in debug mode). The foreground icon colour is flipped automatically — do not also set a manualIcon(color:)on the child.


