10

关于使用索引堆栈在选项卡之间导航以显示相关页面的问题。我这样做是为了保持页面的滚动/状态。这工作正常。我可以通过单击选项卡更改当前显示的页面 - 并且还可以在每个页面内导航(每个页面都用它自己的导航器包装)。这是渲染页面的代码。

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

}

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

不确定我是否在这里遗漏了一些东西,或者有更好的方法来实现底部导航栏。顺便说一句,我也在使用 Provider 进行状态管理。

4

5 回答 5

2

我遇到了同样的问题。我的解决方案是保存已加载选项卡的列表,然后使用它IndexedStack在方法内构建子列表Widget build(BuildContext context)。然后在 的onTap方法中BottomNavigationBar,我调用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 调用在导航到之前不会调用。

于 2021-09-20T13:21:46.030 回答
2

@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])

并在该主基本页面中创建底部导航栏,然后在导航栏图标选项卡上通过setState((){})当前状态管理索引页面的非属性状态到currentIndex

它应该可以解决您的问题,但一年后为时已晚

于 2021-08-02T10:26:37.060 回答
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();
  }
}

例如,在我的代码中,我有这样的东西来为每个选项卡构建导航器,其中_selectedIndex的选定位置是BottomNavigationBar并且tabPosition是该页面在BottomNavigationBar

Widget _buildTabPage(int tabPosition) {
    final visibility = _selectedIndex == tabPosition;
    return BaseTabPage(
      isVisible: visibility,
      child: _buildNavigator(tabPosition),
    );
  }

有了这个,我完全在子小部件中拥有了 api 调用的逻辑,底部导航对它们一无所知。如果您发现它有问题,请告诉我,因为我对颤振有点陌生。

于 2020-12-06T23:56:41.473 回答
0

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

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

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

于 2020-06-11T03:34:25.667 回答
0

使用 PageView 而不是 IndexedStack

 PageView(
            physics: const NeverScrollableScrollPhysics(),
            controller: _pageController,
            children: const [
              Page1(),
              Page2(),
              Page3(),
            ],
          ),

您可以使用 pageController 切换页面

_pageController.jumpToPage(0);
于 2022-02-06T07:49:07.110 回答