Flutter StatefulWidget. Usos
Revisión del 16:50 23 sep 2021 de Angelfg (discusión | contribuciones)
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):
- <syntaxhighlight lang="java" enclose="div" highlight="">
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: () {},
);
}
}
<syntaxhighlight>
- 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:
- <syntaxhighlight lang="java" enclose="div" highlight="">
/**
* 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(() {
}); }
<syntaxhighlight>
- 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
- <syntaxhighlight lang="java" enclose="div" highlight="3,5-10">
class _ListasInfinitePageState extends State<ListasInfinitePage> {
final _datos = <String>[]; final _numElementosListaAdd = 10;
@override
void initState() {
// TODO: implement initState
super.initState();
_addElementosLista(_numElementosListaAdd);
}
.............
<syntaxhighlight>
- 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.
- <syntaxhighlight lang="java" enclose="div" highlight="5-10,13-15,23">
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);
}
...................
<syntaxhighlight>
- 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:
- <syntaxhighlight lang="java" enclose="div" highlight="">
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(() {
}); }
} <syntaxhighlight>