Toni Joe
Toni Joe

Reputation: 8407

How to get current tab index in Flutter

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?

Upvotes: 58

Views: 98111

Answers (12)

Rifat Khan
Rifat Khan

Reputation: 11

I've followed @CoastalB and its worked but the problem is it's take some delay to get index. so I've used two separate listener (animate and tap) for get index.

The current code is implemented with GetX, but you can easily apply this approach with stateful widget:

class GenerateController extends GetxController with GetSingleTickerProviderStateMixin {
  late TabController tabController;
  RxInt activeTabIndex = 0.obs;

  @override
  void onInit() {
    super.onInit();
    tabController = TabController(vsync: this, length: 2);
    tabController.addListener(_onTabIndexChanged);
    tabController.animation?.addListener(_onTabAnimation);
  }

  void _onTabIndexChanged() {
    // Fires at the end of tab change animation
    activeTabIndex.value = tabController.animation!.value.round();
    print("Tab index finalized: ${tabController.index}");
  }

  void _onTabAnimation() {
    // Fires during the animation
    activeTabIndex.value = tabController.animation!.value.round();
  }
  @override
  void onClose() {
    tabController.dispose();
    super.onClose();
  }
}

Upvotes: 0

RuslanBek
RuslanBek

Reputation: 2009

New working solution

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

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,
    );
    _tabController.addListener(() {
      setState(() {
        _activeIndex = _tabController.index;
        print('_activeIndex: $_activeIndex');
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    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,
      ),
    );
  }
}

Upvotes: 9

Moiz Irshad
Moiz Irshad

Reputation: 720

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;
    });
   },

Upvotes: 1

Zhar
Zhar

Reputation: 3530

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
    });
  }
}

Upvotes: 1

R&#233;mi Rousselet
R&#233;mi Rousselet

Reputation: 277037

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(),
      ),
    );
  }
}

Upvotes: 64

Doan Bui
Doan Bui

Reputation: 4418

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}');
          },
        ),
      );
    }),
  ),

Upvotes: 16

Huzaifa Ahmed
Huzaifa Ahmed

Reputation: 324

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
  }
  });
});

Upvotes: 3

Vipul Garg
Vipul Garg

Reputation: 32

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;
      }
    }

Upvotes: -2

CoastalB
CoastalB

Reputation: 745

Just apply a listener on the TabController.

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

void _setActiveTabIndex() {
  _activeTabIndex = _tabController.index;
}

Upvotes: 23

Usman
Usman

Reputation: 2577

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(),
    ]);

Upvotes: 36

AlexPad
AlexPad

Reputation: 10879

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

Upvotes: 5

google ogiwara
google ogiwara

Reputation: 669

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... 
    })
  ]
)

Upvotes: 66

Related Questions