BottomNavigationBar class
A material widget that's displayed at the bottom of an app for selecting among a small number of views, typically between three and five.
There is an updated version of this component, NavigationBar, that's
preferred for new applications and applications that are configured
for Material 3 (see ThemeData.useMaterial3).
The bottom navigation bar consists of multiple items in the form of text labels, icons, or both, laid out on top of a piece of material. It provides quick navigation between the top-level views of an app. For larger screens, side navigation may be a better fit.
A bottom navigation bar is usually used in conjunction with a Scaffold, where it is provided as the Scaffold.bottomNavigationBar argument.
The bottom navigation bar's type changes how its items are displayed. If not specified, then it's automatically set to BottomNavigationBarType.fixed when there are less than four items, and BottomNavigationBarType.shifting otherwise.
The length of items must be at least two and each item's icon and label must not be null.
- BottomNavigationBarType.fixed, the default when there are less than four items. The selected item is rendered with the selectedItemColor if it's non-null, otherwise the theme's ColorScheme.primary color is used for Brightness.light themes and ColorScheme.secondary for Brightness.dark themes. If backgroundColor is null, The navigation bar's background color defaults to the Material background color, ThemeData.canvasColor (essentially opaque white).
- BottomNavigationBarType.shifting, the default when there are four or more items. If selectedItemColor is null, all items are rendered in white. The navigation bar's background color is the same as the BottomNavigationBarItem.backgroundColor of the selected item. In this case it's assumed that each item will have a different background color and that background color will contrast well with white.
Updating to NavigationBar
The NavigationBar widget's visuals
are a little bit different, see the Material 3 spec at
m3.material.io/components/navigation-bar/overview for
more details.
The NavigationBar widget's API is also slightly different.
To update from BottomNavigationBar to NavigationBar, you will
need to make the following changes.
-
Instead of using BottomNavigationBar.items, which takes a list of BottomNavigationBarItems, use
NavigationBar.destinations, which takes a list of widgets. Usually, you use a list ofNavigationDestinationwidgets. Just like BottomNavigationBarItems,NavigationDestinations have a label and icon field. -
Instead of using BottomNavigationBar.onTap, use
NavigationBar.onDestinationSelected, which is also a callback that is called when the user taps on a navigation bar item. -
Instead of using BottomNavigationBar.currentIndex, use
NavigationBar.selectedIndex, which is also an integer that represents the index of the selected destination. -
You may also need to make changes to the styling of the
NavigationBar, see the properties in theNavigationBarconstructor for more details.
Using BottomNavigationBar
This example shows a BottomNavigationBar as it is used within a Scaffold
widget. The BottomNavigationBar has three BottomNavigationBarItem
widgets, which means it defaults to BottomNavigationBarType.fixed, and
the currentIndex is set to index 0. The selected item is
amber. The _onItemTapped function changes the selected item's index
and displays a corresponding message in the center of the Scaffold.
To see it in action, copy and run this code snippet on DartPad.
import 'package:material_ui/material_ui.dart';
/// Flutter code sample for [BottomNavigationBar].
void main() => runApp(const BottomNavigationBarExampleApp());
class BottomNavigationBarExampleApp extends StatelessWidget {
const BottomNavigationBarExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: BottomNavigationBarExample());
}
}
class BottomNavigationBarExample extends StatefulWidget {
const BottomNavigationBarExample({super.key});
@override
State<BottomNavigationBarExample> createState() =>
_BottomNavigationBarExampleState();
}
class _BottomNavigationBarExampleState
extends State<BottomNavigationBarExample> {
int _selectedIndex = 0;
static const TextStyle optionStyle = TextStyle(
fontSize: 30,
fontWeight: .bold,
);
static const List<Widget> _widgetOptions = <Widget>[
Text('Index 0: Home', style: optionStyle),
Text('Index 1: Business', style: optionStyle),
Text('Index 2: School', style: optionStyle),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BottomNavigationBar Sample')),
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,
),
);
}
}
This example shows how you would migrate the above BottomNavigationBar
to the new NavigationBar.
To see it in action, copy and run this code snippet on DartPad.
import 'package:material_ui/material_ui.dart';
/// Flutter code sample for [NavigationBar].
void main() => runApp(const NavigationBarApp());
class NavigationBarApp extends StatelessWidget {
const NavigationBarApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: NavigationExample());
}
}
class NavigationExample extends StatefulWidget {
const NavigationExample({super.key});
@override
State<NavigationExample> createState() => _NavigationExampleState();
}
class _NavigationExampleState extends State<NavigationExample> {
int currentPageIndex = 0;
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return Scaffold(
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) {
setState(() {
currentPageIndex = index;
});
},
indicatorColor: Colors.amber,
selectedIndex: currentPageIndex,
destinations: const <Widget>[
NavigationDestination(
selectedIcon: Icon(Icons.home),
icon: Icon(Icons.home_outlined),
label: 'Home',
),
NavigationDestination(
icon: Badge(child: Icon(Icons.notifications_sharp)),
label: 'Notifications',
),
NavigationDestination(
icon: Badge(label: Text('2'), child: Icon(Icons.messenger_sharp)),
label: 'Messages',
),
],
),
body: <Widget>[
/// Home page
Card(
shadowColor: Colors.transparent,
margin: const .all(8.0),
child: SizedBox.expand(
child: Center(
child: Text('Home page', style: theme.textTheme.titleLarge),
),
),
),
/// Notifications page
const Padding(
padding: .all(8.0),
child: Column(
children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.notifications_sharp),
title: Text('Notification 1'),
subtitle: Text('This is a notification'),
),
),
Card(
child: ListTile(
leading: Icon(Icons.notifications_sharp),
title: Text('Notification 2'),
subtitle: Text('This is a notification'),
),
),
],
),
),
/// Messages page
ListView.builder(
reverse: true,
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return Align(
alignment: .centerRight,
child: Container(
margin: const .all(8.0),
padding: const .all(8.0),
decoration: BoxDecoration(
color: theme.colorScheme.primary,
borderRadius: .circular(8.0),
),
child: Text(
'Hello',
style: theme.textTheme.bodyLarge!.copyWith(
color: theme.colorScheme.onPrimary,
),
),
),
);
}
return Align(
alignment: .centerLeft,
child: Container(
margin: const .all(8.0),
padding: const .all(8.0),
decoration: BoxDecoration(
color: theme.colorScheme.primary,
borderRadius: .circular(8.0),
),
child: Text(
'Hi!',
style: theme.textTheme.bodyLarge!.copyWith(
color: theme.colorScheme.onPrimary,
),
),
),
);
},
),
][currentPageIndex],
);
}
}
This example shows a BottomNavigationBar as it is used within a Scaffold
widget. The BottomNavigationBar has four BottomNavigationBarItem
widgets, which means it defaults to BottomNavigationBarType.shifting, and
the currentIndex is set to index 0. The selected item is amber in color.
With each BottomNavigationBarItem widget, backgroundColor property is
also defined, which changes the background color of BottomNavigationBar,
when that item is selected. The _onItemTapped function changes the
selected item's index and displays a corresponding message in the center of
the Scaffold.
To see it in action, copy and run this code snippet on DartPad.
import 'package:material_ui/material_ui.dart';
/// Flutter code sample for [BottomNavigationBar].
void main() => runApp(const BottomNavigationBarExampleApp());
class BottomNavigationBarExampleApp extends StatelessWidget {
const BottomNavigationBarExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: BottomNavigationBarExample());
}
}
class BottomNavigationBarExample extends StatefulWidget {
const BottomNavigationBarExample({super.key});
@override
State<BottomNavigationBarExample> createState() =>
_BottomNavigationBarExampleState();
}
class _BottomNavigationBarExampleState
extends State<BottomNavigationBarExample> {
int _selectedIndex = 0;
static const TextStyle optionStyle = TextStyle(
fontSize: 30,
fontWeight: .bold,
);
static const List<Widget> _widgetOptions = <Widget>[
Text('Index 0: Home', style: optionStyle),
Text('Index 1: Business', style: optionStyle),
Text('Index 2: School', style: optionStyle),
Text('Index 3: Settings', style: optionStyle),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BottomNavigationBar Sample')),
body: Center(child: _widgetOptions.elementAt(_selectedIndex)),
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,
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
backgroundColor: Colors.pink,
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
This example shows BottomNavigationBar used in a Scaffold Widget with different interaction patterns. Tapping twice on the first BottomNavigationBarItem uses the ScrollController to animate the ListView to the top. The second BottomNavigationBarItem shows a Modal Dialog.
To see it in action, copy and run this code snippet on DartPad.
import 'package:material_ui/material_ui.dart';
/// Flutter code sample for [BottomNavigationBar].
void main() => runApp(const BottomNavigationBarExampleApp());
class BottomNavigationBarExampleApp extends StatelessWidget {
const BottomNavigationBarExampleApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: BottomNavigationBarExample());
}
}
class BottomNavigationBarExample extends StatefulWidget {
const BottomNavigationBarExample({super.key});
@override
State<BottomNavigationBarExample> createState() =>
_BottomNavigationBarExampleState();
}
class _BottomNavigationBarExampleState
extends State<BottomNavigationBarExample> {
int _selectedIndex = 0;
final ScrollController _homeController = ScrollController();
Widget _listViewBody() {
return ListView.separated(
controller: _homeController,
itemBuilder: (BuildContext context, int index) {
return Center(child: Text('Item $index'));
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(thickness: 1),
itemCount: 50,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BottomNavigationBar Sample')),
body: _listViewBody(),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(
icon: Icon(Icons.open_in_new_rounded),
label: 'Open Dialog',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.amber[800],
onTap: (int index) {
switch (index) {
case 0:
// only scroll to top when current index is selected.
if (_selectedIndex == index) {
_homeController.animateTo(
0.0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeOut,
);
}
case 1:
showModal(context);
}
setState(() {
_selectedIndex = index;
});
},
),
);
}
void showModal(BuildContext context) {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
content: const Text('Example Dialog'),
actions: <TextButton>[
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Close'),
),
],
),
);
}
}
See also:
- BottomNavigationBarItem
- Scaffold
- material.io/design/components/bottom-navigation.html
NavigationBar, this widget's replacement in Material Design 3.
- Inheritance
-
- Object
- DiagnosticableTree
- Widget
- StatefulWidget
- BottomNavigationBar
Constructors
- Creates a bottom navigation bar which is typically used as a Scaffold's Scaffold.bottomNavigationBar argument.
Properties
- backgroundColor → Color?
-
The color of the BottomNavigationBar itself.
final
- currentIndex → int
-
The index into items for the current active BottomNavigationBarItem.
final
- elevation → double?
-
The z-coordinate of this BottomNavigationBar.
final
- enableFeedback → bool?
-
Whether detected gestures should provide acoustic and/or haptic feedback.
final
- fixedColor → Color?
-
The value of selectedItemColor.
no setter
- hashCode → int
-
The hash code for this object.
no setterinherited
- iconSize → double
-
The size of all of the BottomNavigationBarItem icons.
final
-
items
→ List<
BottomNavigationBarItem> -
Defines the appearance of the button items that are arrayed within the
bottom navigation bar.
final
- key → Key?
-
Controls how one widget replaces another widget in the tree.
finalinherited
- landscapeLayout → BottomNavigationBarLandscapeLayout?
-
The arrangement of the bar's items when the enclosing
MediaQueryData.orientation is Orientation.landscape.
final
- mouseCursor → MouseCursor?
-
The cursor for a mouse pointer when it enters or is hovering over the
items.
final
-
onTap
→ ValueChanged<
int> ? -
Called when one of the items is tapped.
final
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
- selectedFontSize → double
-
The font size of the BottomNavigationBarItem labels when they are selected.
final
- selectedIconTheme → IconThemeData?
-
The size, opacity, and color of the icon in the currently selected
BottomNavigationBarItem.icon.
final
- selectedItemColor → Color?
-
The color of the selected BottomNavigationBarItem.icon and
BottomNavigationBarItem.label.
final
- selectedLabelStyle → TextStyle?
-
The TextStyle of the BottomNavigationBarItem labels when they are
selected.
final
- showSelectedLabels → bool?
-
Whether the labels are shown for the selected BottomNavigationBarItem.
final
- showUnselectedLabels → bool?
-
Whether the labels are shown for the unselected BottomNavigationBarItems.
final
- type → BottomNavigationBarType?
-
Defines the layout and behavior of a BottomNavigationBar.
final
- unselectedFontSize → double
-
The font size of the BottomNavigationBarItem labels when they are not
selected.
final
- unselectedIconTheme → IconThemeData?
-
The size, opacity, and color of the icon in the currently unselected
BottomNavigationBarItem.icons.
final
- unselectedItemColor → Color?
-
The color of the unselected BottomNavigationBarItem.icon and
BottomNavigationBarItem.labels.
final
- unselectedLabelStyle → TextStyle?
-
The TextStyle of the BottomNavigationBarItem labels when they are not
selected.
final
- useLegacyColorScheme → bool
-
This flag is controlling how BottomNavigationBar is going to use
the colors provided by the selectedIconTheme, unselectedIconTheme,
selectedItemColor, unselectedItemColor.
The default value is
trueas the new theming logic is a breaking change. To opt-in the new theming logic set the flag tofalsefinal
Methods
-
createElement(
) → StatefulElement -
Creates a StatefulElement to manage this widget's location in the tree.
inherited
-
createState(
) → State< BottomNavigationBar> -
Creates the mutable state for this widget at a given location in the tree.
override
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children.
inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toDiagnosticsNode(
{String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by DiagnosticsNode.toStringDeep.
inherited
-
toString(
{DiagnosticLevel minLevel = DiagnosticLevel.info}) → String -
A string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String -
Returns a string representation of this node and its descendants.
inherited
-
toStringShallow(
{String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String -
Returns a one-line detailed description of the object.
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited