Reputation: 3740
I use TabBarView
and i noticed something really strange. I have put some print messages in all of the tabs' widgets in order to have feedback while swiping tabs. And i got these results:
So really strange things. It actually rebuilds(calls setState
) a tab even tho it's not selected. Take in mind that only Tab 0 and Tab 2 have actual content inside, the other ones have empty containers.
Here is my code:
class Home extends StatefulWidget {
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
final AuthService _authService = AuthService();
final colors = [
Colors.red,
Colors.lightGreen,
Colors.blue,
Colors.amber,
Colors.deepPurpleAccent
];
Color scaffoldColor = Colors.red;
TabController _controller;
@override
void initState() {
super.initState();
_controller = TabController(vsync: this, length: 5);
_controller.addListener(() {
if (_controller.indexIsChanging) {
print("index is changing");
} else {
setState(() {
scaffoldColor = colors[_controller.index];
});
}
});
_controller.index = 1;
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 5,
child: Scaffold(
backgroundColor: scaffoldColor,
appBar: AppBar(
backgroundColor: scaffoldColor,
leading: IconButton(
icon: Icon(Icons.menu),
color: Colors.white,
onPressed: () {},
),
elevation: 0,
title: Text(
'HOME Screen',
style: TextStyle(fontSize: 26, fontWeight: FontWeight.bold),
),
actions: [
FlatButton.icon(
onPressed: () async {
await _authService.signOut();
},
icon: Icon(Icons.exit_to_app),
label: Text('Log out'),
),
],
bottom: TabBar(
isScrollable: true,
controller: _controller,
indicatorWeight: 6,
indicatorColor: Colors.transparent,
tabs: <Widget>[
Tab(
child: Container(
child: Text(
'Chats',
style: TextStyle(
color:
_controller.index == 0 ? Colors.white : Colors.grey,
fontSize: 18),
),
),
),
Tab(
child: Container(
child: Text(
'Online',
style: TextStyle(
color:
_controller.index == 1 ? Colors.white : Colors.grey,
fontSize: 18),
),
),
),
Tab(
child: Container(
child: Text(
'Discover',
style: TextStyle(
color:
_controller.index == 2 ? Colors.white : Colors.grey,
fontSize: 18),
),
),
),
Tab(
child: Container(
child: Text(
'NULL',
style: TextStyle(
color:
_controller.index == 3 ? Colors.white : Colors.grey,
fontSize: 18),
),
),
),
Tab(
child: Container(
child: Text(
'NULL2',
style: TextStyle(
color:
_controller.index == 4 ? Colors.white : Colors.grey,
fontSize: 18),
),
),
),
],
),
),
body: TabBarView(
controller: _controller,
children: <Widget>[
Column(
children: <Widget>[
//CategorySelector(),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: Column(
children: <Widget>[
FavoriteContacts4(),
RecentChats(),
],
),
),
),
],
),
Tab1(),
Column(
children: <Widget>[
//CategorySelector(),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(30),
topRight: Radius.circular(30),
),
),
child: SuggestedUsers(),
),
),
],
),
Tab3(),
Tab4(),
],
),
),
);
}
Widget tmpWidget(int i) {
print("Tab " + i.toString() + " is called");
return Text("Number" + i.toString());
}
}
It is really annoying cause every time i switch(or almost) to another tab it calls an api in tab2. I think it has to do with the controller!
Update:
Even tho i have put AutomaticKeepAliveClientMixin
into every of my Statefull
Tab widgets, there are going to be rebuild. For example when im swiping from Tab3 to go to Tab4 all the Tabs are going to be rebuild. Tab1,2,3,4 all of them.
Here is the code i have put for Tab 1,3 and 4. Tab 0 and 2 have other widgets but act the same.
class Tab1 extends StatefulWidget {
@override
_Tab1State createState() => _Tab1State();
}
class _Tab1State extends State<Tab1> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
print("Tab 1 Has been built");
return Text("TAB 1");
}
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
Upvotes: 8
Views: 6330
Reputation: 21
Placing the DefaultTabController
in a StatefulBuilder
and having all the TabBarView
child widget implementing AutomaticKeepAliveClientMixin
works for me.
Upvotes: 2
Reputation: 2862
That's because you are calling setState({})
which means your build
method will be called and since all your tabs
are in the same build
method all of them will be re created. In order to get ride of this issue you have to:
StatefulWidget
for each child of the TabBarView
StatefulWidget
extends AutomaticKeepAliveClientMixin
(class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin
wantKeepAlive
: @override bool get wantKeepAlive => true;
super.build(context)
as first line in your build
methodThis will prevent re-building your widgets each time
Upvotes: 14