Tab bar
GlassTabBar is the floating iOS 26 tab bar. The selected tab lifts into a glass drop that magnifies the bar and can be dragged from tab to tab.
GlassTabBar · GlassTabItem · kGlassTabDropZoom · kGlassTabDropGrow · Source
GlassTabBar is the floating tab bar of iOS 26: a Bar holding two to five tabs,
each an icon over a label. The selected tab sits on a grey pill in the activeColor. Press it and
the pill lifts into a clear glass drop that magnifies the bar under it; drag the drop along the bar
and the tab under your finger lights up; let go and that tab is selected. A plain tap on a tab works
too.
The layout adapts to the width: below 80 px per tab, the icon sits over the label (phones); above that, they sit side by side (tablets and desktop).
When to use
- The top-level sections of an app: Home, Search, Library, Profile.
- Not for switching views inside one screen: that is a segmented control.
- Not for actions: tabs select a place, they don't do something. Use a toolbar for actions.
- Not for more than five sections. It asserts at least two.
Usage
const List<GlassTabItem> tabs = <GlassTabItem>[
GlassTabItem(icon: Icons.home, label: 'Home'),
GlassTabItem(icon: Icons.search, label: 'Search'),
GlassTabItem(icon: Icons.person, label: 'Profile'),
];
Positioned(
left: 16,
right: 16,
bottom: MediaQuery.paddingOf(context).bottom + 12,
child: GlassTabBar(
items: tabs,
selectedIndex: _tab,
onSelected: (int i) => setState(() => _tab = i),
),
)
The bar takes its width from its parent, so it needs a bounded width: Positioned(left:, right:)
is the usual way. Leave room at the bottom of your scrolling content so the last item is not hidden
behind it.
Behaviour
- The drop magnifies by
dropZoom(defaultkGlassTabDropZoom, 1.17, as on iOS).1means no magnification. - The drop is glass over glass (the bar), so while it is held the host captures one extra level. At rest, the bar is one surface.
- Each tab is a button for screen readers, labelled with its
labeland marked selected.
Warning
If glass cards scroll under the tab bar, wrap it in GlassAbove, or put a bottom
scroll edge under it. Otherwise the bar shows the content but not the cards.
Code
import 'package:flutter/material.dart';
import 'package:g1455/g1455.dart';
/// An app shell: one page per tab, a floating glass tab bar on top.
/// Assumes a GlassHost above the navigator (MaterialApp.builder).
class AppShell extends StatefulWidget {
const AppShell({super.key});
@override
State<AppShell> createState() => _AppShellState();
}
class _AppShellState extends State<AppShell> {
static const List<GlassTabItem> _tabs = <GlassTabItem>[
GlassTabItem(icon: Icons.home, label: 'Home'),
GlassTabItem(icon: Icons.search, label: 'Search'),
GlassTabItem(icon: Icons.library_music, label: 'Library'),
GlassTabItem(icon: Icons.person, label: 'Profile'),
];
int _tab = 0;
@override
Widget build(BuildContext context) {
final double bottom = MediaQuery.paddingOf(context).bottom;
return Scaffold(
body: Stack(
children: <Widget>[
Positioned.fill(
// Keeps every page alive; the bar floats over whichever is shown.
child: IndexedStack(
index: _tab,
children: <Widget>[
for (final GlassTabItem tab in _tabs)
ListView.builder(
// Room at the end so the last row is not under the bar.
padding: EdgeInsets.only(bottom: bottom + 96),
itemCount: 40,
itemBuilder: (BuildContext context, int i) => ListTile(title: Text('${tab.label} ${i + 1}')),
),
],
),
),
Positioned(
left: 16,
right: 16,
bottom: bottom + 12,
child: GlassTabBar(items: _tabs, selectedIndex: _tab, onSelected: (int i) => setState(() => _tab = i)),
),
],
),
);
}
}API
| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<GlassTabItem> |
required | The tabs. At least 2. |
selectedIndex |
int |
required | The selected tab. |
onSelected |
ValueChanged<int>? |
required | Called with the new tab. Null disables the bar. |
activeColor |
Color |
Color(0xFF007AFF) |
Icon and label colour of the selected tab, and of the tab under a held drop. |
dropZoom |
double |
kGlassTabDropZoom |
How much the held drop magnifies the bar. 1 for none; must be above 0. |
key |
Key? |
null |
GlassTabItem
| Parameter | Type | Default | Description |
|---|---|---|---|
icon |
IconData |
required | The tab's icon. |
label |
String |
required | The tab's label, also read by screen readers. |
Constants
| Name | Value | Description |
|---|---|---|
kGlassTabDropZoom |
1.17 |
The default dropZoom, read off iOS 26. |
kGlassTabDropGrow |
10.5 |
How much larger the held drop is than the resting pill, per side, in px. |