How to Create Custom Bottom Navigation Bar in Flutter?

 

Step-by-Step Explanation from Scratch



Where does a user who opens your app look first? Usually at the bottom of the screen. That’s why the Bottom Navigation Bar is at the heart of the user experience. But the default bar is not enough. If you want an experience that’s unique, memorable and your own, a custom bar is a must.

In this article, I’ll explain in plain and simple language how you can write your own custom bottom navigation bar in Flutter from scratch. Let’s get started if you’re ready.

1. Preparation: What will we do?

  • An application with four tabs
  • Each tab will have its own screen

Buttons in the submenu:

  • Will be shown in a different color if selected
  • Will change the page when clicked
  • Will be animated

2. Project Structure

Our home page file:

main.dart

Our page files:

screens/
home_screen.dart
search_screen.dart
profile_screen.dart
settings_screen.dart
widgets/
custom_nav_bar.dart

3. Main Structure: Page Switching with Stateful Widget

First, let’s set up the basic structure in main.dart that will manage the switching of tabs:

import 'package:custom_nav_bar/widgets/custom_nav_bar.dart';
import 'package:flutter/material.dart';
import 'screens/home_screen.dart';
import 'screens/search_screen.dart';
import 'screens/profile_screen.dart';
import 'screens/settings_screen.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Custom BottomNav Demo',
debugShowCheckedModeBanner: false,
home: MainScreen(),
);
}
}

class MainScreen extends StatefulWidget {
const MainScreen({super.key});

@override
State<MainScreen> createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
int currentIndex = 0;

final screens = [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
SettingsScreen(),
];

@override
Widget build(BuildContext context) {
return Scaffold(
body: screens[currentIndex],
bottomNavigationBar: CustomBottomNavBar(
currentIndex: currentIndex,
onTap: (index) {
setState(() {
currentIndex = index;
});
},
),
);
}
}

4. Let’s Write Custom BottomNavigationBar.

Let’s create a widget calledCustomBottomNavBar in the widgets folder:

import 'package:flutter/material.dart';

class CustomBottomNavBar extends StatelessWidget {
final int currentIndex;
final Function(int) onTap;

const CustomBottomNavBar({
required this.currentIndex,
required this.onTap,
});

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(color: Colors.black12, blurRadius: 10),
],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildNavItem(icon: Icons.home, index: 0, label: 'Ana Sayfa'),
_buildNavItem(icon: Icons.search, index: 1, label: 'Ara'),
_buildNavItem(icon: Icons.person, index: 2, label: 'Profil'),
_buildNavItem(icon: Icons.settings, index: 3, label: 'Ayarlar'),
],
),
);
}

Widget _buildNavItem({
required IconData icon,
required int index,
required String label,
}) {
final isSelected = index == currentIndex;

return GestureDetector(
onTap: () => onTap(index),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedContainer(
duration: Duration(milliseconds: 250),
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: isSelected ? Colors.blue.shade100 : Colors.transparent,
shape: BoxShape.circle,
),
child: Icon(
icon,
color: isSelected ? Colors.blue : Colors.grey,
size: isSelected ? 28 : 24,
),
),
const SizedBox(height: 4),
Text(
label,
style: TextStyle(
fontSize: 12,
color: isSelected ? Colors.blue : Colors.grey,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
],
),
);
}
}

5. Add Page Instances

A simple scaffold structure for each screen is enough:

home_screen.dart

import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: Center(
child: Text('Ana Sayfa'),
),
);
}
}

search_screen.dart

import 'package:flutter/material.dart';

class SearchScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueAccent[100],
body: Center(
child: Text('Arama Sayfası'),
),
);
}
}

profile_screen.dart

import 'package:flutter/material.dart';

class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber[100],
body: Center(
child: Text('Profil Sayfası'),
),
);
}
}

settings_screen.dart

import 'package:flutter/material.dart';

class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.purpleAccent[100],
body: Center(child: Text('Ayarlar Sayfası')),
);
}
}

And The Result

Now your app has a simple but powerful bottom navigation bar that reflects your own style.

You can color and shape it however you like, change the icons, or add notification badges to the buttons.

Writing your own custom widget is one of the most enjoyable ways to learn Flutter in depth. The look is yours and the user experience makes all the difference.



I hope it was useful.

Don’t forget to clap and subscribe for more.

Thank you.

Selin.

Hiç yorum yok: