Mam przewijaną listę, w ListView
której liczba elementów może zmieniać się dynamicznie. Za każdym razem, gdy na końcu listy zostanie dodany nowy element, chciałbym programowo przewinąć ListView
do końca. (np. coś w rodzaju listy wiadomości na czacie, gdzie na końcu można dodawać nowe wiadomości)
Domyślam się, że musiałbym utworzyć ScrollController
w moim State
obiekcie i przekazać go ręcznie do ListView
konstruktora, aby móc później wywołać animateTo()
/ jumpTo()
metodę na kontrolerze. Ponieważ jednak nie mogę łatwo określić maksymalnego przesunięcia przewijania, wydaje się niemożliwe wykonanie po prostu scrollToEnd()
rodzaju operacji (podczas gdy mogę łatwo przejść, 0.0
aby przewinąć do pozycji początkowej).
Czy jest na to łatwy sposób?
Używanie reverse: true
nie jest dla mnie idealnym rozwiązaniem, ponieważ chciałbym, aby elementy były wyrównane u góry, gdy jest tylko niewielka liczba elementów, które mieszczą się w ListView
widoku.
Użyj
ScrollController.jumpTo()
lubScrollController.animateTo()
metody, aby to osiągnąć.Przykład:
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')), ); }
Jeśli chcesz płynne przewijanie, zamiast używać
jumpTo
powyższego, użyj_controller.animateTo( _controller.position.maxScrollExtent, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, );
źródło
listViewScrollController.animateTo(listViewScrollController.position.maxScrollExtent)
to najprostszy sposób.źródło
aby uzyskać doskonałe rezultaty, połączyłem odpowiedzi Colina Jacksona i CopsOnRoad w następujący sposób:
_scrollController.animateTo( _scrollController.position.maxScrollExtent, curve: Curves.easeOut, duration: const Duration(milliseconds: 500), );
źródło
Mam tak duży problem, że próbuję użyć kontrolera przewijania, aby przejść na koniec listy, że używam innego podejścia.
Zamiast tworzyć zdarzenie w celu wysłania listy na sam dół, zmieniam logikę na odwróconą listę.
Tak więc za każdym razem, gdy mam nową pozycję, po prostu wstawiłem ją na górze listy.
// 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)) ), ),
źródło
Nie umieszczaj widgetBinding w initstate, zamiast tego musisz umieścić go w metodzie, która pobiera dane z bazy danych. na przykład w ten sposób. Jeśli ustawisz initstate,
scrollcontroller
nie zostanie dołączony do żadnego widoku listy.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'); } }
źródło
Natknąłem się na ten problem, gdy korzystałem z
StreamBuilder
widżetu do pobierania danych z mojej bazy danych. PołożyłemWidgetsBinding.instance.addPostFrameCallback
na górzebuild
metodę widżetu i nie przewijałbym go do końca. Naprawiłem to, robiąc to:... StreamBuilder( stream: ..., builder: (BuildContext context, AsyncSnapshot snapshot) { // Like this: WidgetsBinding.instance.addPostFrameCallback((_) { if (_controller.hasClients) { _controller.jumpTo(_controller.position.maxScrollExtent); } else { setState(() => null); } }); return PutYourListViewHere }), ...
Też próbowałem,
_controller.animateTo
ale nie działało.źródło
możesz użyć tego, gdzie
0.09*height
jest wysokością wiersza na liście i_controller
jest zdefiniowany w ten sposób_controller = ScrollController();
(BuildContext context, int pos) { if(pos != 0) { _controller.animateTo(0.09 * height * (pos - 1), curve: Curves.easeInOut, duration: Duration(milliseconds: 1400)); } }
źródło