You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter单页面中两个不同SliverList的滚动状态保持问题

如何在切换两个SliverList时各自保留滚动状态?

问题场景

开发Instagram克隆应用时,实现了切换查看个人帖子(Posts)和被标记帖子(Tags)的功能,对应两个独立的SliverList。虽然给两个列表添加了PageStorageKey,但切换时发现滚动状态会互相影响:比如Tags列表滚到底部后,切换到Posts列表也会处于底部,反之亦然。

问题原因

当前代码通过_isPosts ? const Posts() : const Tags()进行条件渲染,切换时未选中的SliverList会被从Widget树中完全移除,导致其滚动状态无法被PageStorage正确持久化和恢复。PageStorage需要组件持续存在于Widget树中(即使不可见)才能维护其状态。

解决方案

使用Visibility组件包裹两个SliverList,让它们始终保留在Widget树中,仅控制显示/隐藏;同时为每个列表分配独立的PageStorageBucket,确保滚动状态互相隔离。

修改后的代码

profile.dart

import 'package:bwitch/widgets/profile/personal_detail.dart';
import 'package:bwitch/widgets/profile/posts.dart';
import 'package:bwitch/widgets/profile/tags.dart';
import 'package:flutter/material.dart';

class Profile extends StatefulWidget {
  const Profile({super.key});

  @override
  State<Profile> createState() => _ProfileState();
}

class _ProfileState extends State<Profile> {
  bool _isPosts = true;
  
  void callback(bool isPosts) {
    setState(() {
      _isPosts = isPosts;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 为两个列表创建独立的状态存储桶
    final postsBucket = PageStorageBucket();
    final tagsBucket = PageStorageBucket();

    return SafeArea(
      child: CustomScrollView(
        slivers: [
          SliverList(
            delegate: SliverChildListDelegate(
              [
                const ListTile(
                  horizontalTitleGap: 0, 
                  minVerticalPadding: 0,
                  title: Text('Your Text Bar'),
                ),
              ],
            ),
          ),
          PersonalDetail(isPosts: _isPosts, callback: callback),
          // 控制Posts列表的显示,保留状态
          Visibility(
            visible: _isPosts,
            maintainState: true,
            child: PageStorage(
              bucket: postsBucket,
              child: const Posts(),
            ),
          ),
          // 控制Tags列表的显示,保留状态
          Visibility(
            visible: !_isPosts,
            maintainState: true,
            child: PageStorage(
              bucket: tagsBucket,
              child: const Tags(),
            ),
          ),
        ],
      ),
    );
  }
}

posts.dart(无需修改,保留原PageStorageKey)

import 'package:flutter/material.dart';

class Posts extends StatelessWidget {
  const Posts({super.key});

  @override
  Widget build(BuildContext context) {
    return SliverList(
      key: const PageStorageKey<String>('posts'),
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return ListTile(
            title: Text('Item $index'),
          );
        },
        childCount: 40,
      ),
    );
  }
}

tags.dart(无需修改,保留原PageStorageKey)

import 'package:flutter/material.dart';

class Tags extends StatelessWidget {
  const Tags({super.key});

  @override
  Widget build(BuildContext context) {
    return SliverList(
      key: const PageStorageKey<String>('tags'),
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return ListTile(
            title: Text('Item $index'),
          );
        },
        childCount: 50,
      ),
    );
  }
}

说明

  1. Visibility组件:通过visible控制显示,maintainState: true确保组件不可见时仍保留状态。
  2. 独立PageStorageBucket:每个列表使用单独的存储桶,彻底隔离两个列表的滚动状态,避免互相干扰。
  3. 保留PageStorageKey:配合独立存储桶,进一步确保PageStorage能准确识别和恢复对应列表的滚动位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:29:51