TabBar in Flutter
Implement elegant, functional tabs in your apps
Download Complete ExamplesWhat is TabBar?
The TabBar is a fundamental widget in Flutter that lets you organize content into horizontal tabs, ideal for showing different sections of an application in an organized way.
Types of TabBars
DefaultTabBar
TabBar standard with a simple indicator
TabBar Customized
With custom colors, shapes and styles
Scrollable TabBar
For many tabs with horizontal scrolling
Basic Implementation
DefaultTabController with TabBar
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: 'TabBar Example',
theme: ThemeData(primarySwatch: Colors.blue),
home: const TabBarExample(),
);
}
}
class TabBarExample extends StatelessWidget {
const TabBarExample({super.key});
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: const Text('Mi App con Pestañas'),
bottom: const TabBar(
tabs: [
Tab(icon: Icon(Icons.home), text: 'Home'),
Tab(icon: Icon(Icons.search), text: 'Find'),
Tab(icon: Icon(Icons.person), text: 'Profile'),
],
),
),
body: const TabBarView(
children: [
Center(child: Text('Contenido de Inicio')),
Center(child: Text('Contenido de Búsqueda')),
Center(child: Text('Contenido de Perfil')),
],
),
),
);
}
}
Main Properties
tabs
List of Tab widgets to be shown
Type: List<Widget>
Required: Yes
controller
Controls the selected tab and the animation
Type: TabController
Required: No (created automatically)
isScrollable
Allows horizontal scrolling
Type: bool
Default: false
indicatorColor
Color of the active tab indicator
Type: Color
Default: Theme accentColor
labelColor
Color of the active tab's text
Type: Color
Default: Theme accentColor
unselectedLabelColor
Color of inactive tabs' text
Type: Color
Default: Theme unselectedWidgetColor
Widget Tab
text
Tab text
text: 'Home'
icon
Tab icon
icon: Icon(Icons.home)
child
Custom widget for the tab
child: Text('Home')
height
Custom tab height
height: 60
Tab Examples:
// Tab con icono y textoTab(icon: Icon(Icons.home), text: 'Home')
// Tab solo con textoTab(text: 'Settings')
// Tab solo con iconoTab(icon: Icon(Icons.star))
// Tab personalizadoTab(
child: Row(
children: [
Icon(Icons.notifications),
SizedBox(width: 4),
Text('Notificaciones'),
],
),
)
Visual Examples
1. TabBar Classic
Chat List
- Ana - Hola!
- Carlos - ¿Cómo estás?
- Maria - Reunión mañana
DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: Text('Mi App'),
bottom: TabBar(
tabs: [
Tab(text: 'Chats'),
Tab(text: 'Estados'),
Tab(text: 'Llamadas'),
],
),
),
body: TabBarView(
children: [
ChatsScreen(),
StatusScreen(),
CallsScreen(),
],
),
),
)
2. TabBar with Icons
Your Music Library
Playing: Current Song
TabBar(
tabs: [
Tab(icon: Icon(Icons.music_note), text: 'Música'),
Tab(icon: Icon(Icons.podcasts), text: 'Podcasts'),
Tab(icon: Icon(Icons.radio), text: 'Radio'),
],
)
3. TabBar Custom
Today's Statistics
Sales: \$1,250
Visits: 342
TabBar(
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.deepPurple,
),
labelColor: Colors.white,
unselectedLabelColor: Colors.deepPurple,
tabs: [
Tab(text: 'Hoy'),
Tab(text: 'Semana'),
Tab(text: 'Mes'),
Tab(text: 'Year'),
],
)
TabController Manual
For more advanced control, you can use TabController manually with StatefulWidget.
TabController with StatefulWidget
class TabControllerExample extends StatefulWidget {
const TabControllerExample({super.key});
@override
State<TabControllerExample> createState() => _TabControllerExampleState();
}
class _TabControllerExampleState extends State<TabControllerExample>
with SingleTickerProviderStateMixin {
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(length: 3, vsync: this);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TabController Manual'),
bottom: TabBar(
controller: _tabController,
tabs: const [
Tab(text: 'News'),
Tab(text: 'Sports'),
Tab(text: 'Technology'),
],
),
),
body: TabBarView(
controller: _tabController,
children: const [
NewsScreen(),
SportsScreen(),
TechnologyScreen(),
],
),
);
}
}
Customizer for TabBar
This is the content of the selected tab...
// El código se generará aquí
TabBarView
The TabBarView widget shows the content corresponding to each tab.
Implementation of TabBarView
TabBarView(
children: [
// Contenido para la primera pestaña ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
title: Text('Element \$index'),
);
},
),
// Contenido para la segunda pestaña GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: 10,
itemBuilder: (context, index) {
return Card(
child: Center(child: Text('Card \$index')),
);
},
),
// Contenido para la tercera pestaña Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.person, size: 64),
Text('Perfil de Usuario'),
],
),
),
],
)
Best Practices
✅ What you SHOULD do
- Use between 2 and 5 tabs maximum
- Keep labels short and descriptive
- Use recognizable icons when possible
- Consider using manual TabController for complex cases
- Use TabBarView for related content
❌ What you should NOT do
- Don't use more than 5-6 tabs (better to use scrollable)
- Avoid very long labels
- Don't mix different navigation patterns
- Don't forget to dispose() the manual TabController
- Don't use tabs for unrelated functionality
Pro Tip
To improve performance with many tabs, consider using AutomaticKeepAliveClientMixin to preserve each tab's state and avoid unnecessary rebuilds.
Exercises to Practice
Exercise 1: News App
Create a news app with tabs for different categories.
Suggested tabs: Latest, Sports, Technology, Entertainment
Exercise 2: Weather App
Implement a weather app with tabs for different cities.
Suggested tabs: Current City, Favorites, Extended Forecast
Exercise 3: Tasks App
Create a tasks app with tabs for different statuses.
Suggested tabs: All, Pending, Completed, Archived
Common Troubleshooting
❌ Tabs don't change when swiping
Solution: Make sure to use the same TabController in TabBar and TabBarView
controller: _tabController // en ambos widgets
❌ Error: 'TabController vsync was null'
Solution: Implement SingleTickerProviderStateMixin in your State
with SingleTickerProviderStateMixin
❌ The content doesn't update
Solution: Use AutomaticKeepAliveClientMixin to preserve the state
with AutomaticKeepAliveClientMixin