Программная прокрутка до конца ListView
У меня есть прокручиваемый ListView
, где количество элементов может изменяться динамически. Всякий раз, когда новый элемент добавляется в конец списка, я хотел бы программно прокрутить ListView
до конца. (например, что-то вроде списка сообщений чата, где новые сообщения могут быть добавлены в конце)
Я предполагаю, что мне нужно будет создать ScrollController
в моем State
объекте и передать его вручную конструктору ListView
, поэтому я могу позже вызвать метод animateTo()
/jumpTo()
на контроллере. Однако, поскольку я не могу легко определить максимальное смещение прокрутки, представляется невозможным просто выполнить операцию типа scrollToEnd()
(тогда как я могу легко пройти 0.0
, чтобы прокрутить ее до исходной позиции).
Есть ли простой способ достичь этого?
Использование reverse: true
для меня не идеальное решение, потому что я хотел бы, чтобы элементы выравнивались вверху, когда есть только небольшое количество элементов, которые вписываются в окно просмотра ListView
.
Ответы
Ответ 1
Если вы используете обертку ListView
с reverse: true
, прокручивая ее до 0.0, вы будете делать то, что хотите.
import 'dart:collection';
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Example',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Widget> _messages = <Widget>[new Text('hello'), new Text('world')];
ScrollController _scrollController = new ScrollController();
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Container(
decoration: new BoxDecoration(backgroundColor: Colors.blueGrey.shade100),
width: 100.0,
height: 100.0,
child: new Column(
children: [
new Flexible(
child: new ListView(
controller: _scrollController,
reverse: true,
shrinkWrap: true,
children: new UnmodifiableListView(_messages),
),
),
],
),
),
),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: () {
setState(() {
_messages.insert(0, new Text("message ${_messages.length}"));
});
_scrollController.animateTo(
0.0,
curve: Curves.easeOut,
duration: const Duration(milliseconds: 300),
);
}
),
);
}
}
Ответ 2
Для этого используйте ScrollController.jumpTo()
или ScrollController.animateTo()
.
Вот фрагмент кода (через 1 секунду ListView
прокрутится вниз)
class _HomePageState extends State<HomePage> {
ScrollController _controller = ScrollController();
@override
Widget build(BuildContext context) {
Timer(Duration(milliseconds: 1000), () => _controller.jumpTo(_controller.position.maxScrollExtent));
return ListView.builder(
controller: _controller,
itemCount: 50,
itemBuilder: (context, index) => ListTile(title: Text("ListTile"),));
}
}
Ответ 3
У меня так много проблем при попытке использовать контроллер прокрутки, чтобы перейти к нижней части списка, и я использую другой подход.
Вместо того, чтобы создавать событие для отправки списка в конец, я изменяю свою логику, чтобы использовать обратный список.
Таким образом, каждый раз, когда у меня появляется новый элемент, я просто делаю его на вкладке вверху списка.
// 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))
),
),
Ответ 4
listViewScrollController.animateTo(listViewScrollController.position.maxScrollExtent)
- самый простой способ.