liquidum 0.0.1
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.
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.
GlassTabScaffoldgives 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.
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.pushstays 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 aSafeAreaor a scroll view keeps the last item clear of it. - To switch tabs from code, use
GlassTabScaffold.of(context).index = 1, or pass aGlassTabController.
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.
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 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: [...],
)
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: [...],
)
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.paintlays 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 #
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.
|
1 · Controller and glass field
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
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
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
When the lens lifts, the selected tab grows 16% and is lensed through it, as in UIKit. |
|
5 · Lighting
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
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. |

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 #
GlassTabBaris the bar on its own, for apps that manage their own pages or use a router such as go_router'sStatefulShellRoute. It takesitems,selectedIndexandonSelected, and the same styling options, withpaddingin place ofbarPadding. You place it yourself, so it has no margin.GlassBackdropwraps the content that glass floats over. The bar measures it to adapt; the scaffold adds one for you.Glassturns any widget into a glass surface, a capsule or a rounded rectangle. ItsGlassMaterialpresets areregular,regularDark,clear,clearDarkandidentity, andGlassMaterial.lerpanimates 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, aShaderMaskor 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
RepaintBoundaryinside 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.
