Question regarding navigating between tabs using indexed stack to display relevant page. I'm doing this in order to keep scroll/state of pages. This works fine. I can change the current page displayed by clicking tab - and can also navigate inside each page (each page is wrapped with it's own Navigator). This is the code for rendering the pages.
Widget build(BuildContext context) {
return IndexedStack(
index: widget.selectedIndex,
children: List.generate(widget._size, (index) {
return _buildNavigator(index);
}));
}
Mu problem is that IndexedStack builds all pages at once. In some of my pages I want to load data from an API, I want to do it when the widget first time built and only if the page is currently visible. Is there a way to do so? in my current implementation all widgets build at once and so all my API calls are called even for the pages that are not currently painted.
Not sure if i'm missing something here, or there is a better way to implement bottom navigation bar. BTW i'm also using Provider for state management.
@tsahnar yea i have also faced same issue related with api call indexed widget render all widgets provided it to its children at once so when individual pages are independently fetching data from api then here comes the problem
try this :
PageStorageKey(<key>)
for each widgets)var widgetList = <Widget>[
Page01(key:PageStorageKey(<key>)),
Page02(key:PageStorageKey(<key>))
];
PageStorageBucket()
which stores your widgets state and provides it in future whenever we need it in a lifetime of app even the widget gets disposed from the treefinal _bucket = PageStorageBucket();
then
var currentIndex = 0;
then in your main base page where the bottom navbar exists in your body instead of IndexedStack
use body:PageStorage(bucket: _bucket,child:widgetsList[currentIndex])
and create bottomnavbar in that main base page and then onNavbar icon tab manage index page impherial state by setState((){})
the current state to the currentIndex
it should fix your problem tho its too late after a year
I encountered the same problem. My solution was to save a list of the loaded tabs and then use that to build the list of IndexedStack
children inside the Widget build(BuildContext context)
method. Then in the onTap
method of the BottomNavigationBar
, I called setState()
to update the list of loaded tabs as well as the current index variable. See below:
class Index extends StatefulWidget {
const Index({Key? key}) : super(key: key);
@override
_IndexState createState() => _IndexState();
}
class _IndexState extends State<Index> {
int _currentIndex = 0;
List loadedPages = [0,];
@override
Widget build(BuildContext context) {
var screens = [
const FirstTab(),
loadedPages.contains(1) ? const SecondTab() : Container(),
loadedPages.contains(2) ? const ThirdTab() : Container(),
loadedPages.contains(3) ? const FourthTab() : Container(),
];
return Scaffold(
appBar: AppBar(
// AppBar
),
body: IndexedStack(
index: _currentIndex,
children: screens,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
var pages = loadedPages;
if (!pages.contains(index)) {
pages.add(index);
}
setState(() {
_currentIndex = index;
loadedPages = pages;
});
},
items: const [
// items
],
),
);
}
}
Now, the API calls on the second, third, and fourth tabs don't call until navigated to.
do you found a solution?
I found the same problem as you and I tried this workaround (i didn't found any issues with it yet)
The idea is to make a new widget to control the visibility state of the widgets that made the api call and build it when it became visible.
In your IndexedStack
wrap your _buildNavigator
with a widget like this:
class BaseTabPage extends StatefulWidget {
final bool isVisible;
final Widget child;
BaseTabPage({Key key, this.child, this.isVisible});
@override
State<StatefulWidget> createState() => _BaseTabPageState();
}
/*
This state is to prevent tab pages creation before show them. It'll only add the
child widget to the widget tree when isVisible is true at least one time
i.e. if the child widget makes an api call, it'll only do when isVisible is true
for the first time
*/
class _BaseTabPageState extends State<BaseTabPage> {
bool alreadyShowed = false;
@override
Widget build(BuildContext context) {
alreadyShowed = widget.isVisible ? true : alreadyShowed;
return alreadyShowed ? widget.child : Container();
}
}
For example in my code i have something like this to build the navigators for each tab, where _selectedIndex
is the selected position of the BottomNavigationBar
and tabPosition
is the position of that page in the BottomNavigationBar
Widget _buildTabPage(int tabPosition) {
final visibility = _selectedIndex == tabPosition;
return BaseTabPage(
isVisible: visibility,
child: _buildNavigator(tabPosition),
);
}
With this i have the logic of the api call entirely in the children widgets and the bottom navigation knows nothing about them. Let me know if you see something wrong with it since i'm kind of new with flutter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With