How to detect TabBar change in Flutter?
Solution 1
You need to add a listener to your tab controller - maybe in initState
.
controller.addListener((){
print('my index is'+ controller.index.toString());
});
Solution 2
Swipe functionality is not provided by onTap()
function, for that TabController
is used
class TabBarDemo extends StatefulWidget {
@override
_TabBarDemoState createState() => _TabBarDemoState();
}
class _TabBarDemoState extends State<TabBarDemo>
with SingleTickerProviderStateMixin {
TabController _controller;
int _selectedIndex = 0;
List<Widget> list = [
Tab(icon: Icon(Icons.card_travel)),
Tab(icon: Icon(Icons.add_shopping_cart)),
];
@override
void initState() {
// TODO: implement initState
super.initState();
// Create TabController for getting the index of current tab
_controller = TabController(length: list.length, vsync: this);
_controller.addListener(() {
setState(() {
_selectedIndex = _controller.index;
});
print("Selected Index: " + _controller.index.toString());
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (index) {
// Should not used it as it only called when tab options are clicked,
// not when user swapped
},
controller: _controller,
tabs: list,
),
title: Text('Tabs Demo'),
),
body: TabBarView(
controller: _controller,
children: [
Center(
child: Text(
_selectedIndex.toString(),
style: TextStyle(fontSize: 40),
)),
Center(
child: Text(
_selectedIndex.toString(),
style: TextStyle(fontSize: 40),
)),
],
),
),
);
}
}
Github Repo:
https://github.com/jitsm555/Flutter-Problems/tree/master/tab_bar_tricks
Output:
Solution 3
Here is a full example. Use a TabController and add a callback using addListener.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: MyTabbedPage(),
);
}
}
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
@override
_MyTabbedPageState createState() => _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage> with SingleTickerProviderStateMixin {
var _context;
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: myTabs.length);
_tabController.addListener(_handleTabSelection);
}
void _handleTabSelection() {
if (_tabController.indexIsChanging) {
switch (_tabController.index) {
case 0:
Scaffold.of(_context).showSnackBar(SnackBar(
content: Text('Page 1 tapped.'),
duration: Duration(milliseconds: 500),
));
break;
case 1:
Scaffold.of(_context).showSnackBar(SnackBar(
content: Text('Page 2 tapped.'),
duration: Duration(milliseconds: 500),
));
break;
}
}
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: Builder(
builder: (context) {
_context = context;
return TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
final String label = tab.text.toLowerCase();
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
);
},
),
);
}
}
Solution 4
Warp your tab in BottomNavigationBar . it will give you option onTap() where you can check which tab will clicked.
using this code you will also redirect to particular page when you tap on Tab
import 'package:flutter/material.dart';
class BottomBarList extends StatefulWidget {
@override
_BottomBarListState createState() => _BottomBarListState();
}
class _BottomBarListState extends State<BottomBarList> {
int bottomSelectedIndex = 0;
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[
AllMovieList(),
MovieDescription(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(),
bottomNavigationBar: bottomBar(),
body:_widgetOptions.elementAt(_selectedIndex),
);
}
bottomBar() {
return BottomNavigationBar(
type: BottomNavigationBarType.shifting,
unselectedItemColor: Colors.grey,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.tv),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.black,
backgroundColor: Colors.orange,
onTap: _onItemTapped,
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
Solution 5
You can create wrapper widget
class _DefaultTabControllerListener extends StatefulWidget {
const _DefaultTabControllerListener(
{Key? key, this.onTabSelected, required this.child})
: super(key: key);
final void Function(int index)? onTabSelected;
final Widget child;
@override
_DefaultTabControllerListenerState createState() =>
_DefaultTabControllerListenerState();
}
class _DefaultTabControllerListenerState
extends State<_DefaultTabControllerListener> {
late final void Function()? _listener;
TabController? _tabController;
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
final tabController = DefaultTabController.of(context)!;
_listener = () {
final onTabSelected = widget.onTabSelected;
if (onTabSelected != null) {
onTabSelected(tabController.index);
}
};
tabController.addListener(_listener!);
});
}
@override
void didChangeDependencies() {
_tabController = DefaultTabController.of(context);
super.didChangeDependencies();
}
@override
void dispose() {
if (_listener != null && _tabController != null) {
_tabController!.removeListener(_listener!);
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
And wrap TabBar with this widget
DefaultTabController(
child: _DefaultTabControllerListener(
onTabSelected: (index) {
// Handler
},
child: TabBar(.....
Ashtav
I am fullstack developer, I write code and also make music, I love to play guitar, drum, violin and piano.
Updated on June 30, 2021Comments
-
Ashtav almost 3 years
I need to detect TabBar when I swipe then print somethings on console, how I can do that? This is my code.
bottomNavigationBar: new Material( color: Colors.blueAccent, child: new TabBar( onTap: (int index){ setState(() { _onTap(index); });}, indicatorColor: Colors.white, controller: controller, tabs: <Widget>[ new Tab(icon: new Icon(Icons.shopping_basket)), new Tab(icon: new Icon(Icons.store)), new Tab(icon: new Icon(Icons.local_offer)), new Tab(icon: new Icon(Icons.assignment)), new Tab(icon: new Icon(Icons.settings)), ], ) ),
-
nonybrighto about 5 yearsWhere did you add it? Can you update your code so I can see how you used it?
-
Abdurahman Popal about 4 yearsWe can't detect tab scroll by NeverScrollableScrollPhysics, we disable swiping by it.
-
Mohsen Emami about 4 yearsDisabling swiping
TabBarView
enables you to detect changing between tabs only by tapping on Tabs, that is because there is no listener for swiping tabs -
Abdurahman Popal about 4 yearsWe can listen to tab scroll notification by using NotificationListener Widget stackoverflow.com/a/60353215/10020712
-
Vinoth Vino over 3 years
indexIsChanging
is true only if we tap the tab bar item otherwise it's false (when swipe the tab bar view) -
Expressingx over 3 yearsWorks nice, but
addListener()
gets called twice on tab change. Any way to remove that? -
Admin about 3 yearsThe listener never gets called for me even after adding the controller to the TabBar and the TabBarView
-
Raul Mabe about 3 years@VinothVino Yeah, but
if (!_tabController.indexIsChanging) print(_tabController.index);
actually prints once every time you switch tabs (by swiping AND by tapping). This works as the listener gets called twice when switching tabs by tapping -
Hide in bubble almost 3 years@Expressingx Did you find solution?
-
Drunken Daddy about 2 years
if (tabController.indexIsChanging == true) { return; }
Need this, or else the listener will be called twice while touching a tab to change the tab. It will work without this when tab is changed by swiping -
Maqcel almost 2 years@Expressingx & @tdtkien after tab change you dispose of the state. Afterward, call
initState
once more so that each time you change the tab new listener is being created. You can add a listener to some variable and add it along with removing the listener to the dispose method. That way you ensure just one listener is up. (Directly the one in the tab you are currently at)