Flutter StatefulWidget. Usos
Revisión del 16:50 23 sep 2021 de Angelfg (discusión | contribuciones) (→Scroll infinito aplicado a ListView)
Scroll infinito aplicado a ListView
- En este punto vamos a aprender como podemos cargar de forma dinámica un número indeterminado de elementos que se van a visualizar en una lista.
- Para ello partimos del ejemplo realizado en la UD3 - Componentes => ListView.separated.
- En dicho ejemplo disponíamos de un StatelessWidget que visualizaba con un ListView.separated, un conjunto de datos en una lista.
- Lo que voy a hacer en este ejemplo es hacer crecer la lista de forma dinámica, añadiendo nuevos elementos a la misma, al llegar al final de los elementos cargados actualmente.
- Para ello voy a hacer alguna modificación al código.
- Convertiré el StatelessWidget en un StatefulWidget, ya que al añadir nuevos elementos a la lista, voy a necesitar redibujar el Widget.
- Creamos una nueva página en la carpeta pages/stateful de nombre listasinfinite_page.dart
- En todas las páginas haremos lo mismo:
- Importamos la librería de 'material' (recordar que existe un snippet que lo hace automático).
- Creamos una clase (ListasInfinitePage) que derive de StatefulWidget. Se creará (si lo hacéis con un Snippet) una clase asociada que deriva de State.
- Modificamos la clase State y en su métod build hacemos que devuelva un Scaffold con una Appbar (hacerlo con un Snippet)
- Por lo tanto partimos del siguiente código (el atributo _datos no tiene ningún dato, ya que los iremos cargando dinámicamente):
import 'package:flutter/material.dart'; class ListasInfinitePage extends StatefulWidget { const ListasInfinitePage({Key? key}) : super(key: key); @override _ListasInfinitePageState createState() => _ListasInfinitePageState(); } class _ListasInfinitePageState extends State<ListasInfinitePage> { final _datos = <String>[]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Ejemplo de listas con ListView.separated'), ), body: _obtenerLista() ); } Widget _obtenerLista(){ return ListView.separated( itemBuilder: (buildContext, index) => _elementoLista(index), separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,), itemCount: _datos.length); } Widget _elementoLista(pos) { var elemLista = _datos.elementAt(pos); return ListTile( title: Text(elemLista), subtitle: Text('Texto aclaratorio de $elemLista'), leading: Icon(Icons.accessibility), trailing: Icon(Icons.more_vert), onTap: () {}, ); } }
- Primero voy a crear un método que permita añadir un número de elementos determinado a la lista. Lógicamente, al añadirse, se tendrá que llamar al método setState para que redibuje el StatefulWidget:
/** * Añade el número de elementos indicados a la lista */ void _addElementosLista(int num_elementos){ int ultimoElemento = _datos.length; for(int cont=ultimoElemento; cont < ultimoElemento+ num_elementos; cont++){ _datos.add('Elem $cont'); } setState(() { }); }
- Al iniciarse el StatefulWidget, voy a sobreescribir el método initState() para que cargue datos la lista.
- Recordar que ya comenté en la sección de Eventos algo acerca de ciclo de vida de los Widget´s
class _ListasInfinitePageState extends State<ListasInfinitePage> { final _datos = <String>[]; final _numElementosListaAdd = 10; @override void initState() { // TODO: implement initState super.initState(); _addElementosLista(_numElementosListaAdd); } .............
- Ahora lo que tengo que hacer es 'controlar' cuando llego al final de la lista, para que en ese caso, cargar otro conjunto de elementos llamando al método.
- Para ello hacemos uso del ScrollController.
- Los pasos son parecidos a los que ya hicimos con con el TextField, empleando el TextEditingController.
- Creamos un objeto de la clase ScrollController.
- Asociamos el controlador a la lista, con la propiedad del constructor controller.
- En el método initState, 'nos subscribimos' a los cambios de scroll que haya en la lista.
class _ListasInfinitePageState extends State<ListasInfinitePage> { final _datos = <String>[]; final _numElementosListaAdd = 10; ScrollController _scrollController = ScrollController(); @override void initState() { // TODO: implement initState super.initState(); _addElementosLista(_numElementosListaAdd); _scrollController.addListener(() { print('SCROLL'); }); } ................. Widget _obtenerLista(){ return ListView.separated( controller: _scrollController, itemBuilder: (buildContext, index) => _elementoLista(index), separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,), itemCount: _datos.length); } ...................
- Ahora podemos acceder a los métodos de la clase ScrollPosition accediendo con scrollController.position.
- En esa clase están definidas muchas propiedades. Una de ellas es pixels que nos devuelve la posición actual en pixeles, y maxScrollExtent que nos devuelve la posición máxima en pixeles. Por tanto, cuando ambos valores sean iguales significa que hemos llegado al final del scroll de la lista y podremos añadir un nuevo grupo de elementos.
Código completo listasinfinite_page.dart:
import 'package:flutter/material.dart'; class ListasInfinitePage extends StatefulWidget { const ListasInfinitePage({Key? key}) : super(key: key); @override _ListasInfinitePageState createState() => _ListasInfinitePageState(); } class _ListasInfinitePageState extends State<ListasInfinitePage> { final _datos = <String>[]; final _numElementosListaAdd = 10; ScrollController _scrollController = ScrollController(); @override void initState() { // TODO: implement initState super.initState(); _addElementosLista(_numElementosListaAdd); _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { _addElementosLista(_numElementosListaAdd); } }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Ejemplo de listas con ListView.separated'), ), body: _obtenerLista() ); } Widget _obtenerLista(){ return ListView.separated( controller: _scrollController, itemBuilder: (buildContext, index) => _elementoLista(index), separatorBuilder: (buildContext, index) => Divider(thickness: 10,color: Colors.green,), itemCount: _datos.length); } Widget _elementoLista(pos) { var elemLista = _datos.elementAt(pos); return ListTile( title: Text(elemLista), subtitle: Text('Texto aclaratorio de $elemLista'), leading: Icon(Icons.accessibility), trailing: Icon(Icons.more_vert), onTap: () {}, ); } /** * Añade el número de elementos indicados a la lista */ void _addElementosLista(int num_elementos){ int ultimoElemento = _datos.length; for(int cont=ultimoElemento; cont < ultimoElemento+ num_elementos; cont++){ _datos.add('Elem $cont'); } setState(() { }); } }