# How to Create a Custom Navigation Bar in FlutterFlow

- Tool: FlutterFlow
- Difficulty: Beginner
- Time required: 20-30 min
- Compatibility: FlutterFlow Free+
- Last updated: March 2026

## TL;DR

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.

## Before you start

- 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

### 1. 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.

### 2. 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.

### 3. 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.

### 4. 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.

### 5. 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 code example

File: `custom_nav_bar_component.dart`

```dart
// Custom Navigation Bar Component
// Add this as a Custom Widget in FlutterFlow
// Parameters: activeIndex (int), onTabChanged (Function(int))

import 'package:flutter/material.dart';

class CustomNavBar extends StatelessWidget {
  final int activeIndex;
  final Function(int) onTabChanged;
  final int notificationCount;

  const CustomNavBar({
    super.key,
    required this.activeIndex,
    required this.onTabChanged,
    this.notificationCount = 0,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return SizedBox(
      height: 72,
      child: Stack(
        clipBehavior: Clip.none,
        children: [
          // Nav bar background
          Container(
            height: 72,
            decoration: BoxDecoration(
              color: theme.colorScheme.surface,
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.08),
                  blurRadius: 12,
                  offset: const Offset(0, -2),
                ),
              ],
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                _NavItem(
                  icon: Icons.home_outlined,
                  activeIcon: Icons.home,
                  label: 'Home',
                  isActive: activeIndex == 0,
                  onTap: () => onTabChanged(0),
                ),
                _NavItem(
                  icon: Icons.search_outlined,
                  activeIcon: Icons.search,
                  label: 'Search',
                  isActive: activeIndex == 1,
                  onTap: () => onTabChanged(1),
                ),
                // Center gap for FAB
                const SizedBox(width: 64),
                _NavItem(
                  icon: Icons.notifications_outlined,
                  activeIcon: Icons.notifications,
                  label: 'Alerts',
                  isActive: activeIndex == 3,
                  badge: notificationCount,
                  onTap: () => onTabChanged(3),
                ),
                _NavItem(
                  icon: Icons.person_outlined,
                  activeIcon: Icons.person,
                  label: 'Profile',
                  isActive: activeIndex == 4,
                  onTap: () => onTabChanged(4),
                ),
              ],
            ),
          ),
          // Center FAB
          Positioned(
            top: -24,
            left: 0,
            right: 0,
            child: Center(
              child: GestureDetector(
                onTap: () => onTabChanged(2),
                child: AnimatedContainer(
                  duration: const Duration(milliseconds: 200),
                  width: 56,
                  height: 56,
                  decoration: BoxDecoration(
                    color: activeIndex == 2
                        ? theme.colorScheme.primary
                        : theme.colorScheme.primaryContainer,
                    shape: BoxShape.circle,
                    boxShadow: [
                      BoxShadow(
                        color: theme.colorScheme.primary.withOpacity(0.4),
                        blurRadius: 8,
                        offset: const Offset(0, 4),
                      ),
                    ],
                  ),
                  child: Icon(
                    Icons.add,
                    color: Colors.white,
                    size: 28,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _NavItem extends StatelessWidget {
  final IconData icon;
  final IconData activeIcon;
  final String label;
  final bool isActive;
  final int badge;
  final VoidCallback onTap;

  const _NavItem({
    required this.icon,
    required this.activeIcon,
    required this.label,
    required this.isActive,
    required this.onTap,
    this.badge = 0,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final color = isActive
        ? theme.colorScheme.primary
        : theme.colorScheme.onSurfaceVariant;
    return GestureDetector(
      onTap: onTap,
      child: Stack(
        children: [
          Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(isActive ? activeIcon : icon, color: color, size: 24),
              const SizedBox(height: 2),
              Text(label,
                  style: TextStyle(
                      color: color, fontSize: 11, fontWeight: isActive ? FontWeight.w600 : FontWeight.normal)),
            ],
          ),
          if (badge > 0)
            Positioned(
              right: 0,
              top: 0,
              child: Container(
                padding: const EdgeInsets.all(3),
                decoration: const BoxDecoration(
                    color: Colors.red, shape: BoxShape.circle),
                child: Text('$badge',
                    style: const TextStyle(color: Colors.white, fontSize: 9)),
              ),
            ),
        ],
      ),
    );
  }
}
```

## Common mistakes

- **Adding more than 5 items to the bottom navigation bar** — Material Design guidelines specify a maximum of 5 navigation destinations. More than 5 items make icons too small to tap accurately on mobile screens, and users cannot easily scan which section they are on. Fix: 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.
- **Using the nav bar as a history stack instead of top-level switching** — Tapping a nav bar item should take the user directly to that section's root — not push a new page onto the navigation stack. If you use push navigation for tabs, pressing the Android back button removes the tab instead of going back within the tab's content. Fix: 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.
- **Setting the nav bar background color directly on a Container instead of via the Theme Editor** — Hard-coded colors break dark mode support and make it difficult to update your brand colors later across all pages. Fix: 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.
- **Rebuilding the nav bar Component on every page load instead of using a persistent component** — If the nav bar is recreated on each navigation event, any local state (animation progress, notification count) resets to zero and the bar briefly flickers during transitions. Fix: 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.

## 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.

---

Source: https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-custom-navigation-bar-in-flutterflow
© RapidDev — https://www.rapidevelopers.com/flutterflow-tutorials/how-to-create-a-custom-navigation-bar-in-flutterflow
