liquidum 0.0.1 copy "liquidum: ^0.0.1" to clipboard
liquidum: ^0.0.1 copied to clipboard

The iOS 26 Liquid Glass tab bar for Flutter, measured against UIKit: real refraction, a lifting lens, adaptive light and dark glass, and full styling.

Liquidum #

The iOS 26 Liquid Glass tab bar, for Flutter. It looks and moves like Apple's own, and it takes a few lines to use.

pub package Flutter Renderer Platforms Status License

Liquidum's glass tab bar over colorful content: light, dark, and with a custom gradient and dark glass

Why Liquidum #

  • Measured, not imitated. Every part of the glass was rebuilt from Apple's own bar in the iOS 26.5 simulator: refraction, frost, dispersion, rim light, shadows, the lens and its springs, and the colors over light and dark content. Each was checked side by side with UIKit.
  • One widget. GlassTabScaffold gives you the bar, the pages, the navigation and the iOS behaviour. You describe the tabs.
  • Adapts like iOS. The glass reads what is under it and turns light or dark at UIKit's thresholds, and its colors follow the content.
  • Yours to style. Colors, badges, gradients, shadows, padding and fonts. You can use iOS's own tint model or paint your design's colors exactly.
  • Right to left. In Arabic, Hebrew and other right-to-left apps, the tabs are mirrored as UIKit mirrors them.

Install #

flutter pub add liquidum

Quick start #

import 'package:flutter/cupertino.dart';
import 'package:liquidum/liquidum.dart';

void main() => runApp(
  const CupertinoApp(
    home: GlassTabScaffold(
      tabs: [
        GlassTab(icon: CupertinoIcons.house_fill, label: 'Home', page: HomePage()),
        GlassTab(icon: CupertinoIcons.search, label: 'Search', page: SearchPage()),
        GlassTab(icon: CupertinoIcons.bell_fill, label: 'Alerts', badge: '3', page: AlertsPage()),
      ],
    ),
  ),
);

It works the same inside a MaterialApp.

The default bar: four tabs, the selected one on a light glass pill

What the scaffold does for you #

  • The bar floats over your pages, and its glass adapts to what scrolls under it.
  • The bar sizes itself the way UIKit's does. With up to three tabs it is only as wide as they need, and centered. With four or more it spans the screen.
  • Each tab keeps its page. Scroll position and state survive switching.
  • Each tab has its own navigator. Navigator.push stays inside the tab, under the bar, and the back button pops it first.
  • Tapping the selected tab again returns to its first page.
  • Your pages see the bar in MediaQuery.padding, so a SafeArea or a scroll view keeps the last item clear of it.
  • To switch tabs from code, use GlassTabScaffold.of(context).index = 1, or pass a GlassTabController.

Press and slide #

Press anywhere on the bar and the selection lifts into a clear lens that follows your finger exactly. Slide it across the tabs, then let go to select the tab under it. The tabs under the lens are magnified and refracted through it, and the bar swells, glows and washes under your finger, as in iOS.

Pressing the bar: the lens lifts, slides across the tabs magnifying them, and settles on the tab let go over

Customize #

Every option is optional and defaults to UIKit's look.

Colors and badges #

GlassTabScaffold(
  selectedColor: CupertinoColors.systemPink,
  tabs: [
    GlassTab(icon: CupertinoIcons.house_fill, label: 'Home', page: HomePage()),
    GlassTab(icon: CupertinoIcons.bell_fill, label: 'Alerts', badge: '3', page: AlertsPage()),
  ],
)
The bar with a pink selected tab and red badges

The selected color is mixed with the glass below it, as UITabBar.tintColor is. The other tabs take their colors from the glass, as in iOS 26. Set unselectedColor to override them; UIKit itself can't do that.

Glass colors, at rest and in motion #

The glass under the selected tab and the lens it lifts into while you drag can each have their own color.

GlassTabScaffold(
  selectedColor: CupertinoColors.systemIndigo,
  indicatorColor: Color(0x4D5E5CE6), // the selection at rest
  lensColor: Color(0x99FF2D55),      // the lens, while it moves
  tabs: [...],
)
An indigo selection at rest turns into a pink lens while dragged across the tabs, then back to indigo when let go

glassColor colors the whole bar the same way. The color's alpha sets how strongly it shows.

Decorations: gradients and shadows #

glass, indicator and lens decorate the three pieces of glass: the bar, the selection at rest, and the lens while it moves. Each takes a GlassDecoration, which works like a BoxDecoration.

GlassTabScaffold(
  brightness: Brightness.dark,
  adaptive: false,
  barPadding: EdgeInsets.all(8),
  glass: GlassDecoration(
    color: Color(0xC21C1C24),
    fill: GlassFill.paint,
    shadows: [BoxShadow(color: Color(0x33000000), offset: Offset(0, 4), blurRadius: 16)],
    innerShadows: [BoxShadow(color: Color(0x33FFFFFF), blurRadius: 1, spreadRadius: 1)],
  ),
  indicator: GlassDecoration(
    gradient: LinearGradient(colors: [Color(0xFF5E5CE6), Color(0xFFBF5AF2)]),
    fill: GlassFill.paint,
  ),
  tabs: [...],
)
A three-tab bar with dark glass and a purple gradient selection

There are two ways to fill the glass:

  • GlassFill.tint, the default, colors the glass the way iOS does (UIGlassEffect.tintColor). The color is true over white glass and deeper over dark glass.
  • GlassFill.paint lays the color over the blur exactly as given, the way a design tool does. Use it to match a brand's colors exactly.

Drop shadows are drawn around the glass, not under it. Inner shadows work like inset shadows.

Right to left #

An Arabic bar: the first tab on the right, with a badge

Nothing to set: in a right-to-left Directionality the first tab sits on the right, and badges move to the icon's other side.

All options #

On GlassTab
icon, selectedIcon IconData, or any widget with GlassTab.custom (SVG, image). The bar uses the icon's shape and colors it. Set tintIcon: false to keep its own colors, for example for a profile photo
label shown under the icon, and read by screen readers
badge a count or short word on the icon
color this tab's selected color
page the tab's first page
On GlassTabScaffold
selectedColor the selected tab's color; the system blue by default
unselectedColor the other tabs' color; by default they take their color from the glass
badgeColor the system red by default
glassColor, indicatorColor, lensColor shorthands that tint the bar, the selection at rest, and the lens
glass, indicator, lens GlassDecoration: color or gradient, fill, shadows, inner shadows
brightness, adaptive force light or dark glass; whether it adapts to the content
labelStyle merged into the labels' style, for example a font family
barPadding space inside the bar around the tabs; the bar grows with it (UIKit: 4pt)
barMargin the bar's distance from the screen's sides and bottom (UIKit: 21pt)
backgroundColor painted behind the pages
initialIndex, onTabChanged, controller the selected tab

Built against UIKit, stage by stage #

Liquidum was built in six stages, and each was checked against Apple's bar before the next one started:

  • Same conditions: the same screen, background and touches for both bars.
  • Glass: UIKit's own layers and filters were read in the simulator.
  • Comparison: recordings were compared frame by frame.
UIKit's tab bar above Liquidum's, at rest and while pressed with a custom tint and badges

1 · Controller and glass field

Frames of the lens lifting under a press, UIKit's recording with Liquidum's outlines over it

The lens lifting under a press, frame by frame. This is UIKit's recording, with Liquidum's lens (cyan) and bar (yellow) outlined on it, from the same touches.

2 · One shared backdrop

Apple's lens over a grid above Liquidum's

The bar and the lens read the content behind them from one capture. So the lens can bend content from past its own edge without seeing the bar's glass. Apple's is on top. Below is Liquidum's, whose optics were still placeholders at this stage.

