ios_liquid_glass

Apple's native iOS 26 Liquid Glass for Flutter — real UIGlassEffect surfaces and the real UITabBarController tab bar — plus an Impeller liquid lens tier for the iridescent droplet bottom nav that refracts Flutter content (rainbow rim, jelly morph).

Impeller liquid droplet mid-morph over the tab bar, refracting the album tiles behind it with a rainbow rim Home tab: real native iOS 26 tab bar plus LiquidGlassButton and LiquidGlassIconButton variants

Left — liquid tier: the Impeller droplet caught mid-morph on the example's Droplet lens page. This is real refraction of Flutter pixels: "Night Swim" bends through the lens and the rim splits the backdrop into a rainbow. Right — native tier: Apple's real UITabBarController and UIGlassEffect buttons/cards on iOS 26.

The liquid droplet jelly-morphing between tabs, squashing and stretching while it refracts the album art behind the bar

The jelly morph between tabs (LiquidGlassBottomNav.liquid), recorded on the iOS 26.2 Simulator. The droplet squash-stretches with spring physics and keeps refracting the page content the whole way.

Close-up of the droplet lens mid-travel: the chromatic-aberration rim splits the backdrop colors like a prism

Lens close-up: chromatic aberration splits the backdrop like a prism. Tune the rim strength with chromaticAberration (default 0.02; the shots here use 0.06).

A pushed route: the native tab bar hides itself automatically, no route bookkeeping in the host app Favorites tab showing a tab badge and glass list rows

The native tier in motion: pushing any route hides the system tab bar with zero manual wiring — see LiquidGlassNavigatorObserver — and declarative badges render on the real UITabBar. The Simulator exaggerates native glass artifacts more than a device does — see Known limitations.

Tier When Rendering
liquid LiquidGlassBottomNav.liquid / preferLiquid: true Impeller live-backdrop droplet (needs Impeller)
native iOS 26+ default Native UIGlassEffect platform view / system UITabBar
plain everything else Solid surface, identical layout

Important: Native UIGlassEffect is decorative — it samples layers beneath the Flutter view and does not refract Flutter pixels. For the floating rainbow droplet over your icons/page, use the liquid tier and stack the bar over content.

Install

dependencies:
  ios_liquid_glass:
    path: /path/to/flutter_liquid_glass   # or ^0.9.0 from pub

Host app requirements

  • iOS Info.plist — enable platform views (native tier) and Impeller (liquid tier):
    <key>io.flutter.embedded_views_preview</key>
    <true/>
    <key>FLTEnableImpeller</key>
    <true/>
    
  • iOS 26+ SDK on the build machine for native glass (deployment target can stay lower).
  • Full restart after enabling Impeller (hot reload is not enough).

Usage

Register the package observer once — this alone makes the native bar hide on any pushed route (PIN, Order New Card, a details page, ...) and reappear on pop, with no per-route bookkeeping:

MaterialApp(
  navigatorObservers: [LiquidGlassNavigatorObserver.instance],
  // or with a router:
  // routerDelegate: appRouter.delegate(
  //   navigatorObservers: () => [LiquidGlassNavigatorObserver.instance],
  // ),
  home: const RootTabs(),
)

Then build the tab host:

import 'package:ios_liquid_glass/ios_liquid_glass.dart';

LiquidGlassTabScaffold(
  currentIndex: index,
  onIndexChanged: (i) => setState(() => index = i),
  destinations: [
    LiquidGlassTabDestination(
      title: 'Home',
      systemImage: 'house',
      selectedSystemImage: 'house.fill',
      icon: const Icon(Icons.home_outlined),
      selectedIcon: const Icon(Icons.home),
      page: const HomePage(),
    ),
    LiquidGlassTabDestination(
      title: 'Search',
      systemImage: 'magnifyingglass',
      icon: const Icon(Icons.search),
      page: const SearchPage(),
    ),
  ],
)

For hiding driven by app state instead of (or in addition to) route pushes — a PIN screen or blur overlay modeled as a flag rather than a route — add barVisible and/or shouldHideOverlay:

