CupertinoTabView resets on Hot Reload

Issue

I am developing an app, using the CupertinoApp model. I am using the CupertinoTabView model for the app. Now during development, the tab index resets on hot reload. I tried setting the state on the tab controller but it still resets.

code:

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    CupertinoTabController _controller = CupertinoTabController();

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

    List<Widget> tabs = [
      HomeScreen(_controller),
      PlaySearchScreen(),
      HomeScreen(_controller),
      ProfileScreen(),
    ];
    return CupertinoTabScaffold(
        controller: _controller,
        tabBar: CupertinoTabBar(
          items: [
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.home),
              label: 'Home',
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.play),
              label: 'Play Tennis',
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.calendar),
              label: ' My Schedule',
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.person),
              label: 'Profile',
            ),
          ],
        ),
        tabBuilder: (context, index) {
          return CupertinoTabView(
            builder: (ctx) {
              return GestureDetector(
                child: tabs[index],
                onTap: () => setState(
                  () => _controller.index = index,
                ),
              );
            },
          );
        });
  }
}

class HomeScreen extends StatefulWidget {
  HomeScreen(
    this.controller, {
    Key key,
  }) : super(key: key);

  final CupertinoTabController controller;

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

Is there a way to not have it reset?

Solution

EDIT

As @p2kr pointed out, you got a mix up in your build function (since you also have the dispose callback inside which you want to define outside the build function) and you make use of the CupertinoTabScaffold where the documentation states:

You must listen to the onTap callbacks and call setState with a new
currentIndex for the new selection to reflect. This can also be done
automatically by wrapping this with a CupertinoTabScaffold.

taken from here. So my original answer applies if you don’t make use of the CupertinoTabScaffold. In your case, your class should look like this:

class _HomePageState extends State<HomePage> {
    // Placing the _controller as a property of your class
    // instead of a local variable inside your build method
    // which would be re-instantiated on every build call -
    // as in the case of hot reload
    CupertinoTabController _controller = CupertinoTabController();

    // Also setting the dispose function correctly outside
    // of the build function
    @override
    void dispose() {
        _controller.dispose();
        super.dispose();
    }

    @override
    Widget build(BuildContext context) {
        List<Widget> tabs = [
            HomeScreen(_controller),
            PlaySearchScreen(),
            HomeScreen(_controller),
            ProfileScreen(),
        ];

        ...

ORIGINAL

The CupertinoTabBar has a currentIndex property to maintain a selected tab. Therefore you need to add an additional property to your _HomePageState:

class _HomePageState extends State<HomePage> {
    // Variable to maintain current tab index.
    // Setting it to 0 (first tab) as default here
    int _index = 0;

    ...
}

Then in the actual CupertinoTabBar we add set the currentIndex property and make use of the onTap callback to update our property accordingly:

CupertinoTabBar(
    // Setting the index
    currentIndex: _index,
    // Callback to update our index once a tab is clicked
    onTap: (index) => setState(() => _index = index),
    items: [
        BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.home),
            label: 'Home',
        ),
        BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.play),
            label: 'Play Tennis',
        ),
        BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.calendar),
            label: ' My Schedule',
        ),
        BottomNavigationBarItem(
            icon: Icon(CupertinoIcons.person),
            label: 'Profile',
        ),
    ],
),

Answered By – kounex

Answer Checked By – Clifford M. (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.