56

In flutter implementing a tab layout is easy and straightforward. This is a simple example from the official documentation:

import 'package:flutter/material.dart';

void main() {
  runApp(new TabBarDemo());
}

class TabBarDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new DefaultTabController(
        length: 3,
        child: new Scaffold(
          appBar: new AppBar(
            bottom: new TabBar(
              tabs: [
                new Tab(icon: new Icon(Icons.directions_car)),
                new Tab(icon: new Icon(Icons.directions_transit)),
                new Tab(icon: new Icon(Icons.directions_bike)),
              ],
            ),
            title: new Text('Tabs Demo'),
          ),
          body: new TabBarView(
            children: [
              new Icon(Icons.directions_car),
              new Icon(Icons.directions_transit),
              new Icon(Icons.directions_bike),
            ],
          ),
        ),
      ),
    );
  }
}

But here is the thing, I want to get the active tab index so I can apply some logic on certain tabs. I search the documentation but I wasn't able to figure it out. Can you guys help and thanks?

Toni Joe
  • 7,715
  • 12
  • 50
  • 69
  • The answer to your problem is here [enter link description here](https://stackoverflow.com/a/65161942/14208424) – ALNAJJAR Dec 05 '20 at 21:10

11 Answers11

62

The whole point of DefaultTabController is for it to manage tabs by itself.

If you want some custom tab management, use TabController instead. With TabController you have access to much more informations, including the current index.

class MyTabbedPage extends StatefulWidget {
  const MyTabbedPage({Key key}) : super(key: key);
  @override
  _MyTabbedPageState createState() => new _MyTabbedPageState();
}

class _MyTabbedPageState extends State<MyTabbedPage>
    with SingleTickerProviderStateMixin {
  final List<Tab> myTabs = <Tab>[
    new Tab(text: 'LEFT'),
    new Tab(text: 'RIGHT'),
  ];

  TabController _tabController;

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

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

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        bottom: new TabBar(
          controller: _tabController,
          tabs: myTabs,
        ),
      ),
      body: new TabBarView(
        controller: _tabController,
        children: myTabs.map((Tab tab) {
          return new Center(child: new Text(tab.text));
        }).toList(),
      ),
    );
  }
}
Md. Yeasin Sheikh
  • 54,221
  • 7
  • 29
  • 56
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
59

In this case, using StatefulWidget and State isn't a good idea.

You can get current index by DefaultTabController.of(context).index;.

Follow the code:

...
appBar: AppBar(
  bottom: TabBar(
    tabs: [
      Tab(~), Tab(~)
    ]
  ),
  actions: [
    // At here you have to get `context` from Builder.
    // If you are not sure about this, check InheritedWidget document.
    Builder(builder: (context){
      final index = DefaultTabController.of(context).index;   
      // use index at here... 
    })
  ]
)

google ogiwara
  • 599
  • 4
  • 2
34

You can access the current index when the tab is selected by onTap event of TabBar.

TabBar(
    onTap: (index) {
      //your currently selected index
    },

    tabs: [
      Tab1(),
      Tab2(),
    ]);
Usman
  • 2,547
  • 31
  • 35
  • 25
    This does not work as desired if the user changes tabs by swiping. Only if they actually tap on the tab bar. – dakamojo Aug 26 '19 at 20:59
19

Just apply a listener on the TabController.

// within your initState() method
_tabController.addListener(_setActiveTabIndex);

void _setActiveTabIndex() {
  _activeTabIndex = _tabController.index;
}
CoastalB
  • 695
  • 4
  • 15
15

Use DefaultTabController you can get current index easily whether the user changes tabs by swiping or tap on the tab bar.

Important: You must wrap your Scaffold inside of a Builder and you can then retrieve the tab index with DefaultTabController.of(context).index inside Scaffold.

Example:

DefaultTabController(
    length: 3,
    child: Builder(builder: (BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Home'),
          bottom: TabBar(
              isScrollable: true,
              tabs: [Text('0'), Text('1'), Text('2')]),
        ),
        body: _buildBody(),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            print(
                'Current Index: ${DefaultTabController.of(context).index}');
          },
        ),
      );
    }),
  ),
Doan Bui
  • 3,572
  • 25
  • 36
  • This works great for me.....!!.Thanks Bro@DoanBui – Dante Jun 18 '23 at 17:24
  • Hmm, this doesn't rebuild when the index changes it looks like? I can get the index when the widget is initially built, but it never seems to change / update in the `Builder` context. – Hanzy Aug 20 '23 at 02:23
7

New working solution

I'd suggest you to use TabController for more customisations. To get active tab index you should use _tabController.addListener and _tabController.indexIsChanging.

Use this full code snippet:


class CustomTabs extends StatefulWidget {
  final Function onItemPressed;

  CustomTabs({
    Key key,
    this.onItemPressed,
  }) : super(key: key);

  @override
  _CustomTabsState createState() => _CustomTabsState();
}

