Flutter底部导航栏和索引堆栈

18

关于使用索引堆栈在选项卡之间导航以显示相关页面的问题。我这样做是为了保持页面的滚动/状态。这很好用。我可以通过点击选项卡来更改当前显示的页面,并且还可以在每个页面内导航(每个页面都包装在自己的Navigator中)。下面是呈现页面的代码。

Widget build(BuildContext context) {
return IndexedStack(
    index: widget.selectedIndex,
    children: List.generate(widget._size, (index) {
  return _buildNavigator(index);
}));

我的问题是IndexedStack一次性构建所有页面。在我的某些页面中,我想从API加载数据,我希望在小部件首次构建时执行,并且仅当页面当前可见时执行。 有没有办法这样做? 在我的当前实现中,所有小部件都同时构建,因此即使对于当前未绘制的页面,也会调用所有API调用。

不确定我是否遗漏了什么,或者有更好的实现底部导航栏的方法。顺便说一句,我还使用Provider进行状态管理。

5个回答

6
我遇到了同样的问题。我的解决方案是保存已加载选项卡的列表,然后在Widget build(BuildContext context)方法中使用它来构建IndexedStack子项列表。然后在BottomNavigationBaronTap方法中,我调用setState()来更新已加载选项卡的列表以及当前索引变量。请参见下面的示例代码:
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
        ],
      ),
    );
  }
}

现在,第二、第三和第四个选项卡上的API调用只有在导航到相应的选项卡时才会被调用。

3
请使用 PageView 而不是 IndexedStack。
 PageView(
            physics: const NeverScrollableScrollPhysics(),
            controller: _pageController,
            children: const [
              Page1(),
              Page2(),
              Page3(),
            ],
          ),

您可以使用pageController切换页面。
_pageController.jumpToPage(0);

3

@tsahnar 是的,我也遇到了与API调用有关的同样问题,索引小部件将所有提供给其子小部件的小部件一次性渲染,因此当单独页面从API独立获取数据时,问题就出现了。

尝试这个:

  • 创建导航栏的小部件列表(每个小部件都带有键构造函数,在其中为每个小部件定义PageStorageKey(<key>)
var widgetList = <Widget>[
Page01(key:PageStorageKey(<key>)),
Page02(key:PageStorageKey(<key>))
];
  • 然后创建PageStorageBucket(),它存储您的小部件状态,并在应用程序生命周期内提供该状态,即使小部件已从树中丢弃也是如此

final _bucket = PageStorageBucket();

  • 然后 var currentIndex = 0;

  • 然后在您的主基页中,底部导航栏位于其正文中,而不是使用IndexedStack,使用body:PageStorage(bucket:_bucket,child:widgetsList[currentIndex])

在该主基页中创建bottomnavbar,然后在Navbar图标选项卡中通过setState((){})将当前状态管理为currentIndex

这应该会解决您的问题,尽管一年之后有些晚了


1

你找到解决方案了吗?
我遇到了和你一样的问题,我尝试了这个解决方法(目前还没有发现任何问题)
这个想法是创建一个新的小部件来控制调用 API 的小部件的可见状态,并在它变为可见时构建它。
在你的 IndexedStack 中,使用以下小部件包装你的 _buildNavigator

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();
  }
}

例如,在我的代码中,我有类似以下内容来为每个选项卡构建导航器,其中_selectedIndexBottomNavigationBar的选定位置,而tabPosition是该页面在BottomNavigationBar中的位置。
Widget _buildTabPage(int tabPosition) {
    final visibility = _selectedIndex == tabPosition;
    return BaseTabPage(
      isVisible: visibility,
      child: _buildNavigator(tabPosition),
    );
  }

通过这种方式,我将api调用的逻辑完全放在了子小部件中,底部导航栏对它们一无所知。 如果您发现问题,请告诉我,因为我在Flutter方面还比较新。


0

Flutter会在堆栈内构建所有小部件,因此如果您的页面在initState中进行API调用,则它将在构建时触发。

您可以做的是为API调用编写单独的函数。然后从导航器或状态管理中调用该函数。

我希望这能帮助您了解如何实现这一点。


1
谢谢,但我不确定我理解了。我有一个单独的函数用于API调用,但是什么时候触发调用呢?当构建堆栈的主屏幕构建堆栈中的所有小部件时。我需要在用户点击选项卡并且页面显示在屏幕上时才进行调用。 - tsahnar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接