Pengantar: Mengapa Bottom Navigation Bar Penting?
Dalam dunia pengembangan aplikasi mobile, pengalaman pengguna (User Experience - UX) adalah segalanya. Salah satu komponen UI yang sangat krusial untuk navigasi dan discoverability fitur utama adalah Bottom Navigation Bar. Komponen ini memungkinkan pengguna untuk berpindah antar bagian utama aplikasi dengan cepat dan intuitif, cukup dengan satu ketukan jari di bagian bawah layar.
Bayangkan aplikasi favorit Anda seperti Instagram, Tokopedia, atau WhatsApp. Semuanya menggunakan bottom navigation bar untuk mempermudah akses ke fitur-fitur inti. Tanpa bottom navigation, pengguna mungkin akan kesulitan menemukan atau beralih antar halaman, yang bisa berujung pada pengalaman yang kurang memuaskan.
Flutter, framework UI dari Google, menyediakan widget yang sangat fleksibel dan mudah digunakan untuk membuat bottom navigation bar dengan Flutter. Tutorial ini akan memandu Anda langkah demi langkah, dari persiapan hingga implementasi kode lengkap, sehingga Anda bisa menciptakan navigasi yang elegan dan fungsional di aplikasi Flutter Anda.
Prasyarat
Sebelum kita mulai, pastikan Anda memiliki prasyarat berikut:
- Flutter SDK Terinstal: Pastikan Anda sudah menginstal Flutter SDK dan mengaturnya dengan benar di sistem Anda. Anda bisa memeriksanya dengan menjalankan perintah
flutter doctordi terminal. - IDE (Integrated Development Environment): Visual Studio Code atau Android Studio dengan plugin Flutter/Dart terinstal.
- Dasar-dasar Dart dan Flutter: Pemahaman dasar tentang bahasa pemrograman Dart dan konsep dasar Flutter (widget, stateful/stateless widget) akan sangat membantu.
Langkah-langkah Implementasi: Membuat Bottom Navigation Bar dengan Flutter
Kita akan membuat aplikasi sederhana dengan empat halaman utama (Beranda, Jelajah, Favorit, Profil) yang dapat dinavigasi menggunakan bottom navigation bar. Kita akan menggunakan StatefulWidget untuk mengelola status item yang sedang aktif dan IndexedStack untuk menampilkan halaman yang sesuai.
Langkah 1: Buat Proyek Flutter Baru
Buka terminal atau command prompt Anda dan buat proyek Flutter baru dengan perintah:
flutter create bottom_nav_app
cd bottom_nav_app
Setelah proyek dibuat, buka folder proyek di IDE pilihan Anda.
Langkah 2: Siapkan Struktur Dasar Aplikasi (main.dart)
Buka file lib/main.dart. Kita akan menghapus kode boilerplate yang ada dan menggantinya dengan struktur dasar aplikasi kita. Aplikasi kita akan terdiri dari dua widget utama: MyApp (StatelessWidget) sebagai root aplikasi dan MyBottomNavigationBarApp (StatefulWidget) yang akan mengelola state navigasi.
Kita juga akan membuat beberapa halaman dummy (HomePage, ExplorePage, FavoritesPage, ProfilePage) sebagai tempat tujuan navigasi.
Langkah 3: Implementasi Kode Lengkap Bottom Navigation Bar
Berikut adalah kode lengkap yang akan Anda gunakan untuk membuat bottom navigation bar dengan Flutter. Salin dan tempel kode ini ke dalam file lib/main.dart Anda.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Bottom Nav Demo',
theme: ThemeData(
primarySwatch: Colors.deepPurple, // Mengatur warna utama aplikasi
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MyBottomNavigationBarApp(),
);
}
}
class MyBottomNavigationBarApp extends StatefulWidget {
const MyBottomNavigationBarApp({super.key});
@override
State<MyBottomNavigationBarApp> createState() => _MyBottomNavigationBarAppState();
}
class _MyBottomNavigationBarAppState extends State<MyBottomNavigationBarApp> {
int _selectedIndex = 0; // State untuk melacak indeks item yang sedang aktif
// List widget/halaman yang akan ditampilkan saat item bottom navigation ditekan
// Penting: Pastikan urutan halaman sesuai dengan urutan BottomNavigationBarItem
static const List<Widget> _widgetOptions = <Widget>[
HomePage(),
ExplorePage(),
FavoritesPage(),
ProfilePage(),
];
// Method yang dipanggil saat item bottom navigation ditekan
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index; // Memperbarui indeks item yang aktif
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bottom Nav Bar AnakInformatika'),
backgroundColor: Theme.of(context).primaryColor, // Menggunakan warna utama dari tema
foregroundColor: Colors.white, // Warna teks pada AppBar
),
body: IndexedStack(
index: _selectedIndex, // Menampilkan widget/halaman sesuai dengan indeks yang aktif
children: _widgetOptions, // List halaman yang mungkin ditampilkan
),
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Beranda',
),
BottomNavigationBarItem(
icon: Icon(Icons.explore),
label: 'Jelajah',
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: 'Favorit',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profil',
),
],
currentIndex: _selectedIndex, // Menentukan item mana yang sedang aktif
selectedItemColor: Theme.of(context).primaryColor, // Warna ikon/label item yang dipilih
unselectedItemColor: Colors.grey, // Warna ikon/label item yang tidak dipilih
onTap: _onItemTapped, // Callback saat item ditekan
type: BottomNavigationBarType.fixed, // Tipe navigasi: fixed atau shifting
backgroundColor: Colors.white, // Warna latar belakang bottom navigation bar
elevation: 10, // Ketinggian shadow di atas nav bar
showSelectedLabels: true, // Menampilkan label untuk item terpilih
showUnselectedLabels: true, // Menampilkan label untuk item tidak terpilih (default true untuk fixed)
),
);
}
}
// Halaman-halaman dummy untuk demonstrasi
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.home, size: 80, color: Theme.of(context).primaryColor),
const Text(
'Halaman Beranda',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.deepPurple),
),
const SizedBox(height: 10),
const Text('Selamat datang di beranda aplikasi Anda!', style: TextStyle(fontSize: 16)),
],
),
),
);
}
}
class ExplorePage extends StatelessWidget {
const ExplorePage({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.lightBlue[50],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.explore, size: 80, color: Colors.blue),
const Text(
'Halaman Jelajah',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blue),
),
const SizedBox(height: 10),
const Text('Temukan hal-hal menarik di sini.', style: TextStyle(fontSize: 16)),
],
),
),
);
}
}
class FavoritesPage extends StatelessWidget {
const FavoritesPage({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.pink[50],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.favorite, size: 80, color: Colors.pink),
const Text(
'Halaman Favorit',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.pink),
),
const SizedBox(height: 10),
const Text('Lihat daftar favorit Anda.', style: TextStyle(fontSize: 16)),
],
),
),
);
}
}
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return Container(
color: Colors.green[50],
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.person, size: 80, color: Colors.green),
const Text(
'Halaman Profil',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.green),
),
const SizedBox(height: 10),
const Text('Kelola informasi profil Anda.', style: TextStyle(fontSize: 16)),
],
),
),
);
}
}
Penjelasan Detail Baris per Baris
Mari kita bedah bagian-bagian penting dari kode di atas untuk memahami bagaimana bottom navigation bar bekerja di Flutter.
MyApp (StatelessWidget)
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Bottom Nav Demo',
theme: ThemeData(
primarySwatch: Colors.deepPurple,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MyBottomNavigationBarApp(),
);
}
}
- Ini adalah root aplikasi Flutter. Ia mengembalikan
MaterialApp, yang merupakan widget yang menyediakan fungsionalitas desain Material. title: Judul aplikasi (biasanya muncul di task switcher).theme: Mengatur tema visual aplikasi, termasukprimarySwatchyang akan mempengaruhi warna AppBar danselectedItemColordi Bottom Navigation Bar.home: Menentukan widget yang akan ditampilkan sebagai halaman awal aplikasi, yaituMyBottomNavigationBarApp.
MyBottomNavigationBarApp (StatefulWidget)
class MyBottomNavigationBarApp extends StatefulWidget {
const MyBottomNavigationBarApp({super.key});
@override
State<MyBottomNavigationBarApp> createState() => _MyBottomNavigationBarAppState();
}
- Karena bottom navigation bar perlu berubah (mengganti halaman yang ditampilkan dan menyorot item yang berbeda), kita menggunakan
StatefulWidget. Ini berarti widget ini memiliki "state" atau data yang bisa berubah seiring waktu. createState(): Metode ini membuat dan mengembalikan objekStateyang terkait denganStatefulWidgetini.
_MyBottomNavigationBarAppState (State Object)
class _MyBottomNavigationBarAppState extends State<MyBottomNavigationBarApp> {
int _selectedIndex = 0; // State untuk melacak indeks item yang sedang aktif
static const List<Widget> _widgetOptions = <Widget>[
HomePage(),
ExplorePage(),
FavoritesPage(),
ProfilePage(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
// ...
}
int _selectedIndex = 0;: Variabel ini menyimpan indeks item bottom navigation yang sedang aktif (dimulai dari 0). Ini adalah bagian inti dari "state" aplikasi kita yang akan berubah.static const List<Widget> _widgetOptions: Ini adalah daftar (list) dari widget/halaman yang akan ditampilkan oleh bottom navigation bar. Urutan widget di sini harus sesuai dengan urutanBottomNavigationBarItem.void _onItemTapped(int index): Metode ini dipanggil setiap kali pengguna mengetuk salah satu item di bottom navigation bar.setState(() { ... });: Ini adalah kunci untuk memperbarui UI di Flutter. Setiap perubahan yang Anda ingin refleksikan di UI harus dilakukan di dalam panggilansetState(). KetikasetState()dipanggil, Flutter akan menandai widget ini sebagai "kotor" dan akan membangun ulang (rebuild) bagian UI yang terpengaruh._selectedIndex = index;: Di sini kita memperbarui_selectedIndexdengan indeks item yang baru saja ditekan.
build Method (Struktur UI Utama)
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bottom Nav Bar AnakInformatika'),
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white,
),
body: IndexedStack(
index: _selectedIndex,
children: _widgetOptions,
),
bottomNavigationBar: BottomNavigationBar(
// ... properti bottom navigation bar
),
);
}
Scaffold: Widget ini menyediakan struktur visual dasar aplikasi Material Design, sepertiAppBar,body, danbottomNavigationBar.AppBar: Bilah atas aplikasi, di mana kita menampilkan judul.body: IndexedStack(...): Ini adalah bagian yang sangat penting.IndexedStackadalah widget yang menampilkan satu child dari daftar children-nya berdasarkan propertiindex. Keuntungannya adalah semua children (halaman) tetap berada di "widget tree" dan mempertahankan state mereka, tetapi hanya satu yang terlihat. Ini sangat efisien untuk bottom navigation karena halaman tidak perlu dibangun ulang setiap kali beralih.index: _selectedIndex: Ini mengikat indeks halaman yang ditampilkan ke variabel_selectedIndex, yang diperbarui saat item navigasi ditekan.children: _widgetOptions: Menyediakan daftar halaman yang bisa ditampilkan olehIndexedStack.
bottomNavigationBar: BottomNavigationBar(...): Ini adalah widget yang sebenarnya untuk bottom navigation bar.
BottomNavigationBar Properties
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Beranda',
),
// ... item lainnya
],
currentIndex: _selectedIndex,
selectedItemColor: Theme.of(context).primaryColor,
unselectedItemColor: Colors.grey,
onTap: _onItemTapped,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.white,
elevation: 10,
showSelectedLabels: true,
showUnselectedLabels: true,
),
items: Properti ini menerima daftarBottomNavigationBarItem. Setiap item mewakili satu tombol navigasi.icon: Icon yang akan ditampilkan untuk item tersebut.label: Teks label di bawah icon.
currentIndex: Menerima indeks item yang saat ini aktif. Ini harus cocok dengan_selectedIndexkita.selectedItemColor: Warna ikon dan label untuk item yang sedang aktif. Kita menggunakanTheme.of(context).primaryColoragar konsisten dengan tema.unselectedItemColor: Warna ikon dan label untuk item yang tidak aktif.onTap: Ini adalah callback yang dipicu ketika pengguna mengetuk salah satu item. Kita menghubungkannya ke metode_onItemTappedyang telah kita buat.type: Menentukan perilaku visual item.BottomNavigationBarType.fixed: Semua item terlihat dan memiliki ukuran yang sama, label selalu terlihat. Ideal untuk 3-5 item.BottomNavigationBarType.shifting: Item yang dipilih akan membesar dan menonjol, sementara item lain mengecil dan labelnya mungkin hilang. Cocok untuk 3-5 item dengan efek visual yang lebih dinamis.
backgroundColor: Warna latar belakang bottom navigation bar.elevation: Ketinggian shadow di atas bottom navigation bar.showSelectedLabelsdanshowUnselectedLabels: Mengontrol apakah label teks ditampilkan untuk item yang dipilih atau tidak dipilih. Untukfixedtype, keduanya biasanyatrue.