FlutterFlow's built-in Scaffold Navigation Bar handles most bottom nav needs — set it up in Page Properties with up to 5 tabs, icons, and labels. For animated transitions, badge indicators, or a center-docked floating action button, create a reusable Component using the animated_bottom_navigation_bar package after code export. Always limit navigation items to 5 or fewer to follow Material Design guidelines.
Bottom Navigation Bars in FlutterFlow
The bottom navigation bar is the primary wayfinding element in mobile apps, letting users switch between 2-5 top-level sections without losing their place. FlutterFlow offers two approaches: the built-in Scaffold Nav Bar (fastest, no code required) and a fully custom Component (maximum control). The built-in option handles page routing, active state highlighting, and theme integration automatically. The custom Component approach unlocks animated tab transitions, notification badges, center-docked FABs, and fully custom shapes — all configurable from within FlutterFlow's widget editor.
Prerequisites
- A FlutterFlow project with at least 2-3 pages already created
- Basic understanding of FlutterFlow Pages and Components
- Your app's color theme configured in FlutterFlow's Theme Editor
Step-by-step guide
Enable the built-in Scaffold Navigation Bar on your pages
Enable the built-in Scaffold Navigation Bar on your pages
Open the first page that should show the bottom nav bar. In the Properties panel on the right, scroll to the 'Navigation Bar' section. Toggle 'Show Nav Bar' to on. You will see a Nav Bar editor appear at the bottom of the page canvas. Click 'Add Nav Bar Item' to add each section — set the Icon (from the Material icon picker), the Active Icon (a filled version of the same icon), and the Label text. Repeat the same Nav Bar configuration on every page that should share this bar. FlutterFlow automatically highlights the active page's tab and handles navigation between pages when a tab is tapped. There is no action flow needed for built-in nav bar routing.
Expected result: Each page shows the bottom nav bar with your icons and labels. Tapping a tab navigates to the corresponding page and highlights the active tab.
Customize nav bar appearance in the Theme Editor
Customize nav bar appearance in the Theme Editor
Open the Theme Editor (the paint palette icon in the left toolbar). Scroll to the 'Nav Bar' section. Set the Background Color (the bar's fill), the Selected Item Color (active tab icon and label), the Unselected Item Color (inactive tabs), and the Elevation (the shadow depth — set to 0 for a flat modern look or 8 for a floating card effect). These theme values apply globally to all pages sharing the nav bar. For page-specific overrides, use the Nav Bar section in individual Page Properties to override the background color or elevation. The font for nav bar labels follows the 'Label Small' text style in your theme.
Expected result: The navigation bar reflects your brand colors with a clear visual distinction between active and inactive tabs.
Add a badge indicator to a nav bar item
Add a badge indicator to a nav bar item
Badge indicators (the red dot or count bubble) require a custom approach since the built-in Scaffold Nav Bar does not support dynamic badges natively. Create a new Component called 'NavBadgeIcon' with a Stack widget at its root. Inside the Stack, add an Icon widget bound to a component parameter 'iconData'. Then add a positioned Container in the top-right corner, sized 16x16 with a red circular background, containing a Text widget bound to a component parameter 'badgeCount' (Integer). Add a Conditional visibility rule to hide the Container entirely when badgeCount equals 0. Use this component inside a custom nav bar row instead of the built-in Scaffold Nav Bar for pages where you need badge support.
Expected result: The notification tab shows a red badge with the unread count when there are pending notifications, and hides the badge automatically when count reaches zero.
Build a custom nav bar Component with a center FAB
Build a custom nav bar Component with a center FAB
Create a new Component called 'CustomNavBar'. Set its height to 72 pixels and background to white (or your theme's surface color) with a top shadow. Inside, add a Row with MainAxisAlignment set to spaceAround. For 4 nav items (Home, Search, Notifications, Profile), place each inside an InkWell GestureDetector with a Column (icon + label). In the center gap, add a FloatingActionButton-styled Container: 56x56, circular, elevation 4, Primary color background, with a '+' or action icon inside. The center Container sits above the nav bar using a negative top margin of -28 pixels, achieved by placing it in a Stack that overlaps the nav bar's top edge. Each nav item's onTap action navigates to its page and updates a Page State variable 'activeIndex' which drives the active icon selection via conditionals.
Expected result: A custom nav bar appears with a raised circular FAB in the center, active tab highlighting, and smooth navigation to each section.
Wire the nav bar Component to all pages using a master layout
Wire the nav bar Component to all pages using a master layout
Rather than adding the custom NavBar component to every page individually, create a 'MasterLayout' Component that wraps your app's content area. MasterLayout contains a Column with: a Flexible (Expanded) Container at the top that accepts a child widget via a component parameter named 'body', and your CustomNavBar Component pinned to the bottom. Each of your main pages replaces the Scaffold body with the MasterLayout and passes its content as the body parameter. Alternatively, for the simplest approach, use FlutterFlow's built-in Persistent Component feature: mark your CustomNavBar component as persistent across pages, which keeps it mounted and animated between page transitions without rebuilding.
Expected result: The nav bar persists smoothly across all pages without flickering or rebuilding. The active tab updates correctly on each page visit.
Complete working example
1// Custom Navigation Bar Component2// Add this as a Custom Widget in FlutterFlow3// Parameters: activeIndex (int), onTabChanged (Function(int))45import 'package:flutter/material.dart';67class CustomNavBar extends StatelessWidget {8 final int activeIndex;9 final Function(int) onTabChanged;10 final int notificationCount;1112 const CustomNavBar({13 super.key,14 required this.activeIndex,15 required this.onTabChanged,16 this.notificationCount = 0,17 });1819 @override20 Widget build(BuildContext context) {21 final theme = Theme.of(context);22 return SizedBox(23 height: 72,24 child: Stack(25 clipBehavior: Clip.none,26 children: [27 // Nav bar background28 Container(29 height: 72,30 decoration: BoxDecoration(31 color: theme.colorScheme.surface,32 boxShadow: [33 BoxShadow(34 color: Colors.black.withOpacity(0.08),35 blurRadius: 12,36 offset: const Offset(0, -2),37 ),38 ],39 ),40 child: Row(41 mainAxisAlignment: MainAxisAlignment.spaceAround,42 children: [43 _NavItem(44 icon: Icons.home_outlined,45 activeIcon: Icons.home,46 label: 'Home',47 isActive: activeIndex == 0,48 onTap: () => onTabChanged(0),49 ),50 _NavItem(51 icon: Icons.search_outlined,52 activeIcon: Icons.search,53 label: 'Search',54 isActive: activeIndex == 1,55 onTap: () => onTabChanged(1),56 ),57 // Center gap for FAB58 const SizedBox(width: 64),59 _NavItem(60 icon: Icons.notifications_outlined,61 activeIcon: Icons.notifications,62 label: 'Alerts',63 isActive: activeIndex == 3,64 badge: notificationCount,65 onTap: () => onTabChanged(3),66 ),67 _NavItem(68 icon: Icons.person_outlined,69 activeIcon: Icons.person,70 label: 'Profile',71 isActive: activeIndex == 4,72 onTap: () => onTabChanged(4),73 ),74 ],75 ),76 ),77 // Center FAB78 Positioned(79 top: -24,80 left: 0,81 right: 0,82 child: Center(83 child: GestureDetector(84 onTap: () => onTabChanged(2),85 child: AnimatedContainer(86 duration: const Duration(milliseconds: 200),87 width: 56,88 height: 56,89 decoration: BoxDecoration(90 color: activeIndex == 291 ? theme.colorScheme.primary92 : theme.colorScheme.primaryContainer,93 shape: BoxShape.circle,94 boxShadow: [95 BoxShadow(96 color: theme.colorScheme.primary.withOpacity(0.4),97 blurRadius: 8,98 offset: const Offset(0, 4),99 ),100 ],101 ),102 child: Icon(103 Icons.add,104 color: Colors.white,105 size: 28,106 ),107 ),108 ),109 ),110 ),111 ],112 ),113 );114 }115}116117class _NavItem extends StatelessWidget {118 final IconData icon;119 final IconData activeIcon;120 final String label;121 final bool isActive;122 final int badge;123 final VoidCallback onTap;124125 const _NavItem({126 required this.icon,127 required this.activeIcon,128 required this.label,129 required this.isActive,130 required this.onTap,131 this.badge = 0,132 });133134 @override135 Widget build(BuildContext context) {136 final theme = Theme.of(context);137 final color = isActive138 ? theme.colorScheme.primary139 : theme.colorScheme.onSurfaceVariant;140 return GestureDetector(141 onTap: onTap,142 child: Stack(143 children: [144 Column(145 mainAxisSize: MainAxisSize.min,146 mainAxisAlignment: MainAxisAlignment.center,147 children: [148 Icon(isActive ? activeIcon : icon, color: color, size: 24),149 const SizedBox(height: 2),150 Text(label,151 style: TextStyle(152 color: color, fontSize: 11, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal)),153 ],154 ),155 if (badge > 0)156 Positioned(157 right: 0,158 top: 0,159 child: Container(160 padding: const EdgeInsets.all(3),161 decoration: const BoxDecoration(162 color: Colors.red, shape: BoxShape.circle),163 child: Text('$badge',164 style: const TextStyle(color: Colors.white, fontSize: 9)),165 ),166 ),167 ],168 ),169 );170 }171}Common mistakes when creating a Custom Navigation Bar in FlutterFlow
Why it's a problem: Adding more than 5 items to the bottom navigation bar
How to avoid: Limit the bottom nav to 3-5 items. Move secondary sections into a drawer, tab bar within a page, or a 'More' menu that opens a modal with additional options.
Why it's a problem: Using the nav bar as a history stack instead of top-level switching
How to avoid: Use FlutterFlow's 'Navigate to' with 'Replace Route' (not 'Push') for nav bar tab switches, or use the built-in Scaffold Nav Bar which handles this correctly by default.
Why it's a problem: Setting the nav bar background color directly on a Container instead of via the Theme Editor
How to avoid: Use Theme.of(context).colorScheme.surface or set the nav bar color in FlutterFlow's Theme Editor so it adapts to both light and dark system themes automatically.
Why it's a problem: Rebuilding the nav bar Component on every page load instead of using a persistent component
How to avoid: Mark the custom nav bar Component as Persistent in FlutterFlow's component settings, or use a shared App State variable for the active tab index so it survives page transitions.
Best practices
- Limit bottom navigation to 3-5 destinations — fewer choices reduce cognitive load and increase tap accuracy.
- Use filled icons for the active state and outlined icons for inactive states to create a clear visual distinction without relying on color alone.
- Always include a text label below each icon — icon-only nav bars score lower on accessibility audits and confuse new users.
- Store the active tab index in App State (not Page State) so the correct tab remains highlighted after navigating within a tab's content stack.
- Add haptic feedback (HapticFeedback.selectionClick()) on tab tap for a polished feel on iOS and Android.
- Test your nav bar at 150% font scale (Accessibility settings) to ensure labels do not overflow or truncate.
- Use a bottom safe area padding below the nav bar on iPhone X and newer so content is not obscured by the home indicator.
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a Flutter app with 4 main tabs: Home, Search, Notifications, and Profile. Write a custom BottomNavigationBar widget in Dart with: a center-docked FAB for the 'Create' action, a badge indicator on the Notifications tab that shows an integer count, animated active indicator dots below each icon, and support for both light and dark themes using Theme.of(context).colorScheme.
In my FlutterFlow project, I have 4 pages: HomePage, SearchPage, NotificationsPage, and ProfilePage. I want to add a shared bottom navigation bar that appears on all 4 pages. Walk me through enabling the built-in Scaffold Navigation Bar on each page, configuring the icons and labels, and making the correct tab highlight when each page is active. I also want the nav bar to show the app's primary color for the active tab.
Frequently asked questions
Can I hide the nav bar on specific pages like a full-screen image viewer?
Yes. In the Page Properties panel for any page, toggle 'Show Nav Bar' to off. The nav bar hides on that page only and reappears when the user navigates back to a page with it enabled.
How do I make the nav bar transparent over content?
Set the Nav Bar Background Color to transparent in Page Properties and enable 'extendBodyBehindNavBar' by setting the Scaffold's body to extend below the nav bar. Add bottom padding to your page content equal to the nav bar height (typically 56-80 pixels) so the last item is not hidden.
Can I use a top navigation bar instead of bottom?
Yes. FlutterFlow's TabBar widget can be placed at the top of the page for a Material-style top nav. This is common for secondary navigation within a page (e.g., different categories or time periods), while the main global navigation remains at the bottom.
How do I animate the transition between tabs?
The built-in Scaffold Nav Bar uses instant page transitions. For animated transitions, build a custom nav bar with a PageView widget as the body and a custom bottom nav that calls PageController.animateToPage() with your preferred curve and duration.
My nav bar doesn't show on the FlutterFlow canvas — only in preview. Is that normal?
Yes. The Scaffold Navigation Bar renders in the live preview and on device, but may not fully display on the canvas editor. Use the 'Run' button (top right) to preview your app and see the nav bar in context.
Can I change the nav bar icon when the user is in a specific state, like during a live call?
Yes. Bind the nav bar icon to an App State boolean (e.g., isInCall). Use a Conditional to switch between the normal icon and a 'live' variant with an animated recording indicator. Update the App State variable from your call management logic.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation