如何在 ListView 中放置一个键来维护页面更改时的状态

PJQ*_*Jag 1 flutter flutter-layout

我正在尝试构建用户可以滚动浏览的一系列帖子(例如 Twitter 或 Instagram 类型的帖子)。当他们滚动时,他们可以单击其中一篇文章并导航到新页面。当他们从该页面导航回来时,我希望他们保持在 ListView 中之前所在位置的同一位置。

问题

我当前无法阻止流小部件重建并返回到滚动位置。我知道解决这个问题的方法之一是包含一把钥匙;但是,我尝试将密钥包含在 ListView.builder 中,但没有成功。

问题 我应该将密钥放在哪里?我使用的密钥类型正确吗?

class Stream extends StatefulWidget {
  Stream({Key key, this.user}) : super(key: key);

  final User user;

  @override
  _StreamState createState() => new _StreamState(
      user: user
  );
}

class _StreamState extends State<Stream> {


  _StreamState({this.user});

  final User user;

  Firestore _firestore = Firestore.instance;
  List<DocumentSnapshot> _posts = [];
  bool _loadingPosts = true;
  int _per_page = 30;
  DocumentSnapshot _lastPosts;
  ScrollController _scrollController = ScrollController();
  bool _gettingMorePosts = false;
  bool _morePostsAvailable = true;


  _getPosts() async {
    Query q = _firestore
        .collection('posts')
        .document(user.user_id)
        .collection('posts')
        .orderBy("timePosted", descending: true)
        .limit(_per_page);

    setState(() {
      _loadingPosts = true;
    });
    QuerySnapshot querySnapshot = await q.getDocuments();
    _posts = querySnapshot.documents;

    if (_posts.length == 0) {
      setState(() {
        _loadingPosts = false;
      });
    }

    else {
      _lastPosts = querySnapshot.documents[querySnapshot.documents.length - 1];

      setState(() {
        _loadingPosts = false;
      });
    }
  }

  _getMorePosts() async {

    if (_morePostsAvailable == false) {
      return;
    }

    if (_gettingMorePosts == true) {
      return;
    }

    if (_posts.length == 0) {
      return;
    }

    _gettingMorePosts = true;

    Query q = _firestore
        .collection('posts')
        .document(user.user_id)
        .collection('posts')
        .orderBy("timePosted", descending: true)
        .startAfter([_lastPosts.data['timePosted']]).limit(_per_page);

    QuerySnapshot querySnapshot = await q.getDocuments();

    if (querySnapshot.documents.length == 0) {
      _morePostsAvailable = false;
    }

    if(querySnapshot.documents.length > 0) {
      _lastPosts  = querySnapshot.documents[querySnapshot.documents.length - 1];

    }
    _posts.addAll(querySnapshot.documents);

    setState(() {});

    _gettingMorePosts = false;
  }

  @override
  void initState() {
    super.initState();
    _getPosts();
    _scrollController.addListener(() {
      double maxScroll = _scrollController.position.maxScrollExtent;
      double currentScroll = _scrollController.position.pixels;
      double delta = MediaQuery.of(context).size.height * 0.25;

      if (maxScroll - currentScroll < delta) {

        _getMorePosts();
      }
    });
  }
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[

        new Expanded(
          child: _loadingPosts == true
              ? Container(
            child: Center(
              child: Text(" "),
            ),
          )
              : Container(
            child: Center(
              child: _posts.length == 0
                  ? Center(
                child: Text("Follow friends", style: TextStyle(fontSize: 15),),
              )
                  : ListView.builder(
                key: widget.key,
                  controller: _scrollController,
                  itemCount: _posts.length,
                  itemBuilder: (BuildContext ctx, int index) {
                    return new Widget(
                    //paramenters to build the post widget here
                    );
                  }),
            ),
          ),
        ),
      ],
    );
  }
Run Code Online (Sandbox Code Playgroud)

需要注意的一件事是,由于我不想返回所有页面(由于 Firestore 费用调用了如此多的帖子),因此创建了构建逻辑,以便在滚动时加载更多帖子。我意识到这可能会影响它。

Cop*_*oad 5

简短回答:

您需要像这样提供密钥ListView.builder

ListView.builder(
  key: PageStorageKey("any_text_here"),
  // ...
)
Run Code Online (Sandbox Code Playgroud)

长答案:

您可以看到,当您从屏幕 2 返回到屏幕 1 时,项目 30 仍然位于顶部。

在此输入图像描述


抱歉,由于您正在使用的变量的可用性有限,因此很难重现您的代码。我创建了一个简单的示例来演示您正在寻找的内容。

完整代码:

void main() => runApp(MaterialApp(home: HomePage()));

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: ListView.builder(
        key: PageStorageKey("any_text_here"), // this is the key you need
        itemCount: 50,
        itemBuilder: (_, i) {
          return ListTile(
            title: Text("Item ${i}"),
            onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => DetailPage(index: i))),
          );
        },
      ),
    );
  }
}

class DetailPage extends StatefulWidget {
  final int index;

  const DetailPage({Key key, this.index}) : super(key: key);

  @override
  _DetailPageState createState() => _DetailPageState();
}

class _DetailPageState extends State<DetailPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Text(
          "You clicked ${widget.index}",
          style: Theme.of(context).textTheme.headline,
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)