Flutter,ListView,如何在ListView顶部添加多个项目并使其不滚动到顶部

9
当我在ListView的顶部添加一些项目时,它会滚动到索引0上的顶部项目。但我需要它保持在之前添加项目的相同位置。
例如,在消息列表上方进行聊天历史分页,如果我打开任何一个通信应用(如Telegram、WhatsApp等),打开一个具有很长历史记录的聊天并向下滚动下载历史记录。历史记录将添加到列表的顶部(每次从服务器下载约20条消息),但列表将保持在相同的位置(在滚动时)。
Flutter ListView在底部添加时表现得像这样,但是如果在顶部添加,则会跳转到第一个添加的项目。我希望它保持不变。
2个回答

11

截图:

在此输入图片描述


由于您没有分享任何代码,我只是创建了一个简单的演示,展示了如何实现类似于消息应用程序的效果。代码非常简单易懂,我几乎在每个地方都使用了注释。

代码(空安全):

class _MyPageState extends State<MyPage> {
  // Say you have total 100 messages, and you want to load 20 messages on each scroll.
  final int _totalMessages = 100, _loadInterval = 20;
  final double _loadingOffset = 20;
  late final List<String> _messages;
  bool _loading = false;
  final ScrollController _controller = ScrollController();

  @override
  void initState() {
    super.initState();

    // Initially, load only 20 messages.
    _messages = List.generate(20, (i) => 'Message   #${_totalMessages - i}');
    _controller.addListener(_scrollListener);
  }

  void _scrollListener() async {
    var max = _controller.position.maxScrollExtent;
    var offset = _controller.offset;

    // Reached at the top of the list, we should make _loading = true
    if (max - offset < _loadingOffset && !_loading) {
      _loading = true;

      // Load 20 more items (_loadInterval = 20) after a delay of 2 seconds
      await Future.delayed(Duration(seconds: 2));
      int lastItem = _totalMessages - _messages.length;
      for (int i = 1; i <= _loadInterval; i++) {
        int itemToAdd = lastItem - i;
        if (itemToAdd >= 0) _messages.add('Message   #$itemToAdd');
      }

      // Items are loaded successfully, make _loading = false
      setState(() {
        _loading = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Messages')),
      body: ListView.builder(
        controller: _controller,
        reverse: true,
        itemCount: _messages.length + 1, // +1 to show progress indicator.
        itemBuilder: (context, index) {
          // All messages fetched. 
          if (index == _totalMessages) return Container();

          // Reached top of the list, show a progress indicator. 
          if (index == _messages.length) return Align(child: CircularProgressIndicator());

          // Show messages. 
          return ListTile(title: Text('${_messages[index]}'));
        },
      ),
    );
  }
}

2
是的!就是这样:reverse: true; 谢谢! 我可以在12小时内授予奖励。稍等一下 ;) - Andrew
4
我有一个类似的问题,但仅仅将“reverse:true”设置并不能解决它,因为我的列表可以从顶部和底部增长。已经挣扎了5天,有什么建议吗? - Hasan Ahmed

2

@CopsOnRoad提出的解决方案仅适用于您的Feed只能向一个方向扩展的情况。

解决方案适用于将项目附加到列表的顶部和底部。解决方案的思路如下:您需要创建两个SliverLists并将它们放入CustomScrollView中。

CustomScrollView(
  center: centerKey,
  slivers: <Widget>[
    SliverList(
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
            return Container(
              // Here we render elements from the upper group
              child: top[index]
            )
        }
    ),
    SliverList(
      // Key parameter makes this list grow bottom
      key: centerKey,
      delegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
            return Container(
              // Here we render elements from the bottom group
              child: bottom[index]
            )
        }
    ),
)

第一个列表向上滚动,而第二个列表向下滚动。它们的偏移零点固定在同一点,并且永远不会移动。如果您需要添加一个项目,则将其推送到顶部列表,否则将其推送到底部列表。这样它们的偏移量不会改变,您的滚动视图也不会跳动。 您可以在以下 dartpad 示例中找到解决方案原型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接