liquid_tab_bar 2.0.0
liquid_tab_bar: ^2.0.0 copied to clipboard
An iOS 26-style floating liquid-glass tab bar for Flutter with a deformable droplet, refraction, search, actions, badges, and scroll-aware folding.
2.0.0 #
Breaking: one bar, not two #
1.2.0 shipped two bars side by side — the original, whose selection lens was
a pane of glass that a press could balloon past the capsule (the grab), and
the deformable droplet. This release keeps only the droplet.
If you are on 1.2.0:
package:liquid_tab_bar/droplet.dartis gone. Its bar is now the one and only export ofpackage:liquid_tab_bar/liquid_tab_bar.dart, so drop thedroplet.dartimport (and any prefix) and keep the default one.LiquidTabBarfrom the default import is a different widget with a different theme. Styling moves toLiquidBarStyle,LiquidDropletSurfaceStyle,DropletRefractionStyle,LiquidTabActionStyleandLiquidBadgeStyle.- The grab is gone, and with it
LiquidTabBarTheme.pressLensandGlassStyle.zoom. A press deforms the droplet instead. LiquidTabItem's unnamed constructor is gone; useLiquidTabItem.icon.forceOpaqueis gone; passmaterial: LiquidTabBarMaterial.opaque.foldOnScrollis gone; useshrinkOnScroll.
Staying on the original bar is a supported choice: 1.2.0 carries both and
remains on pub.dev. Pin liquid_tab_bar: 1.2.0 if you want the grab.
- Cleaned up the pre-release public API: removed ineffective search options
(
showClearButton,animationDuration), the duplicate search marker (LiquidTabAction.isSearch), duplicate droplet refraction aliases, and the global debug warning switch. Seedoc/api_cleanup_migration.mdfor source migrations. - Added
LiquidDropletShadowwith only the supported droplet shadow fields, made explicit default refraction styles remain explicit, enabled requested tab icon sizes above the default slot, and added nullable theme reset options. - Corrected API documentation and debug output without changing calibrated Normal, Glossy, Light, or Dark rendering defaults.
- Brightened Light Glossy with a wider polished bevel and clearer frost while preserving Dark Glossy. Increased Medium/Strong droplet optical depth and spectral separation, then narrowed the color fringe for a more natural lens.
- Added opt-in
LiquidBarStyle.glossy(brightness: ...)with a polished neutral bevel, clearer backdrop colors, and coordinated shader/blur palettes; exposed it in the styling demo without changing the default surfaces. - Enabled subtle motion-only, content-sampled RGB dispersion in the droplet
presets. Color splits follow the curved bevel over icons and labels, leaving
flat centers and resting lenses unchanged;
dispersion: 0disables the split. - Updated light and dark surfaces with translucent glass, neutral gray selection, and subtle edge reflections. Removed the droplet's painted rainbow rim and aligned default shader/blur tint palettes across iOS and Android.
- Made the deformable droplet bar the sole public API and consolidated usage
under
package:liquid_tab_bar/liquid_tab_bar.dart. - Restored bounded liquid swelling, travel deformation, current-droplet hit testing, destination capture, redirectable travel, and deferred final selection commits.
- Added stale-selection protection so scrubbing and rapid retargeting produce
only one final
onSelectedcallback. - Added custom SVG/widget icon examples and documented the current presets, light/dark themes, material modes, and folded shapes.
- Fixed the example SVG assets so the custom-icons demo renders correctly.
1.2.0 #
A second bar. The package now ships two, side by side, and nothing about the first one changed — this release only adds.
import 'package:liquid_tab_bar/liquid_tab_bar.dart'; // the default bar
import 'package:liquid_tab_bar/droplet.dart'; // the droplet variant
The droplet variant — package:liquid_tab_bar/droplet.dart #
Contributed by Mohammed Hafiz (@MohammedHafiz27) in #5.
Where the default bar makes its selection lens a pane of glass in its own right — so a press can balloon it past the capsule and magnify what it holds — the droplet keeps the lens a contained pill and refracts the icons and labels behind it through a shader of its own, only while it moves. Two different answers to the same question; neither is the other's successor, which is why they now sit next to each other rather than one replacing the other.
It brings, on its side of the package:
LiquidTabBarScaffold— setsextendBody: true, reserves bottom scroll padding, and observes primary vertical scrolling for the fold with noNotificationListenerof your own.LiquidScrollPadding,SliverLiquidScrollPaddingandLiquidTabBar.reservedPadding(context)cover the layouts it does not.- An expandable search field (
LiquidTabAction.search) that morphs the bar edge to edge and anchors above the keyboard, withopenSearch()/closeSearch()and Android back handling. - Separate action buttons, placed
togetherorsplit. - Badges: unread dots, count pills that fold to
99+, text pills and custom widgets — refracted by the lens as it crosses them. droplet_glass.frag, the dedicated refraction shader.- Style objects:
LiquidBarStyle,LiquidDropletSurfaceStyle,DropletRefractionStyle,LiquidTabActionStyle,LiquidBadgeStyle. LiquidTabBarTheme.dark()/.adaptive(context),GlassStylepresets (frosted,prismaticCaustics,clearCrystal,deepRefraction),LiquidFoldedShape.circle/.oval, andLiquidGovernorConfig.- Accessibility semantics across tabs, badges, search and the folded state.
The two libraries deliberately share type names (LiquidTabBar,
LiquidTabBarTheme, LiquidTabItem, LiquidTabBarController). Import one,
or prefix the other:
import 'package:liquid_tab_bar/droplet.dart' as droplet;
Fixed, in both bars #
- Android Impeller/OpenGLES rendered the glass mirrored. The backdrop tap
flipped Y under
IMPELLER_TARGET_OPENGLES, but Flutter already hands the texture inFlutterFragCoord's orientation, so the flip sampled the screen upside down. Found by Mohammed on physical Android hardware; removed from every shader in the package.
Fixed, in the droplet variant #
- The glass tier is gated on
LiquidGlass.supportedrather than.ready, so Skia and OpenGL backends fall back to blur instead of rendering black. - The frost samples four rings instead of three, clearing the ring banding the old spacing showed on high-contrast pages.
- Split placement no longer centres the capsule or overlaps the tabs, LTR or RTL.
- The frame governor degrades at its threshold rather than a frame later, and
resetGovernor()truly zeroes its counters. - The frost filter cache is a bounded 24-entry LRU with quantized keys, and
the shader
ImageFilteris cached per render object. - Search keeps one persistent input, a stable reported height and a staged close; the action circle and the search pill share a visual centre.
- Multi-touch no longer lets a second finger jump the scrub.
Note on numbering #
The contribution branch carried 0.3.0, which would have landed underneath
the 1.0.1 on pub.dev. Because the default bar's API is untouched and the
droplet arrives as a new library beside it, this is a minor bump, not the
breaking one a replacement would have been.
1.1.0 #
The grab: pressing the bar now balloons the lens past the capsule — 24pt
taller, escaping the bar's top and bottom edge evenly, a tenth wider — while the glass magnifies the tab
it holds (a new zoom knob on GlassStyle, ×1.18 grabbed, with the channels
zooming slightly apart so the magnified glyph fringes at its own edges) and
the dispersion opens to 0.85 with the rim light brightened under it. All of
it rides one press spring, up on touch-down and home on release, replacing
the old instant 6% swell. The lens moved out of the bar's clip to make the
overflow possible; at rest and while folding it still sizes itself inside
the bar, so nothing else changed.
Off switch: LiquidTabBarTheme(pressLens: false) restores the old press
exactly. The blur tier keeps the swell and the fringe threads but cannot
magnify (no shader); zoom: 1 leaves the shader's output bit-identical to
before.
1.0.1 #
The selection lens survives a fold and unfold. Three defects in one cycle of the bar collapsing to its pill and opening again:
- The lens parked by LTR index in an RTL app. Its slot was seeded from
initState, where the widget cannot readDirectionality. It resolves indidChangeDependenciesnow — once, so a later theme or text-scale change never drags the lens off wherever the user last put it. - On unfold the lens stayed wherever it had drifted to.
didUpdateWidgetnever fires on the way back open, becauseselectedIndexdid not change, so nothing re-targeted it. It re-springs to the selected tab now, snapping exactly when the gap is already sub-pixel. - Mid-fold the lens could outgrow the shrinking bar and have its glass edge cut by the clip. Its width lerps toward a pill-safe width and is clamped to what the current bar rect — and the lens's own position in it — can hold. The glass surface is also skipped entirely below 2% fade, where it was invisible to the eye but still painting a backdrop shader inside the fold clip, which cut a visible seam at the boundary.
No API changes: LiquidTabBar, LiquidTabItem, LiquidTabBarTheme,
LiquidTabBarController and LiquidGlass are untouched. The version moves to
1.0 because that surface is now settled, not because anything in it moved.
Thanks to Yousef Sobhy for the fixes.
0.1.0 #
- First release, extracted from the Orderbase courier app: the glass, blur and opaque tiers, the soap-bubble selection lens, finger scrubbing, fold on scroll, the frame governor.
Pre-release development entries #
The two entries below come from the droplet's own development branch and were
never published under those numbers. The releases that exist on pub.dev are
0.1.0, 1.0.1, 1.1.0, 1.2.0 and 2.0.0.
0.3.0 Unreleased #
- Consolidated outer surfaces into
LiquidBarStyle, droplet surfaces intoLiquidDropletSurfaceStyle(includingBoxShadow), selected action fill intoLiquidTabActionStyle, and badge appearance intoLiquidBadgeStyle. - Kept all six
DropletRefractionStylecontrols andnone/subtle/medium/strongpresets; removed obsolete droplet glass/fringe configuration. - Internalized renderer infrastructure from the supported public barrel and
removed redundant
foldOnScrollaliases; useshrinkOnScroll. - Added zero-configuration scrolling and folding to
LiquidTabBarScaffold: automatically observes primary vertical scroll notifications whenshrinkOnScroll: trueis set, with object-identity deduplication for existing manual listeners. LiquidTabItem.iconnow supports const construction.- Fixed expandable search and separate action surface vertical alignment, ensuring the action circle and search pill share an identical visual center.
- Fixed expandable search keyboard layout and input ownership: stable reported height, one persistent input, unified keyboard movement, and staged close.
- Replaced old showcase screens with four focused examples, including an Arabic RTL preview. Overhauled README with comprehensive visuals and code samples.
- Preserved default rendering against locked deterministic visual baselines.
Breaking replacements are documented in the 0.3.0 migration guide.
0.2.0 #
New Features #
- Programmatic Search API (
LiquidTabBarController&LiquidTabBar):- Added
LiquidTabBarController.isSearching,openSearch(), andcloseSearch({bool clearText = false})for programmatic search morph control from external widgets (app bars, buttons, shortcuts). - Added static convenience helpers
LiquidTabBar.openSearch(context),LiquidTabBar.closeSearch(context), andLiquidTabBar.isSearching(context). - Added
LiquidTabBarSearch.clearOnCloseconfiguration to automatically reset the search query when closing the search view.
- Added
- Configurable Folded Shape (
foldedShape):- Added
LiquidFoldedShapeenum (circlevsoval). - Added
foldedShapeproperty to bothLiquidTabBarandLiquidTabBarTheme. - Implemented nullable fallback resolution:
LiquidTabBar.foldedShapedefaults tonullto seamlessly inherit fromtheme.foldedShape, cleanly resolving toLiquidFoldedShape.circle(matching the separate action button) if neither is set.
- Added
- Curated Named
GlassStylePresets:- Promoted the example app's quick optical presets into reusable, first-class compile-time constants on
GlassStyle:GlassStyle.frosted: Classic Apple-style frosted glass with balanced diffusion and gentle rim specular (matches baselinebarparameters).GlassStyle.prismaticCaustics: Vivid chromatic dispersion (0.32) with sharp specular highlights (0.65) and enhanced saturation (1.60).GlassStyle.clearCrystal: Zero-blur glass emphasizing clean refraction, rim highlights, and edge dispersion without obscuring background content.GlassStyle.deepRefraction: Heavy optical slab with deep displacement (depth: 14), thick frost diffusion (blur: 40), and light absorption.
- Promoted the example app's quick optical presets into reusable, first-class compile-time constants on
- Theming & Accessibility:
- Added
LiquidTabBarTheme.dark()preset for sleek dark-mode glass styling. - Added
LiquidTabBarTheme.adaptive(context)factory to automatically match ambientBrightness. - Added
badgeTextandiconSizesupport toLiquidTabItemfor notification counts and custom sizing. - Added comprehensive accessibility semantics for tab items, badges, and folded state ("Expand navigation bar" button).
- Added dynamic text scaling support in
LiquidTabBar.reservedHeight(context).
- Added
- Scroll Handling & Control:
- Added
shrinkOnScroll(andfoldOnScrollalias) toLiquidTabBarandLiquidTabBarControllerto easily toggle bar minimization on scroll. LiquidTabBarController.handleScrollnow filters out inner nested scrollables (depth == 0) by default, with anallowNested: trueoption.
- Added
Fixes & Performance Improvements #
- Dark Mode Glass Tuning:
- Tuned
GlassStyleandGlassLightPainterfor dark backgrounds, preventing washed-out white bands on dark surfaces. - Made
GlassLightPaintertheme-aware: dark mode renders subtle translucent white rim highlights (0.06–0.20alpha) instead of harsh dark drop-shadow edges, creating a razor-crisp chamfer bevel against dark backgrounds. - Boosted caustics and specular clarity in
LiquidLensChromaticPainterfor dark themes.
- Tuned
- Opaque Tier Selection Pill Sync & Geometry:
- Replaced delayed active-color droplet with a flat neutral gray/white-gray pill (
0x14000000light /0x24FFFFFFdark) that updates in immediate lockstep with the selected icon, matching native iOS 26 Segmented Control behavior. - Eliminated desync and lag during fast scrubbing and tab tapping on the opaque tier.
- Replaced delayed active-color droplet with a flat neutral gray/white-gray pill (
- Split Action Placement (
LiquidTabActionPlacement.split):- Fixed geometry calculation where split placement incorrectly centered the capsule or overlapped tabs. Split placement now pins the tab capsule firmly to the leading margin and the separate action button to the trailing margin in both LTR and RTL directions.
- Frame Governor Timing & Reset Lifecycle:
- Fixed governor timing delay: frame degradation now triggers immediately upon reaching the slow frame threshold (
_slow >= 6), without lingering on dropping frames. - Fixed governor reset path (
LiquidTabBarController.resetGovernor()): unwatching frames and re-arming the governor now strictly resets internal counters (_seen = 0,_slow = 0) to prevent residual dropped frame counts from triggering instant re-degradation.
- Fixed governor timing delay: frame degradation now triggers immediately upon reaching the slow frame threshold (
- Frost Cache Memory & LRU Eviction:
- Added key quantization to
_frostFilter(qBlur = (style.blur * 10).round() / 10,qSat = (style.saturation * 100).round() / 100) to prevent micro-float key proliferation during live slider tuning. - Replaced unbounded cache with a bounded 24-entry LRU cache featuring access promotion and least-recently-used eviction, preventing memory leaks while retaining frequently-used presets.
- Added key quantization to
- Skia & Non-Impeller Backend Fallback:
- Gated shader execution on
LiquidGlass.supported(_hasImpeller && isShaderFilterSupported) rather thanLiquidGlass.ready. - Automatically falls back to the backdrop blur tier on Skia/OpenGL backends, preventing driver crashes and corrupted black layers on non-Impeller Android devices. (Note: Verified on Android emulator with
EnableImpeller = false; physical device testing recommended prior to pub.dev publication).
- Gated shader execution on
- Android Back Navigation (
PopScope):- Wrapped
LiquidTabBarinPopScope(canPop: !_isSearching): when search morph is active, pressing the Android hardware back button or predictive back gesture cleanly dismisses search without popping the route or exiting the app.
- Wrapped
- Lifecycle & Memory Allocations:
- Cached
ui.ImageFilter.shader(_shader)as_shaderFilteron_RenderGlassFilter, eliminating per-frame native handle churn. - Pre-allocated
Paintobjects acrossGlassLightPainterandChromaticShaderCache, eliminating GC churn in paint routines. - Removed wrapping
Opacitywidget from_lensSurfaceto avoid offscreen save layer creation that broke backdrop screen coordinates. Replaced with direct alpha attenuation of lens tint, specular, and dispersion. - Added active pointer tracking to isolate touch gestures against multi-touch contention and accidental scrubbing jumps.
- Added assertions and safe guards against empty tab item lists and out-of-bounds
selectedIndex.
- Cached