Master Flutter Navigation: AppBar, Drawer & Bottom Tabs | (Ch. 12)
Welcome to Module 3: Navigation & App Structure!
In the previous modules, we focused on single screens. We learned Dart basics, mastered layout with Rows and Columns, and built lists of data. But real-world apps are rarely just one screen. They are complex structures with menus, navigation bars, and headers.
Think of your favorite apps. They likely have a bar at the top (AppBar), a menu that slides out from the left (Drawer), or a row of tabs at the bottom (Bottom Navigation Bar).
In this chapter, we are going to explore the Scaffold widget in depth. We've used it before as a "blank page," but it is actually a powerful layout engine that has pre-defined "slots" for all these standard app components.
📖 Chapter 12: Table of Contents
1. The Scaffold: More Than Just a Body
We typically use `Scaffold` like this:
return Scaffold(
body: Container(),
);
But `Scaffold` is an implementation of the Material Design visual layout structure. It saves you from having to manually calculate where the status bar is, how high the top bar should be, or how to animate a side menu.
It has three main "slots" we will fill today:
- `appBar`: The fixed header at the top.
- `drawer`: The hidden panel that slides in from the start (left).
- `bottomNavigationBar`: The fixed footer tabs at the bottom.
2. The AppBar: The Crown of Your App
The `AppBar` provides context. It tells the user "Where am I?" and "What can I do here?"
The Anatomy: Leading, Title, Actions
The `AppBar` layout is divided into three specific zones. You don't use `Row` here; you use these properties:
-
`leading`: The icon on the far left.
- If you have a Drawer, this automatically becomes a "Hamburger Menu" icon.
- If you navigated from a previous screen, this automatically becomes a "Back Arrow."
- You can also customize it manually (e.g., to show a logo).
- `title`: The main content in the middle (usually `Text`).
- `actions`: A list of widgets on the far right. This is usually for global actions like "Search," "Settings," or "Notifications."
AppBar(
// 1. The Leading Widget (Left)
leading: Icon(Icons.account_circle),
// 2. The Title (Center/Left depending on OS)
title: Text("My Dashboard"),
// 3. The Actions (Right)
actions: [
IconButton(
icon: Icon(Icons.search),
onPressed: () { print("Search tapped"); },
),
IconButton(
icon: Icon(Icons.settings),
onPressed: () { print("Settings tapped"); },
),
],
)
Styling: Colors and Elevation
You can easily change the look of the AppBar.
AppBar(
backgroundColor: Colors.deepPurple,
foregroundColor: Colors.white, // Color of text and icons
elevation: 10.0, // The size of the drop shadow
centerTitle: true, // Force title to be centered
)
Pro Tip: Setting `elevation: 0` creates a "flat" app bar that blends into the background, a very popular modern design trend.
3. The Drawer: The Hidden Side Menu
The Drawer (often called the "Hamburger Menu") is great for navigation links that aren't important enough to be on the main screen (e.g., "Profile," "About Us," "Log Out").
Adding a Drawer to Scaffold
This is almost too easy. You just add a `Drawer` widget to the `drawer` property of the `Scaffold`.
Scaffold(
appBar: AppBar(title: Text("Home")),
// Just adding this line automatically creates the
// Menu icon in the AppBar and handles the slide animation!
drawer: Drawer(
child: Text("I am a drawer"),
),
)
Building the Drawer Menu
A standard Material Design drawer usually contains a **Header** (with user info) and a **List** of links. We use `ListView` for this.
Drawer(
child: ListView(
padding: EdgeInsets.zero, // Important to remove top padding!
children: [
// The Header
UserAccountsDrawerHeader(
accountName: Text("Flutter Dev"),
accountEmail: Text("student@example.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
child: Text("F"),
),
decoration: BoxDecoration(color: Colors.deepPurple),
),
// The Menu Items
ListTile(
leading: Icon(Icons.home),
title: Text("Home"),
onTap: () {
// Navigate to Home
},
),
ListTile(
leading: Icon(Icons.settings),
title: Text("Settings"),
onTap: () {
// Navigate to Settings
},
),
],
),
)
Closing the Drawer Programmatically
When a user taps a menu item, the Drawer does not close automatically. You must tell it to close.
The Drawer is technically a "route" (like a new screen) sitting on top of your app. To close it, you "pop" it off the stack.
onTap: () {
// This line closes the drawer
Navigator.pop(context);
}
We will cover `Navigator` in detail in the next chapter.
4. BottomNavigationBar: The Thumb Zone
For primary navigation (the most important screens), use a `BottomNavigationBar`. It sits at the bottom of the screen, easily reachable by the user's thumb.
Why Bottom Navigation?
Drawers hide navigation. Bottom Bars expose it. If you have 3-5 top-level screens (like Instagram's Home, Search, Reels, Shop, Profile), use a Bottom Bar.
The "Index Stack" Logic
Implementing a Bottom Bar requires a bit of logic. It is a Stateful interaction.
- You need an integer variable (e.g., `_selectedIndex`) to remember which tab is active.
- You need a `List
` containing the screens you want to show. - In the `Scaffold` body, you display the widget from the list at the current index: `_myPages[_selectedIndex]`.
class _HomeScreenState extends State<HomeScreen> {
// 1. Track the current tab
int _selectedIndex = 0;
// 2. The screens to display
final List<Widget> _pages = [
Center(child: Text("Page 1: Home")),
Center(child: Text("Page 2: Search")),
Center(child: Text("Page 3: Profile")),
];
// ...
Wiring up the `onTap`
The `BottomNavigationBar` widget needs two things: the `currentIndex` (so it knows which icon to highlight) and an `onTap` function (to update the state).
// Inside Scaffold
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex, // 3. Highlight the correct icon
// 4. Handle taps
onTap: (int index) {
setState(() {
_selectedIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Search',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
5. Putting It All Together (Full Code Example)
Here is a complete, working app that combines an AppBar, a Drawer, and a Bottom Navigation Bar into one cohesive structure.
Copy this into your `lib/main.dart` file to see it in action.
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(
debugShowCheckedModeBanner: false,
title: 'Navigation Demo',
theme: ThemeData(
primarySwatch: Colors.indigo,
useMaterial3: true,
),
home: const MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _selectedIndex = 0;
// The content for each tab
final List<Widget> _pages = [
// Tab 0
const Center(
child: Icon(Icons.home, size: 100, color: Colors.indigo),
),
// Tab 1
const Center(
child: Icon(Icons.search, size: 100, color: Colors.pink),
),
// Tab 2
const Center(
child: Icon(Icons.settings, size: 100, color: Colors.orange),
),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
// --- 1. APP BAR ---
appBar: AppBar(
title: const Text('Navigation Master'),
backgroundColor: Colors.indigo,
foregroundColor: Colors.white,
actions: [
IconButton(
icon: const Icon(Icons.notifications),
onPressed: () {},
),
],
),
// --- 2. DRAWER ---
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const UserAccountsDrawerHeader(
accountName: Text("Flutter Student"),
accountEmail: Text("learning@flutter.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
child: Text("FS", style: TextStyle(color: Colors.indigo)),
),
decoration: BoxDecoration(color: Colors.indigo),
),
ListTile(
leading: const Icon(Icons.message),
title: const Text('Messages'),
onTap: () {
Navigator.pop(context); // Close drawer
},
),
ListTile(
leading: const Icon(Icons.account_circle),
title: const Text('Profile'),
onTap: () {
Navigator.pop(context); // Close drawer
},
),
],
),
),
// --- 3. BODY (Dynamic) ---
body: _pages[_selectedIndex],
// --- 4. BOTTOM NAVIGATION ---
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.search),
label: 'Explore',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Settings',
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.indigo,
onTap: _onItemTapped,
),
);
}
}
Conclusion
You now have the "skeleton" of a professional app.
- The AppBar crowns your app and provides actions.
- The Drawer hides secondary navigation links.
- The BottomNavigationBar allows fast switching between main screens.
But there's one thing missing. In our Drawer, we clicked "Messages," and we just closed the drawer. We didn't actually go anywhere.
In the next chapter, we will tackle Basic Navigation. We will learn how to use `Navigator.push` to open a new screen and `Navigator.pop` to go back. This is how you create deep, multi-layered applications.
Comments
Post a Comment