3 · Optics

The lens's end over stripes, three times: UIKit, Apple's optics alone, Liquidum

Refraction and dispersion at the lens's end over stripes, zoomed 3×. From left: UIKit, UIKit's optics rebuilt on their own, Liquidum.

4 · Lifted content

Liquidum's bar at rest and with the lens lifted

When the lens lifts, the selected tab grows 16% and is lensed through it, as in UIKit.

5 · Lighting

UIKit's bar and Liquidum's side by side, at rest and lifted

Rim light, shadows, and the glow under the finger. UIKit is on the left and Liquidum on the right, at rest on top and lifted below.

6 · Adaptive appearance

UIKit's bar and Liquidum's over dark content, at rest and lifted

Over dark content the bar turns dark at UIKit's thresholds, and its colors follow the content. UIKit is on the left and Liquidum on the right.

Liquidum's bar on Android: dark, lifted, over gray and over color blocks
The same bar on Android, with Impeller on OpenGL ES.

At rest the two bars differ by about one level out of 255 on average, mostly at the edges of letters. Each stage's measurements, method and remaining differences are in doc/comparison.

Going further #

  • GlassTabBar is the bar on its own, for apps that manage their own pages or use a router such as go_router's StatefulShellRoute. It takes items, selectedIndex and onSelected, and the same styling options, with padding in place of barPadding. You place it yourself, so it has no margin.
  • GlassBackdrop wraps the content that glass floats over. The bar measures it to adapt; the scaffold adds one for you.
  • Glass turns any widget into a glass surface, a capsule or a rounded rectangle. Its GlassMaterial presets are regular, regularDark, clear, clearDark and identity, and GlassMaterial.lerp animates between them.
Glass(
  material: GlassMaterial.regular,
  child: Padding(padding: EdgeInsets.all(16), child: Text('Liquid glass')),
)

Examples #

The example folder holds three apps. Run them from it.

flutter run the scaffold with four tabs, about 40 lines
flutter run -t lib/playground.dart change colors, badges, appearance and direction live. On iOS, a switch swaps in Apple's own tab bar over the same content to compare them
flutter run -t lib/comparison.dart the harness used to compare with UIKit

Judge motion in profile or release mode: debug builds are much slower.

Requirements and limits #

  • Impeller is required (ImageFilter.shader), on iOS and on Android. Other renderers get a plain frosted bar.
  • Paint glass after the content it floats over, for example later in a Stack; the scaffold does this.
  • Don't wrap glass in an Opacity, a ShaderMask or another save layer. It has to paint into the root layer.
  • Keep tab icons static. The bar draws its tabs from an image of them, rendered again whenever they repaint. A RepaintBoundary inside an icon hides its repaints from the bar.
  • Not done yet:
    • minimizing the bar on scroll,
    • the scroll-edge effect,
    • light that follows the device's tilt,
    • morphing into a search field,
    • the accessibility settings (Reduce Transparency, Increase Contrast).

License #

Liquidum's source is open to read, under the Business Source License 1.1. It is not an open-source license:

  • Non-commercial use is free, in production too. That covers personal, educational and research projects, and apps that earn no revenue.
  • Commercial use needs a commercial license. That means an app that is sold, earns money through subscriptions, in-app purchases or ads, or is made by or for a company. Contact the author.
  • Each version becomes open source later. On 2030-10-13, or four years after that version was released if that comes first, it changes to the Apache License 2.0.
6
likes
150
points
--
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

The iOS 26 Liquid Glass tab bar for Flutter, measured against UIKit: real refraction, a lifting lens, adaptive light and dark glass, and full styling.

Repository (GitHub)
View/report issues

Topics

#liquid-glass #tab-bar #cupertino #ios #glassmorphism

License

unknown (license)

Dependencies

flutter

More

Packages that depend on liquidum