You need to enable JavaScript to run this app. Flutter切换底部导航页如何保留状态及滚动位置避免重复请求数据
优惠活动
大模型
产品
解决方案
定价
更多

Flutter切换底部导航页如何保留状态及滚动位置避免重复请求数据

问题根因
  • 你用到的PageStorageKey仅用于存储页面的临时状态(比如滚动偏移量),并不能阻止页面State对象的销毁重建,所以切换Tab时子页面State重新初始化,initState自然会重复执行触发数据拉取
  • 子页面build方法中额外写了setState(() {}),会触发无意义的重绘,需要删除
解决方案1:用IndexedStack实现页面保活(最简单)

IndexedStack会同时初始化所有子页面,切换时仅控制显示隐藏,不会触发State重建,initState只会在首次加载时执行一次,适合Tab数量≤5的场景,仅需修改主页面代码即可:

class _AppHomePageState extends State<AppHomePage> {
  int _currentIndex = 0;

  onTap(index) => {
        setState(() => {_currentIndex = index})
      };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      /* appbar ... */
      // 替换原来的 body: _navs.elementAt(_currentIndex)
      body: IndexedStack(
        index: _currentIndex,
        children: _navs,
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          /* nav items */
        ],
        currentIndex: _currentIndex,
        onTap: onTap,
      ),
    );
  }
}
解决方案2:子页面混入AutomaticKeepAliveClientMixin(按需保活)

如果不想一开始就加载全部三个页面的请求,可以用这个方案,页面第一次激活时才加载,之后切换不会销毁,适合Tab数量较多的场景:
需要给每个子页面的State类做如下修改:

// 混入 AutomaticKeepAliveClientMixin
class _HotPostsRouteState extends State<HotPostsRoute> with AutomaticKeepAliveClientMixin {
  late PostInstance postInstance;
  List<Post> _posts = [];
  bool _loaded = false;

  // 重写该方法返回true,开启页面保活
  @override
  bool get wantKeepAlive => true;

  fetchPosts(String? after) async {
    var stream = postInstance.front.hot(limit: 10, after: after);
    await for (UserContent post in stream) {
      Submission submission = post as Submission;
      Post pPost = Post(submission);
      pPost.parse().then((value) => setState(() {
            _posts.add(pPost);
          }));
    }
    setState(() {
      _loaded = true;
    });
  }

  @override
  void initState() {
    super.initState();
    if (mounted) {
      // 这里不需要套setState,直接赋值即可
      postInstance =
          Provider.of<PostInstanceState>(context, listen: false)
              .getInstance;
      fetchPosts("");
    }
  }

  // _buildPosts、_buildPost原有逻辑不变

  @override
  Widget build(BuildContext context) {
    // 必须调用super.build,是混入的强制要求
    super.build(context);
    // 删掉原来多余的setState(() {})
    return Container(
      child: _loaded ? _buildPosts() : SpinKitDualRing(color: Colors.white),
    );
  }
}

内容的提问来源于stack exchange,提问作者SupremeDeity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 08:09:00