slide_glass_nav_bar 0.1.0
slide_glass_nav_bar: ^0.1.0 copied to clipboard
A floating liquid-glass bottom nav bar with press-and-hold + slide-to-select gestures, an elastic bounce on commit, and full RTL support.
import 'package:flutter/material.dart';
import 'package:slide_glass_nav_bar/slide_glass_nav_bar.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'slide_glass_nav_bar example',
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
home: const _HomePage(),
);
}
}
class _HomePage extends StatefulWidget {
const _HomePage();
@override
State<_HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<_HomePage> {
int _index = 0;
bool _rtl = false;
bool _customColors = false;
static const _pages = [
_ColorPage(label: 'Home', color: Color(0xFFFFEFD5)),
_ColorPage(label: 'Search', color: Color(0xFFD0F0FF)),
_ColorPage(label: 'Library', color: Color(0xFFE6F4EA)),
_ColorPage(label: 'Profile', color: Color(0xFFFFE2EC)),
];
static const _items = [
SlideGlassNavItem(label: 'Home', iconBuilder: _iconHome),
SlideGlassNavItem(label: 'Search', iconBuilder: _iconSearch),
SlideGlassNavItem(label: 'Library', iconBuilder: _iconLibrary),
SlideGlassNavItem(label: 'Profile', iconBuilder: _iconProfile),
];
static Widget _iconHome(BuildContext context, Color color, double size) =>
Icon(Icons.home_rounded, color: color, size: size);
static Widget _iconSearch(BuildContext context, Color color, double size) =>
Icon(Icons.search_rounded, color: color, size: size);
static Widget _iconLibrary(BuildContext context, Color color, double size) =>
Icon(Icons.collections_bookmark_rounded, color: color, size: size);
static Widget _iconProfile(BuildContext context, Color color, double size) =>
Icon(Icons.person_rounded, color: color, size: size);
@override
Widget build(BuildContext context) {
final style = _customColors
? const SlideGlassNavBarStyle(
selectedForegroundColor: Color(0xFF6750A4),
selectedPillColor: Color(0x33B69DF8),
selectedPillBorderColor: Color(0x66FFFFFF),
)
: const SlideGlassNavBarStyle();
return Directionality(
textDirection: _rtl ? TextDirection.rtl : TextDirection.ltr,
child: Scaffold(
body: Stack(
children: [
IndexedStack(index: _index, children: _pages),
Positioned(
top: MediaQuery.of(context).padding.top + 16,
left: 16,
right: 16,
child: _Controls(
rtl: _rtl,
customColors: _customColors,
onRtlChanged: (v) => setState(() => _rtl = v),
onColorsChanged: (v) => setState(() => _customColors = v),
),
),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: SlideGlassNavBar(
currentIndex: _index,
onChanged: (i) => setState(() => _index = i),
items: _items,
style: style,
),
),
],
),
),
);
}
}
class _Controls extends StatelessWidget {
final bool rtl;
final bool customColors;
final ValueChanged<bool> onRtlChanged;
final ValueChanged<bool> onColorsChanged;
const _Controls({
required this.rtl,
required this.customColors,
required this.onRtlChanged,
required this.onColorsChanged,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 0,
color: const Color(0xDCFFFFFF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: const BorderSide(color: Color(0x22000000)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SwitchListTile(
title: const Text('RTL layout'),
value: rtl,
onChanged: onRtlChanged,
dense: true,
contentPadding: EdgeInsets.zero,
),
SwitchListTile(
title: const Text('Custom selected color'),
value: customColors,
onChanged: onColorsChanged,
dense: true,
contentPadding: EdgeInsets.zero,
),
],
),
),
);
}
}
class _ColorPage extends StatelessWidget {
final String label;
final Color color;
const _ColorPage({required this.label, required this.color});
@override
Widget build(BuildContext context) {
return Container(
color: color,
child: ListView.builder(
padding: const EdgeInsets.fromLTRB(20, 160, 20, 160),
itemCount: 30,
itemBuilder: (context, i) {
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: const Color(0xB4FFFFFF),
borderRadius: BorderRadius.circular(16),
),
child: Text(
'$label item ${i + 1}',
style: const TextStyle(fontSize: 16),
),
);
},
),
);
}
}