Flutter Bottom Navigation
Create bottom navigation bars for easy screen switching
📍 What is Bottom Navigation?
Bottom Navigation Bar is a material design component that displays navigation tabs at the bottom of the screen. It allows users to quickly switch between top-level views with a single tap, making it perfect for apps with three to five primary destinations.
// Add bottom navigation to Scaffold
bottomNavigationBar: BottomNavigationBar(
items: [...],
)
Key Bottom Navigation Concepts
BottomNavigationBar
The main navigation widget
bottomNavigationBar:
BottomNavigationBar(...)
Items
Navigation tab items
items: [
BottomNavigationBarItem(...)
]
Current Index
Track selected tab
currentIndex: _selectedIndex
OnTap
Handle tab selection
onTap: (index) {
setState(() {...});
}
🔹 Basic Bottom Navigation
Create a simple bottom navigation bar with three tabs:
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
int _selectedIndex = 0;
// List of widgets to display
static const List _pages = [
Center(child: Text('Home Page', style: TextStyle(fontSize: 30))),
Center(child: Text('Search Page', style: TextStyle(fontSize: 30))),
Center(child: Text('Profile Page', style: TextStyle(fontSize: 30))),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Demo'),
),
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
),
);
}
}
Result:
✅ Three tabs at bottom: Home, Search, Profile
✅ Tapping a tab switches the displayed page
✅ Selected tab is highlighted
🔹 Styled Bottom Navigation
Customize colors and appearance:
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
onTap: _onItemTapped,
)
Styling Properties:
- selectedItemColor: Color of selected tab
- unselectedItemColor: Color of unselected tabs
- backgroundColor: Background color of the bar
- type: fixed (all items visible) or shifting (animated)
🔹 Bottom Navigation with Pages
Create separate page widgets for each tab:
// Home Page Widget
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.home, size: 100, color: Colors.blue),
SizedBox(height: 20),
Text('Home Page', style: TextStyle(fontSize: 24)),
],
),
);
}
}
// Favorites Page Widget
class FavoritesPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.favorite, size: 100, color: Colors.red),
SizedBox(height: 20),
Text('Favorites Page', style: TextStyle(fontSize: 24)),
],
),
);
}
}
// Settings Page Widget
class SettingsPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.settings, size: 100, color: Colors.grey),
SizedBox(height: 20),
Text('Settings Page', style: TextStyle(fontSize: 24)),
],
),
);
}
}
// Main Widget
class _MyHomePageState extends State {
int _selectedIndex = 0;
final List _pages = [
HomePage(),
FavoritesPage(),
SettingsPage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: (index) => setState(() => _selectedIndex = index),
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.favorite), label: 'Favorites'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),
);
}
}
🔹 Bottom Navigation with Badges
Add notification badges to navigation items:
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Badge(
label: Text('3'),
child: Icon(Icons.notifications),
),
label: 'Notifications',
),
BottomNavigationBarItem(
icon: Badge(
label: Text('5'),
backgroundColor: Colors.red,
child: Icon(Icons.message),
),
label: 'Messages',
),
],
currentIndex: _selectedIndex,
onTap: _onItemTapped,
)
Result:
✅ Red badge with number appears on icons
✅ Perfect for showing unread counts
🔹 Shifting Bottom Navigation
Create an animated shifting navigation bar:
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.blue,
),
BottomNavigationBarItem(
icon: Icon(Icons.explore),
label: 'Explore',
backgroundColor: Colors.green,
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: 'Favorites',
backgroundColor: Colors.red,
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
backgroundColor: Colors.purple,
),
],
currentIndex: _selectedIndex,
type: BottomNavigationBarType.shifting,
onTap: _onItemTapped,
)
Shifting Type Features:
- Background color changes with selected tab
- Smooth animated transitions
- Selected item grows larger
- Best for 4-5 items
🔹 Complete Example
A full working bottom navigation implementation:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Bottom Navigation Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State {
int _currentIndex = 0;
final List _screens = [
HomeScreen(),
SearchScreen(),
NotificationsScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My App'),
),
body: _screens[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications),
label: 'Notifications',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Home Screen', style: TextStyle(fontSize: 24)));
}
}
class SearchScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Search Screen', style: TextStyle(fontSize: 24)));
}
}
class NotificationsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Notifications Screen', style: TextStyle(fontSize: 24)));
}
}
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Profile Screen', style: TextStyle(fontSize: 24)));
}
}