Flutter TabBar Widgets, TabController, TabBarView, and Dart Examples Prepared by Rachit Joshi 1. Introduction Flutter is a UI toolkit for building applications for mobile, web, and desktop from a shared codebase. The TabBar widget presents a row of tabs so users can switch between related sections of an interface. It is commonly paired with TabBarView, which displays the content associated with the selected tab. A tabbed interface is useful when an application has several related sections, such as Overview, Details, and Reviews. Flutter coordinates tab selection and displayed content through a TabController, which can be supplied directly or provided by DefaultTabController. 2. What Is TabBar in Flutter? TabBar is a Material Design widget that displays a horizontal collection of tabs. Each tab may contain text, an icon, or both. When a user selects a tab, the tab controller updates the selected index and the corresponding TabBarView page is shown. • TabBar: displays the tab labels and selection indicator. • Tab: represents an individual tab, often using text or an icon. • TabBarView: displays the widget corresponding to the selected tab. • TabController: coordinates tab selection, animation, and page changes. • DefaultTabController: provides a controller automatically to descendant tab widgets. 3. Basic Syntax DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( title: const Text('My Tabs'), bottom: const TabBar(tabs: [ Tab(text: 'Home'), Tab(text: 'Profile'), Tab(text: 'Settings'), ]), ), body: const TabBarView(children: [ Center(child: Text('Home content')), Center(child: Text('Profile content')), Center(child: Text('Settings content')), ]), ), ) The length value must match the number of tabs and the number of TabBarView children. Keeping these lists in the same order ensures each tab opens the intended content. 4. Complete Flutter Example The following example demonstrates a Material app with three tabs using DefaultTabController. 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 TabBar Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), useMaterial3: true, ), home: DefaultTabController( length: 3, child: Scaffold( appBar: AppBar( title: const Text('Flutter TabBar'), bottom: const TabBar(tabs: [ Tab(icon: Icon(Icons.home), text: 'Home'), Tab(icon: Icon(Icons.person), text: 'Profile'), Tab(icon: Icon(Icons.settings), text: 'Settings'), ]), ), body: const TabBarView(children: [ Center(child: Text('Welcome to Home')), Center(child: Text('Your Profile')), Center(child: Text('App Settings')), ]), ), ), ); } } 5. Using TabController Directly For more control over tab selection or programmatic navigation, create a TabController in a StatefulWidget. Use SingleTickerProviderStateMixin as the vsync provider and dispose of the controller when it is no longer needed. class TabsPage extends StatefulWidget { const TabsPage({super.key}); @override State<TabsPage> createState() => _TabsPageState(); } class _TabsPageState extends State<TabsPage> with SingleTickerProviderStateMixin { late final TabController _controller; @override void initState() { super.initState(); _controller = TabController(length: 2, vsync: this); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) => Scaffold( appBar: AppBar(bottom: TabBar(controller: _controller, tabs: const [ Tab(text: 'News'), Tab(text: 'Updates'), ])), body: TabBarView(controller: _controller, children: const [ Center(child: Text('News content')), Center(child: Text('Updates content')), ]), ); } 6. Important TabBar Properties Property Purpose Typical use tabs List of Tab widgets displayed in the bar. Define labels, icons, or both. controller Coordinates selection and animation. Use a custom TabController. isScrollable Allows the row of tabs to scroll horizontally. Useful when many tabs do not fit. indicatorColor Sets the selected - tab indicator color. Match the app theme. indicatorWeight Sets the thickness of the indicator. Adjust visual emphasis. labelColor Sets the selected tab label color. Improve contrast for selected state. unselectedLabelColor Sets the color of unselected labels. Distinguish active and inactive tabs. onTap Runs a callback when a tab is tapped. Track selection or trigger lightweight actions. 7. TabBarView and Controller Matching TabBarView displays one child for each tab. The TabController length, TabBar tabs list, and TabBarView children list must all have the same number of entries. A mismatch can cause assertion errors or prevent the interface from working correctly. • Keep tabs and content in the same order. • Use DefaultTabController when a simple shared controller is sufficient. • Use a manually managed TabController when explicit control is needed. • Dispose of a manually created controller in the State object's dispose method. • Place tab widgets beneath a suitable Material widget, commonly Scaffold and AppBar. 8. Making Tabs Scrollable and Customizing Them When the tab list is wider than the available screen, set isScrollable: true. You can also customize the indicator and selected/unselected label styles to fit the app design. TabBar( isScrollable: true, indicatorColor: Colors.teal, indicatorWeight: 3, labelColor: Colors.teal, unselectedLabelColor: Colors.grey, tabs: const [ Tab(text: 'Overview'), Tab(text: 'Specifications'), Tab(text: 'Customer Reviews'), Tab(text: 'Related Items'), ], ) Use adequate spacing and contrast, and test the layout with larger text settings and smaller screens. 9. Advantages of TabBar • Organizes related content into clearly labeled sections. • Lets users switch sections without navigating to a separate route for every section. • Supports text - only, icon - only, and combined icon - and - text tabs. • Works with TabBarView to keep tab selection and displayed content synchronized. • Supports visual customization and horizontal scrolling for larger tab sets. 10. Common Errors and Troubleshooting Tab counts do not match Confirm that DefaultTabController.length or TabController.length matches both the number of Tab widgets and the number of TabBarView children. No TabController found Wrap the tab widgets in a DefaultTabController or provide the same explicit TabController to TabBar and TabBarView. Controller lifecycle issues If creating a TabController manually in a StatefulWidget, initialize it in initState and dispose of it in dispose. Tabs overflow horizontally Consider setting isScrollable: true or shortening labels so the interface fits the target screen. Content does not match the selected tab Check the order of the TabBar tabs and TabBarView children; each child corresponds to the tab at the same index. 11. Best Practices • Use concise, descriptive tab labels. • Keep the number of tabs manageable and group related content. • Maintain matching tab and content order. • Use clear selected - state indicators and accessible color contrast. • Test on different screen sizes and with larger text settings. • Use DefaultTabController for simple cases and a manual controller when explicit control is required. • Follow Flutter's current Material widget documentation when choosing styling properties. 1 2 . Conclusion Flutter's TabBar widget provides a convenient way to organize related screens or content areas within one interface. By combining TabBar, TabBarView, and either DefaultTabController or a manually managed TabController, developers can create responsive and maintainable tabbed layouts. Matching the tab and content counts, managing controller lifecycles, and testing accessibility are important for a reliable implementation.