Ich habe eine Bildlaufleiste, ListView
in der sich die Anzahl der Elemente dynamisch ändern kann. Immer wenn ein neues Element am Ende der Liste hinzugefügt wird, möchte ich das programmgesteuert ListView
bis zum Ende scrollen . (zB so etwas wie eine Chat-Nachrichtenliste, in der am Ende neue Nachrichten hinzugefügt werden können)
Ich vermute, dass ich ScrollController
in meinem State
Objekt ein erstellen und es manuell an den ListView
Konstruktor übergeben müsste , damit ich später animateTo()
/ jumpTo()
method auf dem Controller aufrufen kann . Da ich jedoch den maximalen Bildlaufversatz nicht einfach bestimmen kann, scheint es unmöglich, einfach eine scrollToEnd()
Art von Operation auszuführen (während ich leicht passieren kann 0.0
, um ihn zur Ausgangsposition zu scrollen).
Gibt es einen einfachen Weg, dies zu erreichen?
Die Verwendung reverse: true
ist für mich keine perfekte Lösung, da ich möchte, dass die Elemente oben ausgerichtet werden, wenn nur wenige Elemente in das ListView
Ansichtsfenster passen .
Verwenden
ScrollController.jumpTo()
oderScrollController.animateTo()
Methode, um dies zu erreichen.Beispiel:
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')), ); }
Wenn Sie einen reibungslosen Bildlauf wünschen, verwenden Sie anstelle der
jumpTo
oben genannten Verwendung_controller.animateTo( _controller.position.maxScrollExtent, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, );
quelle
listViewScrollController.animateTo(listViewScrollController.position.maxScrollExtent)
ist der einfachste Weg.quelle
Um die perfekten Ergebnisse zu erzielen, habe ich die Antworten von Colin Jackson und CopsOnRoad wie folgt kombiniert:
_scrollController.animateTo( _scrollController.position.maxScrollExtent, curve: Curves.easeOut, duration: const Duration(milliseconds: 500), );
quelle
Ich habe so viele Probleme beim Versuch, mit dem Scroll-Controller zum Ende der Liste zu gelangen, dass ich einen anderen Ansatz verwende.
Anstatt ein Ereignis zu erstellen, um die Liste nach unten zu senden, ändere ich meine Logik, um eine umgekehrte Liste zu verwenden.
Jedes Mal, wenn ich einen neuen Artikel habe, habe ich ihn einfach beim Einfügen oben in der Liste erstellt.
// 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)) ), ),
quelle
Fügen Sie die WidgetBinding nicht in den Init-Status ein, sondern in die Methode, mit der Ihre Daten aus der Datenbank abgerufen werden. zum Beispiel so. Wenn in initstate gesetzt,
scrollcontroller
wird das nicht an eine Listenansicht angehängt.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'); } }
quelle
Sie können dies verwenden, wenn
0.09*height
die Höhe einer Zeile in der Liste ist und_controller
wie folgt definiert ist_controller = ScrollController();
(BuildContext context, int pos) { if(pos != 0) { _controller.animateTo(0.09 * height * (pos - 1), curve: Curves.easeInOut, duration: Duration(milliseconds: 1400)); } }
quelle