Panagiss
Panagiss

Reputation: 3740

Flutter: TabBarView Moving to a tab rebuilds also the other ones

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

Answers (2)

AD Codeur
AD Codeur

Reputation: 21

Placing the DefaultTabController in a StatefulBuilder and having all the TabBarView child widget implementing AutomaticKeepAliveClientMixin works for me.

Upvotes: 2

ikerfah
ikerfah

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:

  1. Create StatefulWidget for each child of the TabBarView
  2. Make your StatefulWidget extends AutomaticKeepAliveClientMixin (class ExampleState extends State<Example> with AutomaticKeepAliveClientMixin
  3. You have to override wantKeepAlive : @override bool get wantKeepAlive => true;
  4. Call super.build(context) as first line in your build method

This will prevent re-building your widgets each time

Upvotes: 14

Related Questions