liquid_glass_lens_navbar 0.1.0
liquid_glass_lens_navbar: ^0.1.0 copied to clipboard
An iOS 26 Liquid Glass bottom navigation bar for Flutter: frosted glass, a refracting liquid lens selection, hide on scroll, and adaptive quality.
import 'package:flutter/material.dart';
import 'package:liquid_glass_lens_navbar/liquid_glass_lens_navbar.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatefulWidget {
const ExampleApp({super.key});
@override
State<ExampleApp> createState() => _ExampleAppState();
}
class _ExampleAppState extends State<ExampleApp> {
ThemeMode _mode = ThemeMode.light;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'liquid_glass_lens_navbar example',
debugShowCheckedModeBanner: false,
themeMode: _mode,
theme: ThemeData(brightness: Brightness.light, useMaterial3: true),
darkTheme: ThemeData(brightness: Brightness.dark, useMaterial3: true),
home: HomePage(
onToggleTheme: () => setState(
() => _mode = _mode == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light,
),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key, required this.onToggleTheme});
final VoidCallback onToggleTheme;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _index = 1;
final _tabBar = LiquidGlassNavBarController();
LiquidGlassAppearance _appearance = LiquidGlassAppearance.clear;
double? _opacity;
void _openGlassSettings() {
showModalBottomSheet<void>(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setSheet) {
void update(VoidCallback fn) {
setState(fn);
setSheet(() {});
}
return SafeArea(
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Liquid Glass',
style: TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 12),
SegmentedButton<LiquidGlassAppearance>(
segments: const [
ButtonSegment(
value: LiquidGlassAppearance.clear,
label: Text('Clear'),
),
ButtonSegment(
value: LiquidGlassAppearance.tinted,
label: Text('Tinted'),
),
ButtonSegment(
value: LiquidGlassAppearance.solid,
label: Text('Solid'),
),
],
selected: {_appearance},
onSelectionChanged: (v) =>
update(() => _appearance = v.first),
),
const SizedBox(height: 16),
Row(
children: [
const Text('Background opacity'),
const Spacer(),
Text(
_opacity == null
? 'default'
: '${(_opacity! * 100).round()}%',
),
],
),
Slider(
value: _opacity ?? 0.3,
onChanged: (v) => update(() => _opacity = v),
),
TextButton(
onPressed: () => update(() => _opacity = null),
child: const Text('Reset opacity'),
),
],
),
),
);
},
),
);
}
@override
void dispose() {
_tabBar.dispose();
super.dispose();
}
static const _tabs = [
LiquidGlassNavItem(
icon: Icons.article_outlined,
selectedIcon: Icons.article,
label: 'Today',
),
LiquidGlassNavItem(
icon: Icons.rocket_outlined,
selectedIcon: Icons.rocket,
label: 'Games',
),
LiquidGlassNavItem(
icon: Icons.layers_outlined,
selectedIcon: Icons.layers,
label: 'Apps',
),
LiquidGlassNavItem(
icon: Icons.sports_esports_outlined,
selectedIcon: Icons.sports_esports,
label: 'Arcade',
),
LiquidGlassNavItem(icon: Icons.search, label: 'Search'),
];
static const _colors = [
Colors.indigo,
Colors.deepOrange,
Colors.teal,
Colors.purple,
Colors.blueGrey,
];
@override
Widget build(BuildContext context) {
return Scaffold(
// Let content scroll underneath the floating glass bar.
extendBody: true,
appBar: AppBar(
title: Text(_tabs[_index].label),
actions: [
IconButton(
icon: const Icon(Icons.tune),
onPressed: _openGlassSettings,
),
IconButton(
icon: const Icon(Icons.brightness_6_outlined),
onPressed: widget.onToggleTheme,
),
],
),
body: LiquidGlassScrollMinimizer(
controller: _tabBar,
child: ListView.builder(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 120),
itemCount: 30,
itemBuilder: (context, i) => Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Container(
height: 96,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
colors: [
_colors[(i + _index) % _colors.length].shade300,
_colors[(i + _index + 2) % _colors.length].shade600,
],
),
),
alignment: Alignment.center,
child: Text(
'${_tabs[_index].label} card ${i + 1}',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
),
),
),
bottomNavigationBar: LiquidGlassNavBar(
controller: _tabBar,
appearance: _appearance,
backgroundOpacity: _opacity,
items: _tabs,
currentIndex: _index,
onTap: (i) => setState(() => _index = i),
),
);
}
}