Reputation: 8407
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
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
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
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
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
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
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
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
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
Reputation: 745
Just apply a listener on the TabController.
// within your initState() method
_tabController.addListener(_setActiveTabIndex);
void _setActiveTabIndex() {
_activeTabIndex = _tabController.index;
}
Upvotes: 23
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
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
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