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

时间:2019-08-29 04:10:22

标签: 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
                    );
                  }),
            ),
          ),
        ),
      ],
    );
  }

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

2 个答案:

答案 0 :(得分:1)

简短答案:

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

ListView.builder(
  key: PageStorageKey("any_text_here"),
  // ...
)

长答案:

您可以看到,当您从屏幕2返回到屏幕1时,项目30仍停留在顶部。

enter image description here


对不起,由于所使用变量的可用性有限,因此很难再现代码。我创建了一个简单的示例来演示您要查找的内容。

完整代码:

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

答案 1 :(得分:0)

这种键有效:

key: PageStorageKey('Your Key Name'),