Tengo un scroll ListView
donde el número de elementos puede cambiar dinámicamente. Cada vez que se agrega un nuevo elemento al final de la lista, me gustaría desplazarlo ListView
hasta el final mediante programación. (por ejemplo, algo así como una lista de mensajes de chat donde se pueden agregar nuevos mensajes al final)
Supongo que necesitaría crear un ScrollController
en mi State
objeto y pasarlo manualmente al ListView
constructor, para luego poder llamar animateTo()
/ jumpTo()
método en el controlador. Sin embargo, dado que no puedo determinar fácilmente el desplazamiento máximo de desplazamiento, parece imposible simplemente realizar un scrollToEnd()
tipo de operación (mientras que puedo pasar fácilmente 0.0
para que se desplace a la posición inicial).
¿Existe una manera fácil de lograrlo?
Usar reverse: true
no es una solución perfecta para mí, porque me gustaría que los elementos estuvieran alineados en la parte superior cuando solo hay una pequeña cantidad de elementos que quepan en la ListView
ventana gráfica.
Utilice
ScrollController.jumpTo()
oScrollController.animateTo()
método para lograrlo.Ejemplo:
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')), ); }
Si desea un desplazamiento suave, en lugar de usar el
jumpTo
anterior, use_controller.animateTo( _controller.position.maxScrollExtent, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, );
fuente
listViewScrollController.animateTo(listViewScrollController.position.maxScrollExtent)
es la forma más sencilla.fuente
Para obtener los resultados perfectos, combiné las respuestas de Colin Jackson y CopsOnRoad de la siguiente manera:
_scrollController.animateTo( _scrollController.position.maxScrollExtent, curve: Curves.easeOut, duration: const Duration(milliseconds: 500), );
fuente
Tengo tantos problemas al intentar usar el controlador de desplazamiento para ir al final de la lista que utilizo otro enfoque.
En lugar de crear un evento para enviar la lista al final, cambio mi lógica para usar una lista invertida.
Entonces, cada vez que tengo un artículo nuevo, simplemente lo hago al insertar en la parte superior de la lista.
// 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)) ), ),
fuente
No coloque widgetBinding en initstate, en su lugar, debe colocarlo en el método que obtiene sus datos de la base de datos. por ejemplo, así. Si se pone en initstate,
scrollcontroller
no se adjuntará a ninguna vista de lista.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'); } }
fuente
podría usar esto donde
0.09*height
está la altura de una fila en la lista y_controller
se define así_controller = ScrollController();
(BuildContext context, int pos) { if(pos != 0) { _controller.animateTo(0.09 * height * (pos - 1), curve: Curves.easeInOut, duration: Duration(milliseconds: 1400)); } }
fuente