У меня есть прокручиваемый, ListView
где количество элементов может меняться динамически. Всякий раз, когда в конец списка добавляется новый элемент, я хотел бы программно прокрутить его ListView
до конца. (например, что-то вроде списка сообщений чата, в который можно добавлять новые сообщения в конце)
Я предполагаю, что мне нужно будет создать ScrollController
в моем State
объекте и передать его вручную ListView
конструктору, чтобы позже я мог вызвать animateTo()
/ jumpTo()
метод на контроллере. Однако, поскольку я не могу легко определить максимальное смещение прокрутки, кажется невозможным просто выполнить какой-либо scrollToEnd()
тип операции (тогда как я могу легко перейти, 0.0
чтобы заставить его прокрутить до начальной позиции).
Есть ли простой способ добиться этого?
Использование reverse: true
- не идеальное решение для меня, потому что я хотел бы, чтобы элементы были выровнены вверху, когда есть только небольшое количество элементов, которые помещаются в область ListView
просмотра.
Используйте
ScrollController.jumpTo()
илиScrollController.animateTo()
метод для достижения этого.Пример:
final _controller = ScrollController(); @override Widget build(BuildContext context) { // After 1 second, it takes you to the bottom of the ListView Timer( Duration(seconds: 1), () => _controller.jumpTo(_controller.position.maxScrollExtent), ); return ListView.builder( controller: _controller, itemCount: 50, itemBuilder: (_, __) => ListTile(title: Text('ListTile')), ); }
Если вам нужна плавная прокрутка, вместо использования
jumpTo
выше используйте_controller.animateTo( _controller.position.maxScrollExtent, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, );
источник
listViewScrollController.animateTo(listViewScrollController.position.maxScrollExtent)
это самый простой способ.источник
Чтобы получить идеальные результаты, я объединил ответы Колина Джексона и CopsOnRoad следующим образом:
_scrollController.animateTo( _scrollController.position.maxScrollExtent, curve: Curves.easeOut, duration: const Duration(milliseconds: 500), );
источник
У меня так много проблем с попыткой использовать контроллер прокрутки для перехода в конец списка, что я использую другой подход.
Вместо создания события для отправки списка в конец я меняю свою логику на использование перевернутого списка.
Итак, каждый раз, когда у меня появляется новый элемент, я просто вставляю его в начало списка.
// add new message at the begin of the list list.insert(0, message); // ... // pull items from the database list = await bean.getAllReversed(); // basically a method that applies a descendent order // I remove the scroll controller new Flexible( child: new ListView.builder( reverse: true, key: new Key(model.count().toString()), itemCount: model.count(), itemBuilder: (context, i) => ChatItem.displayMessage(model.getItem(i)) ), ),
источник
Не помещайте widgetBinding в initstate, вместо этого вам нужно поместить его в метод, который извлекает ваши данные из базы данных. например, вот так. Если поместить в initstate,
scrollcontroller
не будет прикрепляться ни к какому списку.Future<List<Message>> fetchMessage() async { var res = await Api().getData("message"); var body = json.decode(res.body); if (res.statusCode == 200) { List<Message> messages = []; var count=0; for (var u in body) { count++; Message message = Message.fromJson(u); messages.add(message); } WidgetsBinding.instance .addPostFrameCallback((_){ if (_scrollController.hasClients) { _scrollController.jumpTo(_scrollController.position.maxScrollExtent); } }); return messages; } else { throw Exception('Failed to load album'); } }
источник
вы можете использовать это, где
0.09*height
это высота строки в списке и_controller
определяется следующим образом_controller = ScrollController();
(BuildContext context, int pos) { if(pos != 0) { _controller.animateTo(0.09 * height * (pos - 1), curve: Curves.easeInOut, duration: Duration(milliseconds: 1400)); } }
источник