LiquidGlassTabScaffold(
  currentIndex: index,
  onIndexChanged: (i) => setState(() => index = i),
  barVisible: !isPinOpen,                          // app-driven flag
  shouldHideOverlay: () => isPinOpen || isBlurOpen, // same-frame check
  overlayListenable: router,                        // extra listenable to resync on
  destinations: [ /* ... */ ],
)

Use .shell when you already own the page stack:

LiquidGlassTabScaffold.shell(
  currentIndex: index,
  onIndexChanged: onIndexChanged,
  themeBuilder: (context) => LiquidGlassTheme.adaptive(context),
  overlayListenable: router,
  nativeTabs: [...],
  fallbackItems: [...],
  child: yourBody,
)

Scroll views should use MediaQuery.paddingOf(context).bottom (applied automatically when applyContentInset: true).

Liquid droplet bottom nav (matches the morphing lens look)

import 'package:ios_liquid_glass/ios_liquid_glass.dart';

Scaffold(
  body: Stack(
    fit: StackFit.expand,
    children: [
      Padding(
        padding: EdgeInsets.only(
          bottom: LiquidGlassBottomNav.contentBottomClearance(context),
        ),
        child: yourPage,
      ),
      LiquidGlassBottomNav.liquid(
        currentIndex: index,
        onIndexChanged: (i) => setState(() => index = i),
        items: [
          LiquidGlassNavItem.custom(
            iconBuilder: (context, g) => SvgPicture.asset(
              g.selected ? 'assets/home_fill.svg' : 'assets/home.svg',
              width: g.size,
              height: g.size,
              colorFilter: ColorFilter.mode(g.color, BlendMode.srcIn),
            ),
          ),
          // ...
        ],
      ),
    ],
  ),
);

Tune the rainbow rim with chromaticAberration (default 0.02; the README shots use 0.06). See the Droplet lens page in example/lib/main.dart (DropletNavDemoPage) for the exact setup behind the screenshots.

Native glass card / buttons (iOS 26+)

LiquidGlassContainer(
  borderRadius: 24,
  style: const LiquidGlassStyle(
    nativeVariant: NativeGlassVariant.clear,
  ),
  child: const Text('Hello, glass'),
)

Capability detection

if (LiquidGlassPlatform.supportsLiquidLens) { ... }
if (LiquidGlassPlatform.supportsNativeGlass) { ... } // iOS 26+

The real system tab bar (advanced)

LiquidGlassNativeTabShell overlays Apple's actual UITabBarController (iOS 26+) — see previous docs / example for install, badges, route sync, and modals.

Known limitations

  • Native UIGlassEffect does not refract Flutter-drawn pixels.
  • Liquid tier needs Impeller; on Skia/Web it falls back to a frosted bar.
  • Custom native-tab icons must be pre-rasterized PNG bytes.
  • The iOS Simulator exaggerates native Liquid Glass artifacts; judge native visuals on a device. Liquid Impeller refraction is fine on sim.

Example

example/lib/main.dart — a four-tab LiquidGlassTabScaffold (Home, Search, Favorites, Profile) wired the way a host app is meant to: LiquidGlassNavigatorObserver registered once on the MaterialApp, a Navigator.push from Home that hides the bar with no extra code, a barVisible toggle for app-driven hiding (PIN screens, blur overlays), declarative tab badges, and the button/icon-button gallery from the screenshots above. Native tab shell on iOS 26+, Flutter fallback bars everywhere else.

The Liquid droplet nav button on Home pushes DropletNavDemoPage — the page behind the hero shots: LiquidGlassBottomNav.liquid stacked over a colorful library grid so the droplet has real content to refract (the system bar hides itself for the pushed route, so the two bars never fight).

cd example
flutter run

Libraries

ios_liquid_glass
Apple's native iOS 26 Liquid Glass (UIGlassEffect and UITabBarController) for Flutter, plus an Impeller liquid-lens tier for iridescent droplet nav that refracts Flutter content.
ios_liquid_glass_method_channel
ios_liquid_glass_platform_interface