Flutter Tabs
Create tabbed interfaces for organized content
📑 What are Tabs?
Tabs organize content into separate views where only one view is visible at a time. Users can switch between tabs by tapping on them. Tabs are perfect for categorizing related content like chat conversations, news categories, or product listings in a clean, organized manner.
// Basic tab structure
TabBar(tabs: [Tab(text: 'Tab 1'), Tab(text: 'Tab 2')])
TabBarView(children: [Widget1(), Widget2()])
Key Tab Concepts
TabController
Manages tab state and switching
TabController(
length: 3,
vsync: this
)
TabBar
Displays the tab headers
TabBar(
tabs: [Tab(...), Tab(...)]
)
TabBarView
Shows tab content
TabBarView(
children: [Widget1(), Widget2()]
)
Tab
Individual tab item
Tab(
icon: Icon(Icons.home),
text: 'Home'
)
🔹 Basic Tabs
Create a simple tabbed interface with TabBar and TabBarView:
import 'package:flutter/material.dart';
class TabsDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3, // Number of tabs
child: Scaffold(
appBar: AppBar(
title: Text('Tabs Demo'),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.home), text: 'Home'),
Tab(icon: Icon(Icons.star), text: 'Favorites'),
Tab(icon: Icon(Icons.person), text: 'Profile'),
],
),
),
body: TabBarView(
children: [
Center(child: Text('Home Tab', style: TextStyle(fontSize: 30))),
Center(child: Text('Favorites Tab', style: TextStyle(fontSize: 30))),
Center(child: Text('Profile Tab', style: TextStyle(fontSize: 30))),
],
),
),
);
}
}
Result:
✅ Three tabs appear below the AppBar
✅ Tapping a tab switches the content
✅ Swipe gesture also switches tabs
🔹 Tabs with Custom Controller
Use TabController for more control over tab behavior:
class TabsWithController extends StatefulWidget {
@override
_TabsWithControllerState createState() => _TabsWithControllerState();
}
class _TabsWithControllerState extends State
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
// Listen to tab changes
_tabController.addListener(() {
if (!_tabController.indexIsChanging) {
print('Current tab: ${_tabController.index}');
}
});
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Custom Tab Controller'),
bottom: TabBar(
controller: _tabController,
tabs: [
Tab(text: 'Chats'),
Tab(text: 'Status'),
Tab(text: 'Calls'),
],
),
),
body: TabBarView(
controller: _tabController,
children: [
Center(child: Text('Chats Content')),
Center(child: Text('Status Content')),
Center(child: Text('Calls Content')),
],
),
);
}
}
TabController Benefits:
-
Programmatically switch tabs:
_tabController.animateTo(1) - Listen to tab changes
-
Get current tab index:
_tabController.index - More control over animations
🔹 Styled Tabs
Customize tab appearance with colors and indicators:
AppBar(
title: Text('Styled Tabs'),
backgroundColor: Colors.purple,
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.directions_car), text: 'Car'),
Tab(icon: Icon(Icons.directions_bike), text: 'Bike'),
Tab(icon: Icon(Icons.directions_bus), text: 'Bus'),
],
indicatorColor: Colors.white,
indicatorWeight: 4.0,
labelColor: Colors.white,
unselectedLabelColor: Colors.white70,
labelStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
unselectedLabelStyle: TextStyle(fontSize: 14),
),
)
Styling Properties:
- indicatorColor: Color of the selection indicator
- indicatorWeight: Thickness of the indicator
- labelColor: Color of selected tab text
- unselectedLabelColor: Color of unselected tabs
🔹 Scrollable Tabs
Create scrollable tabs when you have many items:
DefaultTabController(
length: 8,
child: Scaffold(
appBar: AppBar(
title: Text('Scrollable Tabs'),
bottom: TabBar(
isScrollable: true, // Enable scrolling
tabs: [
Tab(text: 'Technology'),
Tab(text: 'Sports'),
Tab(text: 'Entertainment'),
Tab(text: 'Business'),
Tab(text: 'Health'),
Tab(text: 'Science'),
Tab(text: 'Politics'),
Tab(text: 'Travel'),
],
),
),
body: TabBarView(
children: [
Center(child: Text('Technology News')),
Center(child: Text('Sports News')),
Center(child: Text('Entertainment News')),
Center(child: Text('Business News')),
Center(child: Text('Health News')),
Center(child: Text('Science News')),
Center(child: Text('Politics News')),
Center(child: Text('Travel News')),
],
),
),
)
Result:
✅ Tabs can be scrolled horizontally
✅ Perfect for many categories
🔹 Tabs with Icons Only
Create icon-only tabs for a cleaner look:
TabBar(
tabs: [
Tab(icon: Icon(Icons.camera_alt)),
Tab(icon: Icon(Icons.chat)),
Tab(icon: Icon(Icons.settings)),
],
)
🔹 Tabs in Body (Not AppBar)
Place tabs anywhere in your layout:
class TabsInBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(title: Text('Tabs in Body')),
body: Column(
children: [
Container(
color: Colors.blue,
child: TabBar(
tabs: [
Tab(text: 'Overview'),
Tab(text: 'Details'),
],
),
),
Expanded(
child: TabBarView(
children: [
Center(child: Text('Overview Content')),
Center(child: Text('Details Content')),
],
),
),
],
),
),
);
}
}
🔹 Complete Example
A full working tabs implementation with real content:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tabs Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: TabsExample(),
);
}
}
class TabsExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 4,
child: Scaffold(
appBar: AppBar(
title: Text('My Social App'),
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.camera_alt), text: 'Camera'),
Tab(icon: Icon(Icons.chat), text: 'Chats'),
Tab(icon: Icon(Icons.circle), text: 'Status'),
Tab(icon: Icon(Icons.call), text: 'Calls'),
],
),
),
body: TabBarView(
children: [
CameraTab(),
ChatsTab(),
StatusTab(),
CallsTab(),
],
),
),
);
}
}
class CameraTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.camera_alt, size: 100, color: Colors.blue),
SizedBox(height: 20),
Text('Camera', style: TextStyle(fontSize: 24)),
],
),
);
}
}
class ChatsTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(child: Text('${index + 1}')),
title: Text('Contact ${index + 1}'),
subtitle: Text('Last message...'),
trailing: Text('12:30 PM'),
);
},
);
}
}
class StatusTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Status Updates', style: TextStyle(fontSize: 24)),
);
}
}
class CallsTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: 5,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: Icon(Icons.person),
),
title: Text('Contact ${index + 1}'),
subtitle: Text('Yesterday, 3:45 PM'),
trailing: Icon(Icons.call, color: Colors.green),
);
},
);
}
}