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.

Flutter’da Custom Bottom Navigation Bar Nasıl Oluşturulur?

 

Sıfırdan Adım Adım Anlatım


Uygulamanı açan bir kullanıcı ilk olarak nereye bakar? Genelde ekranın en altına. İşte tam bu yüzden Bottom Navigation Bar, kullanıcı deneyiminin kalbinde yer alır. Ama varsayılan bar yetmez. Özgün, akılda kalıcı ve sana ait bir deneyim istiyorsan custom bar şart.

Bu yazıda, Flutter’da kendi özel alt gezinme çubuğunu (Custom Bottom Navigation Bar) nasıl sıfırdan yazabileceğini sade ve anlaşılır bir dille anlatıyorum. Hazırsan başlayalım.

1. Hazırlık: Neyi Yapacağız?

  • Dört sekmeli bir uygulama
  • Her sekmenin kendine ait ekranı olacak

Alt menüdeki butonlar:

  • Seçili ise farklı bir renkte gösterilecek
  • Tıklanınca sayfa değişecek
  • Animasyonla canlandırılacak

2. Proje Yapısı

Ana sayfa dosyamız:

main.dart

Sayfa dosyalarımız:

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

3. Ana Yapı: Stateful Widget ile Sayfa Değişimi

Önce main.dart içinde tabların geçişini yönetecek olan temel yapıyı kuralım:

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. Custom BottomNavigationBar’ı Yazalım

widgets klasörü içerisineCustomBottomNavBar adında bir widget oluşturalım:

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. Sayfa Örneklerini Ekleyelim

Her ekran için basit bir scaffold yapısı yeterli:

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ı')),
);
}
}

Ve Sonuç

Artık uygulamanda kendi tarzını yansıtan, sade ama güçlü bir alt gezinme çubuğu var.
Bu yapıyı dilediğin gibi renklendirebilir, şekillendirebilir, ikonları değiştirebilir ya da butonlara bildirim rozetleri ekleyebilirsin.

Kendi custom widget’ını yazmak, Flutter’ı derinlemesine öğrenmenin en keyifli yollarından biri. Hem görünüm senin olur, hem de kullanıcı deneyimi fark yaratır.



Umarım faydalı olmuştur.

Daha fazlası için abone olmayı unutmayın.

Teşekkürler.

Selin.

Is It Harder to Learn Software or Be a Mom?

 

A Personal Journey on Doing Both at the Same Time

image created by chatgpt

At one point in my life, I never thought I would spend my mornings writing code and my afternoons chasing toy trucks in the park. But now, I try to balance between these two worlds: being a mom trying to learn software development.

This article is not a success story. Rather, it is a journey with its ups and downs, days when I came close to giving up and moments when I got up again with tiny successes.

2019: First Step to Software Development

I started learning software in 2019. I was single at the time, working at a private bank. Life was simpler, time was more flexible. I had a better chance to focus, but this did not make the process easier.

Because banking was not as systematic as it seemed from the outside. Intense tempo, customer pressure, high stress levels, after work, I found myself trying to learn in a completely different — and completely unfamiliar — world: the world of software.

Codes, loops, bugs… Sometimes I applied it without understanding it, sometimes I was stuck for hours. But still, something inside me whispered that I could belong to this world.

Between School and Code

I made a decision to put what I have learned over the years into a framework and to have my diploma ready if I have the opportunity to work in a company in the future.

And this month, in June 2025, I graduated from Atatürk University Open Education Faculty Web Design and Computer Programming.

Completing this department after becoming a mother was a challenge in itself. But I did it. I passed every exam with insomnia at night, studying in the hours between children’s games.

Code, House or Child? Who said the day is 24 hours long?

Now with motherhood, this journey has taken a whole new turn. But not only motherhood, but also running the house is part of the equation.
7:30 a.m. My son wakes up. Before I even open my eyes, I start the day with the sound of “Mom, come on, get up!” Breakfast is immediately prepared, the kitchen is tidied up and games are played with my son. Perhaps taking a short cartoon break, I run to the kitchen and put the dishes away. Then the washing machine beeps. The day begins but for some reason it feels like it will never end.

If by 10:00 the house is tidied up and the child is off playing… maybe I can get a 20 minute coding window. But even that depends on luck. The coding part of the day usually coincides with his nap time (if he actually sleeps that day). Or there is a short window in the evening when my wife comes home.

But how “focused” these times are, I’ll be honest: not always. Because I always have a to-do list in my mind:

“What will be cooked tonight? Is the laundry dry? Did my son go out today? What was the bug in the app?”

So the time that is really “mine” during the day is very narrow.

Still… I don’t give up.

My Most Difficult Moments

  • My son coming and hitting the keys of my computer while I was writing code 🤭
  • Waking up crying just when I found the solution 🥲
  • Getting stuck in a compiler error after sleepless nights.
  • Feeling like I’m counting down while everyone else is moving forward.
  • Developing projects on the one hand and studying for exams on the other.
  • And, of course, underestimating sentences like “Are you learning something now? But there is a child…”.

I didn’t give up because…

I found the strength to start again every time. Sometimes I could only spare 15 minutes. But I did.

  • I created my own notebook to read code.
  • I turned what I learned into small blog posts.
  • I slowly started to implement my application idea.
  • I spent time with my son by making English games with him and improved myself.
  • I finished school. My dreams for new projects are still alive.

Because learning software offered me not only a job but also “a space of my own”.

A Note to Other Moms

If you are a mom and trying to make time for software (or any other field) at the same time:
Don’t sell yourself short. Even 10 minutes a day is a big thing.
Don’t fall into the trap of perfectionism. Sometimes just “doing” is enough.
Ask for support. From your partner, your family, your environment.
Create sources of motivation. Set small goals for yourself.

You may not be walking this journey alone. I’m still on my way.

In a nutshell: “I’m Here!”

This post is a comeback post. On Medium, in codes, in motherhood… I am still here. And I keep writing, producing, learning.

If you feel lost in your parenting and learning journey, you are not alone.

Stay tuned. Because from now on, there will be more writing, more experiences and maybe new paths we will take together.

Thank you.

Selin.