flutter
/

Flutter TabBar – Complete Guide with TabBar and TabBarView

Last Sync: Today

On this page

10
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

Flutter TabBar – Complete Guide with TabBar and TabBarView

What is TabBar in Flutter?

TabBar and TabBarView are widgets that together create a tabbed interface. The TabBar displays a row of tabs (usually at the top of the screen), and the TabBarView displays the content for each tab. Users can switch between tabs by tapping the tab or swiping horizontally on the content. Flutter provides DefaultTabController for simple cases and TabController for more control.

Basic TabBar with DefaultTabController

The simplest way to add tabs is to wrap your Scaffold with DefaultTabController. You specify the number of tabs, and the controller manages the state automatically. Place the TabBar in the AppBar's bottom property, and the TabBarView in the body.

DARTRead-only
1
DefaultTabController(
  length: 3,
  child: Scaffold(
    appBar: AppBar(
      title: Text('Tabs Demo'),
      bottom: TabBar(
        tabs: [
          Tab(text: 'Tab 1'),
          Tab(text: 'Tab 2'),
          Tab(text: 'Tab 3'),
        ],
      ),
    ),
    body: TabBarView(
      children: [
        Center(child: Text('Content of Tab 1')),
        Center(child: Text('Content of Tab 2')),
        Center(child: Text('Content of Tab 3')),
      ],
    ),
  ),
)

Using TabBarView with Different Content

The TabBarView's children correspond to the tabs in order. You can place any widget, such as ListView, Column, or custom widgets. For performance, consider using ListView or SingleChildScrollView if the content might be large.

Customizing Tab Appearance

TabBar and Tab widgets offer several customization options:

    • labelColor and unselectedLabelColor: Text color of selected/unselected tabs.
    • indicator: A Decoration for the tab indicator (underline). Common options: BoxDecoration, UnderlineTabIndicator.
    • indicatorSize: TabBarIndicatorSize.label (fits text) or TabBarIndicatorSize.tab (fills the whole tab).
    • indicatorColor: Color of the indicator.
    • labelStyle and unselectedLabelStyle: Text styles.
    • padding: Padding around the entire tab bar.
DARTRead-only
1
TabBar(
  labelColor: Colors.white,
  unselectedLabelColor: Colors.white70,
  indicator: BoxDecoration(
    borderRadius: BorderRadius.circular(30),
    color: Colors.blue,
  ),
  indicatorSize: TabBarIndicatorSize.label,
  tabs: [
    Tab(text: 'Home'),
    Tab(text: 'Profile'),
  ],
)

Tab with Icons and Text

You can combine icons and text using the Tab widget's icon and text properties, or use a custom child.

DARTRead-only
1
TabBar(
  tabs: [
    Tab(icon: Icon(Icons.home), text: 'Home'),
    Tab(icon: Icon(Icons.person), text: 'Profile'),
  ],
)

Using TabController Manually

When you need more control (e.g., programmatically changing tabs, animating to a tab), you can create a TabController manually. You must dispose it in dispose(). Also, you need to provide a TickerProvider (usually this with SingleTickerProviderStateMixin).

DARTRead-only
1
class TabDemo extends StatefulWidget {
  @override
  _TabDemoState createState() => _TabDemoState();
}

class _TabDemoState extends State<TabDemo> with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  void _goToSecondTab() {
    _tabController.animateTo(1);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Manual Controller'),
        bottom: TabBar(
          controller: _tabController,
          tabs: [
            Tab(text: 'One'),
            Tab(text: 'Two'),
            Tab(text: 'Three'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          Center(child: Text('One')),
          Center(child: Text('Two')),
          Center(child: Text('Three')),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _goToSecondTab,
        child: Icon(Icons.arrow_forward),
      ),
    );
  }
}

Nested Tabs

You can nest tabs by using another DefaultTabController or TabController inside a tab's content. Be careful to manage the state correctly; nested tab controllers need their own TickerProvider.

Common Mistakes

    • Forgetting to set the number of tabs in DefaultTabController: length must match the number of tabs.
    • Mixing TabBar and TabBarView with different controllers: They must use the same controller.
    • Not disposing the TabController: Causes memory leaks.
    • Using a TabBar without a TabBarView or vice versa: The tabs won't work correctly.
    • Overriding the TabBarView's size: TabBarView automatically sizes to the largest child; if you constrain it, you might get clipping.

Best Practices

    • Use DefaultTabController for simple, static tabs.
    • Use manual TabController when you need to change tabs programmatically.
    • Always dispose of TabController in dispose.
    • For performance, avoid rebuilding large content inside TabBarView unnecessarily; use const or AutomaticKeepAliveClientMixin to preserve state.
    • Consider using Scrollable tabs if the number of tabs is large.

Key Takeaways

    • TabBar displays the tabs; TabBarView displays the content.
    • DefaultTabController simplifies tab management for common cases.
    • Use TabController for custom control (animation, programmatic switching).
    • Customize tab appearance with labelColor, indicator, indicatorSize, etc.
    • Always match the number of tabs with the number of children in TabBarView.

Try it yourself

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TabBar Demo',
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            title: Text('Tabs Demo'),
            bottom: TabBar(
              tabs: [
                Tab(icon: Icon(Icons.home), text: 'Home'),
                Tab(icon: Icon(Icons.search), text: 'Search'),
                Tab(icon: Icon(Icons.person), text: 'Profile'),
              ],
              labelColor: Colors.white,
              unselectedLabelColor: Colors.white70,
              indicator: BoxDecoration(
                borderRadius: BorderRadius.circular(30),
                color: Colors.blue,
              ),
              indicatorSize: TabBarIndicatorSize.label,
            ),
          ),
          body: TabBarView(
            children: [
              Center(child: Text('Home Page', style: TextStyle(fontSize: 24))),
              Center(child: Text('Search Page', style: TextStyle(fontSize: 24))),
              Center(child: Text('Profile Page', style: TextStyle(fontSize: 24))),
            ],
          ),
        ),
      ),
    );
  }
}

Test Your Knowledge

Q1
of 4

Which widget provides a simple way to manage tabs without writing a custom controller?

A
TabController
B
DefaultTabController
C
TabBarController
D
TabBarView
Q2
of 4

Where should you place the `TabBar` to have it inside the app bar?

A
title
B
leading
C
bottom
D
actions
Q3
of 4

What must you do with a custom `TabController` to avoid memory leaks?

A
Call `dispose()` in `dispose` method
B
Set it to null
C
Call `close()`
D
Nothing, it's automatic
Q4
of 4

How do you customize the tab indicator to be a rounded rectangle?

A
indicatorColor
B
indicator: BoxDecoration(...)
C
indicatorStyle
D
indicatorShape

Frequently Asked Questions

How do I make the TabBar appear below the AppBar?

Place the TabBar in the AppBar's bottom property. The AppBar will automatically position it below the title.

How do I change the tab programmatically?

If you have a TabController, call _tabController.animateTo(index) or _tabController.index = index (without animation).

How can I keep the state of each tab when switching?

Wrap the content of each tab with AutomaticKeepAliveClientMixin or use a PageView inside TabBarView with keepPage: true. The default TabBarView does not preserve state, so you need to implement it manually if needed.

How do I create tabs with rounded corners (like a pill)?

Use a custom indicator decoration: indicator: BoxDecoration(borderRadius: BorderRadius.circular(30), color: Colors.blue).

Can I use TabBar without an AppBar?

Yes, you can place TabBar anywhere, but it still needs a TabController (either from DefaultTabController or a custom one).

Previous

flutter drawer widget

Next

flutter setstate

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.