class _CustomTabsState extends State<CustomTabs>
    with SingleTickerProviderStateMixin {

  final List<Tab> myTabs = <Tab>[
    Tab(text: 'LEFT'),
    Tab(text: 'RIGHT'),
  ];

  TabController _tabController;
  int _activeIndex = 0;
  
  @override
  void initState() {
    super.initState();
    _tabController = TabController(
      vsync: this,
      length: myTabs.length,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    _tabController.addListener(() {
      if (_tabController.indexIsChanging) {
        setState(() {
          _activeIndex = _tabController.index;
        });
      }
    });
    return Container(
      color: Colors.white,
      child: TabBar(
        controller: _tabController,
        isScrollable: true,
        indicatorPadding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
        indicator: BoxDecoration(
            borderRadius: BorderRadius.circular(10.0), color: Colors.green),
        tabs: myTabs
            .map<Widget>((myTab) => Tab(
                  child: Container(
                    width: width / 3 -
                        10, // - 10 is used to make compensate horizontal padding
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(10.0),
                      color:
                          _activeIndex == myTabs.indexOf(myTab)
                              ? Colors.transparent
                              : Color(0xffA4BDD4),
                    ),
                    margin:
                        EdgeInsets.symmetric(horizontal: 5.0, vertical: 5.0),
                    child: Align(
                      alignment: Alignment.center,
                      child: Text(
                        myTab.text,
                        style: TextStyle(color: Colors.white),
                      ),
                    ),
                  ),
                ))
            .toList(),
        onTap: widget.onItemPressed,
      ),
    );
  }
}
RuslanBek
  • 1,592
  • 1
  • 14
  • 30
5

Thanks to the example of Rémi Rousselet, you can do it, the code like this:

_tabController.index

This will return the current index of the position of your TabBarView

AlexPad
  • 10,364
  • 3
  • 38
  • 48
3

You can add a listener to listen to changes in tabs like below

tabController = TabController(vsync: this, length: 4)
   ..addListener(() {
setState(() {
  switch(tabController.index) {
    case 0:
      // some code here
    case 1:
     // some code here
  }
  });
});
Huzaifa Ahmed
  • 304
  • 1
  • 3
  • 15
  • 1
    Don't forget to restart the app after this, instead of just using the hot reload. Since the hot reload won't necessarily run the initState for your screen again. – George Nov 13 '20 at 14:11
0

Well, nothing here was working in my case.

I tried several responses so as a result i used a provider to keep and retrieve the current index selected.

First the model.

class HomeModel extends ChangeNotifier {
  int _selectedTabIndex = 0;

  int get currentTabIndex => _selectedTabIndex;

  setCurrentTabIndex(final int index){
    _selectedTabIndex = index;
    // notify listeners if you want here
    notifyListeners();
  }
  
  ...
  
 }

Then i used _tabController.addListener() to update my model.

    class HomePageState extends State<HomeScreen> {
    
      late TabController _tabController;
    
      @override
      void initState() {
        super.initState();
        _tabController = TabController(vsync: this, length: _tabs.length);
        _tabController.addListener(() {
          context.read<HomeModel>().setCurrentTabIndex(_tabController.index);
        });
      }
    
       ...
       
       @override
       Widget build(BuildContext context) {
          return DefaultTabController(
          length: _tabs.length,
          child :
            Scaffold(
              backgroundColor: Colors.white70,
              appBar: AppBar(
                /*iconTheme: IconThemeData(
                    color: Colors.black
                ),*/
                bottom: TabBar(
                  controller: _tabController,
                  tabs: _tabs,
                ),
                title: Text(_getAppBarTitle(),style: const TextStyle(/*color: Colors.red,*/fontSize: 22.0),)
                ...
                
                )
            )
        );
    }
       ...
    }

Finally last but not least retrieve value when you need.

class _AppState extends State<App> {

  @override
  Widget build(BuildContext context) {
    return Consumer<HomeModel>(
        builder: (context, homeModel, child) {
          return Text(homeModel.currentTabIndex); // herre we get the real current index
    });
  }
}
Zhar
  • 3,330
  • 2
  • 24
  • 25
0

Set the variable in top.

    class _MainTabWidgetState extends State<MainTabWidget> {
    
      @override   void initState() {
        // TODO: implement initState
        super.initState();   
       }
    
      int selected_index = 0;
 }

Now set index in Tabbar onTap

    onTap: (index) {
          setState(() {
      selected_index = index;
    });
   },
Moiz Irshad
  • 700
  • 1
  • 6
  • 15
-2

This Code will give you index of Active tab , also save the tab index for future use, and when you back to the tab page the the previous active page will be displayed.

import 'package:flutter/material.dart';

    void main() {
      runApp(new TabBarDemo());
    }

    class TabBarDemo extends StatelessWidget {


      TabScope _tabScope = TabScope.getInstance();

      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          home: new DefaultTabController(
            length: 3,
            index: _tabScope.tabIndex, // 
            child: new Scaffold(
              appBar: new AppBar(
                bottom: new TabBar(
                  onTap: (index) => _tabScope.setTabIndex(index),  //current tab index
                  tabs: [
                    new Tab(icon: new Icon(Icons.directions_car)),
                    new Tab(icon: new Icon(Icons.directions_transit)),
                    new Tab(icon: new Icon(Icons.directions_bike)),
                  ],
                ),
                title: new Text('Tabs Demo'),
              ),
              body: new TabBarView(
                children: [
                  new Icon(Icons.directions_car),
                  new Icon(Icons.directions_transit),
                  new Icon(Icons.directions_bike),
                ],
              ),
            ),
          ),
        );
      }
    }

    class TabScope{ // singleton class
      static TabScope _tabScope;
      int tabIndex = 0;

      static TabScope getInstance(){
        if(_tabScope == null) _tabScope = TabScope();

        return _tabScope;
      }
      void setTabIndex(int index){
        tabIndex = index;
      }
    }
Vipul Garg
  • 32
  • 7