dynamic_glass_glmv 0.1.0
dynamic_glass_glmv: ^0.1.0 copied to clipboard
A Flutter package for reusable dynamic glass UI surfaces and adaptive platform bottom navigation.
dynamic_glass_glmv #
Reusable frosted-glass surfaces and a fully adaptive bottom navigation bar for Flutter.
- iOS 26+ → real
UITabBarController/UITabliquid-glass bar viacupertino_native. - Every other platform (Android, iOS < 26, macOS, web, …) →
GlassPillNavBar: a blurred floating pill bar with an animated sliding selection indicator, styled after modern Telegram Android.
Installation #
Add to your pubspec.yaml:
dependencies:
dynamic_glass_glmv: ^0.1.0
Then run:
flutter pub get
Minimum iOS deployment target: 14.0 (set by
cupertino_native_glmv).
Dart SDK:^3.10.4· Flutter:>=3.3.0
Platform behaviour #
| Platform | What you get |
|---|---|
iOS 26+ (useNativeBottomBar: true, default) |
Native UITabBarController → system liquid-glass material |
iOS 26+ (useNativeBottomBar: false) |
GlassPillNavBar (frosted pill) |
| iOS < 26 | GlassPillNavBar (frosted pill) |
| Android | GlassPillNavBar (frosted pill) |
| macOS / Windows / Linux / Web | GlassPillNavBar (frosted pill) |
The scaffold on iOS (all versions) wraps content in a CupertinoPageScaffold and
overlays the bar in a Stack so the blur bleeds to the screen edge. On Android and other
platforms it uses a Scaffold with extendBody: true so the body scrolls behind the
floating pill.
Quick start #
import 'package:dynamic_glass_glmv/dynamic_glass_glmv.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(useMaterial3: true, colorSchemeSeed: const Color(0xFF0A84FF)),
home: DynamicGlassNavigationScaffold(
selectedItemColor: const Color(0xFF0A84FF),
items: const [
AdaptiveNavigationDestination(icon: 'house.fill', label: 'Home'),
AdaptiveNavigationDestination(icon: 'bell.fill', label: 'Inbox', badgeCount: 3),
AdaptiveNavigationDestination(icon: 'person.fill', label: 'Profile'),
],
screens: const [HomeScreen(), InboxScreen(), ProfileScreen()],
),
);
}
}
That single widget gives you:
- Animated sliding glass pill on Android / all non-iOS-26 platforms.
- Native liquid-glass
UITabBarControlleron iOS 26. - Badge drawn as
UITabBarItem.badgeValueon iOS 26, or as a red overlay bubble everywhere else.
Icon types #
AdaptiveNavigationDestination.icon accepts several forms:
| Value | iOS 26 native | iOS fallback | Android / other |
|---|---|---|---|
'house.fill' — SF Symbol name (no /) |
✅ native symbol | mapped CupertinoIcon | mapped Material icon |
'assets/icons/logo.png' — asset path (contains /) |
✅ asset image | ImageIcon |
ImageIcon |
AssetImage('assets/...') |
✅ asset image | ImageIcon |
ImageIcon |
Icons.home / CupertinoIcons.* (IconData) |
❌ silently dropped | Icon widget |
Icon widget |
Any Widget |
❌ silently dropped | used as-is | used as-is |
For best cross-platform parity use SF Symbol names — they are mapped to the nearest
CupertinoIcons on older iOS and the nearest Icons on Android automatically.
GlassPillNavBar #
The default non-iOS-26 renderer is GlassPillNavBar — a floating rounded bar with a
gradient sliding pill inspired by Telegram Android's current bottom navigation.
Standalone usage #
GlassPillNavBar(
items: destinations,
selectedIndex: _index,
onTap: (i) => setState(() => _index = i),
)
Customising the style #
Pass a GlassPillNavBarStyle to glassPillStyle on the scaffold (or directly to
GlassPillNavBar):
DynamicGlassNavigationScaffold(
glassPillStyle: const GlassPillNavBarStyle(
blurSigma: 32,
borderRadius: 32,
pillHeightFactor: 0.70,
tapFeedback: GlassPillTapFeedback.ripple,
showLabels: false, // icon-only mode
),
...
)
Key GlassPillNavBarStyle properties
| Property | Default | What it does |
|---|---|---|
blurSigma |
28 |
Backdrop blur strength |
borderRadius |
28 |
Pill corners of the outer bar |
height |
64 |
Bar content height (dp, before safe-area) |
horizontalPadding |
12 |
Side margin so the bar floats |
bottomPadding |
8 |
Gap above the home indicator |
pillHeightFactor |
0.78 |
Selection pill height as a fraction of bar height |
pillHorizontalInset |
8 |
Extra shrink on each side of a slot for the pill |
pillColors |
frosted white gradient | Custom gradient stop colors for the pill |
pillShineEnabled |
true |
Renders a hairline shine at the top of the pill |
showPill |
true |
Set to false for a pill-less icon-highlight style |
showLabels |
true |
false for icon-only mode |
tapFeedback |
none |
none · ripple · scale |
animationDuration |
320ms |
Pill slide speed |
animationCurve |
easeOutCubic |
Pill slide easing |
shadows |
subtle | List<BoxShadow> under the bar |
pillShadows |
glow + inner | List<BoxShadow> under the pill |
Preset examples
// Telegram dark (AMOLED-friendly)
const GlassPillNavBarStyle(
backgroundColor: Color(0xCC1C1C1E),
borderColor: Color(0x33FFFFFF),
pillColors: [Color(0xFF2C2C2E), Color(0xFF1C1C1E)],
pillBorderColor: Color(0x40FFFFFF),
)
// Accent pill (tinted glass pill instead of white)
GlassPillNavBarStyle(
pillColors: [
accentColor.withValues(alpha: 0.25),
accentColor.withValues(alpha: 0.10),
],
pillBorderColor: accentColor.withValues(alpha: 0.35),
)
// Minimal icon-only, no pill
const GlassPillNavBarStyle(
showLabels: false,
showPill: false,
tapFeedback: GlassPillTapFeedback.scale,
)
Custom icon / label renderers #
GlassPillNavBar(
items: items,
selectedIndex: _index,
onTap: (i) => setState(() => _index = i),
// Return any widget — Lottie, SVG, custom painter, …
iconBuilder: (context, item, selected, color, size) {
return LottieBuilder.asset(
selected ? item.selectedIcon : item.icon,
width: size, height: size,
);
},
labelBuilder: (context, item, selected, color) {
return Text(
item.label.toUpperCase(),
style: TextStyle(fontSize: 9, letterSpacing: 0.8, color: color),
);
},
)
iOS 26 exclusive features #
These only take effect when useNativeBottomBar: true (the default) and the app runs on
iOS 26+. They are silently ignored on every other platform.
DynamicGlassNavigationScaffold(
// Minimize the bar when the user scrolls down.
minimizeBehavior: TabBarMinimizeBehavior.onScrollDown,
// Split: trailing item rendered as a separate side pill (Apple News style).
split: true,
rightCount: 1, // number of items in the side pill
splitSpacing: 8.0,
items: [
const AdaptiveNavigationDestination(icon: 'house.fill', label: 'Home'),
const AdaptiveNavigationDestination(icon: 'bell.fill', label: 'Inbox', badgeCount: 2),
const AdaptiveNavigationDestination(icon: 'person.fill', label: 'Profile'),
const AdaptiveNavigationDestination(
icon: 'magnifyingglass',
label: 'Search',
isSearch: true, // → UISearchTab on iOS 26
hideLabel: true, // icon-only in the GlassPillNavBar on other platforms
),
],
screens: const [...],
)
| Feature | Property | iOS 26 | Other |
|---|---|---|---|
| Liquid-glass native bar | useNativeBottomBar: true |
✅ | — |
| Scroll-minimize | minimizeBehavior |
✅ | ignored |
| Search tab | isSearch: true |
✅ UISearchTab | ignored |
| Split / side pill | split: true |
✅ | ignored |
| Badge value | badgeCount |
✅ native badge | ✅ bubble overlay |
| Hide label | hideLabel: true |
best-effort | ✅ |
DynamicGlass surface #
A standalone frosted-glass surface you can wrap around any content:
DynamicGlass(
blur: 24,
opacity: 0.18,
tint: Colors.white,
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white38),
padding: const EdgeInsets.all(16),
child: const Text('Content behind this is blurred.'),
)
| Property | Default | |
|---|---|---|
blur |
20 |
ImageFilter sigma |
tint |
0xFFFFFFFF |
Color blended over the blurred area |
opacity |
0.16 |
Alpha applied to tint |
borderRadius |
zero |
Clip + decoration radius |
border |
null |
Optional BoxBorder |
padding / margin |
null |
Inner / outer spacing |
AdaptiveBottomNavigationBar (lower-level) #
If you manage the scaffold yourself, drop in the bar directly:
AdaptiveBottomNavigationBar(
items: items,
selectedIndex: _index,
onTap: (i) => setState(() => _index = i),
selectedItemColor: Theme.of(context).colorScheme.primary,
glassPillStyle: const GlassPillNavBarStyle(blurSigma: 40),
// Override everything for the non-iOS-26 branch:
fallbackNavBar: MyCustomBar(...),
)