2

The default implementation of Material's Scaffold & BottomNavigationBar is to destroy and re-create each tab in each tab change.

In the case of CupertinoTabScaffold, CupertinoTabView and CupertinoPageScaffold the behavior is different: the state of each tab is maintained, so switching between tabs won't re-trigger the initState() method of the tab in each change.

My question is: how to modify this behaviour to act the same as Material? I want each tab to be destroyed and re-created, thus calling the initState() method in each tab change, same as with the Material Widgets.

Thanks.

svprdga
  • 2,171
  • 1
  • 28
  • 51

1 Answers1

4

I would say this is a hack, but one way to actually have elements rebuild is to just use new elements.

But obviously, you need the same widget class to be rendered, so how can it be new ?

Keys.

Flutter check the type of your widget as well as it's key (if provided) to validate whether an widget is same or not so as to rebuild.

You can use keys this way. Consider this simple example.

@override
Widget build(BuildContext context) {
  return CupertinoTabScaffold(
    tabBar: CupertinoTabBar(
      items: [
        BottomNavigationBarItem(icon: Icon(Icons.add_a_photo)),
        BottomNavigationBarItem(icon: Icon(Icons.add)),
        BottomNavigationBarItem(icon: Icon(Icons.calendar)),
      ],
    ),
    tabBuilder: (_, i) => Sample(
      key: ValueKey(Random().nextInt(255)),
    ),
  );
}

I have a Sample class which I am using to build each screen in the CupertinoTabScaffold. But instead of using it like Sample(), I have passed an extra key parameter which is just a random number.

Now, my Sample class looks like this,

class Sample extends StatefulWidget {
  Sample({Key key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return SampleState();
  }
}

Now, even if you change something in your SampleState class and then visit another Tab and then comeback to the previous Tab, since you are using a new Key, Flutter will think it is a new Widget and will rebuild it.

Problem solved.

However, I must ask, why though ?

Nisanth Reddy
  • 5,967
  • 1
  • 11
  • 29
  • I want this behaviour because each tab must load the current data when switching, so I can go to tab A, change something, then go to tab B, the change made in tab A can impact tab B. – svprdga May 20 '21 at 10:22
  • I see. Glad to have helped :) – Nisanth Reddy May 20 '21 at 11:46
  • Wow, I found a strange behavior when using the unique key. If I try to open two dialogs in a row a FlutterError is thrown when trying to open the second dialog when getting the context: framework.dart#909("his widget has been unmounted, so the State no longer has a context (and should be considered defunct). Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active."). This only in a CupertinoApp context, not in Material App. If I delete the uniquer key the error vanishes. I cannot apply your solution without fixing this :( – svprdga May 21 '21 at 10:17
  • I am thinking of sending a stream event to the tab so it refreshes by itself, I was trying to avoid this solution but I think I will do it. – svprdga May 21 '21 at 10:19
  • That's weird. How are you opening a dialog ? Can you post your full code using pastebin ? – Nisanth Reddy May 21 '21 at 10:24
  • Yes, it's something like that: https://pastebin.com/6WinC9cv – svprdga May 21 '21 at 10:47
  • No no. I mean could you give, reproducible code ? Maybe just the widget code where this function is being used. – Nisanth Reddy May 21 '21 at 10:50