extended_bottom_navigation_bar 1.0.3
extended_bottom_navigation_bar: ^1.0.3 copied to clipboard
A clean, highly customizable and expandable bottom navigation bar with a central FAB slot and staggered animation grid.
import 'package:flutter/material.dart';
import 'package:extended_bottom_navigation_bar/extended_bottom_navigation_bar.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Extended Bottom Nav Bar Demo',
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.system,
theme: ThemeData(
brightness: Brightness.light,
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF6200EE),
brightness: Brightness.light,
),
),
darkTheme: ThemeData(
brightness: Brightness.dark,
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFFBB86FC),
brightness: Brightness.dark,
),
),
home: const DemoPage(),
);
}
}
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
int _currentIndex = 0;
bool _isNavExpanded = false;
bool _showNotchAndFab = true;
// Track the name of the last tapped secondary item
String _lastSecondaryTapped = 'None';
// Navigation handlers
void _onTabSelect(int index) {
setState(() {
_currentIndex = index;
});
}
void _onSecondaryItemTap(String label) {
setState(() {
_lastSecondaryTapped = label;
});
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Selected secondary action: $label'),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
),
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
// Define Left and Right Primary Items
final leftItems = [
PrimaryNavBarItem(
icon: Icons.home_outlined,
activeIcon: Icons.home,
label: 'Home',
isActive: _currentIndex == 0,
onTap: () => _onTabSelect(0),
),
PrimaryNavBarItem(
icon: Icons.analytics_outlined,
activeIcon: Icons.analytics,
label: 'Analytics',
isActive: _currentIndex == 1,
onTap: () => _onTabSelect(1),
),
];
final rightItems = [
PrimaryNavBarItem(
icon: Icons.notifications_none_outlined,
activeIcon: Icons.notifications,
label: 'Alerts',
isActive: _currentIndex == 2,
onTap: () => _onTabSelect(2),
),
// Note: If secondaryItems is not empty, you can only have 1 rightItem,
// as the 2nd slot is taken up by the grid "More" toggle tab.
if (!_showNotchAndFab)
PrimaryNavBarItem(
icon: Icons.settings_outlined,
activeIcon: Icons.settings,
label: 'Settings',
isActive: _currentIndex == 3,
onTap: () => _onTabSelect(3),
),
];
// Define Secondary (Expanded Grid) Items
final secondaryItems = [
SecondaryNavBarItem(
icon: Icons.account_balance_wallet_outlined,
activeIcon: Icons.account_balance_wallet,
label: 'Wallet',
onTap: () => _onSecondaryItemTap('Wallet'),
),
SecondaryNavBarItem(
icon: Icons.receipt_long_outlined,
activeIcon: Icons.receipt_long,
label: 'Invoices',
onTap: () => _onSecondaryItemTap('Invoices'),
),
SecondaryNavBarItem(
icon: Icons.people_outline,
activeIcon: Icons.people,
label: 'Contacts',
onTap: () => _onSecondaryItemTap('Contacts'),
),
SecondaryNavBarItem(
icon: Icons.calculate_outlined,
activeIcon: Icons.calculate,
label: 'Calculator',
onTap: () => _onSecondaryItemTap('Calculator'),
),
SecondaryNavBarItem(
icon: Icons.category_outlined,
activeIcon: Icons.category,
label: 'Categories',
onTap: () => _onSecondaryItemTap('Categories'),
),
SecondaryNavBarItem(
icon: Icons.help_outline,
activeIcon: Icons.help,
label: 'Help',
onTap: () => _onSecondaryItemTap('Help'),
),
];
// Page titles to render in body
final pages = [
'Dashboard & Home Overview',
'Detailed Analytics & Reports',
'Recent Notifications & Alerts',
'System Settings & Config',
];
// Center FAB Widget
final customFab = GestureDetector(
onTap: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Central FAB Tapped!'),
behavior: SnackBarBehavior.floating,
),
);
},
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: const LinearGradient(
colors: [Colors.purpleAccent, Colors.blueAccent],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
boxShadow: [
BoxShadow(
color: Colors.blueAccent.withValues(alpha: 0.4),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: const Icon(
Icons.add,
color: Colors.white,
size: 30,
),
),
);
return Scaffold(
body: Stack(
children: [
// Background Color / Content
Positioned.fill(
child: Container(
color: isDark ? const Color(0xFF121212) : const Color(0xFFF6F6F9),
),
),
// Main Scrollable Content
SafeArea(
child: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: [
SliverAppBar(
expandedHeight: 120,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text(
'Extended Bottom Nav Bar',
style: TextStyle(
color: isDark ? Colors.white : Colors.black,
fontWeight: FontWeight.bold,
),
),
centerTitle: false,
),
),
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Card with Info
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Current Tab: ${pages[_currentIndex.clamp(0, pages.length - 1)]}',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'Last Tapped Grid Item: $_lastSecondaryTapped',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.secondary,
),
),
],
),
),
),
const SizedBox(height: 24),
Text(
'Configuration Controls',
style: theme.textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
// Switch to toggle FAB / Notch
SwitchListTile(
value: _showNotchAndFab,
title: const Text('Show Notch & Central FAB'),
subtitle: const Text(
'Toggle the center floating action button & notch clipper.'),
onChanged: (val) {
setState(() {
_showNotchAndFab = val;
// Ensure current index is in range if we shift layout
if (!val && _currentIndex > 3) {
_currentIndex = 0;
}
});
},
),
const Divider(),
const SizedBox(height: 12),
Text(
'About this package',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
const Text(
'This package delivers an expandable footer navigation bar, supporting left/right tabs, a center FAB layout, and a hidden grid for secondary options. It includes staggered slide and fade animations when expanded.',
),
const SizedBox(height: 120), // Padding to allow scrolling past bottom bar
],
),
),
),
],
),
),
// Backdrop Overlay to dim screen when expanded
if (_isNavExpanded)
Positioned.fill(
child: GestureDetector(
onTap: () {
// Collapse via a dynamic action or tapping the overlay
// Note: The ExpandableBottomNav handles collapses on tab selections,
// but we can force collapse by rebuilding/tapping the backdrop,
// or letting it naturally toggle on the navigation bar button tap.
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 250),
color: Colors.black.withValues(alpha: 0.4),
),
),
),
],
),
bottomNavigationBar: ExpandableBottomNav(
leftItems: leftItems,
rightItems: rightItems,
secondaryItems: _showNotchAndFab ? secondaryItems : const [],
fab: _showNotchAndFab ? customFab : null,
notchRadius: 32,
curve: Curves.easeInCubic,
fabSize: 40,
onExpandChanged: (expanded) {
setState(() {
_isNavExpanded = expanded;
});
},
moreLabel: 'Explore',
closeLabel: 'Close',
activeColor: Colors.deepPurple,
inactiveColor: isDark ? Colors.grey[400] : Colors.grey[600],
backgroundColor: isDark ? const Color(0xFF1E1E2C) : Colors.white,
),
);
}
}