Hi! This tutorial will guide you through creating a fully functional Bottom Navigation Bar in your Flutter application, complete with code snippets and explanations.
In this Article
Why Bottom Navigation Bar?
In the landscape of mobile applications, the ease of navigation is a crucial element in defining the user experience. A Bottom Navigation Bar is a set of navigation buttons anchored at the bottom of the screen, making it accessible for users to navigate with thumbs while holding their devices. It’s optimal for apps with 3 to 5 top-level destinations.
Prerequisites
Ensure you have:
- Flutter installed on your machine
- Basic knowledge of Flutter & Dart
- An IDE (Android Studio/VS Code)
Step 1: Setting Up Your Flutter Project
Begin by creating a new Flutter project. Open your terminal or command prompt and navigate to your desired directory. Execute the command:
flutter create bottom_nav_bar
Navigate into your project directory, and you will find the main entry point of your app in lib/main.dart
.
Step 2: Building the Scaffold
Open lib/main.dart
and start with a simple Scaffold
. The Scaffold
widget provides a framework that includes an AppBar, a Body, and a BottomNavigationBar.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Bottom Navigation Bar',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0; // new
void _onItemTapped(int index) { // new
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar Tutorial'),
),
body: Center( // new
child: Text('Tab $_selectedIndex content displays here'),
),
bottomNavigationBar: BottomNavigationBar( // new
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
),
],
currentIndex: _selectedIndex, // new
selectedItemColor: Colors.amber[800], // new
onTap: _onItemTapped, // new
),
);
}
}
In this code, we’ve set up a Scaffold
with a BottomNavigationBar
. The _selectedIndex
is managed by the state of _MyHomePageState
, which changes when a different tab is tapped, thanks to the _onItemTapped
function.
Step 3: Adding Navigation Logic
Now, let’s define the content for each tab and manage the navigation logic.
// Define a widget for each page
class HomeWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Home Page'));
}
}
class BusinessWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('Business Page'));
}
}
class SchoolWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(child: Text('School Page'));
}
}
// Update the _MyHomePageState class
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
static const TextStyle optionStyle = TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
static List<Widget> _widgetOptions = <Widget>[
HomeWidget(),
BusinessWidget(),
SchoolWidget(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar Tutorial'),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
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.amber[800],
onTap: _onItemTapped,
),
);
}
}
In this code, we’ve created three stateless widgets, one for each page corresponding to the Bottom Navigation Bar items. We also have a _widgetOptions
list that holds the widgets and is used to display the current selection.
Step 4: Customizing the Bottom Navigation Bar
Next, we’ll customize the appearance of the Bottom Navigation Bar to fit the app’s design language.
// Customization options for BottomNavigationBar
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
backgroundColor: Colors.red,
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
label: 'Business',
backgroundColor: Colors.green,
),
BottomNavigationBarItem(
icon: Icon(Icons.school),
label: 'School',
backgroundColor: Colors.purple,
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey,
onTap: _onItemTapped,
type: BottomNavigationBarType.shifting, // new
),
In the above snippet, type: BottomNavigationBarType.shifting
is added to provide a shifting animation when changing tabs, and backgroundColor
is set for each item.
Step 5: Handling Page State
When switching between tabs, it’s crucial to maintain the state of each page. Flutter’s PageView
combined with IndexedStack
is a great way to maintain the state of each page without rebuilding them from scratch every time a tab is tapped.
// Update the _MyHomePageState class to use IndexedStack
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
final PageController _pageController = PageController();
void _onPageChanged(int index) {
setState(() {
_selectedIndex = index;
});
}
void _onItemTapped(int index) {
_pageController.jumpToPage(index);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bottom Navigation Bar Tutorial'),
),
body: PageView(
controller: _pageController,
onPageChanged: _onPageChanged,
children: _widgetOptions,
physics: NeverScrollableScrollPhysics(), // Disable swipe to change tabs
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
// BottomNavigationBarItems...
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
Here, we’ve introduced a PageController
to manage which page is visible in the body of the Scaffold
. We also replaced the Center
widget with a PageView
that’s controlled by the PageController
. This allows us to preserve the state of our pages.
Step 6: Adding Transitions and Animations
To enhance the user experience, we can add transitions and animations when switching between tabs. Flutter makes it easy to create these animations with its built-in widgets and properties.
// Update the _onItemTapped function to animate page transitions
void _onItemTapped(int index) {
_pageController.animateToPage(
index,
duration: Duration(milliseconds: 500), // Adjust the speed of the animation
curve: Curves.easeInOut, // Choose the type of animation curve
);
}
In this updated _onItemTapped
method, we’re using _pageController.animateToPage
instead of jumpToPage
, which lets us specify the duration and curve for the page transition animation.
Step 7: Dynamic Theming
Flutter’s flexibility allows us to dynamically change the theme of the Bottom Navigation Bar based on the active tab or user preferences.
// Example of dynamic theming based on the selected index
BottomNavigationBar(
// ...
backgroundColor: _selectedIndex == 0 ? Colors.red : _selectedIndex == 1 ? Colors.green : Colors.blue,
// ...
),
In this example, we change the backgroundColor
of the Bottom Navigation Bar dynamically, based on the _selectedIndex
.
Step 8: Adaptive Layouts
As we are developing for multiple platforms and screen sizes, it’s important to make our Bottom Navigation Bar adaptive.
// Use MediaQuery to adapt the layout based on the screen size
Widget build(BuildContext context) {
var isWideScreen = MediaQuery.of(context).size.width > 600; // arbitrary breakpoint for wide screens
return Scaffold(
// ...
bottomNavigationBar: isWideScreen ? null : BottomNavigationBar(
// ...
),
drawer: isWideScreen ? Drawer(
// Add a drawer with navigation items for wide screens
child: ListView(
// Drawer items...
),
) : null,
// ...
);
}
This code uses MediaQuery
to check the screen width and decide whether to show a BottomNavigationBar
or a Drawer
, making it responsive to different screen sizes.
Conclusion
With these steps, you now have a robust and feature-rich Bottom Navigation Bar in your Flutter application. The code examples provided offer a comprehensive understanding of how to implement, manage, and enhance this component.
Remember, the key to a successful implementation is not only the initial setup but also considering user interaction, maintaining state, and ensuring your UI adapts to different devices and orientations.
For more advanced topics, such as incorporating nested navigation or adding badges to navigation bar items, check out the official Flutter documentation.
Happy coding, and may your Flutter development journey be as smooth as your app’s new navigation!