Flutter - ListView.builder: 初始滚动位置

17

我想设置ListView.builder的初始滚动位置,我希望列表从底部开始0.0

如果我在listView上设置reverse,当然我可以获得所需的初始滚动位置,但我需要的是将最后一个子组件放在底部,这是一个聊天应用程序。

这是List builder,MessageItem() 是聊天消息。

ListView.builder(
                    shrinkWrap: true,
                    controller: _scrollController,
                    itemCount: snapshot.data.documents.length,
                    padding: EdgeInsets.all(10.0),
                    itemBuilder: (BuildContext context, int index) =>
                        MessageItem(
                            index: index,
                            document: snapshot.data.documents[index],
                            myId: myId));

这是当有人 发送 消息时我拥有的动画效果


_scrollController.animateTo(
                _scrollController.position.maxScrollExtent,
                duration: const Duration(milliseconds: 500),
                curve: Curves.easeOut);

动画效果还可以。


我想要的是当用户进入聊天室时,列表滚动位置已经在底部。


使用0毫秒的持续时间会起作用吗? - E.Bradford
为什么不在widget的initState中设置滚动控制器的初始位置。这样,在初始widget构建时,它将滚动到底部位置。 - Salma
那只是当用户按下“发送消息”按钮时发生的动画,它可以正常工作。我尝试将动画代码放在initState()中,但会抛出错误:I/flutter (15097): ScrollController not attached to any scroll views. I/flutter (15097): 'package:flutter/src/widgets/scroll_controller.dart': I/flutter (15097): Failed assertion: line 110 pos 12: '_positions.isNotEmpty'我应该把它放在哪里,以便在开始时触发它? - Irving Armenta
在初始化scrollController时将其设为0.0 >>> _scrollController = new ScrollController(      初始滚动偏移量:0.0,      保持滚动偏移量:true, ); - Salma
只需将索引更改为snapshot.data.length-index-1。 - Osama Buzdar
显示剩余2条评论
6个回答

28
使用WidgetsBinding在构建完成后进行调用,以避免滚动未连接到任何列表的错误。

void _scrollToBottom() {
    if (_scrollController.hasClients) {
      _scrollController.animateTo(_scrollController.position.maxScrollExtent,
          duration: Duration(milliseconds: 300), curve: Curves.elasticOut);
    } else {
      Timer(Duration(milliseconds: 400), () => _scrollToBottom());
    }
 }

@override
Widget build(BuildContext context) {

  WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom());

  {...} //rest of code;
}

2
不确定为什么这个答案没有得到更多的赞。它解决了我一直以来在ListView中选择复选框并保持视图静态的问题,而不是总是跳回顶部。谢谢! - ConleeC

18

您可以设置 ScrollController 的一个属性:initialScrollOffset

但前提是您知道目标项目/索引的偏移位置。

ScrollController _controller = ScrollController(initialScrollOffset: itemHeight * index)

请注意,此示例假设您列表中的小部件大小是恒定的,但如果您的列表中没有恒定的小部件大小,则必须能够计算目标项的最终偏移位置 - 这是完全不同的问题。

或者,如果你想让它始终成为最后一个项目/索引,那么就容易得多:

ScrollController _controller = ScrollController(initialScrollOffset: _controller.position.maxScrollExtent)

15
构造控制器时不能使用“_controller”。 - maff
1
@maff,针对这个问题有一个特殊的方法——addpostframecallback,它会在小部件树构建完成后立即触发,因此您不必猜测initstate中的延迟。 - Zhangir Siranov
@maff 我同意你的看法,但你可以像这样检查一下: if(_controller.hasClients){ <然后滚动到所需位置>} - Mahesh Jamdade
initialOffset 不会动画到该位置 - Sisir
这实际上完美地运作了,你需要计算偏移量并将其作为参数传递。我测试过了,它完美地工作。 - carrasc0

8
我的解决方案是:
SingleChildScrollView(
                reverse: true,
                child: Container(),
              ),

ListView还拥有reverse属性。


3
你应该反转你的数组消息。 - Farhan Syedain

3

我了解你的痛点,因为这是一个必要的需求,因为单个聊天中可能会有成千上万条消息。

为了解决这个问题,您可以使用

SchedulerBinding.instance.addPostFrameCallback((_){});

这个回调函数会在小部件树构建完成后立即触发,因此您可以直接进行操作。
scrollController.jump(scrollController.position.maxScrollExtent);

然而,如果您有异步出现的消息,也就是在initstate之后通过一些从firestore文档中拉取它的函数出现的消息,则此方法将不起作用。 在这种情况下,您需要先加载它们,然后才能执行上述步骤。希望这可以帮助到您。


0

有一件简单的事情,假设您正在使用FutureBuilder从API获取列表,并返回ListView.builder

snapshot.data.length-index-1

从listview.builder中删除reverse属性。

-2

您还可以使用FixedExtentScrollController来处理大小相同的项目,并设置initialItem的索引:

controller: FixedExtentScrollController(initialItem: itemIndex);

文档:创建一个滚动控制器,用于具有相同大小的可滚动项。

1
目前,“FixedExtentScrollController”只能与“ListWheelScrollViews”一起使用。参考:https://github.com/flutter/flutter/issues/28012 - David Miguel

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